1. 程式人生 > >system函式的原理和呼叫方法

system函式的原理和呼叫方法

在C/C++中我們有時需要用到system系統呼叫來完成一些作業系統“更加擅長”的工作,本篇對system呼叫原理,呼叫方法,返回值封裝巨集,system簡單原始碼等內容進行了描述。

1. 應用例子

#include <stdlib.h>

int system(const char *command);

返回值:

通常:shell命令執行成功返回0,失敗返回非0.

1. 若引數string為空指標(NULL),則返回非零值

2. 若system()在呼叫/bin/sh時失敗則返回127

返回127的例子:

#include <stdio.h>

#include <stdlib.h>

#include <sys/wait.h>

int main(int argc, char **argv)

{

  const char* cmd = "ll /home/wipan";

  int status = system(cmd);

  printf("Returned code: %d\n", WEXITSTATUS(status));

}

輸入結果為:

[[email protected] ~/src]$ ./system

sh: ll: command not found

Returned code: 127

3. 若fork不成功返回-1.

4. 若system()呼叫成功則最後會返回執行shell命令後的返回值(此值也可能是127,若shell命令執行的最後有返回值,則需注意其返回值的判斷方法)

例子:

 while ( !HDDready )

  {

    status = system("/usr/dhafw/InstallHDD/isHddMounted.sh > /tmp/isHddMounted");

    if (status == 0)   //呼叫成功

      {

         // Now check result of command

         fp = fopen("/tmp/isHddMounted", "r");

         ……

          }

      }

    else  //呼叫失敗

      {

        sleep(1);

      }

  }

2. system的呼叫過程原理

原理:當system接受的命令為NULL時直接返回,否則fork出一個子程序,因為fork在兩個程序:父程序和子程序中都返回,這裡要檢查返回的pid,fork在子程序中返回0,在父程序中返回子程序的pid,父程序使用waitpid等待子程序結束,子程序則是呼叫execl來啟動一個程式代替自己,execl("/bin/sh", "sh", "-c", cmdstring, (char*)0)是呼叫shell,這個shell的路徑是/bin/sh,後面的字串都是引數,然後子程序就變成了一個shell程序,這個shell的引數是cmdstring,就是system接受的引數。在windows中的shell是command,想必大家很熟悉shell接受命令之後做的事了。

3. 判斷system返回是否成功更加詳細的方法(實踐中我們用上一種即可)

3.1   背景

        exit的名字就能看出,這個系統呼叫是用來終止一個程序的。無論在程式中的什麼位置,只要執行到exit系統呼叫,程序就會停止剩下的所有操作,清除包括PCB在內的各種資料結構,並終止本程序的執行。

        exit系統呼叫帶有一個整數型別的引數status,我們可以利用這個引數傳遞程序結束時的狀態,比如說,該程序是正常結束的,還是出現某種意外而結束的,一般來說,0表示沒有意外的正常結束;其他的數值表示出現了錯誤,程序非正常結束。我們在實際程式設計時,可以用wait系統呼叫接收子程序的返回值,從而針對不同的情況進行不同的處理。關於wait的詳細情況,我們將在以後的篇幅中進行介紹。

        在一個程序呼叫了exit之後,該程序並非馬上就消失掉,而是留下一個稱為殭屍程序(Zombie)的資料結構。在Linux程序的5種狀態中,殭屍程序是非常特殊的一種,它已經放棄了幾乎所有記憶體空間,沒有任何可執行程式碼,也不能被排程,僅僅在程序列表中保留一個位置,記載該程序的退出狀態等資訊供其他程序收集。

        先來了解一下殭屍程序的來由,我們知道,Linux和UNIX總有著剪不斷理還亂的親緣關係,殭屍程序的概念也是從UNIX上繼承來的,而UNIX的先驅們設計這個東西並非是因為閒來無聊想煩煩其他的程式設計師。殭屍程序中儲存著很多對程式設計師和系統管理員非常重要的資訊,首先,這個程序是怎麼死亡的?是正常退出呢,還是出現了錯誤,還是被其它程序強迫退出的?其次,這個程序佔用的總系統CPU時間和總使用者CPU時間分別是多少?發生頁錯誤的數目和收到訊號的數目。這些資訊都被儲存在殭屍程序中,試想如果沒有殭屍程序,程序一退出,所有與之相關的資訊都立刻歸於無形,而此時程式設計師或系統管理員需要用到,就只好乾瞪眼了。

        waitpid呼叫和wait呼叫。這兩者的作用都是收集殭屍程序留下的資訊,同時使這個程序徹底消失。程序一旦呼叫了wait,就立即阻塞自己,由wait自動分析是否當前程序的某個子程序已經退出,如果讓它找到了這樣一個已經變成殭屍的子程序,wait就會收集這個子程序的資訊,並把它徹底銷燬後返回;如果沒有找到這樣一個子程序,wait就會一直阻塞在這裡,直到有一個出現為止。

