Linux系統程式設計之程式替換:exec 函式族

千鋒教育官方發表於2019-09-05


        Windows 平臺下,我們可以透過雙擊執行可執行程式,讓這個可執行程式成為一個程式;而在 Linux 平臺,我們可以透過 ./ 執行,讓一個可執行程式成為一個程式。

但是,如果我們本來就執行著一個程式(程式),我們如何在這個程式內部啟動一個外部程式,由核心將這個外部程式讀入記憶體,使其執行起來成為一個程式呢?這裡我們透過 exec 函式族實現。

exec 函式族,顧名思義,就是一簇函式,在 Linux 中,並不存在 exec() 函式,exec 指的是一組函式,一共有 6 個:

#include <unistd.h>  

int execl(const char *path, const char *arg, ...);  

int execlp(const char *file, const char *arg, ...);  

int execle(const char *path, const char *arg, ..., char * const envp[]);  

int execv(const char *path, char *const argv[]);  

int execvp(const char *file, char *const argv[]);  

int execve(const char *path, char *const argv[], char *const envp[]);  

其中只有 execve() 是真正意義上的系統呼叫,其它都是在此基礎上經過包裝的庫函式。

exec 函式族提供了六種在程式中啟動另一個程式的方法。exec 函式族的作用是根據指定的檔名或目錄名找到可執行檔案,並用它來取代呼叫程式的內容,換句話說,就是在呼叫程式內部執行一個可執行檔案。

程式呼叫一種 exec 函式時,該程式完全由新程式替換,而新程式則從其 main 函式開始執行。因為呼叫 exec 並不建立新程式,所以前後的程式 ID (當然還有父程式號、程式組號、當前工作目錄……)並未改變。exec 只是用另一個新程式替換了當前程式的正文、資料、堆和棧段(程式替換)。

 

 

exec 函式族的 6 個函式看起來似乎很複雜,但實際上無論是作用還是用法都非常相似,只有很微小的差別。

   

l(list):引數地址列表,以空指標結尾。

v(vector):存有各引數地址的指標陣列的地址。

p(path):按 PATH 環境變數指定的目錄搜尋可執行檔案。

e(environment):存有環境變數字串地址的指標陣列的地址。

exec 函式族裝入並執行可執行程式 path/file,並將引數 arg0( arg1, arg2, argv[], envp[] ) 傳遞給此程式。

exec 函式族與一般的函式不同,exec 函式族中的函式執行成功後不會返回,而且,exec 函式族下面的程式碼執行不到。只有呼叫失敗了,它們才會返回 -1,失敗後從原程式的呼叫點接著往下執行。

excel程式碼

#include <stdio.h>

#include <unistd.h>

int main(int argc, char *argv[])

{

printf("before exec\n\n");

/* /bin/ls:外部程式,這裡是/bin目錄的 ls 可執行程式,必須帶上路徑(相對或絕對)

   ls:沒有意義,如果需要給這個外部程式傳參,這裡必須要寫上字串,至於字串內容任意

   -a,-l,-h:給外部程式 ls 傳的引數

   NULL:這個必須寫上,代表給外部程式 ls 傳參結束

*/

execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);

// 如果 execl() 執行成功,下面執行不到,因為當前程式已經被執行的 ls 替換了

perror("execl");

printf("after exec\n\n");

return 0;

}

執行結果:

 

execv()示例程式碼:

execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改為用指標陣列。

 

 

#include <stdio.h>

#include <unistd.h>

 

int main(int argc, char *argv[])

{

// execv() 和 execl() 的用法基本是一樣的,無非將列表傳參,改為用指標陣列

// execl("/bin/ls", "ls", "-a", "-l", "-h", NULL);

/* 指標陣列

   ls:沒有意義,如果需要給這個外部程式傳參,這裡必須要寫上字串,至於字串內容任意

   -a,-l,-h:給外部程式 ls 傳的引數

   NULL:這個必須寫上,代表給外部程式 ls 傳參結束

*/

char *arg[]={"ls", "-a", "-l", "-h", NULL};

// /bin/ls:外部程式,這裡是/bin目錄的 ls 可執行程式,必須帶上路徑(相對或絕對)

// arg: 上面定義的指標陣列地址

execv("/bin/ls", arg);

perror("execv");

return 0;

}

 

execlp() 或 execvp() 示例程式碼:

execlp() 和 execl() 的區別在於,execlp() 指定的可執行程式可以不帶路徑名,如果不帶路徑名的話,會在環境變數 PATH指定的目錄裡尋找這個可執行程式,而 execl() 指定的可執行程式,必須帶上路徑名。

#include <stdio.h>  

#include <unistd.h>  

  

int main(int argc, char *argv[])  

{  

    // 第一個引數 "ls",沒有帶路徑名,在環境變數 PATH 裡尋找這個可執行程式  

    // 其它引數用法和 execl() 一樣  

    execlp("ls", "ls", "-a", "-l", "-h", NULL);  

      

    /* 

    char *arg[]={"ls", "-a", "-l", "-h", NULL}; 

    execvp("ls", arg); 

    */  

      

    perror("execlp");  

      

    return 0;  

}  

 

execle() 或 execve() 示例程式碼:

execle() 和 execve() 改變的是 exec 啟動的程式的環境變數(只會改變程式的環境變數,不會影響系統的環境變數),其他四個函式啟動的程式則使用預設系統環境變數。

execle()示例程式碼:

 

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h> // getenv()

 

int main(int argc, char *argv[])

{

// getenv() 獲取指定環境變數的值

printf("before exec:USER=%s, HOME=%s\n", getenv("USER"), getenv("HOME"));

// 指標資料

char *env[]={"USER=EDU", "HOME=/tmp", NULL};

/* ./edu:外部程式,當前路徑的 edu 程式,透過 gcc edu.c -o edu 編譯

edu:這裡沒有意義

NULL:給 edu 程式傳參結束

env:改變 edu 程式的環境變數,正確來說,讓 edu 程式只保留 env 的環境變數

 */

execle("./edu", "edu", NULL, env);

/*

char *arg[]={"edu", NULL};  

execve("./edu", arg, env);

*/

perror("execle");

return 0;

}

 

外部程式,edu.c 示例程式碼:

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

 

int main(int argc, char *argv[])

{

printf("\nin the edu fun, after exec: \n");

printf("USER=%s\n", getenv("USER"));

printf("HOME=%s\n", getenv("HOME"));

return 0;

}

 

執行結果:

 

 

最後:

關注回覆“物聯網”即可獲取物聯網全套影片教程

 

 

 

 

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69914734/viewspace-2656127/,如需轉載,請註明出處,否則將追究法律責任。

相關文章