目录
退出码
退出信号
进程终止情况3
如何进程终止
return退出
库函数exit
系统调用函数_exit
exit和_exit的区别缓冲区
exit
_exit
退出码
回顾上篇
- 代码跑完,结果正确(退出码为0)
- 代码跑完,结果不正确(退出码为非0)
- 结果不正确退出码有相应的错误描述:系统定义和自定义。
- 所以进程终止代码跑完,结果正不正确是根据子进程的退出码来决定的。
当然,除此之外还存在第三种情况:代码没跑完。代码执行时,出现了异常,提前退出了。
- 先确认是否异常
- 不是异常,就一定是代码跑完了,结果是否正确,看退出码
退出信号
子进程执行过程中出现了错误,OS此刻直接把正在执行的进程干掉,不让进程继续执行了。当我们的程序运行(进程)语言层面上是崩溃,系统层面上是OS杀死进程。
- 语言层面上,编程运行奔溃了
- 系统层面上,OS杀死了进程,此时退出码就没有意义了。
- 进程出现异常,我们还是需要知道为什么的?用户也需要知道为什么?
- 进程出现异常,本质上是因为进程收到了OS发给进程的信号❗(这个过程类似我们用kill -9 进程pid 杀死某个进程的过程)
- kill -9 进程pid 说明进程如果永远都不退出,但是系统层面上用信号的方式让进程提前终止或者结束循环
- 查看所有信号:kill -l
- 野指针和被除数为0等错误操作都会造成进程出现异常
- int *p = NULL *p =100;(访问0号地址,0号地址进程不能/也没有权力访问,造成了野指针)
- Segmentation fault段错误,OS提前终止进程。
- OS:都是野指针了,终止进程,别执行了。OS测进程异常,给它发了信号,终止进程。异常触发了OS给进程发信号。
- OS系统中常见的报错(奔溃)最终在OS层面上都是OS给指定的进程发送信号,进而终止进程,把错误信息展现给用户。
触发进程信号:
- 可以手动触发kill -9 进程pid
- 可以自动触发系统给进程发送信号
综上所述:我们可以通过查看进程终止的时候,退出信号是多少,就可以判断进程异常的原因了❗
int main()
37 {
38 int *p = NULL;
39 int result = Div(10, 100);
40 printf("result: %d [%s]\n", result, CodeToErrString(exit_code));
41 result = Div(10, 0);
42 printf("result: %d [%s]\n", result, CodeToErrString(exit_code));
43 *p = 100;//野指针
44 return exit_code;
45 }
进程终止情况3
进程终止存在3种情况:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
衡量一个进程退出,我们只需要两个数字:退出码和退出信号。全部都是由这两个数字组合。这两个数字一定会让父进程知道子进程执行的情况。
联系下篇进程等待
- 子进程终止了,子进程处于僵尸状态Z状态。
- 子进程终止后,代码和数据都会被OS释放掉,不会立刻把进程PCB释放掉。
- 子进程的PCB需要维护一段时间,里面存有子进程退出的数据(退出码和退出信号),等待父进程来读取。
- 一个进程退出时,进程的信号/提出码都会写入进程的PCB。
- 父进程等待就会拿到进程的退出信息,并让用户看到。
- 退出信息:
- int sig_code;
- int exit_code;
如何进程终止
- 以下进程终止不考虑进程异常的情况。
- 进程终止也就是main函数结束了
进程常见的退出方法:
- 正常终止(可以通过 echo $? 查看进程退出码):
- 从main返回
- 调用exit
- _exit
- 异常退出:
- ctrl + c,信号终止
综下所述:
- main函数return,表示进程终止(非main函数,return,函数结束)
- 代码调用exit函数,在代码任意位置调用exit,都表示进程退出。exit的参数就是我们的退出码。不传参数就是系统自带的退出码和退出描述。
- 系统调用接口函数_exit
return退出
- 在main函数中,return表示进程终止
- 在非main函数中,return表示函数结束
- 函数结束并不一定代表进程终止
- return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。
- main函数return,表示进程终止(非main函数,return,函数结束)
1 #include<stdio.h>
2 int Add(int x,int y)
3 {
4 return x+y;
5 }
6 int main()
7 {
8 int ret=Add(10,20);
9 printf("%d\n",ret);
10 return 0;
11 }
库函数exit
- 查看exit:man 3 exit(3号手册)
- exit引起一个正常的进程终止。
- int status类似退出码
- exit在main函数内部可以直接终止进程
- exit在函数内部也可以直接终止进程
- #include <unistd.h>
- void exit(int status);
- 代码调用exit函数,在代码任意位置调用exit,都表示进程退出。exit的参数就是我们的退出码。不传参数就是系统自带的退出码和退出描述。
exit最后也会调用exit, 但在调用exit之前,还做了其他工作:
- 执行用户通过 atexit或on_exit定义的清理函数。
- 关闭所有打开的流,所有的缓存数据均被写入
- 调用_exit
【exit在main函数】
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int Add(int x,int y)
5 {
6 return x+y;
7 }
8 int main()
9 {
10 int ret=Add(10,20);
11 sleep(2);
12 exit(7);
13 printf("%d\n",ret);
14 return 0;
15 }
【exit在函数】
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int Add(int x,int y)
5 {
6 exit(123);
7 return x+y;
8 }
9 int main()
10 {
11 int ret=Add(10,20);
12 printf("%d\n",ret);
13 return 0;
14 }
系统调用函数_exit
- 查看系统调用函数_exit():man 2 _exit(2号手册)
- 终止一个调用进程
- 同样_exit()和exit()一样无论在代码任意位置使用都可以终止进程。
- #include <unistd.h>
- void _exit(int status);
- 参数:status 定义了进程的终止状态,父进程通过wait来获取该值
- 说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。
- ❗_exit和exit的区别是缓冲区
exit和_exit的区别缓冲区
- 综下所述:exit会在进程退出的时候,冲刷缓冲区。_exit不会,把数据丢弃直接终止进程。说明目前我们所说的缓冲区不是内核缓冲区。缓冲区一定不在OS内部维护。
- exit是标准C语言封装的库函数
- _exit是OS给上层用户提供的系统调用接口函数
- 缓冲区一定不在系统调用接口下面,不在_exit内部/OS内部(因为在的_exit会做刷新)
- 其实,exit底层就是调用的_exit系统调用接口(终止进程的本质是让进程释放进程的代码/数据所占用的内存资源,释放除进程PCB外的其他内核数据结构,就是对进程做管理的一种方式。用户是没有权力对OS内部的任何字段做任何访问的)
- 缓冲区一定在_exit之上。
exit
无\n
- 先休眠再冲刷缓冲区,让缓冲区的数据打印在显示器上。
- 进程开始执行的时候,打印的结果并未显示出来,sleep(2)时,打印的结果数据放在缓冲区中。
- 最后exit(3)时,会协助进程,在进程退出的时候冲刷缓冲区的数据到显示器上。
- 注意exit底层是先冲刷缓冲区再调用系统调用函数_exit函数终止进程。
//先冲刷缓冲区再休眠/n
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("Hello linux!\n");
7 sleep(2);
8 exit(7);
9 }
//先休眠再冲刷缓冲区
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("Hello linux!");
7 sleep(2);
8 exit(7);
9 }
_exit
无\n时,发现_exit没有刷新缓冲区的数据,就直接终止程序了。
//先冲刷缓冲区再休眠/n
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("Hello linux!\n");
7 sleep(2);
8 _exit(7);
9 }
//先休眠再冲刷缓冲区
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 int main()
5 {
6 printf("Hello linux!");
7 sleep(2);
8 _exit(7);
9 }
🙂感谢大家的阅读,若有错误和不足,欢迎指正。因为LinuxOS系统概念很多所以呢要学习更加细致才能写博客会更新的比较慢一些🙂,下篇进程等待。