linux系統程式設計之程式(七):system()函式使用

mickole發表於2013-07-13

一,system()理解

功能:system()函式呼叫“/bin/sh -c command”執行特定的命令,阻塞當前程式直到command命令執行完畢

原型:

int system(const char *command);

返回值:

如果無法啟動shell執行命令,system將返回127;出現不能執行system呼叫的其他錯誤時返回-1。如果system能夠順利執行,返回那個命令的退出碼。

說明:

man幫助:

       #include <stdlib.h>

       int system(const char *command);

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

RETURN VALUE
       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 non-zero if the shell
       is available, and zero if not.

       system() does not affect the wait status of any other children.

二,system()函式原理

system函式執行時,會呼叫fork、execve、waitpid等函式。

linux版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 != EINTER){
                     status = -1;
                     break;
                 }
             }
         }
         return status;
 }
  • 函式說明
    system()會呼叫fork()產生子程式,由子程式來呼叫/bin/sh-c string來執行引數string字串所代表的命令,此命>令執行完後隨即返回原呼叫的程式。
    在呼叫system()期間SIGCHLD 訊號會被暫時擱置,SIGINT和SIGQUIT 訊號則會被忽略。
    返回值
    =-1:出現錯誤 
    =0:呼叫成功但是沒有出現子程式 
    >0:成功退出的子程式的id
    如果system()在呼叫/bin/sh時失敗則返回127,其他失敗原因返回-1。若引數string為空指標(NULL),則返回非零值>。如果system()呼叫成功則最後會返回
    執行shell命令後的返回值,但是此返回值也有可能為 system()呼叫/bin/sh失敗所返回的127,因此最好能再檢查errno 來確認執行成功。
    附加說明
    在編寫具有SUID/SGID許可權的程式時請勿使用system(),system()會繼承環境變數,通過環境變數可能會造成系統安全的問題。

system函式對返回值的處理,涉及3個階段:


階段1:建立子程式等準備工作。如果失敗,返回-1。
階段2:呼叫/bin/sh拉起shell指令碼,如果拉起失敗或者shell未正常執行結束(參見備註1),原因值被寫入到status的低8~15位元位中。system的man中只說明瞭會寫了127這個值,但實測發現還會寫126等值。
階段3:如果shell指令碼正常執行結束,將shell返回值填到status的低8~15位元位中。
備註1:
只要能夠呼叫到/bin/sh,並且執行shell過程中沒有被其他訊號異常中斷,都算正常結束。
比如:不管shell指令碼中返回什麼原因值,是0還是非0,都算正常執行結束。即使shell指令碼不存在或沒有執行許可權,也都算正常執行結束。
如果shell指令碼執行過程中被強制kill掉等情況則算異常結束。

如何判斷階段2中,shell指令碼子程式是否正常執行結束呢?系統提供了巨集:WIFEXITED(status)。如果WIFEXITED(status)為真,則說明正常結束。
如何取得階段3中的shell返回值?你可以直接通過右移8bit來實現,但安全的做法是使用系統提供的巨集:WEXITSTATUS(status)。

由於我們一般在shell指令碼中會通過返回值判斷本指令碼是否正常執行,如果成功返回0,失敗返回正數。
所以綜上,判斷一個system函式呼叫shell指令碼是否正常結束的方法應該是如下3個條件同時成立:
(1)-1 != status
(2)WIFEXITED(status)為真
(3)0 == WEXITSTATUS(status)
注意:
根據以上分析,當shell指令碼不存在、沒有執行許可權等場景下時,以上前2個條件仍會成立,此時WEXITSTATUS(status)為127,126等數值。
所以,我們在shell指令碼中不能將127,126等數值定義為返回值,否則無法區分中是shell的返回值,還是呼叫shell指令碼異常的原因值。shell指令碼中的返回值最好多1開始遞增。

示例程式:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#define EXIT_ERR(m) \
do\
{\
    perror(m);\
    exit(EXIT_FAILURE);\
}\
while (0);\

int main(void)
{
    int status ;
    status = system("ls -l|wc -l");

    if(status == -1){
        EXIT_ERR("system error");
    }

    else{
        if(WIFEXITED(status))
        {
            if(WEXITSTATUS(status) == 0)
                printf("run command successful\n");
            else
                printf("run command fail and exit code is %d\n",WEXITSTATUS(status));
        }
        else
            printf("exit status = %d\n",WEXITSTATUS(status));
    }
    return 0;
}

結果:

QQ截圖20130713131149

相關文章