目录
进程创建
fork函数
进程终止
终止时干了什么
进程终止的三种情况
main函数的返回值
打印默认退出码
自定义退出码
总结
进程终止
exit函数
_exit函数
exit和_exit的区别
进程等待
什么是进程等待
为什么要有进程等待
wait函数
waitpid函数
阻塞等待与非阻塞等待
总结
进程创建
进程 = 内核的相关管理数据结构(task_struct + mm_struct + 页表) + 代码和数据
fork函数
函数原型:pid_t
包含头文件:<unistd.h>和<sys/type.h>(后者为了适应pid_t类型的变量)
返回值:子进程中返回0,父进程返回子进程的pid,出错返回-1(进程太多等原因造成)
父进程调用fork函数,在操作系统内核执行该函数的函数体,执行时内核会做出以下反应:
- 分配新的内存块和内核数据给子进程
- 将父进程的部分数据结构内容拷贝至子进程,子进程共享父进程的代码,写时拷贝父进程的数据(子进程的PID、PPID、优先级与父进程等不同)
- 添加子进程到系统列表中
- fork返回,开始调度器调度
3、fork之后,谁先执行完全由调度器决定
如何理解进程独立性:fork后父子进程的内核管理数据结构(页表等)互相独立,对代码是共享的(代码只有度权限不会改变),父子不写数据时相当于共享,写数据时发生写时拷贝,一人对数据的修改影响不了另一个人
如何理解fork函数有两个返回值:
如何理解调用fork函数后父进程中fork函数的返回值是子进程的pid,子进程中的fork函数的返回值是0:便于父进程对子进程进行标识,从而进行管理
fork的具体问题可以查看这篇文章:http://t.csdnimg.cn/wMBPV
进程终止
终止时干了什么
进程创建时,操作系统先为进程创建内核的相关数据结构task_struct、mm_struct、页表等,然后再加载进程的代码和数据代码(相当于大学录取后高中的学籍信息已经被大学获取,但缴费等操作是到了大学后才进行的)
那么进程终止时也应该先释放曾经的代码和数据所占据的空间,后释放内核数据结构,并让task_struct变为Z僵尸状态(相当于大学毕业后离开宿舍时将自己的东西都带走了,然后学校仍然保留你的一些关键信息存档保留一段时间)
进程终止的三种情况
main函数的返回值
- 有返回值是因为父进程想要知道子进程运行的结果
- 返回值为0,表示进程运行成功
- 返回值不为0,表示进程运行失败,不同的数字表示不同的失败原因
- 可以使用默认的错误码,也可以自定义错误码
打印默认退出码
C语言的sterror函数:http://t.csdnimg.cn/1HvUR
自定义退出码
- 结论:代码运行完后是否正确可以通过进程的退出码决定,退出码可默认可自定
- 补充:代码执行时,出现异常导致提前退出的根本原因是因为该进程收到了OS发给它的信号
- 信号:kill - l 查看信号找到确定退出原因
C语言的联合体和枚举:http://t.csdnimg.cn/NWYct
总结
1、进程终止一共有三种情况:
- 代码跑完,结果正确
- 代码跑完,结果不正确
- 代码执行时,出现异常导致提前退出
2、衡量一个进程退出情况可以依据退出码和退出信号
3、子进程的pcb中有它执行完后的退出码和退出信号信息,子进程结束后,它们会跟随pcb处于Z僵尸状态等待父进程的读取,源码如下:
进程终止
进程有两种终止方式:main函数的return表示进程终止 和 调用exit或_exit函数直接退出进程
exit函数
函数原型:void exit(int status);
- status:进程的终止原因,父进程通过wait获取
-
status仅有低8位可以被父进程所用,故_exit(-1)时,返回值是255
包含头文件:<stdlib.h>
功能:遇到exit进程直接终止,退出码即status中存放的值
int test3()
{
int sum = 0;
for(int i = 0; i < 50; ++i)
{
sum += i;
}
exit(99);
return sum;
}
int main()
{
int ret = test3();
exit(ret);
return 0;
}
[yyf@hecs-165234 linux101]$ ./myproc
[yyf@hecs-165234 linux101]$ echo $?
99
_exit函数
函数原型:void _exit(int status);
包含头文件:<unistd.h>
注意事项:
1、_exit 是系统调用,而 exit 是库函数(C语言库函数)
2、_exit和_Exit函数本质上是同一个函数
NAME
_exit, _Exit - terminate the calling process
SYNOPSIS
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);
exit和_exit的区别
不同类型的流,默认情况下使用不同的缓存模式:
- 行缓存模式:缓冲区中遇到
\n
或者达到一定长度时自动刷新 - 全缓存模式:关闭文件或手动调用 fflush函数前都不会进行刷新操作
- 无缓存模式:调用stderr时,每个字符都立即被发送给终端并打印出来
结论:exit会在进程退出时冲刷缓冲区,_exit不会,因此红框例子中放在缓冲区中的字符产在调用_exit函数导致进程结束后并不会打印到显示器上,而是进程退出时与其他资源一起销毁
进程等待
什么是进程等待
1、任何进程,在退出时,一般必须要被父进程进行等待(退出信息要交给父进程)
产生问题:如果父进程不管Z状态的子进程,直接退出,则造成内存泄漏(子进程的PCB)
为什么要有进程等待
1、父进程通过等待,解决子进程退出的僵尸问题,回收系统资源(等待目标一定要释放资源)
2、获取子进程的退出信息,知道子进程是因为什么原因退出的(可以获取退出原因就更好)
wait函数
函数原型:pid_t wait(int *status)
- 输出型参数:由操作系统填充,指向子进程退出信息的整型指针,若不关心子进程的死亡原因(退出信息),可以将status设为NULL,只返回结束子进程的pid,若关心则将指定子进程的退出信息反馈给父进程(后面有详解)
- 返回值:等待成功时为子进程的pid,等待失败时为NULL
包含头文件:<sys/types.h> 和 <sys/wait.h>
注意事项:
1、父进程一旦调用wait,就会被阻塞(挂起):
- 如果wait参数不为NULL,就等待指定子进程的返回,不返回父进程就一直阻塞,子进程返回后父进程会拿到子进程的退出信息并清理Z状态子进程的pcb,最后唤醒父进程
- 如果wait参数为NULL,只要父进程中有一个子进程结束父进程就会拿到子进程的退出信息并清理Z状态子进程的pcb,最后唤醒父进程
2、父进程的等待的本质就是在等待某种资源就绪,等到后操作系统内核就会将位于等待ZMOBIE的等待队列的父进程唤醒,并将相应信息放入int* status指向到内存空间中
对于阻塞队列的内容可以查看:http://t.csdnimg.cn/8N7Nf
waitpid函数
包含头文件:<sys/types.h> 和 <sys/wait.h>
函数原型:pid_t waitpid(pid_t pid,int *status,int options)
返回值:
- 如果成功等到了一个子进程结束,则返回该子进程PID
- 如果指定了WNOHANG选项,且没有任何一个子进程已经终止,则返回0
-
如果出错(例如,指定了无效的 PID 或者调用过程被信号中断),则返回 -1
参数:
- pid参数:为-1则表示等待任意一个子进程,不为-1则表示等待一个指定子进程
- 输出型参数:提前开好一片内存空间,将该空间的地址传递给status,子进程返回时,操作系统会将子进程的退出信息放入该空间中
- status参数:输出型参数,指向子进程的退出信息(退出码 + 退出信号),status是一个四字节三十二个比特位的整型变量,但是它的高两个字节(前十六个比特位)在这里不会被使用,只使用它的低16位,在低十六位中前八位表示退出码,后七位表示退出信号(后八位的最左的一位现在不解释)
打印退出信息、退出码和退出信号:
- 进程未受到信号干扰,顺利执行完时的退出码不为0,退出信号为0
打印因信号终止时的退出码和退出信号:
- 因信号导致进程终止时,退出码为0,退出信号不为0
WIFEXITED(status):子进程正常返回,则为真(查看进程是否是正常退出)
WEXITSTATUS(status):若WIFEXITED为真,查看子进程的退出码
阻塞等待与非阻塞等待
阻塞等待:父进程在等待子进程返回时什么都不做(稳定可靠)
非阻塞等待:父进程在等待子进程返回时可以去做一些事情(while循环实现),并且在做事情的间隙频繁访问子进程是否返回
waitpid的options参数的取值:
- 0:表示当前父进程是阻塞等待
- WNOHANG:表示当前父进程为非阻塞等待,可以与waitpid函数返回值为0时产生互动
非阻塞轮询 = 非阻塞等待 + 循环
父进程利用回调函数在阻塞轮询的时候执行自己的事情:19、1:00:00处
总结
1、可以自定义退出码,退出信号是由操作系统提供的不能自定义
2、>>移位不会改变数字的比特位,>>=才会,0XFF表示1111 1111,0X7F表示0111 1111
3、父进程的等待是必须的,但是获取子进程的退出信息不是必须的,status设为NULL也行
也可以参考这位大佬的文章:http://t.csdnimg.cn/syuOf
~over~