在阅读《程序员的自我修养》一书中,关于可执行文件的装载一节中有一个实现minishell脚本的一段代码,大致功能是在该程序中输入需要加载的应用的名称,回车后可以得到对应的应用,有点类似一个shell终端,下面是我自己编程测试以及在这个过程中关于多进程的一些测试:
情况一:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
pid_t pid;
char buf[200] = {0};
while(1)
{
printf("minibash:");
scanf("%s",buf);
pid = fork();
if(pid < 0)
{
printf("fork error\n");
}
else if(pid == 0) //child exe
{
printf("i am child process,my number is:%d\n",getpid());
if(execlp(buf,0) < 0)
printf("execlp error\n");
}
else //father exe
{
int status;
waitpid(pid,&status,0); //防止僵尸进程的产生 参数1确认之等待进程id为pid的进程
printf("child exe is return\n");
}
}
return 0;
}
该源码利用fork系统调用复制了一份testShell的进程(共享代码段和数据段,堆共享,栈不共享),然后在子进程中利用execlp装载新的应用,同时在父进程中调用waitpid挂起,直到等到子进程返回信号或异常退出。
根据测试情况可知,在子进程中装载完execlp新的进程之后,原来复制的那一份子进程就已经返回了,现在的进程是一个全新的进程,因此父进程中waitpid正常返回,并且继续等待下一个scanf用户输入。
这种情况父进程正确处理了对子进程的资源的回收,不会出现僵尸进程的情况。
情况二:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char *argv[])
{
pid_t pid;
char buf[200] = {0};
while(1)
{
printf("minibash:");
scanf("%s",buf);
pid = fork();
if(pid < 0)
{
printf("fork error\n");
}
else if(pid == 0) //child exe
{
printf("i am child process,my number is:%d\n",getpid());
//if(execlp(buf,0) < 0)
//printf("execlp error\n");
}
else //father exe
{
int status;
//waitpid(pid,&status,0); //防止僵尸进程的产生 参数1确认之等待进程id为pid的进程
printf("child exe is return\n");
}
}
return 0;
}
将新进程的创建这一步屏蔽,同时父进程中对资源的回收也屏蔽,这样的结果就是在minishell中执行任意输入之后,会创建本进程的一个拷贝,并且该子进程不会退出(while(1)),如果此时使用命令kill掉对应的pid,将会造成僵尸进程的情况发生:
情况三
此时想到,waitpid(pid,NULL,0)在父进程的调用中将会阻塞父进程,导致父进程不能执行其他的功能,网上说waitpid(pid,NULL,WNOHANG)第三个参数可以设置为非阻塞,后面再由于系统询问该父进程是否有子进程退出,但是这种方式实际编码测试,确实没有阻塞,但还是会造成产生僵尸进程的结果。
此时使用第三种方法,利用signal函数注册子进程退出的信号,再由专门的信号处理函数处理子进程的资源回收。代码如下:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
void func_waitpid(int signo)
{
pid_t pid;
int status;
while((pid = waitpid(-1,&status,WNOHANG)) > 0)
{
printf("child %d exit\n",pid);
}
//return;
}
int main(int argc,char *argv[])
{
signal(SIGCHLD,&func_waitpid); //信号注册 防止僵尸进程的产生
pid_t pid;
char buf[200] = {0};
while(1)
{
printf("minibash:");
scanf("%s",buf);
pid = fork();
if(pid < 0)
{
printf("fork error\n");
}
else if(pid == 0) //child exe
{
printf("i am child process,my number is:%d\n",getpid());
if(execlp(buf,0) < 0)
printf("execlp error\n");
}
else //father exe
{
int status;
//waitpid(-1,&status,WNOHANG); //防止僵尸进程的产生 参数1确认之等待进程id为pid的进程
printf("father process is run\n");
}
}
return 0;
}