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

Linux环境下的进程创建-fork函数的使用, 进程退出exit和_exit的区别,以及进程等待waitpid和status数据的提取方法

目录

一、进程创建

1.fork函数

1)进程调用fork函数是如何创建子进程的

2)代码示范

2.写时拷贝

二、进程退出

1.退出码

1)什么是退出码?

2)为什么要有退出码?

3)退出码是怎么做到的?

4)echo $?:查看最近一个进程的退出码

2.进程的三种退出状态

1)又可分为能运行完和没运行完

2)exit( )和_exit( )的区别

三、进程等待

1.进程等待是什么

2.为什么要进程等待

3.父进程是怎么等待的

1)wait和waitpid

2)写入型参数status的使用方式

①终止信号

②退出状态

③status相关宏定义

3)使用示范

4)阻塞等待与非阻塞等待的区别

总结



一、进程创建

1.fork函数

在linux种fork函数的作用是:从存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

返回值子进程中返回0,父进程返回子进程id,出错返回-1。

这里的pid_t 可以看成是对int的封装

1)进程调用fork函数是如何创建子进程的

①通过分配新的内存块和内核数据结构给子进程

②将父进程部分数据结构内容拷贝至子进程

③添加子进程到系统进程列表当中 ,fork返回。

理解fork函数需要对进程的地址空间有着一定的理解,若对进程地址空间有疑惑的读者可以翻看笔者之前的“短”文看最后的“进程地址空间详解”就可以了,这里给出链接:进程优先级介绍,详解环境变量,详解进程地址空间-CSDN博客

2)代码示范

这里利用fork对父子进程返回的id值的不同,使得父子进程运行不同得条件语句。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main()
{pid_t id=fork();if(id<0){printf("创建子进程失败\n");exit(1);}else if(id>0){//父进程while(1){printf("我是父进程,我的id是:%d,我的父进程id是:%d\n",getpid(),getppid());sleep(1);}}else{//子进程while(2){printf("我是子进程,我的id是:%d,我的父进程id是:%d\n",getpid(),getppid());sleep(2);}}return 0;
}

运行结果试图:(可以通过CTRL+C终止进程)

2.写时拷贝

在通过fork函数创建子进程时,父进程将部分数据结构内容拷贝至子进程,此时在内存中,操作系统为节省内存,父子进程的页表指向的是其实是同一块物理地址,换句话说,此时父子代码共享,数据也是共享的

写时拷贝的概念是:若当父子进程中有一方要修改某个变量时,操作系统会临时将该变量拷贝一份放在另一块内存中,并修改该进程的页表指向临时拷贝出来的变量,以确保进程的独立性

二、进程退出

1.退出码

1)什么是退出码?

退出码就是main函数的而返回值,其中包括return返回或者exit返回

2)为什么要有退出码?

父进程会通过检查子进程的退出码从而确定子进程执行情况,然后再将子进程的资源释放——删除残留的task_struct(PCB),以结束子进程的僵尸状态

3)退出码是怎么做到的?

Linux环境下的退出码共有134种,可分为两种情况:

退出码方便计算机识别程序运行中出现的不同错误,但对人而言就不是很友好。

我们可以通过strerror()函数将退出码翻译为解释语言:

#include<stdio.h>
#include<string.h>int main()
{for(int i=0;i<150;++i)printf("%d:%s\n",i,strerror(i));return 0;
}

读者若感兴趣可自我尝试,这里截取一些常见的退出码:

如上,共134种错误。

4)echo $?:查看最近一个进程的退出码

正如上述,在Linux种有一个变量“?”记录着最近一个进程的退出码。

#include<stdio.h>
#include<stdlib.h>int main()
{exit(10);return 0;
}

值得注意的是如果连续两次使用echo $?指令,第二次打印出的总是0,有误echo也是个程序也有返回值。

2.进程的三种退出状态

1)又可分为能运行完和没运行完

2)exit( )和_exit( )的区别

除了通过return返回外,程序还可通过exit和_exit两个函数返回。

exit是“stdlib.h”中的函数,是基于_exit做的封装

_exit是系统调用,在“unistd.h”中。

他们的作用都是“终止调用进程”,但执行起来有些微区别:

exit在结束进程时会刷新缓冲区,且会执行自定义的清理函数而_exit不会。

#include<stdio.h>
#include<stdlib.h>
#inlcude<unistd.h>int main()
{printf("hello Linux");//注意没有换行符exit(0);//_exit(0);return 0;
}

exit执行情况:

_exit执行情况:

三、进程等待

1.进程等待是什么

每个子进程在运行结束后都会有一段僵尸进程状态,而进程等待是父进程通过系统调用等待子进程的一种方式。

僵尸进程是指 已终止但未被父进程回收(reap)的进程

2.为什么要进程等待

①释放子进程的僵尸状态;②获取子进程的运行状态

3.父进程是怎么等待的

1)wait和waitpid

waitpid参数解释:

pid——要等待(回收)的子进程;

*status写入型参数——将读取到的子进程状态写入status中;

