linux系统编程之进程(四)
今天继续研究进程相关的东东,话不多说,进入正题:
SIGCHLD:
关于它,之前章节的学习中已经用到了,具体可以参考博文:http://www.cnblogs.com/webor2006/p/3500972.html,这里会进一步来理解它:
![](http://images0.cnblogs.com/blog/324374/201401/092109428168.png)
【说明:关于信号,很快就会有一个专题来仔细研究它,现在可以简单认为:它是一种异步通知事件】
![](http://images0.cnblogs.com/blog/324374/201401/092112492074.png)
【说明:如果父进程没有查询子进程的退出状态,子进程是没有办法真正完全退出的,这时子进程的状态就称为僵尸状态,该进程就叫僵尸进程】
![](http://images0.cnblogs.com/blog/324374/201401/092115116912.png)
下面就对这两个函数进行详析:
wait:
![](http://images0.cnblogs.com/blog/324374/201401/092117296441.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的意义:
从图中可以感受到,父进程虽然是已经输出了,但是一直是等到子进程退出了才退出,这也就是wait会让父进程去查子进程的退出状态,从而避免了僵尸进程的出现。
编译运行:
对于这些状态信息,可以通过调用系统宏来查询,下面具体来介绍下:
用来代码来解释:
编译运行:
下面我们可以用abort函数,来模拟子进程非法退出的情况:
这时再编译运行:
实际上,对于子进程非法退出的,还可以判断得再仔细一些,因为有好几种情况可以造成子进程非法退出,如上图所示,一是因为捕获信号而终止,二是被暂停了,具体用法如下:
编译运行:
关于信号,由于还没有学到,所以暂停的信号就不多说了,这里就不演示暂停信号的情况了,重在理解一下获取子进程退出状态信息的用法。
【说明:上面的这些宏在sys/wait.h头文件里定义】
waitpid:
对于上面刚学完的wait,它是等待随意的进程退出,因为一个父进程可以有多个子进程,如果只要有一个子进程退出,父进程的wait就会返回;
而waitpid则可以等待特定的进程退出,这是两者的区别,下面就来具体学习下这个函数的用法:
![](http://images0.cnblogs.com/blog/324374/201401/092219438943.png)
对于waitpid的pid参数的解释与其值有关:
![](http://images0.cnblogs.com/blog/324374/201401/092221004261.png)
实际上可以通过查看man帮助得到这个信息:
![](http://images0.cnblogs.com/blog/324374/201401/092222295519.png)
可以将我们之前的程序用waitpid替换一下,效果一样:
![](http://images0.cnblogs.com/blog/324374/201401/092224391917.png)
![](http://images0.cnblogs.com/blog/324374/201401/092225461296.png)
![](http://images0.cnblogs.com/blog/324374/201401/092226308634.png)
也同样,用它来改装我们之前的程序,对于父进程,实际上只有一个子进程,就可以直接传子进程的id既可,只等待这个子进程:
![](http://images0.cnblogs.com/blog/324374/201401/092229430359.png)
效果一样:
![](http://images0.cnblogs.com/blog/324374/201401/092225461296.png)
![](http://images0.cnblogs.com/blog/324374/201401/092230245989.png)
![](http://images0.cnblogs.com/blog/324374/201401/092232360045.png)
比如:waitpid(-100,&status,0)的意思就是,等待进程组ID=100里面的任一一个子进程。
最后,关于wait和waitpid,进行一个总结:
![](http://images0.cnblogs.com/blog/324374/201401/092239134888.png)
另外,对于僵进程,已经被提到过好几次了,最后再来总结一下:
僵进程:
![](http://images0.cnblogs.com/blog/324374/201401/092241097853.png)
如何避免僵进程:
![](http://images0.cnblogs.com/blog/324374/201401/092242245194.png)
system:
![](http://images0.cnblogs.com/blog/324374/201401/102145050357.png)
实际上,它就等于用代码去执行我们在命令行中敲的那些shell命令,下面就以实际代码来认识这个函数的使用:
![](http://images0.cnblogs.com/blog/324374/201401/102150028321.png)
编译运行:
![](http://images0.cnblogs.com/blog/324374/201401/102151253794.png)
实际上,system()函数是调用"/bin/sh -c",如下:
![](http://images0.cnblogs.com/blog/324374/201401/102153106443.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; }
编译运行:
可见,通过自己实现的system,就能够大致弄清楚它的原理,虽说程序比较少,但是五脏俱全,因为可以把自己学过的知识给运用起来。
好了,今天的学习就到这,下次见。