今天继续研究进程相关的东东,话不多说,进入正题:
SIGCHLD:
关于它,之前章节的学习中已经用到了,具体可以参考博文:http://www.cnblogs.com/webor2006/p/3500972.html,这里会进一步来理解它:
![](https://i-blog.csdnimg.cn/blog_migrate/ee8dccba6282ae161bfcb582c0592028.png)
【
说明:关于信号,很快就会有一个专题来仔细研究它,现在可以简单认为:
它是一种异步通知事件】
![](https://i-blog.csdnimg.cn/blog_migrate/dd9bd58967638b89f6d40cac5c3927d8.png)
【
说明:如果父进程没有查询子进程的退出状态,子进程是没有办法真正完全退出的,这时子进程的状态就称为
僵尸状态,该进程就叫
僵尸进程】
![](https://i-blog.csdnimg.cn/blog_migrate/e9029ac1877317e8ac45808d1be46dee.png)
下面就对这两个函数进行详析:
wait:
![](https://i-blog.csdnimg.cn/blog_migrate/fe6b43d382f1e69c7f67d1f48b33ff1d.png)
下面用具体代码来进行说明:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>//提供wait函数声明
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid == 0)
{
sleep(3);//子进程休眠,是为了看到父进程会等待子进程退出
printf("this is child\n");
exit(100);
}
printf("this is parent\n");
int status;
wait(&status);等待子进程退出
return 0;
}
看一下编译运行效果,下面用动画来展现,以便能体现到wait的意义:
![](https://i-blog.csdnimg.cn/blog_migrate/ae07956e3331033c477760a1849632d2.gif)
从图中可以感受到,父进程虽然是已经输出了,但是一直是等到子进程退出了才退出,这也就是wait会让父进程去查子进程的退出状态,从而避免了僵尸进程的出现。
![](https://i-blog.csdnimg.cn/blog_migrate/691e2996c222329f876a47e5f744d573.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e43a41ac7facdb3cce702a8935b07f2d.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/3f6c95d3ef1f4ec4275027b7280dab7c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/59d6c947a5c721ef575c87cb469cffc2.png)
对于这些状态信息,可以通过调用系统宏来查询,下面具体来介绍下:
![](https://i-blog.csdnimg.cn/blog_migrate/7bd1393fa94893d50f97072fd8d8c96a.png)
用来代码来解释:
![](https://i-blog.csdnimg.cn/blog_migrate/65650a2c58db005853b7a699fd31f37d.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/28d7c6c6ffde72c42d4e9bbd9e14647b.png)
下面我们可以用abort函数,来模拟子进程非法退出的情况:
![](https://i-blog.csdnimg.cn/blog_migrate/596548dd50a183d628fe35d0bf6611a5.png)
这时再编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/57bf3f349c4d9a01c7a3483c5c567f9d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/873a942a513459860d83b9f6f51da5d5.png)
实际上,对于子进程非法退出的,还可以判断得再仔细一些,因为有好几种情况可以造成子进程非法退出,如上图所示,一是因为捕获信号而终止,二是被暂停了,具体用法如下:
![](https://i-blog.csdnimg.cn/blog_migrate/76322d591fe0ad1a5ace04c6cb4ceffa.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/f90fef121d31d69a42e12aba85e5b464.png)
关于信号,由于还没有学到,所以暂停的信号就不多说了,这里就不演示暂停信号的情况了,重在理解一下获取子进程退出状态信息的用法。
【说明:上面的这些宏在sys/wait.h头文件里定义】
waitpid:
对于上面刚学完的wait,它是等待随意的进程退出,因为一个父进程可以有多个子进程,如果只要有一个子进程退出,父进程的wait就会返回;
而waitpid则可以等待特定的进程退出,这是两者的区别,下面就来具体学习下这个函数的用法:
![](https://i-blog.csdnimg.cn/blog_migrate/753165477614f6fff24257ff680871db.png)
对于waitpid的
pid参数的解释与其值有关:
![](https://i-blog.csdnimg.cn/blog_migrate/4a5591577032aebd269c57be86dedd49.png)
实际上可以通过查看man帮助得到这个信息:
![](https://i-blog.csdnimg.cn/blog_migrate/ffc75e08825fb5c558cf4ef09287d095.png)
可以将我们之前的程序用waitpid替换一下,效果一样:
![](https://i-blog.csdnimg.cn/blog_migrate/658cd29103dbe5df9adbc9d70cc31c9b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ee02dc27df0d0cf31d3c54dc5f052285.png)
![](https://i-blog.csdnimg.cn/blog_migrate/344b9fbcc389669a2627761aef1c1618.png)
也同样,用它来改装我们之前的程序,对于父进程,实际上只有一个子进程,就可以直接传子进程的id既可,只等待这个子进程:
![](https://i-blog.csdnimg.cn/blog_migrate/56f892efa732de27bca6bb6d3b346806.png)
效果一样:
![](https://i-blog.csdnimg.cn/blog_migrate/c9a30f8a087d3a48d44aada5905853ef.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a1fa68d300bb18a8b3e529e7e9cfecee.png)
![](https://i-blog.csdnimg.cn/blog_migrate/dc0178b63c8ff7cb3c9740a11a097f55.png)
比如:waitpid(-100,&status,0)的意思就是,等待进程组ID=100里面的任一一个子进程。
最后,关于wait和waitpid,进行一个总结:
![](https://i-blog.csdnimg.cn/blog_migrate/c69d601c4cfb0ca0484f0e1159bd25ca.png)
另外,对于僵进程,已经被提到过好几次了,最后再来总结一下:
僵进程:
![](https://i-blog.csdnimg.cn/blog_migrate/11c042035a05fb985d31e3e347be68b4.png)
如何避免僵进程:
![](https://i-blog.csdnimg.cn/blog_migrate/a6d4bce189084932a3692f0ab5cacf9f.png)
system:
![](https://i-blog.csdnimg.cn/blog_migrate/89686441e0e308fdda97f559c8f57e2a.png)
实际上,它就等于用代码去执行我们在命令行中敲的那些shell命令,下面就以实际代码来认识这个函数的使用:
![](https://i-blog.csdnimg.cn/blog_migrate/34813d85f628934d2b06f4d0d05d536f.png)
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/3017b6453dc642ab7d78791d3f487d77.png)
实际上,system()函数是调用"/bin/sh -c",如下:
![](https://i-blog.csdnimg.cn/blog_migrate/3b1ae9292a4b8a6d17f9abbf3b785f9d.png)
对于这个函数的使用,其实没什么难的,但是这个函数很有代表性,我们可以通过它来综合运用我们所学习东西,这样其实还是挺有意义的,接下来,会自己实现一个跟system同样的功能,来达到理解system函数的实现原理:
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int my_system(const char *command);//自己实现有system函数声明
int main(int argc, char *argv[])
{
my_system("ls -l | wc -w");//这里改用自己实现在system
return 0;
}
int my_system(const char *command)
{
pid_t pid;
int status;
if (command == NULL)
return 1;
if ((pid = fork()) < 0)
status = -1;//出现不能执行system调用的其他错误时返回-1
else if (pid == 0)
{//子进程
execl("/bin/sh", "sh", "-c", command, NULL);//替换成sh进程
exit(127);//如果无法启动shell运行命令,system将返回127,因为如果成功替换了之后,是不会执行到这句来的
}
else
{//父进程会等到子进程执行完
while (waitpid(pid, &status, 0) < 0)
{
if (errno == EINTR)//如果是被信号打断的,则重新waitpid
continue;
status = -1;
break;
}
//这时就顺利执行完了
}
return status;
}
编译运行:
![](https://i-blog.csdnimg.cn/blog_migrate/e5f1f6d27a39f6c4e86d4f19136b8a34.png)
可见,通过自己实现的system,就能够大致弄清楚它的原理,虽说程序比较少,但是五脏俱全,因为可以把自己学过的知识给运用起来。
好了,今天的学习就到这,下次见。