options为等待方式,传0则是阻塞式等待,传入WNOHANG则是非阻塞等待(若有疑问的读者可直接跳转至“阻塞等待与非阻塞等待的区别”查看)

2)写入型参数status的使用方式

写入型参数,顾名思义就是系统会向这个形参中写入数据,通过写入的数据便可以判别进程的运行状态。

一般status创建为整型初始化为0,而系统写入的数据一般在status变量的低十六位

解释:4字节的整形status由32位二进制组成,而系统写入的数据则在低16位,也就是2^0-2^15之间,而就这16位二进制包含了两份数据——从0位到7位的二进制表示终止信号从8位到15的二进制表示退出状态

①终止信号

还记得上面讲述的进程的三种退出状态吗

二者三种状态又可分为两种情况:

①情况一:程序无异常,代码能跑完,无退出码

②情况二:程序出现异常,中途退出,无退出码

终止信号数据用于记录第二种情况——进程没跑完,中途出现异常的情况

若进程代码顺利跑完,则终止信号默认为0(status初始化值就是0)

获取终止信号的方式:

(status & 0X7F),0X7F是十六进制表示111 1111(其他全为0),&是位运算,(上下)同为1结果才是1

Linux环境下的终止信号有个64个,可以通过kill -l指令查看:

②退出状态

status二进制的次八位,也就是退出码

获取退出状态(退出码)的方式:

(status>>8)& 0XFF,>>是位运算符,作用是将status的二进制右移动八位,前边补0.

Linux环境下的退出码有134种。

③status相关宏定义

若是觉得status中的两个数据使用过于麻烦,系统为我们提供了两个宏以便使用:

一个是WIFEXITED(status),他的作用是检查进程是否运行完毕,有无异常。若进程正常退出则WIFEXITED(status)值为非零,否则为0.

另一个是WEXITSTATUS(status),他的值是进程的退出码。

3)使用示范

值得注意的是waitpid的返回值就是等待进程的id

#include<stdio.h>
#include<string.h>
#include<stdlib.h>//exit
#include<sys/types.h>//waitpid
#include<sys/wait.h>//waitpid
#include<unistd.h>//forkint main()
{pid_t id=fork();if(id==0){//子进程int cnt=5;while(cnt--){printf("我的id:%d,父进程id:%d\n",getpid(),getppid());sleep(1);}exit(10);}//父进程int _status=0;int s_id=waitpid(id,&_status,0);printf("我的id:%d,子进程id:%d,sig number:%d,child exit code:%d",getpid(),s_id,(_status & 0X7F),(_status>>8&0XFF));sleep(1);return 0;
}

4)阻塞等待与非阻塞等待的区别

阻塞等待与非阻塞等待各有优劣,使用哪种等待方式全看当时情况:

比如,父进程只需等待单个子进程,且无其他任务需并行处理时就适合用阻塞等待,原因是可以将cpu资源让出来供其他进程使用。


总结

本文从进fork函数入手介绍了什么是进程创建,再通过strerror函数引出退出码以及exit和_exit的区别与联系,最后通过waitpid引出status的使用与相关数据的提取。本文还介绍了阻塞等待与非阻塞等待的区别。
 

http://www.xdnf.cn/news/282619.html

相关文章:

  • 一款免费的现场大屏幕知识竞赛抢答软件
  • SpringBoot校园失物招领平台源码开发实现
  • 永磁同步电机无速度算法--基于ESO-PLL的永磁同步电机无位置传感器控制
  • 适配器模式(Adapter Pattern)
  • 内容中台的AI中枢是什么?
  • LeetCode 热题 100 48. 旋转图像
  • lombok详解
  • cline或业务系统集成n8n的工作流(MCP Server Trigger、Call n8n Workflow Tool node)
  • 【力扣刷题记录】hot100错题本(一)
  • MySQL--索引精通详解
  • QT6(33)4.6滑动组件QSlider 与QProgressBar:理论,例题的界面搭建,与功能的代码实现
  • PATHWAYS: 用于机器学习的异步分布式数据流
  • Python Cookbook-6.20 精确和安全地使用协作的超类调用
  • 【AI提示词】黑天鹅模型专家
  • 从图文到声纹:DeepSeek 多模态技术的深度解析与实战应用
  • 气泡图、桑基图的绘制
  • Sway初体验
  • 微软Phi-4-reasoning技术报告解读
  • HTML基础1-元素与页面的结构
  • “链式前向星”等三种存图方式分别输出“无向无权图”的“DFS序列”
  • ABC404E 题解
  • 2025牛客五一集训派对day4
  • 纯继电器电路控制小车自动往复运动
  • (39)VTK C++开发示例 ---选择区域
  • MFiX(Multiphase Flow with Interphase eXchanges)软件介绍
  • 5块钱的无忧套餐卡可以变成流量卡吗
  • Winform(10.常用控件3)
  • ResNet改进(36):ResNeXt与ResNet的混合模型实现
  • Spring AI 实战:第十一章、Spring AI Agent之知行合一
  • 线程与进程深度解析:从fork行为到生产者-消费者模型