Linux下C语言的WAITPID函数的使用

进程与程序

进程是程序执行的过程,它是动态的。包括动态创建、调度、以及消亡。当用户在终端键入一个命令的时候,将启动一个进程。

eg:

1
vim

这个命令启动了一个vim进程,多个用户可以同时运行vim,但是程序只有一个,位于系统目录里的/usr/bin,每个用户的vim进程都是独立的。

C语言的fork函数

使用C语言中的fork函数之前,需要include的头文件有unistd.hsys/types.h,fork创建的进程叫子进程,原来调用fork的进程被称为父进程。
函数原型:
pid_t fork(void);

函数返回值:
执行失败返回-1,原因存于errno。
执行成功在子进程中返回0,父进程返回子进程的Pid。

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

int main(){
pid_t result;
result = fork();
int ret;
if(result==-1){
perror("创建子进程失败");
exit;
}
else if(result==0){
//sleep(5); //子进程先休眠
printf("子进程PID:%d,父进程PID:%d\n",getpid(),getppid());
ret = system("ls -l");
exit(0);
}
else{
sleep(1);
//exit(0); //父进程先停止,测试一下什么结果,结果:父进程结束,子进程等待5s后继续输出ls -l
printf("返回值:%d,子进程PID:%d,父进程PID:%d\n",result,getpid(),getppid());
ret = system("ping baidu.com");
exit(0);
}
}

上面的代码是一个最简单的fork程序,子进程与父进程共享同一段代码,根据fork的返回值判断谁是子进程谁是父进程,从而做出不同的动作。
编译后输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
子进程PID:506,父进程PID:505
total 36
-rwxrwxrwx 1 liu liu 8608 Oct 19 15:37 a.out
-rwxrwxrwx 1 liu liu 628 Oct 19 15:37 fork.c
-rwxrwxrwx 1 liu liu 8608 Oct 19 14:20 forkt
-rwxrwxrwx 1 liu liu 187 Oct 19 13:16 new.c
-rwxrwxrwx 1 liu liu 205 Oct 19 13:32 new2.c
-rwxrwxrwx 1 liu liu 130 Oct 19 12:44 pid.c
-rwxrwxrwx 1 liu liu 140 Oct 19 13:25 system.c
-rwxrwxrwx 1 liu liu 8616 Oct 19 15:22 wait
-rwxrwxrwx 1 liu liu 483 Oct 19 14:37 wait_forkpid.c
-rwxrwxrwx 1 liu liu 571 Oct 19 15:25 waitpid.c
返回值:506,子进程PID:505,父进程PID:4
PING baidu.com (123.125.115.110) 56(84) bytes of data.
64 bytes from 123.125.115.110 (123.125.115.110): icmp_seq=1 ttl=48 time=31.0 ms
64 bytes from 123.125.115.110 (123.125.115.110): icmp_seq=2 ttl=48 time=31.4 ms
64 bytes from 123.125.115.110 (123.125.115.110): icmp_seq=3 ttl=48 time=31.0 ms
64 bytes from 123.125.115.110 (123.125.115.110): icmp_seq=4 ttl=48 time=33.5 ms
64 bytes from 123.125.115.110 (123.125.115.110): icmp_seq=5 ttl=48 time=32.0 ms
^C
--- baidu.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4004ms
rtt min/avg/max/mdev = 31.031/31.829/33.562/0.942 ms

子进程的父进程Pid是505,同时,父进程的父进程是Bash(Pid:4)

避免僵尸进程

如果一个子进程终止了,但父进程没有wait或者waitpid,子进程就变成了Zombie,为了避免这种情况,使用waitpid函数。具体原因不解释了。。

waitpid函数原型:
pid_t waitpid(pid_t pid, int *ststus, int options);

pid子进程号,status子进程状态,options可为0(阻塞)或WNOHANG、WUNTRACED。

函数返回值:
执行成功返回子进程Pid,失败返回-1,原因存于errno。子进程未结束返回0。

eg:
要求子进程sleep等待10秒,父进程waitpid函数等待子进程结束,父进程等待时不阻塞,每1s在屏幕上输出一行文字,若子进程退出,打印出子进程Pid和退出状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(){
pid_t f_res;
int status,ret;
f_res = fork();
if(f_res==-1){
perror("Fork失败:");
}
else if(f_res==0){
printf("子进程Pid:%d\n",getpid());
sleep(10);
exit(6);
}
else{
while(1){
ret=waitpid(f_res,&status,WNOHANG); //WNOHANG不阻塞,没有停止的子进程立马返回,不等待。
printf("等待中... waitpid函数返回值:%d\n ",ret);
if(ret==0){
sleep(1);
}
else{
break;
}
}
printf("子进程Pid:%d,子进程结束状态:%d",ret,WIFEXITED(status));
}
}

编译后输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
子进程Pid:538
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:0
等待中... waitpid函数返回值:538
子进程Pid:538,子进程结束状态:1

另辟蹊径:
父线程fork出子线程,子线程fork出孙线程,然后子线程退出,孙线程变成孤儿进程被init接管,这样绝对不会出现僵尸进程的情况了。

Linux下C语言的WAITPID函数的使用

https://lyq.blogd.club/2018/10/18/wait-pid/

Author

lyq1996

Posted on

2018-10-19

Updated on

2022-06-05

Licensed under

Comments