說是exec系統調用,實際上在Linux中,並不存在一個exec()的函數形式,exec指的是一組函數,一共有6個,分別是:
#include <unistd.h>extern char **environ;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 argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);
其中只有execve是真正意義上的系統調用,其它都是在此基礎上經過封裝的庫函數。
exec函數族的作用是根據指定的檔案名稱找到可執行檔,並用它來取代調用進程的內容,換句話說,就是在調用進程內部執行一個可執行檔。這裡的可執行檔既可以是二進位檔案,也可以是任何Linux下可執行檔指令檔。
與一般情況不同,exec函數族的函數執行成功後不會返回,因為調用進程的實體,包括程式碼片段,資料區段和堆棧等都已經被新的內容取代,只留下進程ID等一些表
面上的資訊仍保持原樣,頗有些神似"三十六計"中的"金蟬脫殼"。看上去還是舊的軀殼,卻已經注入了新的靈魂。只有調用失敗了,它們才會返回一個-1,從原程式的調用點接著往下執行。
現在我們應該明白了,Linux下是如何執行新程式的,每當有進程認為自己不能為系統和擁護做出任何貢獻了,他就可以發揮最後一點餘熱,調用任何一個exec,讓自己以新的面貌重生;或者,更普遍的情況是,如果一個進程想執行另一個程式,它就可以
fork出一個新進程,然後調用任何一個exec,這樣看起來就好像通過執行應用程式而產生了一個新進程一樣。
事實上第二種情況被應用得如此普遍,以至於Linux專門為其作了最佳化,我們已經知道,fork會將調用進程的所有內容原封不動的拷貝到新產生的子進程中去,
這些拷貝的動作很消耗時間,而如果fork完之後我們馬上就調用exec,這些辛辛苦苦拷貝來的東西又會被立刻抹掉,這看起來非常不划算,於是人們設計了一種"寫時拷貝(copy-on-write)"技術,使得fork結束後並不立刻複製父進程的內容,而是到了真正實用的時候才複製,這樣如果下一條語句是exec,它就不會白白作無用功了,也就提高了效率。
傳回值:如果執行成功則函數不會返回,執行失敗則直接返回-1,失敗原因存於errno 中。
大家在平時的編程中,如果用到了exec函數族,一定記得要加錯誤判斷語句。因為與其他系統調用比起來,exec很容易受傷,被執行檔案的位置,許可權等很多因素都能導致該調用的失敗。最常見的錯誤是:
1.找不到檔案或路徑,此時errno被設定為ENOENT;
2.數組argv和envp忘記用NULL結束,此時errno被設定為EFAULT;
3.沒有對要執行檔案的運行許可權,此時errno被設定為EACCES。
l表示以參數列表的形式調用
v表示以參數數組的方式調用
e表示可傳遞環境變數
p表示PATH中搜尋執行的檔案,如果給出的不是絕對路徑就會去PATH搜尋相應名字的檔案,如PATH沒有設定,則會預設在/bin,/usr/bin下搜尋。
另:調用時參數必須以NULL結束。原進程開啟的檔案描述符是不會在exec中關閉的,除非用fcntl設定它們的“執行時關閉標誌(close on exec)”而原進程開啟的目錄流都將在新進程中關閉。
例子:
#include <unistd.h>int main(int argc, char *argv[]){char *envp[]={"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};char *argv_execv[]={"echo", "excuted by execv", NULL};char *argv_execvp[]={"echo", "executed by execvp", NULL};char *argv_execve[]={"env", NULL};if(fork()==0) {if(execl("/bin/echo", "echo", "executed by execl", NULL)<0)perror("Err on execl");}if(fork()==0) {if(execlp("echo", "echo", "executed by execlp", NULL)<0)perror("Err on execlp");}if(fork()==0){if(execle("/usr/bin/env", "env", NULL, envp)<0)perror("Err on execle");}if(fork()==0){if(execv("/bin/echo", argv_execv)<0)perror("Err on execv");}if(fork()==0){if(execvp("echo", argv_execvp)<0)perror("Err on execvp");}if(fork()==0){if(execve("/usr/bin/env", argv_execve, envp)<0)perror("Err on execve");}}