目錄
fork 系統呼叫
vfork 系統呼叫
exec 族函式
在子程序中執行任務 總結
linux 作業系統緊緊依賴程序建立來滿足使用者的需求。例如,只要使用者輸入一條命令,shell 程序就建立乙個新程序,新程序執行 shell 的另乙個拷貝並執行使用者輸入的命令。linux 系統中通過 fork/vfork 系統呼叫來建立新程序。本文將介紹如何使用 fork/vfork 系統呼叫來建立新程序並使用 exec 族函式在新程序中執行任務。
要建立乙個程序,最基本的系統呼叫是 fork:
# include pid_t fork(void);
pid_t vfork(void);
呼叫 fork 時,系統將建立乙個與當前程序相同的新程序。通常將原有的程序稱為父程序,把新建立的程序稱為子程序。子程序是父程序的乙個拷貝,子程序獲得同父程序相同的資料,但是同父程序使用不同的資料段和堆疊段。子程序從父程序繼承大多數的屬性,但是也修改一些屬性,下表對比了父子程序間的屬性差異:
繼承屬性
差異uid,gid,euid,egid
程序 id
程序組 id
父程序 id
session id
子程序執行時間記錄
所開啟檔案及檔案的偏移量
父程序對檔案的鎖定
控制終端
設定使用者 id 和 設定組 id 標記位
根目錄與當前目錄
檔案預設建立的許可權掩碼
可訪問的記憶體區段
環境變數及其它資源分配
下面是乙個常見的演示 fork 工作原理的 demo(筆者的環境為 ubuntu 16.04 desktop):
#include #include #include #include int main(void)
if(pid == 0)
else
return 0;
}
把上面的**儲存到檔案 forkdemo.c 檔案中,並執行下面的命令編譯:
$ gcc forkdemo.c -o forkdemo
然後執行編譯出來的 forkdemo 程式:
$ ./forkdemo
fork 函式的特點是 "呼叫一次,返回兩次":在父程序中呼叫一次,在父程序和子程序中各返回一次。在父程序中返回時的返回值為子程序的 pid,而在子程序中返回時的返回值為 0,並且返回後都將執行 fork 函式呼叫之後的語句。如果 fork 函式呼叫失敗,則返回值為 -1。
我們細想會發現,fork 函式的返回值設計還是很高明的。在子程序中 fork 函式返回 0,那麼子程序仍然可以呼叫 getpid 函式得到自己的 pid,也可以呼叫 getppid 函式得到父程序 pid。在父程序中用 getpid 函式可以得到自己的 pid,如果想得到子程序的pid,唯一的辦法就是把 fork 函式的返回值記錄下來。
注意:執行 forkdemo 程式時的輸出是會發生變化的,可能先列印父程序的資訊,也可能先列印子程序的資訊。
vfork 系統呼叫和 fork 系統呼叫的功能基本相同。vfork 系統呼叫建立的程序共享其父程序的記憶體位址空間,但是並不完全複製父程序的資料段,而是和父程序共享其資料段。為了防止父程序重寫子程序需要的資料,父程序會被 vfork 呼叫阻塞,直到子程序退出或執行乙個新的程式。由於呼叫 vfork 函式時父程序被掛起,所以如果我們使用 vfork 函式替換 forkdemo 中的 fork 函式,那麼執行程式時輸出資訊的順序就不會變化了。
使用 vfork 建立的子程序一般會通過 exec 族函式執行新的程式。接下來讓我們先了解下 exec 族函式。
使用 fork/vfork 建立子程序後執行的是和父程序相同的程式(但有可能執行不同的**分支),子程序往往需要呼叫乙個 exec 族函式以執行另外乙個程式。當程序呼叫 exec 族函式時,該程序的使用者空間**和資料完全被新程式替換,從新程式的起始處開始執行。呼叫 exec 族函式並不建立新程序,所以呼叫 exec 族函式前後該程序的 pid 並不改變。
exec 族函式一共有六個:
#include int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp);
int execv(const char *path, char *const ar**);
int execvp(const char *file, char *const ar**);
int execve(const char *path, char *const ar**, char *const envp);
事實上,只有 execve 是真正的系統呼叫,其它五個函式最終都呼叫 execve。這些函式之間的關係如下圖所示(此圖來自網際網路):
exec 族函式的特徵:呼叫 exec 族函式會把新的程式裝載到當前程序中。在呼叫過 exec 族函式後,程序中執行的**就與之前完全不同了,所以 exec 函式呼叫之後的**是不會被執行的。
下面讓我們通過 vfork 和 execve 函式實現在子程序中執行 ls 命令:
#include #include #include #include int main(void)
else if(pid==0)
; char *envp[ ]=;
if(execve("/bin/ls", ar**, envp) < 0)
// 子程序要麼從 ls 命令中退出,要麼從上面的 exit(1) 語句退出
// 所以**的執行路徑永遠也走不到這裡,下面的 printf 語句不會被執行
printf("you should never see this message.");
}else
return 0;
}
把上面的**儲存到檔案 subprocessdemo.c 檔案中,並執行下面的命令編譯:
$ gcc subprocessdemo.c -o subprocessdemo
然後執行編譯出來的 subprocessdemo程式:
fork/vfork 函式和 exec 族函式都是 linux 系統中非常重要的概念。本文試圖通過簡單的 demo 來演示這些函式的基本用法,為理解 linux 系統中父程序與子程序的概念提供一些直觀的感受。
linux c建立子程序
前言 了解fork 函式 乙個程序呼叫fork 函式建立該程序子程序,系統會為該子程序分配資源儲存資料和 的空間,父程序將資料和 複製給子程序,子程序按父程序 重新執行,即轉殖了父程序並重新執行。fork 函式的返回值,1即fork失敗,值為0時即子程序,返回值大於0即子程序id c 樣例 incl...
Linux 建立子程序執行任務
linux 作業系統緊緊依賴程序建立來滿足使用者的需求。例如,只要使用者輸入一條命令,shell 程序就建立乙個新程序,新程序執行 shell 的另乙個拷貝並執行使用者輸入的命令。linux 系統中通過 fork vfork 系統呼叫來建立新程序。本文將介紹如何使用 fork vfork 系統呼叫來...
Linux 建立子程序執行任務的實現方法
linux 作業系統緊緊依賴程序建立來滿足使用者的需求。例如,只要使用者輸入一條命令,shell 程序就建立乙個新程序,新程序執行 shell 的另乙個拷貝並執行使用者輸入的命令。linux 系統中通過 fork vfork 系統呼叫來建立新程序。本文將介紹如何使用 fork vfork 系統呼叫來...