1. 进程概念

1.1 进程的本质

核心定义

  • 用户视角:程序的动态执行实例(如同时运行多个Chrome窗口即多个进程)。

  • 内核视角:资源分配的最小实体单位,独享CPU时间片、内存空间和文件资源。

  • 现代定义
    进程 = 内核数据结构(task_struct) + 程序代码和数据段

进程不仅是“程序的执行实例”或“正在执行的程序”,从内核角度看,它是分配系统资源(CPU时间、内存)的实体。更精确地说,进程 = 内核数据结构(task_struct) + 程序代码和数据。当程序被加载到内存时,操作系统为其创建一个task_struct实例,该结构体封装了进程的所有属性和状态信息。生动示例:想象一个C程序(如hello.c)被编译执行时,操作系统会动态分配内存和CPU时间片,并将程序指令映射到虚拟地址空间,同时初始化task_struct来跟踪其状态,就像给每个运行的程序贴上一个“身份证”和“健康记录卡”。

示例:启动两个vim编辑不同文件时,系统创建两个独立进程,各自拥有独立的代码执行流和内存空间,互不干扰。

 我们先创建一个myprocess.c文件,然后死循环,每隔一秒打印

这里直接运行这个可执行程序,当我们把这个程序运行起来,其实就是一个进程


1.2 进程控制块 (PCB)

