1.进程终止
进程终止的本质是释放系统资源,就是释放进程申请的相关内核数据和对应的数据和代码。
进程退出场景
代码运行完毕,结果正确
代码运行完毕,结果错误
代码异常终止
进程常见退出办法
正常终止:
1.从main返回
2.调用exit
3._exit
异常退出:
ctrl+c 信号终止
退出码
退出码(退出状态)可以告诉我们最后一次执行的命令的状态。在命令结束后,可以知道命令是成功完成还是以错误结束的,基本思想是,程序返回退出代码0表示执行成功,没有问题。代码1或0以外的任何代码都视为不成功。
退出码0表示命令执行无误,这是完成命令的理想状态
退出码1可以解释为不被允许的操作,例如没有sudo情况下使用apt或者yum;例如除以0等操作也会返回错误代码1。
130(SIGINT或^C)和143(SIGTERM)等终止信号是非常典型的,它们属于128+n信号,其中n代表终止码
可以通过strerror函数获取退出码对应的描述
指令显示退出码
echo $?
_exit函数
#include<unistd.h>
void _exit(int status);
参数:status定义了进程的终止状态,父进程通过wait获取该值
注意:虽然status是int,但是仅有低八位可以被父进程所用。所以_exit(-1)时。在终端执行 echo $?时返回值是255.
exit函数
#include<unistd.h>
void exit(int status);
exit函数里面是调用了_exit,但exit函数还做了其它事情:
1.执行用户通过atexit或on_exit定义的清理函数
2.关闭所有打开的流,所有的缓存数据均被写入
3.调用_exit
_exit函数是系统层面的,在任何地方调用这个函数都会结束整个进程,而exit在子进程调用不会影响父进程,会把退出码返回给父进程
代码示例:
int main()
{
printf("hello");
exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
hello[root@localhost linux]#
int main()
{
printf("hello");
_exit(0);
}
运⾏结果:
[root@localhost linux]# ./a.out
[root@localhost linux]#
return退出
return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值做exit的参数。
进程等待必要性
子进程退出,父进程如果不理会,就可能造成僵尸进程问题,进而造成内存泄漏
另外,进程一旦变成僵尸状态,就刀枪不如了,kill -9也无能为力了,无法杀死一个已经死去的进程。
最后,父进程派给子进程的任务完成的如何,是需要知道的。子进程运行完成,结果对还是不对,或者是否正常退出。
父进程通过等待的方式,回收子进程资源,获取子进程退出的信息
进程等待的方法
wait方法
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
返回值:
成功就返回被等待进程pid,失败返回-1
参数:
输出型参数,获取子进程退出状态,不关心可以设置为NULL
waitpid方法
#include<sys/types.h>
#include<sys/wait.h>
1
pid_t waitpid(pid_t pid,int* status,int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置了选项WNOHANG,而调用中发现没有已经退出可收集,则返回0;
如果调用中出错,则返回-1,这时error会被设置成相应的值以指示错误所在
参数:
pid:
pid=-1,等待任意一个子进程,与wait等效
pid>0,等待其进程ID与pid相等的子进程
status:输出型参数
WIFEXITED(status):若为正常终止子进程返回的状态,则为真(查看进程是否正常退出)
WEXITSTATUS(status):若WIFEXITED非零,提取子进程的退出码(查看进程的退出码)
option:默认为0,表示阻塞等待
WNOHANG:若pid指定的子进程没有结束,则waitpid()函数返回0,不予等待。若正常结束,则返回子进程的ID。
如果子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
如果子进程不存在,则立刻出错返回。
如果子进程结束的话,父进程会阻塞直到子进程结束才继续执行父进程代码
获取子进程status
wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出信息状态。
操作系统会根据该参数,将子进程的退出信息反馈给父进程
status不能简单的当作整型来看待,可以当作位图来看待,具体细节下图(为status低16位)
若进程正常结束则最低的七位为0。
对于32位的然后得到退出状态的8位,先右移八位然后&0xFF,这样只有最低八位是有效的,其余制成0了。
进程之间是独立的,父进程是如何得到子进程的退出码?因为waitpid是系统的,系统是可以得到进程的信息的,然后再给父进程,父进程就得到了子进程的信息
代码示例:
1 #include<unistd.h>
2 #include<sys/wait.h>
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<string.h>
6 #include<errno.h>
7
8 int main()
9 {
10 pid_t pid;
11 if((pid=fork())==-1)
12 {
13 perror("fork fail:");
14 exit(1);
15 }
16 if(pid == 0)
17 {
18 sleep(2);
19 exit(10);
20 }
21 else
22 {
23 int st;
24 int ret=wait(&st);
25 if(ret>0&&(st&0x7F)==0)
26 {
27 printf("child exit code:%d\n",(st>>8)&0xFF);
28 }
29 else if(ret>0)
30 {
31 printf("sig code:%d\n",st&0x7F);
32 }
33 }
34
35
36 }
阻塞与非阻塞等待
进程的阻塞等待方式:
一直等子进程结束,不干别的
1 #include<unistd.h>
2 //#include<vector>
3 #include<sys/wait.h>
4 #include<stdio.h>
5 #include<stdlib.h>
6
7
8 int main()
9 {
10 pid_t pid;
11 pid= fork();
12 if(pid ==0)
13 {
14 printf("child is run,pid is :%d\n",getpid());
15 sleep(5);
16 exit(257);
17 }
18 else
19 {
20 int status =0;
21 pid_t ret=waitpid(-1,&status,0);//阻塞式等待
22 printf("waiting\n");
23 if(WIFEXITED(status)&&ret==pid)
24 {
25 printf("success,child return code is:%d\n",WEXITSTATUS(status));
26
27 }
28 else
29 {
30 printf("wait fail");
31 return 1;
32 }
33 }
34 return 0;
35
36
37 }
非阻塞状态
这个状态父进程可以不用只等待子进程,可以边等边干别的事情
#include <sys/wait.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include<stdio.h>
6 #include<unistd.h>
7 //#include<vector>
8
9 typedef void (*handler_t)(); // 函数指针类型
10 std::vector<handler_t> handlers; // 函数指针数组
11 void fun_one() {
12 printf("这是⼀个临时任务1\n");
13 }
14 void fun_two() {
15 printf("这是⼀个临时任务2\n");
16 }
17 void Load() {
18 handlers.push_back(fun_one);
19 handlers.push_back(fun_two);
20 }
21 void handler() {
22 if (handlers.empty())
23 Load();
24 for (auto iter : handlers)
25 iter();
26 }
27 int main() {
28 pid_t pid;
29 pid = fork();
30 if (pid < 0) {
31 printf("%s fork error\n", __FUNCTION__);
32 return 1;
33 }
34 else if (pid == 0) { // child
35 printf("child is run, pid is : %d\n", getpid());
36 sleep(5);
37 exit(1);
38 }
39 else {
40 int status = 0;
41 pid_t ret = 0;
42 do {
43 ret = waitpid(-1, &status, WNOHANG); // ⾮阻塞式等待
44 if (ret == 0) {
45 printf("child is running\n");
46 }
47 handler();
48 } while (ret == 0);
49 if (WIFEXITED(status) && ret == pid) {
50 printf("wait child 5s success, child return code is :%d.\n",
51 WEXITSTATUS(status));
52}
53 else {
54 printf("wait child failed, return.\n");
55 return 1;
56 }
57 }
58 return 0;
59 }