开发者学堂课程【物联网开发- Linux 高级程序设计全套视频:Wait 函数】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/660/detail/10989
Wait 函数
内容简介:
一、wait 或 waitpid 的作用
二、代码实现的过程及内容
三、wait 的相关内容
四、取出子进程的退出状态
一、wait 或 waitpid 的作用
进程的等待,那父子进程有时候需要进行简单的进程间同步,比如父进程等待子进程结束,子进程结束了之后,父进程再去干它的事情,然后父进程等待子进程结束,那个等是用的wait或者waitpid ,这两个函数当中的其中一个都能去等待子进程结束,
注意一下 wait 和 waitpid 的作用,其实它有两个作用,一个是等,父进程调用 wait 之后,只能是父等子,即父进程等子进程,父进程的程序执行 wait 或者 waitpid 的时候有两个作用,一个是挂起,等待子进程结束,等子进程结束了之后,它再解除挂起,然后继续往下运行代码,即就在等;第二个作用就是回收子进程的资源,子进程结束了之后回收子进程的资源,如果不调用 wait 或者 waitpid 等子进程结束了,父进程还没结束,这属于什么?
没有调用 wait ,父进程没有结束,子进程已经结束了,这个情况子进程的资源就没有被回收,子进程就处于僵尸状态,所以有一个概念叫僵尸进程,那么什么叫做僵尸进程?
子进程已经结束了,父进程没有结束,而且父进程没有去调 wait 去回收它的资源。
二、代码实现的过程及内容
1、修改上节代码
在讲wait函数之前先把代码改一下,先输以下代码:
[01_day]./fork3
In write to stdout
Before fork
In son process
In father process
[01_day]./fork3 > aaa.txt
[01_day]vi aaa.txt
[01_day]cp fork.c wait.c
[01_day]vi wait.c
点执行之后代码如下所示:
/********************************************************
* Filename: fork.c
* Description:
* Version: 1.0
* Created: 2019年01月08日 01时43分00秒
* Revision: none\
* Compiler: gcc
* Author: YOUR NAME().
* Company:
**********************************************************
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid=fork();
if(pid<0)
{
perror(“fork”)
return 0;
}
else if(pid == 0)
{
while(1)
{
printf(“this is son process\n”);
sleep(1);
}
}
else
{
while(1)
}
2、再将代码中 while 循环改成 for 循环
可以看到子进程 while 循环,现将 while 循环改成 for 循环,如下所示:
/********************************************************
* Filename: fork.c
* Description:
* Version: 1.0
* Created: 2019年01月08日 01时43分00秒
* Revision: none\
* Compiler: gcc
* Author: YOUR NAME().
* Company:
**********************************************************
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t pid;
int i;
pid=fork();
if(pid<0)
{
perror(“fork”)
return 0;
}
else if(pid == 0)
{
//while(1)
for(i=0;i<5;i++)
{
printf(“this is son process\n”);
sleep(1);
}
}
else
while(1)
}
I 小于五, I 加加是让它循环五次,当然在此前要定义一个整形的变量 i ,如上所示般,那样子进程只能活五秒,而父进程一直在循环,父进程没有调 wait ,没有调 wait 它既不会等,也不会回收子进程的资源。
3、运行上述代码及说明
运行以上修改之后的代码,运行结果如下:
[01_day]./fork3
In write to stdout
Before fork
In son process
In father process
[01_day]./fork3 > aaa.txt
[01_day]vi aaa.txt
[01_day]cp fork.c wait.c
4、ps两个进程的状态及说明
可以看到前五秒的时候它俩都在,五秒钟之后子进程已经结束了,而父进程一直在执行,这时用 ps 来这两个进程的状态,
结果如下所示:
[~]ps -a
PID TTY TIME CMD
6574 pts/0 00:00:00 wait
6575 pts/0 00:00:00 wait <defunct>
6763 pts/0 00:00:00 ps
可以看到父进程的进程号是6574,是一个正常的进程,而子进程还在,按理说这个时候子进程已经都结束了,但是“ps -a”的时候,子进程的进程信息还能打出来,可以看到后面带了一个单词,那就说明这个时候子进程已经结束了,但是它的资源没有被回收,子进程处于僵尸状态,进程已经结束,但资源没有被回收,但是尸体没烂,没有被大自然分解,这相当于父进程还在,子进程已经结束了,父进程如果不回收它的资源,那么子进程就是僵尸,如果父进程也结束了,父进程也没回收,那父子进程都挂了,系统就回收,但只要父进程还在,系统就不会回收子进程的资源。
5、修改程序的说明
再将程序稍微改一改,在 while 处添加 wait ,调一下 wait ,它会先等子进程结束了,回收子进程的资源,再运行父进程的 while循环,此程序一开始父进程就执行,然后先让它等,怎么等?
这个函数有一个参数是一个整型的指针,这个参数用来保存子进程的退出状态的,定义一个整形的变量传递指针进去,这个指针指向的整形变量就保存了子进程的退出状态。
三、wait 的相关内容
1、返回值
返回值是什么?如果执行成功,返回的是子进程的进程号,出错返回负一。
2、功能及其代码和说明
看此函数的功能,它详细的描述等待子进程终止,如果子进程终止了,则该函数回收它的资源,如果子进程没有终止就等,调用 wait 函数的进程会被挂起,即进入挂起状态,直到它的一个子进程退出或收到一个不能被忽视的信号才会被唤醒,父进程只要调了 wait ,父进程就开始等,一直停在那里,直到它有一个子进程退出了或者是收到一个信号了,它才不等了,如果等的时候,如果调用进程没有子进程,是等不到的,或者它的子进程已经结束了,然后该函数它就不会堵塞,直接立即返回,现在试一下,将代码进行稍微的修改,如下所示:
/********************************************************
* Filename: fork.c
* Description:
* Version: 1.0
* Created: 2019年01月08日 01时43分00秒
* Revision: none\
* Compiler: gcc
* Author: YOUR NAME().
* Company:
**********************************************************
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid_t pid_bk;
int i;
int s;
pid=fork();
if(pid<0)
{
perror(“fork”)
return 0;
}
else if(pid == 0)
{
//while(1)
for(i=0;i<5;i++)
{
printf(“this is son process\n”);
sleep(1);
}
}
else
{
pid_bk=wait(&s)
printf(“pid_bk=%d\n”,pid-bk”)
while(1)
{
printf(“this is father process\n”)
sleep(1)
}
}
用 s 保存子进程的退出状态,可以看到子进程是循环五次的,父进程当中调 wait ,传 s 的地址进来。接 wait 的返回值,如果 wait成功,它应该是子进程的进程号, s 里边保存着子进程的退出状态,如果不想保存子进程的退出状态。
可以看到子进程在“for(i=0;i<5;i++)
{
printf(“this is son process\n”);
sleep(1);
}
”循环5秒,然后父进程就会“pid_bk=wait(&s)”再等五秒,等五秒钟到了之后,子进程结束了,然后它将状态保存在 S 里边,并且返回值给 pid_bk 赋值,即子进程的进程号,然后 wait 还会干一件事情,即回收子进程的资源,然后父进程再进入循环。
3、运行上述代码及说明
将以上代码运行,效果如下:
[01_day]./wait
this is son process
this is son process
this is son process
this is son process
this is son process
pid_bk=6815
this is father process
this is father process
this is father process
this is father process
this is father process
可以看到前五秒的时候只有子进程在打印,父进程在 wait ,五秒钟之后,拿到了子进程的进程号码,父进程就开始在运行 while (1)了。
4、ps -a代码及说明
这时再去 ps -a ,执行代码效果如下:
[~]ps -a
PID TTY TIME CMD
6814 pts/0 00:00:00 wait
6817 pts/2 00:00:00 ps
会发现子进程看不见了,只有父进程在,因为在代码当中去 wait的时候, wait 几个功能,一个是等到了之后,它要回收子进程的资源,然后将子进程的退出状态保存 S 里边,并且返回子进程的记号,这是 wait 的功能, wait 通过上述例子改编,能看到wait的效果。
四、取出子进程的退出状态
1、分析
子进程的退出状态怎么拿到?可以通过 WIFEXITED(status) 和WEXITSTATUS(status) ,子进程退出状态保存在 S 里边,然后可以通过 S ,
通过 WIFEXITED(status) 和 WEXITSTATUS(status) ,通过 WIFEXITED(status) 传 S 进去,这个 WIFEXITED(status) 的意思是:如果子进程是正常终止的,那取出的字段为非零,就是整个表达式的结果不为零,什么是非正常终止?
如果收到信号,然后终止的话,即被别人杀掉了,这就是非正常终止,如果程序正常结束了,即Retire了,就是正常终止,如果是正常终止的。
2、代码修改
现在将以上代码加个判断,如下所示:
/********************************************************
* Filename: fork.c
* Description:
* Version: 1.0
* Created: 2019年01月08日 01时43分00秒
* Revision: none\
* Compiler: gcc
* Author: YOUR NAME().
* Company:
**********************************************************
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid_t pid_bk;
int i;
int s;pid=fork();
if(pid<0)
{
perror(“fork”)
return 0;
}
else if(pid == 0)
{
//while(1)
for(i=0;i<5;i++)
{
printf(“this is son process\n”);
sleep(1);
}
return 5
}
else
{
pid_bk=wait(&s)
if(WIFEXITED(s) != 0)
{
printf(“%d\n”,WEXITSTATUS(s));
}
printf(“pid_bk=%d\n”,pid-bk”)
while(1)
{
printf(“this is father process\n”)
sleep(1)
}
}
3、代码说明
在上文代码中如果它不等于零,即是正常结束的,如果正常结束,看一下子进程的返回值,子进程会执行 return 0 ,那在上文代码中return 5 ,结束了之后就可以拿到它的返回值,那么怎么拿到它的返回值?可以通过 WEXITSTATUS(status) ,它传 S 进去。
4、首次执行结果出错
将以上代码运行,结果如下:
[01_day]gcc wait.c -o wait
/tmp/cc4Gf6DO.o: In function `main’:
wait.c:(.text+0x83) : undefined reference to `WIFEXITED’
wait.c:(.text+0x83) : undefined reference to `WEXITSTATUS’
collect2 : 1d
返回 1
5、对代码进行修改
`WIFEXITED’这个不认识,那是因为什么?因为 wait 函数应该包含头文件,所以将以上代码修改为:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
pid_t pid;
pid_t pid_bk;
int i;
int s;
pid=fork();
if(pid<0)
{
return 0;
}
else if(pid == 0)
{
//while(1)
for(i=0;i<5;i++)
{
printf(“this is son process\n”);
sleep(1);
}
return 5
}
else
{
pid_bk=wait(&s)
if(WIFEXITED(s) != 0)
{
printf(“%d\n”,WEXITSTATUS(s));
}
printf(“pid_bk=%d\n”,pid-bk”)
while(1)
{
printf(“this is father process\n”)
sleep(1)
}
}
6、再次运行代码及说明
再将上述代码运行试一下效果,效果如下:
可以看到前五秒都是子进程,父进程等五秒,五秒钟到了之后,打了那个五,所以说这是 wait 的功能,即子进程的退出状态保存在 S里边,
然后可以通过特定的 WIFEXITED(status)和WEXITSTATUS(status) 判断资金是否是正常终止退出的、子进程的返回值是多少,到此为止 wait 讲的差不多了。