task_struct:Linux的PCB实现

  • 存储位置:常驻内存(RAM),由内核动态管理。

  • 关键字段分类(扩展版):

    字段类别具体内容
    标识符PID(进程ID)、PPID(父进程ID)、PGID(进程组ID)
    状态运行态(TASK_RUNNING)、睡眠态(TASK_INTERRUPTIBLE)、僵尸态(EXIT_ZOMBIE)等
    内存指针代码段(mm_struct->code_start)、数据段(mm_struct->data_start)指针
    上下文数据保存暂停时的CPU寄存器值(eip, eax等),用于恢复执行
    文件描述符表记录打开的文件(files_struct结构体)
    资源限制最大文件打开数、CPU时间配额(struct rlimit

进程组织方式

// 内核源码示例(简化版)
struct task_struct {volatile long state;          // 进程状态struct mm_struct *mm;         // 内存管理结构体pid_t pid;                    // 进程IDstruct files_struct *files;   // 打开文件表struct list_head tasks;       // 双向链表指针// ... 其他字段
};
  • 全局进程链表:内核通过struct list_head tasks所有进程组成双向链表,头节点为init_task(PID=1的init进程)。


1.3 查看进程的实战方法

1. /proc文件系统

  • 动态虚拟文件系统:以目录形式暴露内核进程信息。

    # 查看PID为1的进程信息
    $ ls /proc/1
    exe -> /usr/lib/systemd/systemd  # 可执行文件链接
    cwd                              # 当前工作目录
    fd/                              # 打开的文件描述符
    status                           # 进程状态摘要

2. 命令行工具

# 显示进程树(含父子关系)
$ pstree -p
systemd(1)─┬─sshd(1234)───bash(5678)───vim(9012)└─crond(2345)# 动态监控进程
$ top -p 9012  # 监控PID 9012(vim进程)的资源占用

3. ps指令

一、ps 命令核心功能

作用:捕捉系统当前进程快照(非实时),用于:

  • 查看进程状态(运行/睡眠/僵尸)

  • 分析资源占用(CPU/内存)

  • 定位问题进程

  • 查看进程间关系


二、参数详解与使用场景
1. 基础查看
命令作用示例输出片段
ps aux查看所有用户的所有进程USER PID %CPU %MEM VSZ RSS TTY...
ps ajx显示进程树关系(含PPID/PGID)PPID PID PGID SID TTY COMMAND...

输出字段解析

  • VSZ:虚拟内存大小 (KB)

  • RSS:实际物理内存 (KB)

  • TTY:关联终端(? 表示无终端)

  • STAT:进程状态(后文详解)


2. 进程状态(STAT)解码
状态码含义说明
R运行中 (Running)正在执行或就绪状态
S可中断睡眠 (Sleeping)等待事件完成(如 I/O 操作)
D不可中断睡眠 (Disk sleep)通常发生在磁盘 I/O,不可被信号中断
T暂停状态 (Stopped)被信号暂停(如 SIGSTOP
Z僵尸进程 (Zombie)进程已终止,但父进程未回收
<高优先级进程优先级高于默认值
N低优先级进程优先级低于默认值
s会话领导者 (Session leader)控制终端的进程
+前台进程组 (Foreground group)与终端交互的进程

示例Ss+ = 会话领导者 + 可中断睡眠 + 前台进程


3. 高级过滤与显示
参数组合作用示例应用场景
ps -e | grep ssh查找特定进程检查 SSH 服务是否运行
ps -fC nginx显示进程完整命令行 (-f) + 按名称过滤查看 Nginx 配置参数
ps -p 1234 -o pid,ppid,cmd自定义输出字段查看指定进程的父子关系
ps --forest树形显示进程层级分析进程派生关系
ps -eo pid,ppid,cmd --sort=-%mem按内存排序找出内存消耗最大的进程

1.4 获取进程标识符(代码解析)

我们可以通过man手册来查看一下getpid

基本定义

  • 进程ID(PID)

    • 定义:进程ID(Process ID)是操作系统分配给每个进程的唯一标识符。它用于区分系统中的不同进程。
    • 作用
      • 资源分配:PID是分配系统资源(如内存、CPU时间等)的重要依据。

      • 进程控制:操作系统可以使用PID来对进程进行操作,例如启动、停止、暂停或终止进程。

      • 唯一标识:每个进程在系统中都有一个唯一的PID,操作系统通过PID来管理进程。

    • 示例cat进程的PID为3538。

  • 父进程ID(PPID)

    • 定义:父进程ID(Parent Process ID)是指创建当前进程的进程的ID。每个进程都有一个父进程(除了初始进程)。

    • 作用

      • 进程关系:PPID用于表示进程之间的父子关系。通过PPID,可以追踪进程的创建过程。

      • 资源继承:子进程通常会继承父进程的资源(如文件描述符、环境变量等)。

      • 进程管理:操作系统可以通过PPID来管理进程树结构,例如在父进程终止时,清理其子进程。

    • 示例bash进程(PPID=2686)创建了cat进程(PID=3538),因此cat的PPID为2686。


核心特性

(1) 父子进程关系
  • 创建机制:父进程通过系统调用(如fork())创建子进程。子进程继承父进程环境,但操作系统为其分配新PID,同时将其PPID设为父进程的PID。
    代码示例(C++):

    pid_t t = fork();  // 创建子进程
    if (t == 0) {// 子进程:getpid()返回自身PID,getppid()返回父进程PIDcout << "子进程 PID:" << getpid() << " PPID:" << getppid() << endl; 
    } else {// 父进程:getpid()返回自身PIDcout << "父进程 PID:" << getpid() << endl; 
    }
    
  • 关系规则

    • 一个父进程可创建多个子进程,所有子进程共享同一PPID(即父进程PID)。
    • 子进程退出后,父进程需回收其资源,否则可能产生僵尸进程。(至于什么是僵尸进程后面章节会讲)
(2) 特殊进程
  • init进程(PID=1)
    系统启动的第一个进程(Linux中通常为systemd),是所有用户进程的最终祖先,其PPID为0或-1(表示无父进程)。

    示例:进程表中PID=1的进程PPID为-1。
  • 内核进程(PID=0)
    管理内存交换等核心任务,无PPID。

注意:

数据类型本质
pid_t 是一个带符号整数类型signed int),在 Linux 系统中被明确定义为 int 的别名。

  • 设计目的
    提供进程 ID 的抽象表示,屏蔽不同操作系统(如 Linux/Windows)或硬件架构(32/64 位)的底层差异,增强代码可移植性。例如:

    • Linux 使用 pid_t 表示 PID,而 Windows 使用 HANDLE
    • 直接使用 int 可能导致平台兼容性问题。

进程id示例:

我们修改一下之前的代码

运行结果:

ltx@hcss-ecs-d90d:~/lesson2$ make
gcc -o myprocess myprocess.c
ltx@hcss-ecs-d90d:~/lesson2$ ./myprocess
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288
我是一个进程!我的pid:453288...

运行后我们可以发现可执行程序myprocess的pid是453288,我们还可以来验证一下

使用ps指令来查看:

ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep
  • ps ajx

    • ps 是进程查看命令,用于显示当前系统中的进程快照(非动态更新)。
    • 选项 ajx 指定输出格式:a 显示所有用户进程,j 以作业控制格式显示,x 包括无终端的进程(如后台进程)。
    • 示例输出:包含 PID(进程ID)、PPID(父进程ID)、COMMAND(命令名称)等列。
  • | head -1

    • | 是管道符,将前一个命令的输出作为后一个命令的输入。
    • head -1 只保留输出的第一行,即进程信息的表头(如 PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND)。
    • 作用:确保后续进程信息有清晰的列名,便于阅读。
  • &&

    • 逻辑与运算符,表示只有前一个命令(ps ajx | head -1)成功执行(返回状态码 0)时,才执行后续命令。
    • 这里用于分隔两个独立操作:先显示表头,再显示进程详情。
  • ps ajx | grep myprocess

    • 再次运行 ps ajx 获取所有进程信息。
    • grep myprocess 过滤出包含关键字 "myprocess" 的行(通常是目标进程的命令名称)。
    • 问题grep 命令自身在运行时也会被列为进程,且其命令中包含 "myprocess",因此会被错误地包含在结果中(例如输出 grep --color=auto myprocess)。
  • | grep -v grep

    • grep -v 表示反向过滤,排除包含指定关键字 "grep" 的行。
    • 作用:移除 grep myprocess 自身产生的进程条目,避免干扰。例如,如果未加此部分,输出会多出一行 grep --color=auto myprocess

如果我们想杀掉这个进程可以使用快捷键CTRL + c(左边的Shell),或者在右边的Shell中使用

kill -9 [想要杀掉的pid]

至于kill这个指令的是如何杀掉这个进程的,我们在后面的信号章节也会讲到

父进程id示例:

再次修改一下代码

运行

ltx@hcss-ecs-d90d:~/lesson2$ make
gcc -o myprocess myprocess.c
ltx@hcss-ecs-d90d:~/lesson2$ ./myprocess
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
我是一个进程!我的pid:451964,我的父进程id:450425
...

运行结果可看到进程pid为451964,父进程ppid为450425

同样也可以在验证一下

这里我们可以看到怎么这次的父进程ppid和上次的父进程ppid一样,都是450425.

我们可以多次运行看一下

父进程id一直不变,这是什么情况呢?

我们也可以用ps来查一下

我们可以看到原来我们的父进程是bash

因为 你每次都是在同一个交互式 Bash 会话里手动启动程序,所以:

  • Bash 是 Linux 默认的命令行解释器(Shell),用户通过终端输入命令时,Bash 会创建子进程来执行这些命令

  • 父进程就是当前这个 Bash 进程

  • Bash 进程的 PID 在你退出或关闭终端之前不会改变

  • 于是你看到的 PPID 始终就是 那个 Bash 的 PID-bashbash)。

