1. 程式人生 > >Linux下使用system()函式一定要謹慎

Linux下使用system()函式一定要謹慎

曾經的曾經,被system()函式折磨過,之所以這樣,是因為對system()函數了解不夠深入。只是簡單的知道用這個函式執行一個系統命令,這遠遠不夠,它的返回值、它所執行命令的返回值以及命令執行失敗原因如何定位,這才是重點。當初因為這個函式風險較多,故拋棄不用,改用其他的方法。這裡先不說我用了什麼方法,這裡必須要搞懂system()函式,因為還是有很多人用了system()函式,有時你不得不面對它。 先來看一下system()函式的簡單介紹: ?
1 2 #include <stdlib.h> intsystem(constchar*command);

system() executes a command specified in command by calling /bin/sh -c command,and returns after the command has been completed.During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

system()函式呼叫/bin/sh來執行引數指定的命令,/bin/sh 一般是一個軟連線,指向某個具體的shell,比如bash,-c選項是告訴shell從字串command中讀取命令; 在該command執行期間,SIGCHLD是被阻塞的,好比在說:hi,核心,這會不要給我送SIGCHLD訊號,等我忙完再說; 在該command執行期間,SIGINT和SIGQUIT是被忽略的,意思是程序收到這兩個訊號後沒有任何動作。 再來看一下system()函式返回值: The value returned is -1 on error (e.g. fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).
If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not. 為了更好的理解system()函式返回值,需要了解其執行過程,實際上system()函式執行了三步操作: 1.fork一個子程序; 2.在子程序中呼叫exec函式去執行command; 3.在父程序中呼叫wait去等待子程序結束。 對於fork失敗,system()函式返回-1。 如果exec執行成功,也即command順利執行完畢,則返回command通過exit或return返回的值。 (注意,command順利執行不代表執行成功,比如command:"rm debuglog.txt",不管檔案存不存在該command都順利執行了) 如果exec執行失敗,也即command沒有順利執行,比如被訊號中斷,或者command命令根本不存在,system()函式返回127. 如果command為NULL,則system()函式返回非0值,一般為1. 看一下system()函式的原始碼
看完這些,我想肯定有人對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()函式返回0呢?只在command命令返回0時。 看一下該怎麼監控system()函式執行狀態 這裡給我出的做法:
int status;
if(NULL == cmdstring) //如果cmdstring為空趁早閃退吧,儘管system()函式也能處理空指標
{
    return XXX;
}
status = system(cmdstring);
if(status < 0)
{
    printf("cmd: %s\t error: %s", cmdstring, strerror(errno)); // 這裡務必要把errno資訊輸出或記入Log
    return XXX;
}
 
if(WIFEXITED(status))
{
    printf("normal termination, exit status = %d\n", WEXITSTATUS(status)); //取得cmdstring執行結果
}
else if(WIFSIGNALED(status))
{
    printf("abnormal termination,signal number =%d\n", WTERMSIG(status)); //如果cmdstring被訊號中斷,取得訊號值
}
else if(WIFSTOPPED(status))
{
    printf("process stopped, signal number =%d\n", WSTOPSIG(status)); //如果cmdstring被訊號暫停執行,取得訊號值
}

system()函式用起來很容易出錯,返回值太多,而且返回值很容易跟command的返回值混淆。這裡推薦使用popen()函式替代,關於popen()函式的簡單使用也可以通過上面的連結檢視。

popen()函式較於system()函式的優勢在於使用簡單,popen()函式只返回兩個值:
成功返回子程序的status,使用WIFEXITED相關巨集就可以取得command的返回結果;
失敗返回-1,我們可以使用perro()函式或strerror()函式得到有用的錯誤資訊。

這篇文章只涉及了system()函式的簡單使用,還沒有談及SIGCHLD、SIGINT和SIGQUIT對system()函式的影響,事實上,之所以今天寫這篇文章,是因為專案中因有人使用了system()函式而造成了很嚴重的事故。現像是system()函式執行時會產生一個錯誤:“No child processes”。