exec函數(shù)族
1)exec函數(shù)族說明
fork()函數(shù)用于創(chuàng)建一個子進程,該子進程幾乎復制了父進程的全部內(nèi)容,但是,這個新創(chuàng)建的進程如何執(zhí)行呢?exec函數(shù)族就提供了一個在進程中啟動另一個程序執(zhí)行的方法。它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來取代原調用進程的數(shù)據(jù)段、代碼段和堆棧段,在執(zhí)行完之后,原調用進程的內(nèi)容除了進程號外,其他全部被新的進程替換了。另外,這里的可執(zhí)行文件既可以是二進制文件,也可以是Linux下任何可執(zhí)行的腳本文件。
在Linux中使用exec函數(shù)族主要有兩種情況:
● 當進程認為自己不能再為系統(tǒng)和用戶做出任何貢獻時,就可以調用exec函數(shù)族中的任意一個函數(shù)讓自己重生。
● 如果一個進程想執(zhí)行另一個程序,那么它就可以調用fork()函數(shù)新建一個進程,然后調用exec函數(shù)族中的任意一個函數(shù),這樣看起來就像通過執(zhí)行應用程序而產(chǎn)生了一個新進程(這種情況非常普遍)。
2)exec函數(shù)族語法
實際上,在Linux中并沒有exec()函數(shù),而是有6個以exec開頭的函數(shù),它們之間的語法有細微差別,本書在后面會詳細講解。
表2列舉了exec函數(shù)族的6個成員函數(shù)的語法。
表2 exec函數(shù)族成員函數(shù)語法
所需頭文件 |
#include <unistd.h> |
函數(shù)原型 |
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[]) |
函數(shù)返回值 |
-1:出錯 |
這6個函數(shù)在函數(shù)名和使用語法的規(guī)則上都有細微的區(qū)別,下面就從可執(zhí)行文件查找方式、參數(shù)傳遞方式及環(huán)境變量這幾個方面進行比較。
● 查找方式。讀者可以注意到,表2中的前4個函數(shù)的查找方式都是完整的文件目錄路徑,而后兩個函數(shù)(也就是以p結尾的兩個函數(shù))可以只給出文件名,系統(tǒng)就會自動按照環(huán)境變量“$PATH”所指定的路徑進行查找。
● 參數(shù)傳遞方式。exec函數(shù)族的參數(shù)傳遞有兩種方式:一種是逐個列舉的方式,而另一種則是將所有參數(shù)整體構造指針數(shù)組傳遞。在這里是以函數(shù)名的第5位字母來區(qū)分的,字母為“l(fā)”(list)的表示逐個列舉參數(shù)的方式,其語法為const char *arg;字母為“v”(vertor)的表示將所有參數(shù)整體構造指針數(shù)組傳遞,其語法為char *const argv[]。讀者可以觀察execl()、execle()、execlp()的語法與execv()、execve()、execvp()的區(qū)別,它們的具體用法在后面的實例講解中會具體說明。
這里的參數(shù)實際上就是用戶在使用這個可執(zhí)行文件時所需的全部命令選項字符串(包括該可執(zhí)行程序命令本身)。要注意的是,這些參數(shù)必須以NULL結束。
● 環(huán)境變量。exec函數(shù)族可以默認系統(tǒng)的環(huán)境變量,也可以傳入指定的環(huán)境變量。這里以“e”(environment)結尾的兩個函數(shù)execle()和execve()就可以在envp[]中指定當前進程所使用的環(huán)境變量。
表3再對這6個函數(shù)中的函數(shù)名和對應語法做了一個小結,主要指出了函數(shù)名中每一位所表明的含義,希望讀者結合此表加以記憶。
表3 exec函數(shù)名對應含義
前4位 |
統(tǒng)一為:exec |
第5位 |
l:參數(shù)傳遞為逐個列舉方式 |
execl、execle、execlp |
v:參數(shù)傳遞為構造指針數(shù)組方式 |
execv、execve、execvp |
第6位 |
e:可傳遞新進程環(huán)境變量 |
execle、execve |
p:可執(zhí)行文件查找方式為文件名 |
execlp、execvp |
事實上,這6個函數(shù)中真正的系統(tǒng)調用只有execve(),其他5個都是庫函數(shù),它們終都會調用execve()這個系統(tǒng)調用。在使用exec函數(shù)族時,一定要加上錯誤判斷語句。exec很容易執(zhí)行失敗,其中常見的原因有:
● 找不到文件或路徑,此時errno被設置為ENOENT。
● 數(shù)組argv和envp忘記用NULL結束,此時errno被設置為EFAUL。
● 沒有對應可執(zhí)行文件的運行權限,此時errno被設置為EACCES。
3)exec使用實例
下面的第一個示例說明了如何使用文件名的方式來查找可執(zhí)行文件,同時使用參數(shù)列表的方式。這里用的函數(shù)是execlp()。
/* execlp.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if (fork() == 0)
{
/* 調用execlp()函數(shù),這里相當于調用了“ps –ef”命令 */
if ((ret = execlp("ps", "ps", "-ef", NULL)) < 0)
{
printf("Execlp error\n");
}
}
}
在該程序中,首先使用fork()函數(shù)創(chuàng)建一個子進程,然后在子進程中使用execlp()函數(shù)。讀者可以看到,這里的參數(shù)列表列出了在shell中使用的命令名和選項,并且當使用文件名進行查找時,系統(tǒng)會在默認的環(huán)境變量PATH中尋找該可執(zhí)行文件。讀者可將編譯后的結果下載到目標板上,運行結果如下:
$ ./execlp
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”是一樣的,當然,在不同系統(tǒng)的不同時刻可能會有不同的結果。
接下來的示例使用完整的文件目錄來查找對應的可執(zhí)行文件。注意,目錄必須以“/”開頭,否則將其視為文件名。
/* execl.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if (fork() == 0)
{
/* 調用execl()函數(shù),注意這里要給出ps程序所在的完整路徑 */
if (execl("/bin/ps","ps","-ef",NULL) < 0)
{
printf("Execl error\n");
}
}
}
同樣將代碼下載到目標板上運行,運行結果同上例。
下面的示例利用execle()函數(shù)將環(huán)境變量添加到新建的子進程中,這里的“env”是查看當前進程環(huán)境變量的命令,代碼如下:
/* execle.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
/* 命令參數(shù)列表,必須以NULL結尾 */
char *envp[]={"PATH=/tmp","USER=david", NULL};
if (fork() == 0)
{
/* 調用execle()函數(shù),注意這里也要指出env的完整路徑 */
if (execle("/usr/bin/env", "env", NULL, envp) < 0)
{
printf("Execle error\n");
}
}
}
下載到目標板后的運行結果如下:
$ ./execle
PATH=/tmp
USER=sunq
后一個示例使用execve()函數(shù),通過構造指針數(shù)組的方式來傳遞參數(shù),注意參數(shù)列表一定要以NULL作為結尾標識符。其代碼如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
/* 命令參數(shù)列表,必須以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");
}
}
}
下載到目標板后的運行結果如下:
$ ./execve
PATH=/tmp
USER=david
本文選自華清遠見嵌入式培訓教材《從實踐中學嵌入式Linux應用程序開發(fā)》
熱點鏈接:
1、Linux下多進程編程之fork()函數(shù)語法
2、Linux下多進程編程之fork()函數(shù)說明
3、Linux下多任務系統(tǒng)之線程介紹
4、Linux下進程的內(nèi)存結構
5、Linux下進程的創(chuàng)建、執(zhí)行和終止
更多新聞>> |