换句话说,只要你不关掉这个终端(或显式 exit 掉这个 Bash),它就是所有你手动启动命令的父进程,PPID 自然看起来“不变”。


1.5 fork() 机制深度解析

同样我们可以使用man手册来查一下

关键特性

  1. 一次调用,两次返回

    • 父进程返回子进程PID(>0)

    • 子进程返回0

    • 失败返回-1(如进程数超限)

  2. 写时拷贝(Copy-On-Write)

    • 初始状态:父子进程共享同一物理内存。

    • 修改触发:当任一进程尝试写入数据时,内核为该进程复制新内存页。

我们先来修改一下代码,浅尝一下fork

运行结果:

fork之后的代码被执行了两次,why?

fork: 如何呢?又能怎?

核心原理:一次调用,两次返回

当程序执行到 fork() 系统调用时,操作系统会创建一个与原进程(父进程)几乎完全相同的副本(子进程)。这个副本包括:

  1. 代码段的复制(共享只读)

  2. 数据段和堆栈的复制(写时拷贝)

  3. 程序计数器(PC)位置 - 指向 fork() 之后的下一条指令

#include <stdio.h>
#include <unistd.h>int main() {printf("父进程开始运行,pid:%d\n", getpid());  // 步骤1:父进程执行fork();  // 步骤2:分水岭!// ↓ 步骤3:此处开始有两个独立的执行流printf("进程开始运行,pid:%d\n", getpid());  // 步骤4:父子进程各执行一次return 0;
}

执行流程:

