1. fork的常规用法
- 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
- 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数;
创建子进程去执行和父进程一样的代码:也是可以的,但是并没有意义,所以一般我们不会这么做;
2. fork调用失败的原因
1. 系统中有太多的进程; 进程的创建是比较耗费资源滴,单独对于一个进程来说:不单单是把代码和数据加载内存就可以,还要为它创建PCB,这个PCB就很耗费资源了; 2. 实际用户的进程数超过了限制;
3. 进程退出场景
1. 代码运行完毕,结果正确; 2. 代码运行完毕,结果不正确; 3. 代码异常终止;
对于第一种场景:一般我们使用0表示正常退出,也就是我们所谓的return 0语句,但是这个进程的return 0语句要表示进程退出状态,只能在 main函数中生效,也就是说,假如在其他函数出现 return 0语句,它不表示进程退出,只表示函数指向完毕返回一个结果罢了; 但是:一个进程运行完毕,它的返回值可以有很多的,不仅仅是0,只不过我们约定0是正常返回而已,如果,你愿意,也可以在你的程序之中,main函数返回其他任意的值,也可以表示进程退出,但是这样就没有任何意义了,因为在我们Linux中,进程的退出状态,都是用0来表示正常退出的,而假如你的进程是正退出,还返回其他不是0的值,那么显得你在搞事情了;
对于第二种场景:我们的进程也可以退出,但是返回的状态码并不是0,这个返回的状态码也就是return返回的值,当然也可以是exit 函数;而是其他值,在Linux中,非0值就表示异常退出,也即是这个代码运行结束了,但是并没有达到我们运行的预期,它也结束了;
我们关心:进程退出的结果不正确的情况,不会关心进程运行结束正常的状况,因为进程退出结果不正常,会有很多不一样的表示,非0的值那么多,每一个值,都可以表示不正确的原因是什么,正常退出就一个0值; 我们可以通过:C 标准库的一个函数 strerror查看状态码对应的原因;
#include<stdio.h>
#include<string.h>
int main()
{
int i = 0;
for(;i<135;i++){
printf("%d:%s\n",i,strerror(i));
}
return 0;
}
Linux测试状态码有134个,我这里截图只截取前面几个,由于图片太长就不截取了; 我们需要知道 strerror这个函数,可以查看状态码的原因即可;
第三种场景:异常退出的状态:代码没运行到return 0语句时候,可以就因为你的某种异常操作导致退出了; 比如越界内存,除于0操作等;其实我们也可以通过一些手段获得进程异常退出的原因,但是不是我们今天要讨论的话题;
4. 进程退出的方式
正常终止(可以通过 echo $? 查看进程退出码):
- 从main返回;
- 调用exit;
- _exit;
异常退出:
- ctrl + c,信号终止;
对于:exit函数:参数就是退出码;exit函数有个特点:不管你是在哪个位置调用,只要运行到了exit函数, 就会使得你的进程退出;一旦进程退出,exit 后面的那些代码也就自然而然不会被执行了;
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main ()
{
printf("这是执行exit函数之前的代码我会被执行\n");
exit(0);
printf("我是exit函数之后的代码,我不会被执行");
return 0;
}
测试结果如下:
exit 在退出进程时候,还会刷新我们的输出缓冲区内容,再退出进程的;
我们可以通过一个小demo来测试一下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("hello world");
sleep(4);
exit(0);
}
结果:先睡4s再打印我们的 hello world;原因是 hello world 先存在缓冲区的,4s过后,执行了 exit,而exit会刷新缓冲区,所以才会有hello world到屏幕;
我们退出进程还有一个函数是_exit;它的使用和exit是完全一样的,但是,这个函数相对于exit就少了一个功能: _exit并不会刷新缓冲区,它仅仅就是进程退出,不会做任何事情,不清理资源,不会关闭文件流等操作;
这个图:可以表示_exit和exit的区别:exit是在进程退出时候,会做一些操作,而_exit进程推出时候,并不会做任何事情,而是直接退出; 进程退出都需要先进入操作系统内核的,进去清理PCB资源啥啥啥的; 并且我们上面所谈的缓冲区都是用户级别缓冲区,并不是内核级别的缓冲区; 很容易理解:假如不是在不是用户级缓冲区的话,_exit和exit都会进入内核,那么就会都刷新缓冲区,实际上,并不是这样;所以可以说:我们谈的刷新缓冲区都是用户级别的;
5. 进程退出是否会刷新缓冲区(用户级别)的总结
6. 进程退出总结
|