Linux 进程创建与管理实战:从 fork、wait 到 ps、pgrep、kill(cosmos 操作系统源码解析)
Linux 进程创建与管理实战从 fork、wait 到 ps、pgrep、killcosmos 操作系统源码解析【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址: https://gitcode.com/gh_mirrors/co/cosmosLinux 中的“进程”是正在运行的程序实例操作系统通过进程表Process Table跟踪每一个进程的生命周期。本文以 cosmos 仓库 中code/operating_system/src/processCreation/目录下的文档与源码Processes.c、vfork.c为主线系统讲解 Linux 下创建进程与控制进程的核心系统调用fork、vfork、wait、exit、getpid、getppid以及日常运维最常用的进程查看与管理命令ps、pgrep、kill。读完本文你将能够独立编写进程创建程序、理解父子进程间的执行流与内存关系并熟练用命令行观察和终止系统上的进程。一、进程与 PIDLinux 进程模型入门在 Linux 中进程是程序的一次执行实例每个进程都由内核分配一个唯一的进程标识符PIDProcess ID。进程之间具有严格的“父子”层级除 0 号进程swapper和 1 号进程systemd/init外每个进程都有一个父进程父进程的 PID 称为PPIDParent PID。在 C 程序中获取当前进程与父进程的标识符只需两个系统调用getpid()返回当前进程的 PIDgetppid()返回当前进程的父进程 PID。Processes.c 开篇即演示了这两个调用printf(I am the parent, my PID is %d\n, getpid()); printf(My parents PID is %d\n, getppid());进程不是凭空出现的新进程由已有进程创建——这正是本文的核心fork()。二、fork()创建新进程的系统调用fork()是 Linux 中创建新进程的基础系统调用。它由当前进程父进程发起内核会复制一份父进程的地址空间、寄存器上下文和打开的文件描述符生成一个几乎完全相同的子进程。调用一次fork()会返回两次这是初学者最容易困惑的地方fork()返回值含义-1创建失败如进程数达上限或内存不足0返回给子进程表示当前处于子进程上下文中大于0子进程 PID返回给父进程便于父进程识别并管理子进程Processes.c 完整演示了三种返回值的处理分支child fork(); if (child -1) { // fork() returns -1 on failure printf(fork() failed.); return (-1); } else if (child 0) { // fork() returns 0 to the child process printf(I am the child, my PID is %d\n, getpid()); printf(My parents PID is %d\n, getppid()); } else { // fork() returns the PID of the child to the parent wait(status); // wait for the child process to finish... printf(I am the parent, my PID is still %d\n, getpid()); }这段代码的运行逻辑清晰展示了进程创建的关键事实父进程先执行打印自己的 PID 与 PPIDfork()之后程序分裂为两条执行流但都从fork()返回的下一行继续执行子进程分支中getpid()返回的是子进程自己的新 PID而其getppid()返回的正是父进程的 PID——因此子进程可以通过 PPID 反向认识自己的“父亲”父进程分支中wait(status)会阻塞父进程直到子进程结束从而保证父子进程的执行顺序可控详见下文 wait 一节注意父子进程共享同一份代码段但各自拥有独立的地址空间——子进程修改自己的变量不会影响父进程这与后面要讲的vfork()形成鲜明对比。编译运行示例仓库中的源码是完整的可编译程序可用任意 C 编译器构建例如gcc Processes.c -o processes ./processes典型的输出形态如下PID 数值因机器而异I am the parent, my PID is 12345 My parents PID is 12344 I am going to create a new process... I am the child, my PID is 12346 My parents PID is 12345 I am the parent, my PID is still 12345可以看到子进程的 PPID 恰好等于父进程的 PID父进程在wait()返回后 PID 保持不变。三、wait()控制父子进程的执行流程fork()之后父子进程的执行顺序是不确定的由内核调度器决定。若父进程需要在继续运行前确保子进程已经结束例如收集子进程的退出状态、回收子进程占用的资源以避免僵尸进程就必须使用wait()系列系统调用。wait(int *status)阻塞调用进程直到任意一个子进程终止status用于接收子进程的退出状态waitpid(pid_t pid, int *status, int options)等待指定的子进程功能更精细wait(NULL)不关心退出状态仅等待子进程结束。在 Processes.c 中父进程调用wait(status)后才会打印最后一行这就是“控制执行流程”的直观体现wait(status); // wait for the child process to finish... printf(I am the parent, my PID is still %d\n, getpid());vfork 版本的源码 vfork.c 中同样使用了wait(NULL)来回收子进程。四、exit() 与子进程的终止子进程结束自己的生命周期时应调用exit(int status)或_exit()exit(status)标准 C 库函数会先执行atexit()注册的清理函数、刷新标准 I/O 缓冲区再向内核递交退出状态_exit(status)系统调用立即终止进程不做任何清理返回退出状态码0通常表示成功非零表示出错该状态被父进程的wait()/waitpid()捕获。在 vfork.c 中子进程修改完变量后显式调用exit(0)结束自己print(exiting child process\n); exit(0);五、vfork()共享地址空间的快速进程创建仓库在 vfork.c 中提供了vfork()的完整演示。vfork()与fork()的关键区别在于内存语义fork()创建子进程时内核为子进程复制或写时复制 COW一份独立的地址空间父子互不影响vfork()则让子进程共享父进程的地址空间且父进程会被挂起suspended直到子进程调用exec系列函数加载新程序或调用_exit()/exit()终止自己。vfork.c 文件头部的注释精确描述了这一行为When a vfork system call is issued, the parent process will be suspended until the child process has either completed execution or been replaced with a new executable image via one of the exec family of system calls.源码完整演示了这一“共享”特性——子进程修改变量后父进程再打印时看到的是修改后的值if((xvfork())0) { printf(In child process(B)\n); printf(B : pid %d pgid %d ppid %d\n,getpid(),getpgid(0),getppid()); a10; b5; print(a is %d\n,a); print(b is %d\n,b); print(exiting child process\n); exit(0); } else { wait(NULL); print(back in parent process\n); print(a is %d\n,a);//a is updated even in parent by child print(b is %d\n,b);//b is updated even in parent by child if vfork }注释行// a is updated even in parent by child与// b is updated even in parent by child if vfork直白地说明了vfork 的子进程对a、b的修改父进程同样可见——这正是共享地址空间的结果。该示例还顺带演示了getpgid(0)获取当前进程所在进程组 ID以及父进程通过wait(NULL)等待子进程结束再继续执行的流程。使用注意事项由于父子共享地址空间子进程在vfork()后应立即调用exec或_exit()不应修改数据、不应从函数返回否则可能破坏父进程的运行环境。在 fork 已实现写时复制的现代 Linux 内核上vfork()的性能优势已十分有限一般优先使用fork()。六、观察进程ps 命令详解创建进程之后如何在系统上“看到”它们文档指出使用ps命令配合附加选项可以获取系统中正在运行进程的详细信息-a列出系统上所有用户的进程-u显示进程的详细信息用户名、CPU 占用、内存占用、启动时间、命令行等-x列出后台静默运行的进程这些进程通常称为守护进程daemon名称常以字母d结尾例如systemd。命令行实践# 查看系统中所有进程的详细列表 ps -aux # 等价于 -a -u -x 三个选项的组合是 Linux 上最常用的进程查看方式 ps auxps aux的输出列通常包括USER进程所有者、PID进程号、%CPU、%MEM、VSZ/RSS虚拟/常驻内存、STAT进程状态如S睡眠、R运行、Z僵尸、高优先级、START启动时间、COMMAND命令名。七、按模式定位进程pgrep 命令当进程数量庞大时逐行翻看ps输出并不高效。文档给出pgrep命令用于按文本模式匹配进程pgrep 会查找进程的元数据中匹配特定文本模式的进程并显示这些进程的 PID 以及程序名称。文档中的典型示例pgrep -l chrome-llong format选项让 pgrep 在输出 PID 的同时显示程序名称不带-l时仅输出 PID。该命令会在所有进程的命令行/程序名元数据中搜索包含chrome模式的进程。类似的用法还有pgrep -l systemd # 查找名字中包含 systemd 的进程 pgrep -l -u root bash # 查找 root 用户下名字包含 bash 的进程pgrep 与ps aux | grep pattern相比更简洁且不会匹配到 grep 自身进程适合在脚本中快速获取目标进程 PID。八、终止进程kill 命令与进程创建相对的是进程的终止。Linux 下最直接的终止手段是kill命令其本质是向目标进程发送信号kill PID # 默认发送 SIGTERM信号 15请求进程优雅退出 kill -9 PID # 发送 SIGKILL信号 9内核强制杀死进程无法被捕获或忽略结合 pgrep 可以先定位后终止# 找到所有 chrome 相关进程的 PID再逐一终止 pgrep chrome # 批量终止匹配模式的进程谨慎使用 pkill chrome其中pkill是 pgrep/kill 的组合形式直接按名称模式发送信号。注意SIGKILL-9不会给进程任何清理机会可能导致数据丢失应优先使用默认的SIGTERM。九、进程创建的更多源码佐证fork()在 cosmos 仓库中还有更贴近实战的使用场景可作为进一步研读的参考shell/c/shell.c一个简易 Linux shell 的实现通常通过fork()exec家族系统调用来执行用户输入的外部命令是理解“进程创建 → 加载新程序”完整流程的绝佳范本concurrency/monitors/monitors_system_v/main.c系统 V 管程示例涉及多进程间的同步memory_management/memory_mapping/mapping.c结合fork()与内存映射展示父子进程的内存视图。这些文件与processCreation目录共同构成了 code/operating_system/src 中操作系统主题的完整学习链路进程创建 → 调度 → 同步 → 内存管理。十、总结与实操清单Linux 进程管理的核心心法可以概括为创建fork()复制出子进程返回 0 进入子进程返回子 PID 进入父进程需要共享地址空间且立即 exec/exit 的场景可用vfork()识别getpid()/getppid()获取自身与父进程标识同步wait()/waitpid()阻塞父进程直至子进程结束同时回收子进程资源终止子进程用exit()主动退出外部用kill/kill -9发送信号观察ps -aux全量查看pgrep -l pattern按模式定位pkill pattern按模式终止。建议动手实践的顺序先用gcc编译并运行 Processes.c 与 vfork.c对比两者的输出差异尤其注意 vfork 场景下子进程对变量的修改如何“泄漏”回父进程再在另一个终端中用ps aux | grep processes和pgrep -l processes观察运行中的进程最后用kill终止测试进程完整走一遍“创建—观察—控制—终止”的 Linux 进程生命周期。【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址: https://gitcode.com/gh_mirrors/co/cosmos创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考