关键机制解析:

  1. 写时拷贝 (Copy-On-Write)

    • 子进程创建时不立即复制物理内存

    • 父子共享相同物理内存页(标记为只读)

    • 当任一进程尝试写入内存时,触发缺页异常

    • 内核再为该进程复制新的内存页

  2. 程序计数器继承

    • 子进程创建时复制父进程的CPU寄存器状态

    • 包括指向 fork() 后下一条指令的EIP寄存器

    • 因此子进程从 fork() 返回处开始执行

  3. 返回值差异

    返回值含义执行进程
    >0子进程PID父进程
    0成功创建标志子进程
    -1创建失败父进程

要是我们想让父子进程执行不同的代码逻辑,应该怎么做呢

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{printf("父进程开始运行,pid:%d\n", getpid());pid_t id = fork();if(id < 0){perror("fork");return 1;}else if(id == 0){// childwhile(1){printf("我是一个子进程!我的pid:%d,我的父进程id:%d\n", getpid(), getppid());sleep(1);             }}else{// fatherwhile(1){printf("我是一个父进程!我的pid:%d,我的父进程id:%d\n", getpid(), getppid());sleep(1);             }}//printf("进程开始运行,pid:%d\n", getpid());//while(1)//{//    printf("我是一个进程!我的pid:%d\n", getpid());//    printf("我是一个进程!我的pid:%d,我的父进程id:%d\n", getpid(), getppid());//    sleep(1);//}return 0;
}

修改完代码我们来运行一下

ltx@hcss-ecs-d90d:~/lesson2$ make
gcc -o myprocess myprocess.c
ltx@hcss-ecs-d90d:~/lesson2$ ./myprocess
父进程开始运行,pid:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425
我是一个子进程!我的pid:457988,我的父进程id:457987
我是一个父进程!我的pid:457987,我的父进程id:450425...

运行可以看到父进程myprocess可执行程序id为457987,父进程的父进程bash的id为450425,父进程的子进程id为457988。

1. 为什么fork要给父子进程返回不同的值?

这是为了在代码中区分父子进程的执行路径,让程序员能编写不同的逻辑分支:

设计考量

  • 父进程需要知道子进程ID:用于后续管理(等待、发送信号等)

  • 子进程需要明确自身身份:避免递归创建进程

  • 错误处理统一:只有父进程能处理fork失败

类比:就像双胞胎出生时获得不同的名字,虽然基因相同但身份不同

2. 为什么fork会"返回两次"?

实际上不是函数返回两次,而是创建了两个独立的执行流

关键机制

  1. 调用fork时,内核复制父进程的:

    • 寄存器状态(包括程序计数器PC)

    • 页表(通过写时拷贝)

    • 文件描述符表

  2. 返回用户空间前,内核修改:

    • 父进程的EAX寄存器 = 子进程PID

    • 子进程的EAX寄存器 = 0

  3. 两个进程从相同的代码位置继续执行:

    • 父进程:从fork()调用后继续

    • 子进程:"诞生"后的第一条指令就是fork()之后的代码

3. 为什么同一个变量既等于0又大于0?

核心原理:两个进程拥有独立的地址空间

int ret = fork();  // 这行代码在两个进程中都有!// 内存布局示意:
// 父进程内存空间:ret_addr = 0x1000, 值=457988(子进程PID)
// 子进程内存空间:ret_addr = 0x1000, 值=0

执行过程

时间线        父进程(PID=457987)                子进程(PID=457988)
---------------------------------------------------------------T1         执行 fork() 系统调用T2             |                            内核创建子进程T3             |                            设置父进程返回值=457988T4             |                            设置子进程返回值=0T5         从fork返回 ↓T6         ret = 457988 (>0)                从"诞生点"开始执行 ↓T7         执行 else 分支                    ret = 0 (==0)T8         printf("Parent...")              执行 else if 分支T9                                          printf("Child...")

注意:ret 不是同一个物理内存位置!父子进程有各自独立的变量副本。

技术本质:写时拷贝(COW)的作用

int global = 100;  // 全局变量pid_t pid = fork();if (pid == 0) {global = 200;  // 子进程修改
} else {sleep(1);printf("%d", global); // 父进程仍输出100
}

内存变化

  1. fork时:父子共享同一物理内存页(标记为只读)

  2. 子进程写global:触发页错误

  3. 内核复制该内存页给子进程

  4. 子进程在新页上修改值

  5. 父进程仍访问原内存页

