fork 和 vfork 使用的注意事項和 system() 函式的替代

weixin_34306676發表於2016-07-07

在Linux程式設計中,我們經常使用 Fork()。然而不少情況下,fork是有危險的。但是又不能簡單使用vfork替換就成了。這個筆記說明了兩者使用的一些注意點。
本文地址:https://segmentfault.com/a/11...


Reference:

fork與vfork的區別
vfork,fork,exec函式的區別
C語言函式 atexit execl execlp ……(太長了)

vfork() 與 fork()

pid_t vfork(void);

vfork的作用

函式vfork的作用是建立一個子程式,而這個子程式的作用是用於呼叫exec(),從而再執行一個新程式(往往是用來執行其它的程式)

程式碼空間

在程式的執行效果上,fork會將父程式的地址空間複製一份,,但是vfork並不是這麼做,而是 vfork 之後的子程式,在呼叫 exec 或 exit 之前,在父程式的空間中執行

執行順序

vfork保證子程式優先執行到execexit之前,父程式都不會被排程。
fork父子程式執行順序不確定。

因此,執行了vfork之後,子程式請立即執行 exec,而不要再執行一次 fork,否則就可能導致死鎖。
或者這麼說,如果在execexit之前依賴於父程式的進一步動作,就會導致死鎖

另外請留意,exec並不是建立程式,只是用新程式替換了當前程式的上下文。

system()的替代

頻繁呼叫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即可,還支援動態引數列表呢

相關文章