当前位置: 首页 > news >正文

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 ( (pid=fork()) == -1 )perror("fork()"),exit(1);printf("After:pid is %d, fork return %d\n", getpid(), pid);sleep(1);return 0;}
运行结果:
[root@localhost linux]# ./a.outBefore: pid is 43676After:pid is 43676, fork return 43677After: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_exit

int 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.h>

<unistd.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);

功能​​:等待特定子进程终止,提供更多控制选项

参数:

pid

(1)>0:等待特定PID的子进程

(2)-1:等待任意子进程(类似wait)

(3)0:等待与调用进程同组ID的任何子进程

(4)<-1:等待进程组ID等于pid绝对值的任何子进程

status

WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出) WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options

(1)WNOHANG:非阻塞,即使没有子进程退出也立即返回

(2)WUNTRACED:报告已停止的子进程

(3)WCONTINUED:报告已继续的子进程

返回值:成功:返回子进程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 ( (pid=fork()) == -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", (st>>8)&0XFF);} else if( ret > 0 ) {  // 异常退出printf("sig code : %d\n", st&0X7F );}}}

测试结果:

(1)# ./a.out #等20秒退出

          child exit code:10
(2)# ./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)如果没有子进程状态改变,父进程会一直等待。

返回条件:(1)waitpid()会返回​​任意一个终止或暂停的子进程​​的 PID。(2)如果没有子进程匹配指定的 pid参数,则返回 -1(错误码 ECHILD)。

其他option模式:

选项标志

说明

​​WNOHANG​​

非阻塞模式:如果没有子进程状态改变,立即返回 0(不阻塞父进程)。

​​WUNTRACED​​

报告被暂停的子进程状态(如 SIGSTOP)。

​​WCONTINUED​​

报告被恢复的子进程状态(如 SIGCONT)。

http://www.dtcms.com/a/507045.html

相关文章:

  • 虚幻引擎5 GAS开发俯视角RPG游戏 P06-09 玩家等级与战斗接口
  • JavaSE内容梳理与整合
  • JavaScript日期处理:格式化与倒计时实现
  • 网页与网站设计 什么是属性网站开发用的框架
  • 长沙正规网站建设价格公司概况简介
  • STM32卡尔曼滤波算法详解与实战应用
  • 【自适应粒子滤波 代码】Sage Husa自适应粒子滤波,用于克服初始Q和R不准确的问题,一维非线性滤波。附有完整的MATLAB代码
  • 未来的 AI 操作系统(三)——智能的中枢:从模型到系统的统一
  • 群晖无公网IP内网穿透工具—ZeroNews(零讯)套件详解
  • [日常使用]Anaconda 常见问题排查手册
  • 【Python入门】第3篇:流程控制之条件判断
  • 网站建设初级教程seo高效优化
  • 智能排课系统实战 Java+MySQL实现课程自动编排与冲突检测
  • 【EE初阶 - 网络原理】传输层协议
  • 电子商务网站建设的难点设计创意网站推荐
  • 【Linux环境下安装】SpringBoot应用环境安装(五)-milvus安装
  • Windows使用docker安装milvus的配置文件
  • 记录之Ubuntu22.4虚拟机及hadoop为分布式安装
  • K8s 运维三大核心难题:DNS 故障、有状态存储、AI 赋能 SRE 解决方案
  • c#WPF基础知识
  • 云栖实录|阿里云 Milvus:AI 时代的专业级向量数据库
  • 科技网站小编账号运营竞争性谈判
  • 华为FreeBuds 7i空间音频不灵敏怎么办?
  • Java Stream 高级应用:优雅地扁平化(FlatMap)递归树形结构数据
  • git推送本地仓库到远程 以及 模拟多人协作
  • 【开题答辩实录分享】以《预约上门维修服务运营与数据分析系统的设计与实现》为例进行答辩实录分享
  • 数据结构7:栈和队列
  • SpringBoot的启动流程原理——小白的魔法引擎探秘
  • Vue3 + Element Plus 弹框树形结构首次打开不更新问题排查与解决
  • 我先做个网站怎么做网络推广技术外包