目录
进程终止
进程终止是在做什么?
- 释放曾经的代码和数据所占空间
- 释放内核数据结构
进程终止的三种情况
- 代码跑完,结果正确
- 代码跑完,结果不正确
- 代码执行时出现了异常,提前退出了
#include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 int main()
5 {
6 printf("I am fatherprocess, pid:%d,ppid:%d\n",getpid(),getppid());
7 return 0;
8 }
在main函数中:return 数值有什么意义?
return 0;运行正常
return 1,2,3,4.......一方面表示失败,另一方面表示失败原因。(每个数值都有它对应的原因)
我们可以通过代码查询一下每个数值所代码的意义:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<string.h>
5 int main()
6 {
7 int errcode;
8 for(errcode=0;errcode<=255;errcode++)
9 {
10 printf("%d:%s\n",errcode,strerror(errcode));
11
12 }
13
14 sleep(2);
15 return 0;
16
17 }
(1)那么我们如何查询我们运行的进程到底是成功了还是失败呢?
echo $? :查看进程的退出码,通过退出码的数值来对应我们的进程是否运行成功;
注:退出码可以使用默认的,也可以自定义。
如果是代码出现了异常,正如我们使用vs的时候,运行崩溃-------也就对应了OS发现了进程做了不该做的事情,OS杀掉该进程;
所以在异常时,退出码也就没有意义了;
(2) 那么我们就需要去寻找为什么出现了异常?原因是什么?
进程出现异常,本质是因为进程收到了OS发给进程的信号!
查找出现异常的原因我们可以用:kill -11 PID(退出信号)
(3)那么我们如何判断一个进程是异常的,还是代码跑完结果对,还是代码跑完结果不对?
1、先确认是否异常
2、不是异常,看退出码
注意:衡量一个进程退出:退出码,退出信号
如何终止?
- main函数return表示终止(非main,return,函数结束)
- 代码调用exit函数,(任意位置都可以exit)
- _exit() -------system call (系统调用)
exit函数
#include <unistd.h>
void exit(int status);参数:status 定义了进程的终止状态,父进程通过wait来获取该值
_exit函数
#include <unistd.h>
void _exit(int status);虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。
对比一下下面两个代码的运行结果:
我们会发现:有\n的会立即显示出hello ,但是没有的会等待一会才打印到屏幕上;
这是因为缓存区的原因;
如果在代码中分别加入exit(1)和_exit(1),我们会发现加exit也就是上面运行结果的第一种,会显示运行结果,但是加_exit不会显示除了hello;
可以证明exit在进程退出时,会冲刷缓冲区,_exit不会冲刷缓冲区;
进程等待
进程等待是什么?
任何子进程在退出的情况下,一般必须要被父进程等待。进程在退出时,如果父进程不管不顾的话,退出进程会出现(Z)僵尸状态,从而造成内存泄漏。
为什么要有进程等待?
1、父进程通过等待解决子进程的僵尸问题,回收系统资源
2、获取子进程的退出信息,知道子进程是通过什么圆心退出的(可选)
怎么使进程等待?
wait / waitpid
wait
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
成功返回被等待进程pid,失败返回-1。
参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/types.h>
4 #include<string.h>
5 #include<stdlib.h>
6 #include<sys/wait.h>
7
8 int main()
9 {
10
11 printf("I am father process, pid:%d,ppid:%d\n",getpid(),getppid());
12 pid_t id = fork();
13 if(id==0)
14 {
15 int cnt=5;
16 while(cnt)
17 {
18 printf("I am child process,pid:%d,ppid:%d,cnt:%d\n",getpid(),getppid() ppid(),cnt);
19 cnt--;
20 sleep(1);
21 }
22 printf("child quit...\n");
23 exit(0);
24 }
25 sleep(10);
26 pid_t rid =wait(NULL);
27
28 if(rid>0)
29 {
30 printf("wait sucess,rid:%d\n",rid);
31 }
32 sleep(3);
33 printf("father quit...\n");
34 return 0;
35 }
通过ps来查看进程情况:
while :;do ps ajx | head -1 && ps ajx |grep myprocess | grep -v grep; sleep 1; done
我们会发现wait确实解决了(Z)僵尸问题。
waitpid
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID;
如果设置选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
pid:
Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待其进程ID与pid相等的子进程。
status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。
将wait代码中的pid_t rid =wait(NULL);改为pid_t rid = waitpid(-1,NULL,0);即可。运行结果一样。
status
我们发现在wait和waitpid中都有status参数,该参数是一个输出型参数,由操作系统填充。
status不能简单的当作整形来看待,可以当作位图来看待。(只研究低16位)
我们可以通过代码打印出status的退出码和退出信号。
int status=0;
pid_t rid = waitpid(-1,&status,0);
if(rid>0)
{
printf("wait sucess,rid:%d\n",rid);
}
else
{
printf("wait failed !\n");
}
sleep(3);
printf("father quit,status:%d,child quit code:%d,child quit signal:%d\n",status,(status>>8)&0xFF,status&0x7F);
退出码:(status>>8)&0xFF
退出信号:status&0x7F
但是由于这种写法有点麻烦,我们还可以用宏:
WIFEXITED(status):退出信号
WEXITSTATUS(status):退出码
int status=0;
pid_t rid = waitpid(-1,&status,0);
if(rid>0)
{
if(WIFEXITED(status))
{
printf("child quit sucess,child exit code:%d\n",WEXITSTATUS(status));
}
else{
printf("child quit unnormal!\n");
}
printf("wait sucess,rid:%d\n",rid);
}
else
{
printf("wait failed !\n");
}
阻塞等待,非阻塞等待
pid_t>0:等待成功,子进程退出了,且父进程回收成功
pid_t<0:等待失败
pid_t==0:检测成功了,只不过子进程还没有退出
阻塞等待:
如果子进程没有退出,而父进程执行waitpid进程等待,为阻塞等待;
非阻塞等待:(WNOHANG)
子进程还没有退出,父进程在等待时,允许父进程做一些其他的事情;