|     exec函數族     1)exec函(han)數族(zu)說明(ming)     fork()函數用(yong)于創(chuang)建一個(ge)子進(jin)程(cheng)(cheng)(cheng),該子進(jin)程(cheng)(cheng)(cheng)幾乎復制(zhi)了(le)父(fu)進(jin)程(cheng)(cheng)(cheng)的(de)全(quan)部內容(rong),但是(shi),這個(ge)新創(chuang)建的(de)進(jin)程(cheng)(cheng)(cheng)如何執(zhi)行(xing)呢?exec函數族就(jiu)提供了(le)一個(ge)在進(jin)程(cheng)(cheng)(cheng)中啟動另一個(ge)程(cheng)(cheng)(cheng)序執(zhi)行(xing)的(de)方法。它(ta)可以根據指定的(de)文件名或(huo)目錄(lu)名找到可執(zhi)行(xing)文件,并(bing)用(yong)它(ta)來取代原調用(yong)進(jin)程(cheng)(cheng)(cheng)的(de)數據段(duan)、代碼段(duan)和堆棧段(duan),在執(zhi)行(xing)完之后,原調用(yong)進(jin)程(cheng)(cheng)(cheng)的(de)內容(rong)除了(le)進(jin)程(cheng)(cheng)(cheng)號外,其他(ta)全(quan)部被新的(de)進(jin)程(cheng)(cheng)(cheng)替換了(le)。另外,這里的(de)可執(zhi)行(xing)文件既可以是(shi)二進(jin)制(zhi)文件,也可以是(shi)Linux下任何可執(zhi)行(xing)的(de)腳本(ben)文件。     在Linux中使用exec函數族主要有兩種情況:● 當進程認為自己不能再為系統和用戶做出任何貢獻時,就可以調用exec函數族中的任意一個函數讓自己重生。
 ● 如果一個(ge)(ge)進(jin)程(cheng)(cheng)想執行(xing)另一個(ge)(ge)程(cheng)(cheng)序,那么它就可以調(diao)用fork()函(han)(han)數(shu)新建(jian)一個(ge)(ge)進(jin)程(cheng)(cheng),然后(hou)調(diao)用exec函(han)(han)數(shu)族中的任意一個(ge)(ge)函(han)(han)數(shu),這樣看(kan)起來就像通過執行(xing)應用程(cheng)(cheng)序而產生了一個(ge)(ge)新進(jin)程(cheng)(cheng)(這種情況非常普遍(bian))。
     2)exec函數族語法     實(shi)際上,在(zai)(zai)Linux中并(bing)沒有exec()函(han)數,而(er)是有6個以exec開(kai)頭的函(han)數,它們之間的語法有細(xi)微差別,本(ben)書在(zai)(zai)后面會詳細(xi)講解。     表(biao)2列舉了exec函數族的6個成員函數的語法。 表2  exec函數(shu)族成員函數(shu)語法 
    
    | 所需頭文件 | #include <unistd.h> |  
    | 函數原型 | int execl(const char *path, const char *arg, ...) |  
    | int execv(const char *path, char *const argv[]) |  
    | int execle(const char *path, const char *arg, ..., char *const envp[]) |  
    | int execve(const char *path, char *const argv[], char *const envp[]) |  
    | int execlp(const char *file, const char *arg, ...) |  
    | int execvp(const char *file, char *const argv[]) |  
    | 函數返回值 | -1:出錯 |      這6個函數在函數名和使用語法的規則上都有細微的區別,下面就從可執行文件查找方式、參數傳遞方式及環境變量這幾個方面進行比較。● 查找方式。讀者可以注意到,表2中的前4個函數的查找方式都是完整的文件目錄路徑,而后兩個函數(也就是以p結尾的兩個函數)可以只給出文件名,系統就會自動按照環境變量“$PATH”所指定的路徑進行查找。
 ● 參(can)數(shu)(shu)(shu)傳遞方式(shi)。exec函(han)數(shu)(shu)(shu)族的(de)參(can)數(shu)(shu)(shu)傳遞有兩種方式(shi):一種是逐個(ge)列舉(ju)的(de)方式(shi),而另一種則是將所(suo)有參(can)數(shu)(shu)(shu)整(zheng)體(ti)構造指(zhi)針數(shu)(shu)(shu)組傳遞。在(zai)這(zhe)里是以函(han)數(shu)(shu)(shu)名的(de)第5位字母來區分的(de),字母為(wei)“l”(list)的(de)表示(shi)逐個(ge)列舉(ju)參(can)數(shu)(shu)(shu)的(de)方式(shi),其(qi)語法為(wei)const char *arg;字母為(wei)“v”(vertor)的(de)表示(shi)將所(suo)有參(can)數(shu)(shu)(shu)整(zheng)體(ti)構造指(zhi)針數(shu)(shu)(shu)組傳遞,其(qi)語法為(wei)char *const argv[]。讀者可以觀察execl()、execle()、execlp()的(de)語法與execv()、execve()、execvp()的(de)區別,它們的(de)具體(ti)用法在(zai)后面的(de)實例講解中會具體(ti)說明。
     這里的參數實際上就是用戶在使用這個可執行文件時所需的全部命令選項字符串(包括該可執行程序命令本身)。要注意的是,這些參數必須以NULL結束。● 環境變(bian)量(liang)(liang)。exec函數族可以默(mo)認(ren)系統的環境變(bian)量(liang)(liang),也(ye)可以傳入指(zhi)定(ding)的環境變(bian)量(liang)(liang)。這里以“e”(environment)結尾(wei)的兩個函數execle()和execve()就可以在envp[]中指(zhi)定(ding)當前進程所使用的環境變(bian)量(liang)(liang)。
     表3再對(dui)這6個(ge)函數中(zhong)的(de)(de)函數名和對(dui)應語法(fa)做了(le)(le)一個(ge)小結,主要(yao)指出了(le)(le)函數名中(zhong)每一位所(suo)表明的(de)(de)含義(yi),希望讀者結合(he)此表加以記憶。 表3  exec函數名(ming)對(dui)應含(han)義 
    
    | 前4位 | 統一為:exec |  
    | 第5位 | l:參數傳遞為逐個列舉方式 | execl、execle、execlp |  
    | v:參數傳遞為構造指針數組方式 | execv、execve、execvp |  
    | 第6位 | e:可傳遞新進程環境變量 | execle、execve |  
    | p:可執行文件查找方式為文件名 | execlp、execvp |      事實上,這6個函數中真正的系統調用只有execve(),其他5個都是庫函數,它們終都會調用execve()這個系統調用。在使用exec函數族時,一定要加上錯誤判斷語句。exec很容易執行失敗,其中常見的原因有: ● 找不到文件或路徑,此時errno被設置為ENOENT。
 ● 數組argv和envp忘記用NULL結束,此時errno被設置為EFAUL。
 ● 沒有對應(ying)可(ke)執行文(wen)件的運行權限(xian),此時errno被(bei)設置為(wei)EACCES。
     3)exec使(shi)用實例     下面的(de)第一個示例(li)說明了如(ru)何使(shi)用文(wen)件(jian)名的(de)方式(shi)來(lai)查找可執行(xing)文(wen)件(jian),同時使(shi)用參(can)數列表的(de)方式(shi)。這里用的(de)函數是execlp()。     /* execlp.c */#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 int main()
 {
 if (fork() == 0)
 {
 /* 調用execlp()函數,這里相當于調用了“ps –ef”命令 */
 if ((ret = execlp("ps", "ps", "-ef", NULL)) < 0)
 {
 printf("Execlp error\n");
 }
 }
 }
     在(zai)(zai)該(gai)程序(xu)中(zhong)(zhong),首先使(shi)(shi)用(yong)(yong)fork()函數創(chuang)建一個子(zi)進程,然后(hou)(hou)在(zai)(zai)子(zi)進程中(zhong)(zhong)使(shi)(shi)用(yong)(yong)execlp()函數。讀者可以看到,這里的(de)參數列(lie)表列(lie)出了(le)在(zai)(zai)shell中(zhong)(zhong)使(shi)(shi)用(yong)(yong)的(de)命令(ling)名和選項,并且當使(shi)(shi)用(yong)(yong)文件名進行(xing)查(cha)找時(shi),系統(tong)會在(zai)(zai)默認的(de)環境變(bian)量PATH中(zhong)(zhong)尋找該(gai)可執行(xing)文件。讀者可將編譯后(hou)(hou)的(de)結(jie)(jie)果(guo)下(xia)(xia)載(zai)到目標板(ban)上,運行(xing)結(jie)(jie)果(guo)如(ru)下(xia)(xia):     $ ./execlpPID TTY    Uid    Size    State    Command
 1          root   1832    S        init
 2          root   0       S        [keventd]
 3          root   0       S        [ksoftirqd_CPU0]
 4          root   0       S        [kswapd]
 5          root   0       S        [bdflush]
 6          root   0       S        [kupdated]
 7          root   0       S        [mtdblockd]
 8          root   0       S        [khubd]
 35         root   2104    S        /bin/bash /usr/etc/rc.local
 36         root   2324    S        /bin/bash
 41         root   1364    S        /sbin/inetd
 53         root   14260   S        /Qtopia/qtopia-free-1.7.0/bin/qpe -qws
 54         root   11672   S        quicklauncher
 65         root   0       S        [usb-storage-0]
 66         root   0       S        [scsi_eh_0]
 83         root   2020    R        ps -ef
 $ env
 …
 PATH=/Qtopia/qtopia-free-1.7.0/bin:/usr/bin:/bin:/usr/sbin:/sbin
 …
     此程序(xu)的(de)(de)運行結果與(yu)在shell中直接輸入命令“ps -ef”是一樣(yang)的(de)(de),當(dang)然,在不同(tong)(tong)系統的(de)(de)不同(tong)(tong)時刻可能會有不同(tong)(tong)的(de)(de)結果。     接下來(lai)的(de)(de)示例(li)使用完整的(de)(de)文件目(mu)錄(lu)來(lai)查找對應(ying)的(de)(de)可執行(xing)文件。注(zhu)意,目(mu)錄(lu)必須以“/”開頭(tou),否(fou)則將(jiang)其(qi)視為文件名。     /* execl.c */#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 int main()
 {
 if (fork() == 0)
 {
 /* 調用execl()函數,注意這里要給出ps程序所在的完整路徑 */
 if (execl("/bin/ps","ps","-ef",NULL) < 0)
 {
 printf("Execl error\n");
 }
 }
 }
     同(tong)樣將代碼下(xia)載到目標板(ban)上運(yun)行(xing),運(yun)行(xing)結果同(tong)上例。     下面的(de)示例利用execle()函數將(jiang)環境變量添加到新建的(de)子(zi)進程中,這里的(de)“env”是查(cha)看當前進程環境變量的(de)命令,代(dai)碼如下:     /* execle.c */#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 
 int main()
 {
 /* 命令參數列表,必須以NULL結尾 */
 char *envp[]={"PATH=/tmp","USER=david", NULL};
 
 if (fork() == 0)
 {
 /* 調用execle()函數,注意這里也要指出env的完整路徑 */
 if (execle("/usr/bin/env", "env", NULL, envp) < 0)
 {
 printf("Execle error\n");
 }
 }
 }
     下載到目標(biao)板后的運行(xing)結果如下:     $ ./execlePATH=/tmp
 USER=sunq
     后一(yi)個示(shi)例使用(yong)execve()函數(shu),通過構造指針數(shu)組的方式來傳遞參數(shu),注意(yi)參數(shu)列表一(yi)定(ding)要以NULL作(zuo)為結尾標識(shi)符。其(qi)代(dai)碼如(ru)下(xia):     #include <unistd.h>#include <stdio.h>
 #include <stdlib.h>
 
 int main()
 {
 /* 命令參數列表,必須以NULL結尾 */
 char *arg[] = {"env", NULL};
 char *envp[] = {"PATH=/tmp", "USER=david", NULL};
 
 if (fork() == 0)
 {
 if (execve("/usr/bin/env", arg, envp) < 0)
 {
 printf("Execve error\n");
 }
 }
 }
     下(xia)載到(dao)目標板后的運(yun)行結(jie)果如下(xia):     $ ./execvePATH=/tmp
 USER=david
     本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發》    熱點鏈(lian)接(jie): 
         1、Linux下多進程編程之fork()函數語法2、Linux下多進程編程之fork()函數說明
 3、Linux下多任務系統之線程介紹
 4、Linux下進程的內存結構
 5、Linux下進程的創建、執行和終止
 
 更多新聞>>  |