3.2   判斷返回值的巨集

如果引數status的值不是NULL,wait就會把子程序退出時的狀態取出並存入其中,這是一個整數值(int),指出了子程序是正常退出還是被非正常結束的,或被哪一個訊號結束的等資訊。由於這些資訊被存放在一個整數的不同二進位制位中,所以用常規的方法讀取會非常麻煩,人們就設計了一套專門的巨集(macro)來完成這項工作,下面我們來學習一下其中最常用的兩個:

1)  WIFEXITED(status) 這個巨集用來指出子程序是否為正常退出的,如果是,它會返回一個非零值。

2)  WEXITSTATUS(status) 當WIFEXITED返回非零值時,我們可以用這個巨集來提取子程序的返回值,如果子程序呼叫exit(5)退出,WEXITSTATUS(status)就會返回5;如果子程序呼叫exit(7),WEXITSTATUS(status)就會返回7。請注意,如果程序不是正常退出的,也就是說,WIFEXITED返回0,這個值就毫無意義。

例子如下:

#include <sys/types.h>

#include <sys/wait.h>

#include <unistd.h>

main()

{

       int status;

       pid_t pc,pr;

       pc=fork();

       if(pc<0) 

              printf("error ocurred!\n");

       else if(pc==0){    

              printf("This is child process with pid of %d.\n",getpid());

              exit(3);  

       }

       else{             

              pr=wait(&status);

              if(WIFEXITED(status)){   

                     printf("the child process %d exit normally.\n",pr);

                     printf("the return code is %d.\n",WEXITSTATUS(status));

              }else                   

                     printf("the child process %d exit abnormally.\n",pr);

       }

}

結果如下:

$ cc wait2.c -o wait2

$ ./wait2

This is child process with pid of 1538.

the child process 1538 exit normally.

the return code is 3.

3.3   判斷system呼叫的返回值

