美文网首页
fork 和 vfork 使用的注意事项和 system() 函

fork 和 vfork 使用的注意事项和 system() 函

作者: 丶Em1tu0F | 来源:发表于2018-07-12 14:26 被阅读0次

    转载自# :https://segmentfault.com/a/1190000005900042

    在Linux编程中,我们经常使用 Fork()。然而不少情况下,fork是有危险的。但是又不能简单使用vfork替换就成了。这个笔记说明了两者使用的一些注意点。

    vfork() 与 fork()

    #include <sys/types.h>
    #include <unistd.h>
    pid_t vfork(void);
    pid_t fork(void);
    

    vfork的作用

    函数vfork的作用是创建一个子进程,而这个子进程的作用是用于调用exec()函数族,从而再执行一个新进程(往往是用来执行其它的程序)

    代码空间

    在程序的执行效果上,fork会将父进程的地址空间复制一份,,但是vfork并不是这么做,而是 vfork 之后的子进程,在调用 exec 或 exit 之前,在父进程的空间中执行

    执行顺序

    vfork保证子进程优先执行到exec或exit之前,父进程都不会被调度。
    fork父子进程执行顺序不确定。
    因此,执行了vfork之后,子进程请立即执行 exec,而不要再执行一次 fork,否则就可能导致死锁。或者这么说,如果在exec或exit之前依赖于父进程的进一步动作,就会导致死锁

    另外请留意,exec并不是创建进程,只是用新程序替换了当前进程的上下文。

    system()的替代

    system函数的实现代码如下所示

    int system(const char * cmdstring)
    {
        pid_t pid;
        int status;
     
        if(cmdstring == NULL)
        {
            return (1); //如果cmdstring为空,返回非零值,一般为1
        }
        if((pid = fork())<0)
        {
            status = -1; //fork失败,返回-1
        }
        else if(pid == 0)
        {
            execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
            _exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
        }
        else //父进程
        {
            while(waitpid(pid, &status, 0) < 0)
            {
                if(errno != EINTR)
                {
                    status = -1; //如果waitpid被信号中断,则返回-1
                    break;
                }
            }
        }
         
        return status; //如果waitpid成功,则返回子进程的返回状态
    }
    

    频繁调用system()之后,有可能卡死不返回,就是因为出现了这个情况。system是基于fork实现的,调用后父子进程调用顺序不一定,可能导致system()调用死锁。
    这个危险的函数,现在我们已经禁用了。换成使用vfork或者是__libc_fork实现,代码如下(这份是我自己写的,不是公司的代码,不过原理上差不多):

    #define _CMD_LEN    (256)
    
    static int _system (char *cmd);
    int AMCSystemCmd (const char *format, ...)
    {
        char cmdBuff[_CMD_LEN];
        va_list vaList;
        
        va_start (vaList, format);
        vsnprintf ((char *)cmdBuff, sizeof(cmdBuff), format, vaList);
        va_end (vaList);
        
        return _system ((char *)cmdBuff);
    }
    
    extern int __libc_fork (void);
    static int _system (char *command)
    {
        int pid = 0;
        int status = 0;
        char *argv[4];
        extern char **environ;
        
        if (NULL == command) {
            return -1;
        }
        
        pid = __libc_fork();        /* vfork() also works */
        if (pid < 0) {
            return -1;
        }
        if (0 == pid) {             /* child process */
            _close_all_fds();       /* 这是我自己写的一个函数,用来关闭所有继承的文件描述符。可不用 */
            argv[0] = "sh";
            argv[1] = "-c";
            argv[2] = command;
            argv[3] = NULL;
            
            execve ("/bin/sh", argv, environ);    /* execve() also an implementation of exec() */
            exit (127);
        }
        
        // else
        /* wait for child process to start */
        do
        {
            if (waitpid (pid, &status, 0) < 0) {
                if (errno != EINTR) {
                    return -1;
                }
                else {
                    return status;
                }
            }
        } while (1);
        
        return 0;
    }
    

    使用时用AMCSystemCmd()直接替代system即可,还支持动态参数列表呢

    相关文章

      网友评论

          本文标题:fork 和 vfork 使用的注意事项和 system() 函

          本文链接:https://www.haomeiwen.com/subject/biikpftx.html