程式執行的時候,可以建立與自己關聯的子程序,建立了這個子程序之後,可以選擇等待這個子程序執行完畢,也可以讓子程序與自己並行執行,還可以終止自己轉而執行子程序。這些操作都是通過一系列相似而又有細微區別的庫函式來實現。下面對這些可以使用的庫函式進行介紹。
它的函式定義如下:
#include int system(const char * string);
這個程序會在系統中另外啟動乙個shell,並在新的shell中執行引數string
給定的命令。如果無法啟動shell,這個函式會返回127
錯誤碼,其他錯誤返回-1
,否則返回該命令的退出碼。
在程式中呼叫這個函式之後,呼叫程序會等待新shell中命令執行完成,之後在繼續執行。但是也可以在string
引數的命令後面加上&
讓命令後台執行,這樣就能實現呼叫程序與新shell程序並行執行。
下面是乙個例子:
#include #include int main(void)
編譯並執行上面的程式會得到下面的輸出:
now running a new process.
f s uid pid ppid c pri ni addr sz wchan tty time cmd
0 s 1000 5691 5646 0 80 0 - 581762 poll_s pts/0 00:00:04 vim
0 s 1000 6424 6215 0 80 0 - 570 do_wai pts/1 00:00:00 a
0 s 1000 6425 6424 0 80 0 - 598 do_wai pts/1 00:00:00 sh
0 r 1000 6426 6425 0 80 0 - 2897 - pts/1 00:00:00 ps
returned from the sub process.
可以看到程式執行到一半時,建立了乙個子程序ps
,等到子程序執行完畢,才回到自己的程式繼續執行。
如果在執行的命令ps -al
後面加上&
,就會讓子程式後台執行,從而呼叫程式與子程式就能並行執行。
上面的system()
需要啟動乙個新shell,並在新的shell中執行子程式,這樣做的執行效率不高,並且因為不同的環境shell版本不一樣,所以也會造成相容性的問題。因此我們在需要建立子程序的時候使用exec
系列函式,而不是system()
。
exec
系列函式的作用都是結束本程式的執行,轉而執行另外乙個程式。新建立的子程式整合了原來程序的資源,有著同樣的pid
。
新的程式預設狀態下會繼承已開啟的檔案,但是如果檔案使用fcntl()
開啟並且設定了fd_cloexec
,則執行新程序的時候會關閉檔案。
下面是所有exec
系列函式的定義:
int execl(const char *pathname, const char *arg, ...)
int execv(const char *pathname, char *const ar**)
int execle(const char *pathname, const char *arg, ..., char *const envp)
int execve(const char *pathname, char *const ar**, char *const envp)
int execlp(const char *filename, const char *arg, ...)
int execvp(const char *filename, char *const ar**)
其中檔名的不同表示了傳遞引數的方式,查詢程式的方式以及是否可以設定環境變數不同。對於名字的前四個字母exec
都是統一的函式名,後面跟上l
表示按照引數列表的方式接受多個函式引數;v
表示利用字串陣列來接受多個引數;p
表示系統將通過搜尋path
環境變數來查詢程式;e
表示可以傳入環境變數。
下面是乙個使用exec
系列函式從當前程序啟動ps
程式的例子,在這個例子中使用了上面的所有方法:
#include #include int main(void); //就算使用陣列的形式傳入引數,也要用0表示引數的結尾
char * const ps_envp = ;
//下面的函式只有第乙個可以執行,剩餘的只是作為演示
execl("/bin/ps", "blahhh", "-al", 0);
//剩下的函式不會被執行
execlp("ps", "ps", "-af", 0);
execle("/bin/ps", "ps", "-af", 0, ps_envp);
execv("/bin/ps", ps_ar**);
execvp("ps", ps_ar**);
execve("/bin/ps", ps_ar**, ps_envp);
}
關於這個程式,有幾個需要注意的地方:
使用fork()
函式可以複製當前的程序。新建立的程序與原來的程序幾乎完全一樣,但是兩個程序都有自己的資料,環境和檔案描述符。並且新建立的程序是當前程序的子程序,基於這個特性,可以使用fork()
先建立乙個與當前程序一模一樣的子程序,然後使用exec()
函式使子程序轉而執行另乙個程式,就可以使另外乙個子程序成為當前程序的子程序。
如果子程序建立失敗,fork()
函式會返回-1
,並且將錯誤碼儲存在errno
中,如果呼叫成功則返回子程序的pid
。因為子程序跟父程序執行的**相同,所以在子程序中也會有乙個fork()
函式,但是子程序中的fork()
函式不會建立另外乙個子程序,因為如果這樣就會無限迴圈下去,取而代之子程序的fork()
函式不會建立任何子程序,同時返回0
,可以用這一點來判斷當前程序是乙個父程序還是乙個子程序。下面是乙個例子:
#include #include int main(void)
return 0;
}
當使用fork()
函式建立乙個子程序之後,子程序與父程序是並行執行的,如果想將父程序掛起知道子程序執行結束,可以使用wait()
函式。這個函式會讓父程序等待到子程序執行完畢之後在繼續執行。當子程序結束之後,這個函式會返回子程序的pid
,並且會將子程序返回的狀態碼儲存到指定位置stat_loc
,指定的位置只需要乙個整型變數即可。
下面是該函式的定義:
#include #include pid_t wait(int *stat_loc);
函式返回的狀態資訊被儲存在乙個整型數里,這個資料不是人能看得懂的,如果要檢視子程序結束時的狀態,可以使用sys/wait.h
中定義的巨集來實現,常用的巨集有:巨集說明
wifexited(stat_val)
如果子程序正常結束,它就取乙個非零值
wexitstatus(stat_val)
如果子程序正常結束,返回子程序的退出碼
wifsignaled(stat_val)
如果子程序因為乙個未捕獲的訊號終止,返回非零值
wtermsig(stat_val)
如果子程序因為乙個未捕獲的訊號終止,返回訊號**
wifstopped(stat_val)
如果子程序意外終止,返回乙個非零值
wstopsig(stat_val)
如果子程序意外終止,返回乙個訊號**
下面是乙個例子:
#include #include #include #include int main(void)
return 0;
}
這個函式有另外乙個加強版本,它會讓程式等待另外任意乙個程式執行完畢。它的定義為:
#include #include pid_t waitpid(pid_t pid, int * stat_loc, int options);
這個函式的返回值跟wait()
函式一樣,但是引數卻不一樣,pid
表示要等待的程序的pid
,stat_loc
表示要儲存程序返回狀態的地方,options
是用於控制該函式行為的選項,其中乙個常用的選項是wnohang
,它的作用是防止呼叫程式在等待另乙個程序的時候被掛起。 linux c建立子程序
前言 了解fork 函式 乙個程序呼叫fork 函式建立該程序子程序,系統會為該子程序分配資源儲存資料和 的空間,父程序將資料和 複製給子程序,子程序按父程序 重新執行,即轉殖了父程序並重新執行。fork 函式的返回值,1即fork失敗,值為0時即子程序,返回值大於0即子程序id c 樣例 incl...
linux建立子程序
include include include intmain int argc,char ar else if pid 0 else if pid 0 return0 include include include intmain int argc,char ar else if pid 0 el...
Unix建立子程序
fork函式用於在已存在程序中新建程序。fork函式呼叫一次,返回兩次。當fork返回值為0時,當前程序為fork函式建立的子程序 當fork函式返回值大於0時,當前程序與fork函式呼叫前的程序一致,我們稱之為父程序 當fork函式返回值為 1時,fork函式建立子程序失敗。系統無法保證父程序與子...