exec()函式
(1)exec函式說明
fork函式是用於建立一個子程式,該子程式幾乎是父程式的副本,而有時我們希望子程式去執行另外的程式,exec函式族就提供了一個在程式中啟動另一個程式執行的方法。它可以根據指定的檔名或目錄名找到可執行檔案,並用它來取代原呼叫程式的資料段、程式碼段和堆疊段,在執行完之後,原呼叫程式的內容除了程式號外,其他全部被新程式的內容替換了。另外,這裡的可執行檔案既可以是二進位制檔案,也可以是Linux下任何可執行指令碼檔案。
(2)在Linux中使用exec函式族主要有以下兩種情況
當程式認為自己不能再為系統和使用者做出任何貢獻時,就可以呼叫任何exec 函式族讓自己重生。
如果一個程式想執行另一個程式,那麼它就可以呼叫fork函式新建一個程式,然後呼叫任何一個exec函式使子程式重生。
(3)exec函式族語法
實際上,在Linux中並沒有exec函式,而是有6個以exec開頭的函式族,下表列舉了exec函式族的6個成員函式的語法。
所需標頭檔案 |
#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,失敗原因記錄在error中 |
這6 個函式在函式名和使用語法的規則上都有細微的區別,下面就可執行檔案查詢方式、參數列傳遞方式及環境變數這幾個方面進行比較說明。
① 查詢方式:上表其中前4個函式的查詢方式都是完整的檔案目錄路徑,而最後2個函式(也就是以p結尾的兩個函式)可以只給出檔名,系統就會自動從環境變數“$PATH”所指出的路徑中進行查詢。
② 引數傳遞方式:exec函式族的引數傳遞有兩種方式,一種是逐個列舉的方式,而另一種則是將所有引數整體構造成指標陣列進行傳遞。
在這裡引數傳遞方式是以函式名的第5位字母來區分的,字母為“l”(list)的表示逐個列舉的方式,字母為“v”(vertor)的表示將所有引數整體構造成指標陣列傳遞,然後將該陣列的首地址當做引數傳給它,陣列中的最後一個指標要求是NULL。讀者可以觀察execl、execle、execlp的語法與execv、execve、execvp的區別。
③ 環境變數:exec函式族使用了系統預設的環境變數,也可以傳入指定的環境變數。這裡以“e”(environment)結尾的兩個函式execle、execve就可以在envp[]中指定當前程式所使用的環境變數替換掉該程式繼承的所以環境變數。
(3)PATH環境變數說明
PATH環境變數包含了一張目錄表,系統通過PATH環境變數定義的路徑搜尋執行碼,PATH環境變數定義時目錄之間需用用“:”分隔,以“.”號表示結束。PATH環境變數定義在使用者的.profile或.bash_profile中,下面是PATH環境變數定義的樣例,此PATH變數指定在“/bin”、“/usr/bin”和當前目錄三個目錄進行搜尋執行碼。
PATH=/bin:/usr/bin:.
export $PATH
(4)程式中的環境變數說明
在Linux中,Shell程式是所有執行碼的父程式。當一個執行碼執行時,Shell程式會fork子程式然後呼叫exec函式去執行執行碼。Shell程式堆疊中存放著該使用者下的所有環境變數,使用execl、execv、execlp、execvp函式使執行碼重生時,Shell程式會將所有環境變數複製給生成的新程式;而使用execle、execve時新程式不繼承任何Shell程式的環境變數,而由envp[]陣列自行設定環境變數。
(5)exec函式族關係
第4位 |
統一為:exec |
|
第5位 |
l:引數傳遞為逐個列舉方式 |
execl、execle、execlp |
v:引數傳遞為構造指標陣列方式 |
execv、execve、execvp |
|
第6位 |
e:可傳遞新程式環境變數 |
execle、execve |
p:可執行檔案查詢方式為檔名 |
execlp、execvp |
事實上,這6個函式中真正的系統呼叫只有execve,其他5個都是庫函式,它們最終都會呼叫execve這個系統呼叫,呼叫關係如下圖12-11所示:
圖12-11 exec函式族關係圖
(6)exec
呼叫舉例如下
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);
請注意exec函式族形參展開時的前兩個引數,第一個引數是帶路徑的執行碼(execlp、execvp函式第一個引數是無路徑的,系統會根據PATH自動查詢然後合成帶路徑的執行碼),第二個是不帶路徑的執行碼,執行碼可以是二進位制執行碼和Shell指令碼。
(7)exec函式族使用注意點
在使用exec函式族時,一定要加上錯誤判斷語句。因為exec很容易執行失敗,其中最常見的原因有:
① 找不到檔案或路徑,此時errno被設定為ENOENT。
② 陣列argv和envp忘記用NULL結束,此時errno被設定為EFAULT。
③ 沒有對應可執行檔案的執行許可權,此時errno被設定為EACCES。
(8)exec後新程式保持原程式以下特徵
Ÿ 環境變數(使用了execle、execve函式則不繼承環境變數);
Ÿ 程式ID和父程式ID;
Ÿ 實際使用者ID和實際組ID;
Ÿ 附加組ID;
Ÿ 程式組ID;
Ÿ 會話ID;
Ÿ 控制終端;
Ÿ 當前工作目錄;
Ÿ 根目錄;
Ÿ 檔案許可權遮蔽字;
Ÿ 檔案鎖;
Ÿ 程式訊號遮蔽;
Ÿ 未決訊號;
Ÿ 資源限制;
Ÿ tms_utime、tms_stime、tms_cutime以及tms_ustime值。
對開啟檔案的處理與每個描述符的exec關閉標誌值有關,程式中每個檔案描述符有一個exec關閉標誌(FD_CLOEXEC),若此標誌設定,則在執行exec時關閉該描述符,否則該描述符仍開啟。除非特地用fcntl設定了該標誌,否則系統的預設操作是在exec後仍保持這種描述符開啟,利用這一點可以實現I/O重定向。
(9)execlp函式舉例
execlp.c原始碼如下:
#include <stdio.h>
#include <unistd.h>
int main()
{
if(fork()==0){
if(execlp("/usr/bin/env","env",NULL)<0)
{
perror("execlp error!");
return -1 ;
}
}
return 0 ;
}
編譯 gcc execlp.c –o execlp。
執行 ./execlp,執行結果如下:
HOME=/home/test
DB2DB=test
SHELL=/bin/bash
……
由執行結果看出,execlp函式使執行碼重生時繼承了Shell程式的所有環境變數,其他三個不以e結尾的函式同理。
(10)execle函式舉例
利用函式execle,將環境變數新增到新建的子程式中去。
execle.c原始碼如下:
#include <unistd.h>
#include <stdio.h>
int main()
{
/*命令引數列表,必須以 NULL 結尾*/
char *envp[]={"PATH=/tmp","USER=sun",NULL};
if(fork()==0){
/*呼叫 execle 函式,注意這裡也要指出 env 的完整路徑*/
if(execle("/usr/bin/env","env",NULL,envp)<0)
{
perror("execle error!");
return -1 ;
}
}
return 0 ;
}
編譯:gcc execle.c –o execle。
執行./execle,執行結果如下:
PATH=/tmp
USER=sun
可見,使用execle和execve可以自己向執行程式傳遞環境變數,但不會繼承Shell程式的環境變數,而其他四個exec函式則繼承Shell程式的所有環境變數。
摘錄自《深入淺出Linux工具與程式設計》
相關文章
- exec函式族函式
- exec函式簇函式
- python函式每日一講 - exec執行函式Python函式
- 正規表示式match()函式和exec()函式的區別函式
- python內建函式-eval()函式與exec()函式的區別Python函式
- PHP exec system passthru系統函式PHP函式
- 如何使用python中的exec函式?Python函式
- 伺服器不支援:curl_exec函式伺服器函式
- 正規表示式exec()函式只有第一執行有效分析函式
- Linux系統程式設計(9)—— 程式之程式控制函式exec系列函式Linux程式設計函式
- Linux系統程式設計之程式替換:exec 函式族Linux程式設計函式
- PHP4使用者手冊:函式->CURL->curl_exec (轉)PHP函式
- 多程式函式系列fork(), wait(), exec()系列,system(), posix_spawn()例項詳解函式AI
- linux系統程式設計之程式(五):exec系列函式(execl,execlp,execle,execv,execvp)使用Linux程式設計函式
- MySQL函式大全(字串函式,數學函式,日期函式,系統級函式,聚合函式)MySql函式字串
- Oracle 函式大全(字串函式,數學函式,日期函式,邏輯運算函式,其他函式)Oracle函式字串
- 【函式式 Swift】函式式思想函式Swift
- python中id()函式、zip()函式、map()函式、lamda函式Python函式
- 【函式】Oracle函式系列(2)--數學函式及日期函式函式Oracle
- Python 擴充之特殊函式(lambda 函式,map 函式,filter 函式,reduce 函式)Python函式Filter
- 第7章 IF函式 COUNTIF函式 SUMIF函式函式
- 字元函式、數字函式和日期函式字元函式
- 【函式】Oracle EXTRACT()函式與to_char() 函式函式Oracle
- MySQL(四)日期函式 NULL函式 字串函式MySql函式Null字串
- 【函式】ORACLE函式大全函式Oracle
- PHP-fpm Linux 環境使用 exec 函式呼叫 FFmpeg,報錯 FFmpeg: command not found 的解決方法PHPLinux函式
- (譯) 函式式 JS #2: 函式!函式JS
- python execPython
- 核函式 多項式核函式 高斯核函式(常用)函式
- 函式名/函式地址/函式指標函式指標
- 第 8 節:函式-匿名函式、遞迴函式函式遞迴
- sqlplus"strace: exec: Exec format error"故障處理SQLORMError
- lambda匿名函式sorted排序函式filter過濾函式map對映函式函式排序Filter
- js函式 函式自呼叫 返回函式的函式 (閉包)JS函式
- main函式的入口函式AI函式
- (函式)實現strstr函式函式
- 字串函式之Strtok()函式字串函式
- SQL函式之日期函式SQL函式