【Linux】进程概念

[复制链接]
发表于 2026-10-5 20:21:32 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

进程是操作系统里最基础的概念,也是"程序跑起来之后"的那个东西。这篇把进程是什么、Linux 用什么数据结构描述进程、task_struct 里到底装了什么、PID 和 PPID 怎么看进程父子关系、以及 fork 之后父子数据为什么互不干扰,一次讲透,并且每段都配上能直接敲的命令和可编译的代码。

一、什么是进程

进程是正在运行的程序的实例,它不只是代码,还包括代码执行时所需的资源和状态信息。简单记:

进程 = 程序的代码和数据 + 内核数据结构(内核数据结构用来管理进程的资源与状态)

二、描述进程:PCB 与 task_struct

1. PCB 是什么

PCB(Process Control Block)是操作系统中用于管理每个进程的重要数据结构,包含管理该进程所需的全部信息,用来跟踪、控制和调度进程。每个进程对应唯一的 PCB,系统靠 PCB 识别和管理进程状态与资源。

程序运行时要先被加载进内存,然后由 CPU 读取执行。如果只加载代码和数据,那进程的状态、标识符、优先级这些信息怎么管?于是把这些代表性的信息合起来,建出一个 task_struct 来管理进程。

2. task_struct 里有什么

struct task_struct {
    struct list_head tasks;                  /* 同一进程组中所有任务的列表头 */
    pid_t pid;                               /* 进程 ID */
    int state;                               /* 进程状态 */
    unsigned int flags;                      /* 进程标志 */
    struct task_struct *parent;              /* 父进程 PCB 指针 */
    struct list_head children;               /* 子进程链表 */
    struct list_head sibling;                /* 兄弟进程链表 */
    struct thread_info *thread_info;
    struct fs_struct *fs;                    /* 文件系统结构体 */
    struct files_struct *files;              /* 打开文件结构体 */
    struct signal_struct *signal;            /* 进程信号管理结构体 */
    struct sighand_struct *sighand;          /* 信号处理函数集合 */
    u64 start_time;                          /* 进程开始时间 */
    struct taskstats *stats;                 /* 与进程相关的统计信息 */
};

struct task_struct 里有一个指针指向下一个进程,还有一个指针指向自己对应的进程——其实就是一个双向链表结构,靠 children 和 sibling 串起整个进程树。

3. task_struct 内容九分类

  • 标识符:描述本进程的唯一标识,用来区别其他进程;
  • 状态:任务状态、退出代码、退出信号等;
  • 优先级:相对于其他进程的优先级;
  • 程序计数器:程序中即将被执行的下一条指令的位置;
  • 内存指针:程序代码和相关数据的指针,以及与其他进程共享的内存块的指针;
  • 上下文数据:进程执行时处理器寄存器中的数据;
  • I/O 状态信息:待处理的 I/O 请求、分配给进程的 I/O 设备和被进程使用的文件列表;
  • 记账信息:处理器时间总和、使用时钟数总和、时间限制、记账号等;
  • 其他信息。

三、标识符:看 PID 和 PPID

1. 找出指定进程

ps ajx | grep myexe

ps ajx 看全部进程,加管道 grep 就看指定进程。全量输出第一排有表头,自己的没有,那就把表头加回来:

ps ajx | head -1 && ps ajx | grep myexe

表头里的 pid 就是标识符。pid 具有唯一性,能唯一定位一个进程。

2. 父子关系

用 pid 查指定进程时会多出一个 bash 进程——myexe 的 ppid 和 bash 的 pid 相同,说明 bash 是父、myexe 是子。bash 是命令解释器,启动时它 fork 出一个子进程去执行我们的命令。这就是 shell 里每条命令都是子进程的根本原因。

ppid 就是父进程 id(parent pid)。

3. 怎么结束进程

  • 第一种:Ctrl + C;
  • 第二种:kill + 进程标识符。

四、手动创建子进程:fork

fork 函数可以手动创建一个子进程。返回值规则:给父进程返回子进程的 pid,给子进程返回 0,创建失败返回 -1。

int main()
{
    pid_t id = fork();
    while (1) {
        if (id > 0) {
            printf("我是父进程,pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);
        } else if (id == 0) {
            printf("我是子进程,pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    return 0;
}

父子都打印了,可代码是 if 和 else if,为什么会同时成立?因为创建的子进程与父进程共享同一份代码,但数据是私有的。

1. 验证数据私有

int gval;

int main()
{
    pid_t id = fork();
    while (1) {
        if (id > 0) {
            printf("我是父进程,pid:%d, ppid:%d, id:%d, gval:%d\n", getpid(), getppid(), id, gval);
            sleep(1);
        } else if (id == 0) {
            printf("我是子进程,pid:%d, ppid:%d, id:%d, gval:%d\n", getpid(), getppid(), id, gval);
            gval++;
            sleep(1);
        }
    }
    return 0;
}

全局变量 gval,子进程负责 ++ 和读取,父进程只读取。运行一下会发现只有子进程的 gval 在涨,父进程不动——两个进程数据私有,进程之间具有高度独立性。

2. 多进程创建

按上面同样的套路循环 fork,就能一次拉起一批子进程。注意别写成无上限的递归,否则进程数会瞬间爆掉。

五、一句话记住

进程 = 内核数据结构(task_struct)+ 自己的代码和数据。PCB 是描述它的容器,PID 是它的身份,fork 是造它的手段,父子各自一份数据。这四句话连起来,后面学进程等待、进程替换、线程,就有了落脚点。

qidao123.com企服评测-应用市场 编辑提示

本文按进程的组成公式(代码数据 + 内核数据结构)、PCB 与 task_struct 的定义与字段作用、struct task_struct 关键成员清单、task_struct 内容的九类划分(标识符/状态/优先级/程序计数器/内存指针/上下文数据/I-O 状态/记账信息/其他)、ps ajx 与 head -1 与 grep 组合查 PID 与 PPID 的父子关系、Ctrl+C 与 kill 两种结束方式、fork 返回值规则与父子共享代码但数据私有的验证方法(含全局变量 gval 实验代码),重排成一条"概念—描述—查看—创建"的学习路径。文中代码与结构体片段为教学示意,函数名与字段含义取自 Linux 内核公开源码与 POSIX 标准;进程创建与终止操作请在自有编译与测试环境中进行,避免失控 fork 耗尽系统资源。

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表