总结要点

  1. 进程是资源容器:通过task_struct实现资源隔离与调度。

  2. fork()是进程分身术:通过写时拷贝高效复制,双返回值区分父子。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88526.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88526.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88526.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从LLM到VLM:视觉语言模型的核心技术与Python实现

本教程的完整代码可以在GitHub上找到&#xff0c;如果你有任何问题或建议&#xff0c;欢迎交流讨论。 引言&#xff1a;为什么需要VLM&#xff1f; 当我们与ChatGPT对话时&#xff0c;它能够理解复杂的文字描述&#xff0c;生成流畅的回答。但如果我们给它一张图片&#xff0c…

老系统改造增加初始化,自动化数据源配置(tomcat+jsp+springmvc)

老系统改造增加初始化&#xff0c;自动化数据源配置一、前言二、改造描述1、环境说明2、实现步骤简要思考三、开始改造1、准备sql初始化文件2、启动时自动读取jdbc文件&#xff0c;创建数据源&#xff0c;如未配置&#xff0c;需要一个默认的临时数据源2.1去掉sping mvc原本配置…

卫星通信终端天线的5种对星模式之二:DVB跟踪

要实现稳定可靠的卫星通信&#xff0c;地面终端天线必须精准地对准远方的卫星。对星的过程是一个不断搜索、不断逼近的过程&#xff0c;其目标是让天线波束中心精确指向卫星&#xff0c;从而获得最大信号接收与发射效率。 卫星通信终端天线的对星技术是保障卫星通信链路稳定的…

重构下一代智能电池“神经中枢”:GCKontrol定义高性能BMS系统级设计标杆

概述BMS&#xff08;电池管理系统&#xff09;作为新能源汽车动力电池与整车的核心纽带&#xff0c;通过实时监控电压、电流、温度及SOC等参数&#xff0c;控制电池充放电过程&#xff0c;保障电池安全性与使用寿命。随着电动汽车智能化发展&#xff0c;对BMS的响应速度、精度和…

面试150 对称二叉树

思路 联想递归三部曲&#xff1a;传入参数、遍历方式、返回什么。本题联想到先序遍历的方式,需要遍历整颗二叉树,最后返回的是一个布尔值。然后我们需要传入的是左子树和左子树的节点,然后分别进行比较。 # Definition for a binary tree node. # class TreeNode: # def __…

多线程的区别和联系

进程和线程的区别和联系1.一个进程可以包含多个线程&#xff0c;不能够没有线程2.进程是系统资源分配的基本单位&#xff0c;线程是系统调度执行的基本单位3.同一个进程里的线程之间&#xff0c;共用同一份系统资源4.线程是当下实现并发编程的主流方式&#xff0c;通过多线程&a…

两个文件夹自动同步

两个文件夹自动同步&#xff0c;非常简单&#xff0c;利用一些工具就可以轻松做到&#xff0c;设置完源和目标文件夹&#xff0c;点击启动就马上可以两个文件夹自动同步&#xff0c;对于一些有文件同步、文件灾备需求的老登&#xff0c;用起来会非常顺手&#xff0c;比如PanguF…

虚拟商品交易维权指南:数字经济时代的消费者权益保护

首席数据官高鹏律师数字经济团队创作AI辅助在元宇宙、NFT、虚拟情绪产品等新兴领域蓬勃发展的今天&#xff0c;虚拟商品交易已成为数字经济的重要组成部分。从游戏皮肤、在线课程到数字藏品&#xff0c;消费者在享受虚拟商品便捷性的同时&#xff0c;也面临着诸多法律风险。作为…

mysql 一条语句的执行流程

文章目录一条查询语句的执行流程连接器管理连接权限校验分析器优化器采样统计优化器选错索引改正执行器查询缓存存储引擎一条update语句的执行流程redo logredo log buffer结构redo log日志类型写入时机配置innodb_flush_log_at_trx_commitbinlogredo log和binlog 对比配置两阶…

【视频观看系统】- 需求分析

&#x1f3af; 一、项目目标 构建一个功能完备的视频观看网站&#xff0c;用户可以上传、浏览、观看视频&#xff0c;并在观看过程中实时发送/接收弹幕。系统具备良好的性能、可扩展性与用户体验&#xff0c;未来可逐步扩展为多媒体平台。&#x1f464; 二、用户角色分析用户类…

模型驱动的架构MDA的案例