#inlucde <wait.h>
...
   sprintf(szCommand, "compress -fc %s > %s.Z", szFilePath, szFilePath);
   iStatus = system(szCommand);
   if (WIFEXITED(iStatus) && WEXITSTATUS(iStatus) == 0)
   {
      strcat(pstMsgHeader->acFileName, ".Z";
   }
   else
   {
      printf("Compress file <%s> fail with exit status %d.",
         szFilePath, WEXITSTATUS(iStatus)) ;
      return -1;
   }

4. system原始碼


system()函式功能強大,很多人用卻對它的原理知之甚少。看下簡單的linux版system函式的原始碼:
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
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函式原理呼叫方法

在C/C++中我們有時需要用到system系統呼叫來完成一些作業系統“更加擅長”的工作,本篇對system呼叫原理,呼叫方法,返回值封裝巨集,system簡單原始碼等內容進行了描述。 1. 應用例子 #include <stdlib.h> int sys

Ognl表達式基本原理使用方法

ognl表達式 作用 設置 submit implement ring 獲取request 編碼 組成 1.Ognl表達式語言 1.1.概述 OGNL表達式 OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,他是一個開

STL原始碼剖析——stack的實現原理使用方法詳解

Stack 簡介     stack 是堆疊容器,是一種“先進後出”的容器。     stack 是簡單地裝飾 deque 容器而成為另外一種容器。     使用 stack 時需要加上標頭檔案 #include<s

STL原始碼剖析——deque的實現原理使用方法詳解

Deque 簡介     deque是“double—ended queue”的縮寫,和vector一樣都是STL的容器,deque 是雙端陣列,而 vector 是單端的。     deque 在介面上和 vector 非常相似,在許多操作的地方

lua中點號冒號在定義方法呼叫方法的區別

local a = {x=2} --[[function a.foo1(b) self.x = b end a.foo1(5) print(a.x) --報錯:attempt to index global 'self' (a nil value)]] --[[funct

java併發之 CopyOnWriteArrayList的原理使用方法

描述 CopyOnWriteArrayList:CopyOnWriteArrayList這是一個ArrayList的執行緒安全的變體,其原理大概可以通俗的理解為:初始化的時候只有一個容器,很常一段時間,這個容器資料、數量等沒有發生變化的時候,大家(多個執行緒),都是讀取(假設這段時間裡只

懶載入預載入的基本原理實現方法

懶載入的原因: 對於圖片過多的場景,為了提高頁面的載入速度,降低伺服器的負載,增強使用者體驗,我們對還沒出現在視野的圖片先不載入,當元素出現在我們視野中的時候再載入。 懶載入的原理: 我們先將img標籤中的src連結設定為一樣的圖片(空白圖片),將真正的圖片連結放在自定義屬性中,如(da

Docker映象提交命令commit的工作原理使用方法

在本地建立一個容器後,可以依據這個容器建立本地映象,並可把這個映象推送到Docker hub中,以便在網路上下載使用。 下面我們來動手實踐。 docker pull nginx:1.15.3 用命令列啟動一個容器: docker run -d -p 1080:80 --nam

Docker鏡像提交命令commit的工作原理使用方法

nsh gin master image html .com ans res try 在本地創建一個容器後,可以依據這個容器創建本地鏡像,並可把這個鏡像推送到Docker hub中,以便在網絡上下載使用。 下面我們來動手實踐。 docker pull nginx:1.15.

伺服器運維之代理機的原理使用方法

適用場景 堡壘機 防止黑客的攻擊,設定一臺代理伺服器,只有代理伺服器能訪問自己的主機 釋出內外伺服器 適用於公網ip不足的場景 快取場景 類似cdn 靜態資訊放到代理伺服器,動態的交給業務伺服器,8:2 的比例 使用方法

javascript如何獲取函式名稱引數方法例項詳解

一、獲取函式名稱的3種實現方法   例項1: 在js權威指南中看到的一個方法: 1 2 3 Function.prototype.getName = function(){     return

SQL遊標原理使用方法

SQL遊標原理和使用方法 資料庫開發過程中,當你檢索的資料只是一條記錄時,你所編寫的事務語句程式碼往往使用SELECT INSERT 語句。但是我們常常會遇到這樣情況,即從某一結果集中逐一地讀取一條記錄。那麼如何解決這種問題呢?遊標為我們提供了一種極為優秀的解決方案。 1.1 遊標和遊標的優點

960 Grid System 基本原理及使用方法

  由於一些讀者對於960 Grid System CSS Framework的原理和使用方法比較感興趣,暴風彬彬今天將和大家一同分享這篇關於960 grid CSS Framework的基本原理和簡單的使用方法。 關於CSS框架其實一直是一個比較熱門且很有爭議的話題,的確,國內的一些前端er們越來越

idea怎麼像eclipse一樣騷氣地快速打System.out.printlnmain()方法

在eclipse中,我們想打System.out.println()方法,直接輸入syso然後按alt+/聯想然後再按Enter就能打出來,那麼idea怎麼實現這個功能呢? 我們首先開啟File->setting,在搜尋框輸入live,然後找到這個選項: 在右邊選中output選

Sobel函式原理應用

用來表達微分的最常用的操作是Sobel微分運算元。Sobel運算元包含任意階的微分以及融合偏導。 http://blog.csdn.net/tonyshengtan/article/details/43698711 這個帖關於Sobel的卷積運算元怎麼推導的有很詳細的介紹;

詞嵌入向量(Word Embedding)的原理生成方法

Word Embedding 詞嵌入向量(WordEmbedding)是NLP裡面一個重要的概念,我們可以利用Word Embedding將一個單詞轉換成固定長度的向量表示,從而便於進行數學處理。本文將介紹Word Embedding的使用方式,並講解如何通過神經網路生成W

mybatis generator配置呼叫方法

1.下面是配置檔案,需要放到/workspace/xxx_project 資料夾下<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//m

linux shell函式定義呼叫

說起函式呼叫,相信大家也不會陌生,然而對於初學Shell的我來說,Shell中函式呼叫方式卻有點讓我不太習慣,自己也走了不少的彎路,因為傳遞引數時出了一個很“自然”的錯誤,也讓我吃了不少的苦頭,所以總結一下Shell中函式的呼叫方法。 一、Shell中函式的定義 為了方便

libiconv 交叉編譯呼叫方法(UTF-8GB2312轉換)

1、解壓 libiconv-1.14.tar.gz;進入libiconv-1.14目錄 2、./configure --host=arm-none-linux-gnueabi --enable-shared --enable-static --prefix=/opt/lib

url編碼函式encodeURIencodeURIComponent方法

一、作用全域性函式encodeURI和encodeURIComponent方法,都可以用來進行url編碼。之所以要對url進行編碼,是因為瀏覽器不能識別某些字元,例如:空格、中文等。這兩個方法對這些特殊字元用特殊的UTF-8進行編碼,從而使得瀏覽器可以識別。二、區別首先要講解