1.exec函數說明
fork函數是用於創建一個子進程,該子進程幾乎是父進程的副本,而有時我們希望子進程去執行另外的程序,exec函數族就提供了一個在進程中啟動另一個程序執行的方法。它可以根據指定的文件名或目錄名找到可執行文件,並用它來取代原調用進程的數據段、代碼段和堆棧段,在執行完之後,原調用進程的內容除了進程號外,其他全部被新程序的內容替換了。另外,這裡的可執行文件既可以是二進制文件,也可以是Linux下任何可執行腳本文件。
2.在Linux中使用exec函數族主要有以下兩種情況
當進程認為自己不能再為系統和用戶做出任何貢獻時,就可以調用任何exec 函數族讓自己重生。
如果一個進程想執行另一個程序,那麼它就可以調用fork函數新建一個進程,然後調用任何一個exec函數使子進程重生。
exec家族
(1)int execl(const char *path, const char *arg, ......);
(2)int execle(const char *path, const char *arg, ...... , char * const envp[]);
(3)int execv(const char *path, char *const argv[]);
(4)int execve(const char *filename, char *const argv[], char *const envp[]);
(5)int execvp(const char *file, char * const argv[]);
(6)int execlp(const char *file, const char *arg, ......);
其中只有execve是真正意義上的系統調用,其它都是在此基礎上經過包裝的庫函數。它們之間的區別:
第一個區別是:
前四個取路徑名做為參數,後兩個取文件名做為參數,如果文件名中不包含 “/” 則從PATH環境變量中搜尋可執行文件, 如果找到了一個可執行文件,但是該文件不是連接編輯程序產生的可執行代碼文件,則當做shell腳本處理。
第二個區別:
前兩個和最後一個函數中都包括“ l ”這個字母 ,而另三個都包括“ v ”, " l "代表 list即表 ,而" v "代表 vector即矢量,也是是前三個函數的參數都是以list的形式給出的,但最後要加一個空指針,如果用常數0來表示空指針,則必須將它強行轉換成字符指針,否則有可能出錯。,而後三個都是以矢量的形式給出,即數組。
最後一個區別:
與向新程序傳遞環境變量有關,如第二個和第四個以e結尾的函數,可以向函數傳遞一個指向環境字符串指針數組的指針。即自個定義各個環境變量,而其它四個則使用進程中的環境變量。
3.實例講解:
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
#ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> int main(int argc, char *argv[]) { //以NULL結尾的字符串數組的指針,適合包含v的exec函數參數 char *arg[] = {"ls", "-a", NULL}; /** * 創建子進程並調用函數execl * execl 中希望接收以逗號分隔的參數列表,並以NULL指針為結束標志 */ if( fork() == 0 ) { // in clild printf( "1------------execl------------\n" ); if( execl( "/bin/ls", "ls","-a", NULL ) == -1 ) { perror( "execl error " ); exit(1); } } /** *創建子進程並調用函數execv *execv中希望接收一個以NULL結尾的字符串數組的指針 */ if( fork() == 0 ) { // in child printf("2------------execv------------\n"); if( execv( "/bin/ls",arg) < 0) { perror("execv error "); exit(1); } } /** *創建子進程並調用 execlp *execlp中 *l希望接收以逗號分隔的參數列表,列表以NULL指針作為結束標志 *p是一個以NULL結尾的字符串數組指針,函數可以DOS的PATH變量查找子程序文件 */ if( fork() == 0 ) { // in clhild printf("3------------execlp------------\n"); if( execlp( "ls", "ls", "-a", NULL ) < 0 ) { perror( "execlp error " ); exit(1); } } /** *創建子裡程並調用execvp *v 望接收到一個以NULL結尾的字符串數組的指針 *p 是一個以NULL結尾的字符串數組指針,函數可以DOS的PATH變量查找子程序文件 */ if( fork() == 0 ) { printf("4------------execvp------------\n"); if( execvp( "ls", arg ) < 0 ) { perror( "execvp error " ); exit( 1 ); } } /** *創建子進程並調用execle *l 希望接收以逗號分隔的參數列表,列表以NULL指針作為結束標志 *e 函數傳遞指定參數envp,允許改變子進程的環境,無後綴e時,子進程使用當前程序的環境 */ if( fork() == 0 ) { printf("5------------execle------------\n"); if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 ) { perror("execle error "); exit(1); } } /** *創建子進程並調用execve * v 希望接收到一個以NULL結尾的字符串數組的指針 * e 函數傳遞指定參數envp,允許改變子進程的環境,無後綴e時,子進程使用當前程序的環境 */ if( fork() == 0 ) { printf("6------------execve-----------\n"); if( execve( "/bin/ls", arg, NULL ) == 0) { perror("execve error "); exit(1); } } return EXIT_SUCCESS; }