system函数返回值问题(system的实现) 2.4 和2.6内核的差异

http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201121944717783/

 

1、关于在system中获取子进程的返回值与SIGCHLD

        在Linux我们一般写的是Server程序,所以,一般在main函数中,首先将进程转换为后台进程,即调用deamon,deamon的一般实现,参见::

http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201121611452549/

deamon的实现中会忽略下面的信号:

 signal(SIGINT, SIG_IGN);       //当在终端上按下ctrl+c后,会产生SIGINT信号。
 signal(SIGHUP, SIG_IGN);   
//终端退出时,会给所有的进程发送SIGHUP信号。
 signal(SIGQUIT, SIG_IGN);   
//终端退出时,会给所有的进程发送SIGQUIT信号。
 signal(SIGPIPE, SIG_IGN);    //往没有读进程的管道中进行写操作。
 signal(SIGTTOU, SIG_IGN); 
//后台进程写tty
 signal(SIGTTIN, SIG_IGN);  
 //后台进程读tty
 signal(SIGCHLD, SIG_IGN);  
 /*         子进程先于父进程结束时,会给父进程发送SIGCHLD信号
                                                          如果
                                                                    1、父进程没有忽略SGICHLD信号;
                                                                    
或者
                                                                     2、父进程没有调用wait或waitpid函数。
                                                         
那么子进程将僵死。

                                                          (

                                                               在2.6内核,只要父进程显式忽略了SIGCHLD信号,

                                                                那么子进程将不会僵死,那么system将得不到子进程的退出状态。

                                                                 也就是说system函数的返回值并不是子进程退出时的状态。 

                                                                 而2.4内核,只要父进程没有调用wait系列函数,子进程就将僵死。
                                                                                     不论是否忽略了SIGCHLD信号。

                                                          )
                                                 */
 signal(SIGTERM, SIG_IGN);   /*
                                                          当kill    pid时,向进程发送SIGTERM信号。
                                                          SIGTERM信号的默认处理是进程退出。
                                                          SIGTERM是进程在有可能的情况下退出。
                                                          注意::
                                                                        killall  -9    process_name
                                                                        发送的SIGKILL信号,强制进程退出。
                                                   */

如果,我们在我们的server中需要调用system来调用外部脚本或程序来执行某写工作。

例如:

         在脚本中通过wget下载文件::

         #!/bin/bash
        FILENAME=$0
        PATHNAME=$1
        wget $FILENAME $PATHNAME
        if [ $? -eq 0 ] ; then
              exit 0  
        else
              exit -1
        fi

         例如::

        char  command[1024];

        url=http://test/test.rar;

        pathname="./data";

        sprintf(command,"./download.sh  %s   %s",url,pathname);

        int   ret   =    system(command);    

        if( ret == 0)

        {                              //成功

        }  

        else

         {

                             //失败

         }

 

注意::

        其中ret用来接收子进程退出是的返回值。即exit的返回值。

        但是由于在deamon中忽略了SIGCHLD信号,所以主进程将不再接收子进程的返回值。所以,ret的值不能正确反映子进程的退出状态。

 

        正确的做法是::

                signal(SIGCHLD,SIG_DFL);                //默认处理方式,是接收子进程的返回值。

                system(command);

                signal(SIGCHLD,SIG_IGN);  

2、system相关问题::

         system函数其实是调用fork,exec,waitpid来实现的。

              1、fork一个进程;

              2、在子进程中调用exec去执行新程序。

              3、在父进程中调用waitpid去等待子进程结束。

       如果在父进程已经signal(SIGCHLD,SIG_IGN);那么子进程结束时,子进程的返回值不能被waitpid接收。

       这个是必须关注的问题。

 

 

下面我们来分析system的实现:

下面给出system函数及SIGCHLD信号处理分别在2.6及2.4内核下的区别。system函数源码的一个实现如下:

int system(const char * cmdstring)

{

pid_t pid;

int status;

if(cmdstring == NULL)

{

    return (1);

}

if((pid = fork())<0)

{

    status = -1;

}

else if(pid == 0)

{

    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

            _exit(127);

}

else

{

    while(waitpid(pid, &status, 0) < 0)

{

if(errno != EINTR)

{

    status = -1;

   break;

}

}

}

   return status;

}

2.6内核下当父进程未调用wait系列函数等待子进程结束且未显式地忽略SIGCHLD信号,则子进程将成为僵死进程;(如果显示忽略,则子进程不僵死)

   而在2.4内核中只要父进程未调用wait系列函数,则子进程就会成为僵死进程,不管是否显式地忽略SIGCHLD信号。

因而在SIGCHLD信号被显式忽略的情况下,2.6内核子进程将直接退出并释放资源,等父进程调用waitpid时,发现对应的pid进程已不存在,因而返回-1错误,errno为10(No child processes);

而在2.4内核下子进程在父进程waitpid之前不会退出,因而waitpid能成功获取子进程状态。

 

 

总结一下上面的内容:

对于SIGCHLD信号:

             2.6的内核:

                             1、 当未显式忽略SIGCHLD信号的情况下,未调用wait系列函数,若子进程先于父进程结束会变成僵尸进程。

                              2、当未显式忽略SIGCHLD信号的情况下,调用wait系列函数,父进程会收到SIGCHLD信号。

                              3、当显式忽略SIGCHLD信号的情况下,未调用wait系列函数,若子进程先于父进程结束不会变成僵尸进程。

                            4、  当显式忽略SIGCHLD信号的情况下,调用wait系列函数。SIGCHLD信号被显式忽略的情况下,2.6内核子进程将直接退出并释放资源,等父进程调用waitpid时,发现对应的pid进程已不存在,因而返回-1错误,errno为10(No child processes);

                            关于第四种情况。我在内核是  2.6.35.6-45.fc14.i686 机子上发现(一个父进程只派生一个子进程,让子进程先于父进程退出):虽然显式忽略SIG_CHLD信号。但是waitpid仍等待子进程结束后才返回(返回-1错误,errno为10(No child processes));当我尝试一个父进程连续fork出2个子进程时,使用wait等待。父进程在耗时最长的子进程返回后才返回。

                           若显示设置了忽略SIGCHLD信号。system等可能调用wait系列函数的函数返回值没有意义。

                           没看过源代码,记录下这种现象。

 

           2.4的内核:

                              1、当未显式忽略SIGCHLD信号的情况下,未调用wait系列函数,若子进程先于父进程结束会变成僵尸进程。

                              2、 当显式忽略SIGCHLD信号的情况下,未调用wait系列函数,若子进程先于父进程结束会变成僵尸进程。

                            3、  子进程在父进程waitpid之前不会退出,因而waitpid能成功获取子进程状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值