在一个企业资源规划&#xff08;ERP&#xff09;系统开发项目中&#xff0c;目标是为一家中型制造企业打造一套高效且可扩展的管理系统&#xff0c;涵盖订单处理、库存管理等多个业务模块。项目团队采用了 MDA 的设计思想进行开发。​首先是业务需求分析与计算独立模型&#xf…

第一次搭建数据库

本文详细介绍第一次搭建数据库安装和配置过程, 包括卸载旧版本、下载安装、配置服务、环境变量等等 第一步下载mysql 在下载之前需要检查电脑上有没有安装mysql, 如果有再安装, 80%就会有问题 检查方法: 电脑-右键找到管理-服务-在服务中找有没有mysql服务若有请先 1.停止服务 …

洛谷题解 | UVA1485 Permutation Counting

目录题目描述题目思路AC 代码题目描述 https://onlinejudge.org/external/14/p1485.pdf 题目思路 dp。 定义 dpi,jdp_{i,j}dpi,j​ 为前 iii 个数的排列中恰好有 jjj 个小于号的排列总数。 考虑将数字 iii 插入到前 i−1i-1i−1 个数的排列中不同的位置&#xff1a; 如果…

飞算科技:以原创技术赋能电商企业数字化转型

在电商行业从流量竞争迈向精细化运营的当下&#xff0c;技术能力已成为决定企业生存与发展的核心要素。然而&#xff0c;高并发场景下的系统稳定性、个性化推荐算法的迭代效率、营销活动的快速响应等挑战&#xff0c;让许多电商企业陷入“技术投入大、见效慢”的困境。作为国家…

人工智能自动化编程:传统软件开发vs AI驱动开发对比分析

人工智能自动化编程&#xff1a;传统软件开发vs AI驱动开发对比分析 &#x1f31f; 嗨&#xff0c;我是IRpickstars&#xff01; &#x1f30c; 总有一行代码&#xff0c;能点亮万千星辰。 &#x1f50d; 在技术的宇宙中&#xff0c;我愿做永不停歇的探索者。 ✨ 用代码丈量…

用java实现一个自定义基于logback的日志工具类

✅ 动态创建: 无需配置文件&#xff0c;通过代码动态创建logback日志对象 ✅ Class对象支持: 使用LogUtil.getLogger(MyClass.class)的方式获取日志 ✅ 日期格式文件: 自动生成info.%d{yyyy-MM-dd}.log格式的日志文件 ✅ 文件数量管理: 只保留最近3个文件&#xff0c;自动删除历…

面试现场:奇哥扮猪吃老虎,RocketMQ高级原理吊打面试官

“你了解RocketMQ的高级原理和源码吗&#xff1f;” 面试官推了推眼镜&#xff0c;嘴角带笑&#xff0c;眼神里透着一丝轻蔑。 奇哥笑而不语&#xff0c;开始表演。面试场景描写 公司位于高楼林立的CBD&#xff0c;电梯直达28楼。面试室宽敞明亮&#xff0c;空气中混着咖啡香与…

Django Nginx+uWSGI 安装配置指南

Django Nginx+uWSGI 安装配置指南 引言 Django 是一个高级的 Python Web 框架,用于快速开发和部署 Web 应用程序。Nginx 是一个高性能的 HTTP 和反向代理服务器,而 uWSGI 是一个 WSGI 服务器,用于处理 Python Web 应用。本文将详细介绍如何在您的服务器上安装和配置 Djang…

外设数据到昇腾310推理卡 之二dma_alloc_attrs

目录 内核源码及路径 CONFIG_DMA_DECLARE_COHERENT DTS示例配置 dma_direct_alloc 特殊属性快速路径 (DMA_ATTR_NO_KERNEL_MAPPING) 主体流程 1. 内存分配核心 2. 地址转换 3. 缓存一致性处理 映射 attrs不同属性的cache处理 cache的标示&#xff08;ARM64&#xff0…

Java 大视界:基于 Java 的大数据可视化在智慧城市能源消耗动态监测与优化决策中的应用(2025 实战全景)

​​摘要​​在“双碳”战略深化落地的 2025 年&#xff0c;城市能源管理面临 ​​实时性​​、​​复杂性​​、​​可决策性​​ 三重挑战。本文提出基于 Java 技术栈的智慧能源管理平台&#xff0c;融合 ​​Flink 流处理引擎​​、​​Elasticsearch 实时检索​​、​​ECh…