exec函數族
1)exec函數族說明
fork()函數用於建立一個子進程,該子進程幾乎複製了父進程的全部內容,但是,這個新建立的進程如何執行呢?exec函數族就提供了一個在進程中啟動另一個程式執行的方法。它可以根據指定的檔案名稱或目錄名找到可執行檔,並用它來取代原調用進程的資料區段、程式碼片段和堆棧段,在執行完之後,原調用進程的內容除了進程號外,其他全部被新的進程替換了。另外,這裡的可執行檔既可以是二進位檔案,也可以是Linux下任何可執行檔指令檔。
在Linux中使用exec函數族主要有兩種情況:
● 當進程認為自己不能再為系統和使用者做出任何貢獻時,就可以調用exec函數族中的任意一個函數讓自己重生。
● 如果一個進程想執行另一個程式,那麼它就可以調用fork()函數建立一個進程,然後調用exec函數族中的任意一個函數,這樣看起來就像通過執行應用程式而產生了一個新進程(這種情況非常普遍)。
2)exec函數族文法
實際上,在Linux中並沒有exec()函數,而是有6個以exec開頭的函數,它們之間的文法有細微差別,本書在後面會詳細講解。
表2列舉了exec函數族的6個成員函數的文法。
表2 exec函數族成員函數文法
這6個函數在函數名和使用文法的規則上都有細微的區別,下面就從可執行檔尋找方式、參數傳遞方式及環境變數這幾個方面進行比較。
● 尋找方式。讀者可以注意到,表2中的前4個函數的尋找方式都是完整的檔案目錄路徑,而最後兩個函數(也就是以p結尾的兩個函數)可以只給出檔案名稱,系統就會自動按照環境變數“$PATH”所指定的路徑進行尋找。
● 參數傳遞方式。exec函數族的參數傳遞有兩種方式:一種是逐個列舉的方式,而另一種則是將所有參數整體構造指標數組傳遞。在這裡是以函數名的第5位字母來區分的,字母為“l”(list)的表示逐個列舉參數的方式,其文法為const char *arg;字母為“v”(vertor)的表示將所有參數整體構造指標數組傳遞,其文法為char *const argv[]。讀者可以觀察execl()、execle()、execlp()的文法與execv()、execve()、execvp()的區別,它們的具體用法在後面的執行個體講解中會具體說明。
這裡的參數實際上就是使用者在使用這個可執行檔時所需的全部命令選項字串(包括該可執行程式命令本身)。要注意的是,這些參數必須以NULL結束。
● 環境變數。exec函數族可以預設系統的環境變數,也可以傳入指定的環境變數。這裡以“e”(environment)結尾的兩個函數execle()和execve()就可以在envp[]中指定當前進程所使用的環境變數。
表3再對這6個函數中的函數名和對應文法做了一個小結,主要指出了函數名中每一位所表明的含義,希望讀者結合此表加以記憶。
表3 exec函數名對應含義
事實上,這6個函數中真正的系統調用只有execve(),其他5個都是庫函數,它們最終都會調用execve()這個系統調用。在使用exec函數族時,一定要加上錯誤判斷語句。exec很容易執行失敗,其中最常見的原因有:
● 找不到檔案或路徑,此時errno被設定為ENOENT。
● 數組argv和envp忘記用NULL結束,此時errno被設定為EFAUL。
● 沒有對應可執行檔的運行許可權,此時errno被設定為EACCES。
3)exec使用執行個體
下面的第一個樣本說明了如何使用檔案名稱的方式來尋找可執行檔,同時使用參數列表的方式。這裡用的函數是execlp()。
#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");
}
}
}
在該程式中,首先使用fork()函數建立一個子進程,然後在子進程中使用execlp()函數。讀者可以看到,這裡的參數列表列出了在shell中使用的命令名和選項,並且當使用檔案名稱進行尋找時,系統會在預設的環境變數PATH中尋找該可執行檔。讀者可將編譯後的結果下載到目標板上,運行結果如下:
PID 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
…
此程式的運行結果與在shell中直接輸入命令“ps -ef”是一樣的,當然,在不同系統的不同時刻可能會有不同的結果。
接下來的樣本使用完整的檔案目錄來尋找對應的可執行檔。注意,目錄必須以“/”開頭,否則將其視為檔案名稱。
#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");
}
}
}
同樣將代碼下載到目標板上運行,運行結果同上例。
下面的樣本利用execle()函數將環境變數添加到建立的子進程中,這裡的“env”是查看當前進程環境變數的命令,代碼如下:
#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");
}
}
}
下載到目標板後的運行結果如下:
PATH=/tmp
USER=sunq
最後一個樣本使用execve()函數,通過構造指標數組的方式來傳遞參數,注意參數列表一定要以NULL作為結尾標識符。其代碼如下:
#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");
}
}
}
下載到目標板後的運行結果如下:
PATH=/tmp
USER=david
本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程式開發》
文章來源:華清遠見企業學院,原文地址:http://www.farsight.com.cn/news/emb188.htm
更多相關Linux文章查看企業學院技術文章>>