Linux学习笔记(九)--Linux进程终止与进程等待

张开发
2026/4/10 12:39:01 15 分钟阅读

分享文章

Linux学习笔记(九)--Linux进程终止与进程等待
初识fork函数在linux中fork函数时非常重要的函数它从已存在进程中创建一个新进程。新进程为子进程而原进程为父进程。基本概念在父进程中返回子进程的 PID进程ID在子进程中返回 0如果出错则返回 -1。进程调用fork当控制转移到内核中的fork代码后内核做分配新的内存块和内核数据结构给子进程将父进程部分数据结构内容拷贝至子进程添加子进程到系统进程列表当中 fork返回开始调度器调度。当一个进程调用fork之后就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以 开始它们自己的旅程看如下程序。int main( void ) { pid_t pid; printf(Before: pid is %d\n, getpid()); if ( (pidfork()) -1 )perror(fork()),exit(1); printf(After:pid is %d, fork return %d\n, getpid(), pid); sleep(1); return 0; }运行结果 [rootlocalhost linux]# ./a.out Before: pid is 43676 After:pid is 43676, fork return 43677 After:pid is 43677, fork return 0这里看到了三行输出一行before两行after。进程43676先打印before消息然后它有打印after。另一个after 消息有43677打印的。而进程43677没有打印before原理如下图所示所以fork之前父进程独立执行fork之后父子两个执行流分别执行。注意fork之后谁先执行完全由调度器 决定。进程终止进程终止有三种场景分别是1代码运行完毕结果正确 2代码运行完毕结果不正确 3代码异常终止进程常见退出方法1.正常终止可以通过echo $? 查看进程退出码1从main返回 2调用exit 3_exitint main() { return 0; // 进程正常终止 }#include stdlib.h exit(0); // 正常终止状态码0#include unistd.h _exit(0); // 立即终止不执行清理异常终止ctrl c信号终止exit函数和_exit函数的区别#include stdio.h #include stdlib.h #include unistd.h int main() { printf(这行内容在缓冲区中); // 注意没有换行符 // exit(0); // 会输出上面没有换行的内容 _exit(0); // 不会输出上面没有换行的内容 return 0; }特性exit()_exit()头文件stdlib.hunistd.h是否刷新缓冲区是否调用退出处理函数是atexit注册的否关闭标准IO流是否进程等待父进程等待子进程终止并获取其退出状态的机制防止子进程变成僵尸进程从而导致内存泄漏进程等待的方法wait#include sys/wait.h pid_t wait(int *status);功能​​等待任意一个子进程终止参数​​status用于存储子进程退出状态可以为NULL返回值​​成功返回终止的子进程PID / 失败返回-1如没有子进程waitpid#include sys/wait.h pid_t waitpid(pid_t pid, int *status, int options);功能​​等待特定子进程终止提供更多控制选项参数pid10等待特定PID的子进程2-1等待任意子进程类似wait30等待与调用进程同组ID的任何子进程4-1等待进程组ID等于pid绝对值的任何子进程statusWIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常退出 WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码options1WNOHANG非阻塞即使没有子进程退出也立即返回2WUNTRACED报告已停止的子进程3WCONTINUED报告已继续的子进程返回值成功返回子进程PID非阻塞且无子进程退出返回0失败返回-1。如果子进程已经退出调用wait/waitpid时wait/waitpid会立即返回并且释放资源获得子进程退出信息。如果在任意时刻调用wait/waitpid子进程存在且正常运行则进程可能阻塞。如果不存在该子进程则立即出错返回。wait的基本使用示例#include sys/wait.h #include unistd.h #include stdio.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程运行PID%d\n, getpid()); sleep(2); exit(123); } else { int status; printf(父进程等待子进程...\n); pid_t child_pid wait(status); if (WIFEXITED(status)) { printf(子进程%d正常退出状态码%d\n, child_pid, WEXITSTATUS(status)); } } return 0; }原理图获取子进程status在Unix/Linux系统中父进程可以通过wait()或waitpid()系统调用获取子进程的终止状态。这个状态信息存储在status变量中需要使用特定的宏来解析。WIFEXITED(status) // 子进程正常退出 WEXITSTATUS(status) // 获取子进程退出码当WIFEXITED为真 WIFSIGNALED(status) // 子进程被信号终止 WTERMSIG(status) // 获取终止信号当WIFSIGNALED为真 WIFSTOPPED(status) // 子进程是否停止 WSTOPSIG(status) // 获取停止信号当WIFSTOPPED为真 WIFCONTINUED(status) // 子进程是否已继续执行wait和waitpid都有一个status参数该参数是一个输出型参数由操作系统填充。如果传递NULL表示不关心子进程的退出状态信息。否则操作系统会根据该参数将子进程的退出信息反馈给父进程。status不能简单的当作整形来看待可以当作位图来看待只研究status低16比特位进程退出状态低 8 位​​如果进程正常退出WIFEXITED(status)为真则低 8 位status 0xFF是进程的退出状态exit()或_exit()的参数。如果进程被信号终止WIFSIGNALED(status)为真则低 8 位包含终止信号编号WTERMSIG(status)。进程终止原因高 8 位​​第 8 位status 0x80可能指示是否生成了核心转储WCOREDUMP(status)。其他位可能用于特殊标志如WIFSTOPPED(status)表示进程被暂停。测试代码#include sys/wait.h #include stdio.h #include stdlib.h #include string.h #include errno.h int main( void ) { pid_t pid; if ( (pidfork()) -1 ) perror(fork),exit(1); if ( pid 0 ){ sleep(20); exit(10); } else { int st; int ret wait(st); if ( ret 0 ( st 0X7F ) 0 ){ // 正常退出 printf(child exit code:%d\n, (st8)0XFF); } else if( ret 0 ) { // 异常退出 printf(sig code : %d\n, st0X7F ); } } }测试结果1# ./a.out #等20秒退出child exit code:102# ./a.out #在其他终端kill掉sig code : 9进程等待方式在Unix/Linux多进程编程中父进程等待子进程终止有两种主要方式阻塞等待和循环非阻塞等待。1阻塞等待阻塞等待是指父进程调用wait()或waitpid()时会暂停执行直到有子进程状态改变。#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程(PID%d)开始运行\n, getpid()); sleep(3); printf(子进程结束\n); exit(10); } else { printf(父进程(PID%d)开始阻塞等待子进程\n, getpid()); int status; pid_t child_pid wait(status); // 阻塞等待 if (WIFEXITED(status)) { printf(子进程(PID%d)正常退出状态码%d\n, child_pid, WEXITSTATUS(status)); } } return 0; }特点父进程会暂停执行直到子进程终止循环等待非阻塞等待循环等待是指父进程定期检查子进程状态而不是一直阻塞等待。#include sys/wait.h #include stdio.h #include unistd.h #include stdlib.h int main() { pid_t pid fork(); if (pid 0) { printf(子进程(PID%d)开始运行\n, getpid()); sleep(5); printf(子进程结束\n); exit(20); } else { printf(父进程(PID%d)开始循环等待子进程\n, getpid()); int status; pid_t child_pid; int wait_count 0; while (1) { child_pid waitpid(pid, status, WNOHANG); // 非阻塞检查 if (child_pid -1) { perror(waitpid失败); exit(1); } else if (child_pid pid) { if (WIFEXITED(status)) { printf(子进程(PID%d)正常退出状态码%d\n, child_pid, WEXITSTATUS(status)); } break; } else if (child_pid 0) { wait_count; printf(等待中(%d秒)... 父进程可以做其他工作\n, wait_count); sleep(1); // 等待1秒后再次检查 } } } return 0; }特点父进程可以继续执行其他任务需要主动定期检查子进程状态适用于需要父进程同时处理其他任务的场景。option在进程等待如使用waitpid()系统调用时options参数用于控制等待的行为。默认情况下options设置为 ​​0​​表示​​阻塞等待​​即父进程会挂起直到目标子进程状态改变。option 0的默认行为父进程会一直阻塞挂起直到以下情况发生1目标子进程终止正常退出或被信号杀死。2目标子进程被信号暂停如SIGSTOP。3如果没有子进程状态改变父进程会一直等待。返回条件1waitpid()会返回​​任意一个终止或暂停的子进程​​的 PID。2如果没有子进程匹配指定的pid参数则返回-1错误码ECHILD。其他option模式选项标志说明​​WNOHANG​​非阻塞模式如果没有子进程状态改变立即返回0不阻塞父进程。​​WUNTRACED​​报告被暂停的子进程状态如SIGSTOP。​​WCONTINUED​​报告被恢复的子进程状态如SIGCONT。

更多文章