一、程序和进程的本质区别

程序:磁盘上的普通文件,存放代码与数据,是静态的,躺在硬盘里不会运行。 进程:程序加载到内存之后,内核 PCB (task_struct) + 内存中的代码和数据,是动态运行的实体。

双击 exe、Linux 运行可执行程序,本质就是把磁盘程序加载进内存,操作系统创建 PCB,生成进程。 程序可以被多次加载,生成多个不同进程;一个进程对应一份 PCB。

OS 使用struct task_struct(PCB 进程控制块)描述管理每一个进程,内核中所有进程的 task_struct 通过链表组织起来,操作系统靠这个链表找到全部进程。

二、CPU 怎么跑进程:时间片 + 上下文切换

CPU 硬件寄存器只有一套,但系统可以同时跑成千上万个进程,靠的就是时间片轮转调度

  1. OS 给每个进程分配时间片,进程占用 CPU 运行。

  2. 时间片耗尽,发生进程切换:

    1. 保存上下文:把当前 CPU 寄存器(程序计数器 PC、通用寄存器等)的值,存到当前进程task_struct里面。保存该进程运行现场。

    2. 恢复上下文:取出另一个进程 PCB 中保存的寄存器数据,写回 CPU 寄存器,CPU 开始跑新进程。

寄存器属于硬件,全局唯一;上下文数据属于进程私有,保存在各自 PCB。进程暂停时现场被保存,下次调度回来可以接着往下执行,不会错乱。 PC(程序计数器):保存进程下一条要执行指令的地址,是上下文切换最重要寄存器。

三、PID:进程的身份证

每个task_struct内部有唯一标识符 PID(process identification),用来区分系统中不同进程。用户空间不能直接访问内核结构体,操作系统提供系统调用getpid()获取当前进程 PID。

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
    pid_t pid = getpid();
    printf("我的进程PID:%d\n", pid);
    return 0;
}

四、完整流程梳理

  1. 磁盘:可执行程序文件(代码 + 数据,静态)

  2. 运行程序:操作系统把代码数据加载内存,内核创建task_structPCB

  3. PCB 加入内核链表,等待调度

  4. CPU 拿到时间片,加载进程上下文,执行代码

  5. 时间片到期,保存寄存器现场到 PCB,切换别的进程

  6. 再次调度到本进程,恢复寄存器,从上次中断位置继续执行

五、容易混淆的关键点

  1. ❌进程不等于程序:程序在磁盘,进程在内存,附带内核 PCB 管理对象。

  2. ❌CPU 寄存器硬件只有一套,不是每个进程一套寄存器;每个进程在 PCB 保存一份寄存器副本(上下文)。

  3. PID 存在内核 task_struct,用户态只能调用系统调用getpid()拿到,不能直接读写内核结构体。

  4. 进程切换开销来自:寄存器保存恢复、内核调度逻辑,这就是为什么进程多了机器会变卡。


拓展小 demo,观察 PID

#include <stdio.h>
#include <unistd.h>

int main()
{
    while(1)
    {
        printf("pid = %d\n", getpid());
        sleep(1);
    }
    return 0;
}

运行后,ps ax | grep ./a.out,就可以看到内核记录的 PID,和程序打印输出一致。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