【C/C++】Linux下使用system()函數必定要謹慎

曾經的曾經,被system()函數折磨過,之因此這樣,是由於對system()函數了解不夠深刻。只是簡單的知道用這個函數執行一個系統命令,這遠遠不夠,它的返回值、它所執行命令的返回值以及命令執行失敗緣由如何定位,這纔是重點。當初由於這個函數風險較多,故拋棄不用,改用其餘的方法。這裏先不說我用了什麼方法,這裏必需要搞懂system()函數,由於仍是有不少人用了system()函數,有時你不得不面對它。

先來看一下system()函數的簡單介紹:
#include <stdlib.h>
int system(const char *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.shell

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被信號暫停執行,取得信號值
}

到於取得子進程返回值的相關介紹能夠參考另外一篇文章:http://my.oschina.net/renhc/blog/35116bash

 

system()函數用起來很容易出錯,返回值太多,並且返回值很容易跟command的返回值混淆。這裏推薦使用popen()函數替代,關於popen()函數的簡單使用也能夠經過上面的連接查看。函數

popen()函數較於system()函數的優點在於使用簡單,popen()函數只返回兩個值:
成功返回子進程的status,使用WIFEXITED相關宏就能夠取得command的返回結果;
失敗返回-1,咱們能夠使用perro()函數或strerror()函數獲得有用的錯誤信息。spa

這篇文章只涉及了system()函數的簡單使用,尚未談及SIGCHLD、SIGINT和SIGQUIT對system()函數的影響,事實上,之因此今天寫這篇文章,是由於項目中因有人使用了system()函數而形成了很嚴重的事故。現像是system()函數執行時會產生一個錯誤:「No child processes」。.net

關於這個錯誤的分析,感興趣的朋友能夠看一下:http://my.oschina.net/renhc/blog/54582debug

 

2012-04-14 qdurenhongcai@163.com指針

轉載請註明出處。code

相關文章
相關標籤/搜索