探寻进程的身世之谜:从 getppid 到系统调用的本质

资料合集
链接:https://pan.quark.cn/s/770d9387db5f

在我们上一篇关于 ​
​fork()​
​ 的探索中,我们学会了如何创造新进程。但每个进程并非凭空而来,它都有自己的“父母”和“祖先”。今天,我们将扮演一次“进程侦探”,从一个简单的函数 ​
​getppid()​
​ 开始,一步步揭开进程的身世之谜,绘制出壮观的进程家族谱,并最终触及操作系统最核心的概念之一:系统调用

一、 我是谁?我的父亲是谁?—— ​
​getpid()​
​ 与 ​
​getppid()​

每个进程都有一个独一无二的身份证号——PID (Process ID)。我们可以通过 ​
​getpid()​
​ 函数获取自己的PID。但一个进程不仅要知道自己是谁,还要知道自己从哪里来。​
​getppid()​
​ 函数(第一个'p'代表parent)就是用来寻找“父进程”的。


​pid_t getpid(void);​
​ // 获取当前进程ID​
​pid_t getppid(void);​
​ // 获取当前进程的父进程ID

【实验一:父子进程的“亲子鉴定”】 让我们修改之前的 ​
​fork()​
​ 代码,让父子进程都打印出自己的PID和父进程的PID,看看会发生什么。

代码 (
process_ancestry.c
​)



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
 
int main() {
    pid_t pid;
 
    printf("--- Before fork ---
");
    printf("I am the original process. My PID is %d, and my parent's PID is %d.
", getpid(), getppid());
    printf("---------------------

");
 
    pid = fork();
 
    if (pid < 0) {
        perror("fork error");
        return 1;
    } else if (pid == 0) {
        // 子进程的世界
        printf("I am the CHILD. My PID is %d, and my parent's PID is %d.
", getpid(), getppid());
    } else {
        // 父进程的世界
        // sleep(1); // 暂时注释掉,观察随机执行顺序
        printf("I am the PARENT. My PID is %d, and my parent's PID is %d.
", getpid(), getppid());
        wait(NULL);
    }
 
    return 0;
}

编译与运行



gcc process_ancestry.c -o ancestry
./ancestry

运行结果 (PID会变,但关系不变)



--- Before fork ---
I am the original process. My PID is 38120, and my parent's PID is 37580.
---------------------

I am the PARENT. My PID is 38120, and my parent's PID is 37580.
I am the CHILD. My PID is 38121, and my parent's PID is 38120.

结果分析

子进程的“亲子鉴定”:子进程(PID ​
​38121​
​)调用 ​
​getppid()​
​ 得到的结果是 ​
​38120​
​,这正是父进程的PID!亲子关系确认无误。父进程的“身世”:父进程(PID ​
​38120​
​)的父进程是 ​
​37580​
​。这个 ​
​37580​
​ 是谁?它就是我们正在使用的终端程序,比如 ​
​bash​
​。执行顺序问题:你可能会遇到终端提示符(​
​$​
​)插在输出中间的情况。这是因为父子进程抢占CPU,执行顺序不确定。如果父进程先结束,​
​bash​
​ 就会立即打印提示符。解决方法是在父进程的 ​
​wait()​
​ 之前加上 ​
​sleep(1)​
​,确保子进程大概率先执行完。

二、 绘制“进程家族谱” —— ​
​ps​
​ 命令

一个 ​
​bash​
​ 进程又是谁创建的呢?追根溯源,Linux 系统中所有的进程构成了一棵巨大的进程树。树根就是大名鼎鼎的 ​
​init​
​ 进程,它的 PID 永远是 1,是所有进程的“始祖”。

我们可以使用 ​
​ps aux​
​ 命令来查看系统中的所有进程。


​a​
​: 显示所有终端的进程。​
​u​
​: 以用户为中心显示信息。​
​x​
​: 显示没有控制终端的进程。

【实验二:追根溯源,找到
init
​】

获取当前
bash
​ 的 PID 在终端中输入
echo $$



$ echo $$
37580

这个 ​
​37580​
​ 就是我们上个实验中看到的父进程的“父亲”。

使用
ps
​ 和
grep
​ 查找它的信息


ps aux | grep 37580

运行结果 (部分)


coder    37580  0.0  0.1  13284  7456 pts/0    Ss   14:30   0:00 /bin/bash

我们找到了这个 ​
​bash​
​ 进程。

使用
pstree
​ 命令更直观地查看进程树


pstree -p 37580

运行结果


systemd(1)───login(37570)───bash(37580)

这下清晰了!我们的 ​
​bash​
​ 是由 ​
​login​
​ 进程创建的,而 ​
​login​
​ 最终可以追溯到 PID 为 1 的 ​
​systemd​
​(在很多系统中是 ​
​init​
​)。我们成功绘制出了一小段进程家族谱!

三、 深入本质:系统调用 vs. 库函数


​getpid()​
​​、​
​fork()​
​​、​
​open()​
​​、​
​read()​
​​… 这些函数为什么有如此大的“魔力”,能够接触到进程PID、创建新进程、操作硬件?而像 ​
​strcpy()​
​​、​
​printf()​
​​、​
​malloc()​
​ 这些函数似乎只能在程序自己的小世界里打转?

答案在于它们身份的不同:前者是系统调用(System Call),后者是库函数(Library Function)

如何区分它们?—— “二卷硬件内核访,三卷纯在用户晃”

这是个简单又好记的口诀。在 Linux 中,我们可以用 ​
​man​
​ 手册来查询一个函数的“卷号”。

第 2 卷:System Calls。这些函数是通往操作系统内核的桥梁。第 3 卷:Library Functions。这些函数是 C 语言标准库提供的,运行在用户空间。

【实验三:用
man
​ 手册给函数“验明正身”】

查询
getpid


man 2 getpid

结果 (看第一行)


GETPID(2)                  Linux Programmer's Manual                 GETPID(2)

卷号是 2,确认是系统调用。

查询
malloc


man 3 malloc

结果


MALLOC(3)                  Linux Programmer's Manual                 MALLOC(3)

卷号是 3,确认是库函数。

为什么要有这种区分?

系统调用:是用户程序请求内核服务的唯一合法途径。它需要满足以下任一条件:

访问内核数据结构:比如 ​
​fork()​
​ 需要复制进程控制块(PCB)。访问硬件资源:比如 ​
​read()​
​ 需要操作磁盘控制器。 执行系统调用会伴随着从用户态内核态的切换,开销较大。

库函数:完全运行在用户态,不涉及内核。它们通常是为了方便编程(如 ​
​strcpy​
​​)或对系统调用进行封装(如 ​
​printf​
​​ 最终可能会调用 ​
​write​
​​ 系统调用)。​
​malloc​
​ 是一个特例,它管理的是用户空间的堆内存,所以本身是库函数。

总结

今天,我们的“进程侦探”之旅收获颇丰:

我们学会了使用 ​
​getpid()​
​​ 和 ​
​getppid()​
​ 来进行进程的“亲子鉴定”。我们利用 ​
​ps​
​​ 和 ​
​pstree​
​​ 命令,成功绘制出了进程的“家族谱”,理解了所有进程最终都源于 ​
​init​
​ (PID 1) 的树状结构。我们深入到了问题的核心,学会了区分系统调用库函数,并理解了它们在权限和功能上的本质差异。

从一个简单的 ​
​getppid()​
​ 函数出发,我们不仅理清了进程间的父子关系,更窥见了整个操作系统内核与用户程序交互的宏伟蓝图。这正是学习技术的乐趣所在——由点及面,洞见本质。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容