进程终止
- 进程终止
- 进程终止原因
- 进程返回值
- echo $?
- exit()和_exit()
- 父进程获取进程退出码
- wait()方法
- waitpid()方法
- 获取子进程的退出码(status)
- 进程的非阻塞等待(WNOHANG)
- 问题
进程终止
进程终止原因
进程终止原因有三种情况。
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
进程终止时,OS要释放进程申请的向相关内核数据结构和对应的数据和代码。(就是释放系统资源)
进程返回值
进程运行后,一般都有返回值,比如说main函数里面的return 0。
而0一般表示:success。非0表示:程序运行的结果不正确。
echo $?
该命令行符号的意义是:获取最近一个进程,执行完毕时的退出码。
1 #include<stdio.h>
2
3
4 int main()
5 {
6 printf("hello world\n");
7 return 111;
8 }
~
运行结果如下:
exit()和_exit()
这两个函数都可以让进程终止,并且返回指定的status。
#include<stdlib.h>
void exit(int status);
#include<unistd.h>
void _exit(int status);
status是进程的退出状态码。
从该例子可以看出,exit封装了_exit,并且有更丰富的功能。
如下所示:
打印结尾没有’\n’(\n的作用是输出换行,刷新缓冲区),也能照样打印出该结果来。
10 int main()
11 {
12 printf("hello world");
13 exit(100);
14 }
return 退出:
return退出是一种常见的进程退出的方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当作exit的参数。
父进程获取进程退出码
wait()方法
pid_t wait(int* status);
参数:这个是输出型参数,获取子进程的退出状态,不关心则可以设置成NULL
返回值: 返回被等待进程pid,失败返回-1.
如下代码:父进程在阻塞式等待(一直等待wait的退出码),子进程执行完,父进程才开始执行wait下面的内容。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/wait.h>
4 #include<sys/types.h>
5 #include <stdlib.h>
6 int main()
7 {
8
9 pid_t id = fork();
10
11
12 if(id==0)
13 {
14 //子进程
15 int cnt = 5;
16 while(cnt--)
17 {
18 printf("我是子进程,我的pid = %d, ppid = %d , %d\n",getpid(), getppid(), cnt);
19 sleep(1);
20 }
21 exit(1);
22 }
23 else if(id>0)
24 {
25 printf("我是父进程:---------------\n");
26 pid_t ret = wait(NULL);
27 if(ret>0)
28 {
29 printf("等待成功,返回值为:%d \n", ret);
30 }
31 }
32 return 0;
33 }
waitpid()方法
升级版的wait方法。
pid_t waitpid(pid_t pid, int *status, int options);
参数:
pid = -1,等待任何一个子进程,与wait等效。
pid > 0,等待其进程ID与pid相等的子进程。
status:输出型参数:
WIFEXITED(status):若为正常终止子进程返回的状态,则为真。(查看进程是否正常退出)
WEXITSTATUS(status):若WIFEXITED非零,提取子进程的退出码。(查看进程的退出码)
options:
WNOHANG:默认为0,表示不使用这个参数。若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。(用来完成非阻塞等待的接口)
返回值:
正常返回时返回收集到子进程的ID。
如果选项设置了WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0。
如果调用中出错,则返回-1,这时errno会被设置成相应的值指示错误所在。(可以使用perror打印)。
将参数设置成:pid = -1,status=NULL,WNOHANG=0时,等价于wait使用。
获取子进程的退出码(status)
有了上面的两个函数之后,我们就能获取子进程的退出码。
实际上status不能简单当作整形来看待,可以当位图来看。具体细节如下,我们只看低16位。
其中8-15位为进程的退出码(退出状态位)。
0~6位为终止信号的数字。
获取子进程正常退出的退出码:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/wait.h>
4 #include<sys/types.h>
5 #include <stdlib.h>
6 int main()
7 {
8 pid_t id = fork();
9 if(id==0)
10 {
11 //子进程
12 int cnt = 5;
13 while(cnt--)
14 {
15 printf("我是子进程,我的pid = %d, ppid = %d , %d\n",getpid(), getppid(), cnt);
16 sleep(1);
17 }
18 exit(1); //退出时,返回号码。
19 }
20 else if(id>0)
21 {
22 int status = 0;
23 printf("我父进程,我的pid = %d, ppid = %d \n",getpid(), getppid());
24 pid_t ret = waitpid(id,&status, 0); //阻塞式等待,一直等,直到子进程退出
25 if(ret>0)
26 {
printf("等待成功,返回值为ret:%d 子进程退出码:%d ,信号位为:%d \n", ret , (status>>8)&0xFF,(status&0x7f));//获取高8位,低7位为信号位
28 }
29 }
30 return 0;
31 }
若进程非正常退出,它的信号位就可以看出是出什么错了
在子进程运行完之前,使用9号信号杀死它,结果是,状态位为0,信号位为9。
使用宏来获取子进程是否正常退出和退出码
代码修改部分如下:参考waitpid参数介绍部分
24 if(ret>0)
25 {
26 if(WIFEXITED(status))
27 {
28 //子进程正常退出
29 printf("子进程执行完毕,子进程的退出码位:%d \n",WEXITSTATUS(status));
30 }
31 else{
32 printf("子进程异常退出:%d \n",WIFEXITED(status));
33 }
运行结果如下:
=程序发生异常,子进程的退出码就没有意义了(因为结果不对,或者子进程都可能没有跑完)。
进程的非阻塞等待(WNOHANG)
就是父进程不能光等,父进程在等待子进程退出码的同时,也可以运行一些东西。
下面为代码示例:(注意使用c++11标准编译)
#include <iostream>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
//定义非阻塞等待父进程干的事情
typedef void (*hander_t)(); //hander_t = void(*)()
std::vector<hander_t> handlers;
void fun_1()
{
printf("work_1\n");
}
void fun_2()
{
printf("work_2\n");
}
void Load()
{
handlers.push_back(fun_1);
handlers.push_back(fun_2);
}
int main()
{
pid_t id = fork();
if(id==0)
{
int cnt = 5;
while(cnt--)
{
printf("I am child, my pid = %d, my ppid = %d, %d \n",getpid(),getppid(),
cnt);
sleep(1);
}
exit(11);
}
else
{
int quit = 0;
while(1)
{
int status = 0;
pid_t ret = waitpid(-1,&status,WNOHANG);
if(ret>0)
{
printf("等待成功,退出码为:%d \n",WEXITSTATUS(status));
quit = 1;
break;
}
else{
if(ret==0)
{
printf("子进程还没结束,我还得继续干活\n");
if(handlers.empty()) Load();
for(hander_t iter : handlers)
{
iter();
}
}
if(ret<0)
{
printf("child running failed, signal = %d \n", status&&0x7f);
quit = 1;
break;
}
}
sleep(1);
}
}
}
运行结果就是在等待子进程的退出码的过程中,父进程也在做一些其他的事情。
问题
-
为什么要用wait/waitpid函数获取退出码呢?全局变量不可以吗?
进程具有独立性,如果数据发生写时拷贝,那么全局变量就会变成两份数据,父进程拿不到。 -
wait/waitpid系统调用函数如何读取退出码的呢?
因为子进程退出时,变成僵尸进程(资源被释放),但是其pcb仍然保留在内存中,函数通过访问pcb来获取进程的退出码。