1. 程式人生 > >Linux程序控制——exec函式族

Linux程序控制——exec函式族

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                1、簡介 在Linux中,並不存在exec()函式,exec指的是一組函式,一共有6個,分別是: #include <unistd.h>

extern char **environ;
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[]);
詳細定義: execl(執行檔案)
相關函式
    fork,execle,execlp,execv,execve,execvp
表頭檔案
    #include<unistd.h>
定義函式
    int execl(const char * path,const char * arg,....);
函式說明
    execl()用來執行引數path字串所代表的檔案路徑,接下來的引數代表執行該檔案時傳遞過去的argv(0)、argv[1]……,最後一個引數必須用空指標(NULL)作結束。
返回值
    如果執行成功則函式不會返回,執行失敗則直接返回-1,失敗原因存於errno中。
範例
    #include<unistd.h>
main()
{
execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char * )0);
}
執行
    /*執行/bin/ls -al /etc/passwd */
-rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
 
 

execlp(從PATH 環境變數中查詢檔案並執行)
相關函式
    fork,execl,execle,execv,execve,execvp
表頭檔案
    #include<unistd.h>
定義函式
    int execlp(const char * file,const char * arg,……);
函式說明
    execlp()會從PATH 環境變數所指的目錄中查詢符合引數file的檔名,找到後便執行該檔案,然後將第二個以後的引數當做該檔案的argv[0]、argv[1]……,最後一個引數必須用空指標(NULL)作結束。
返回值
    如果執行成功則函式不會返回,執行失敗則直接返回-1,失敗原因存於errno 中。
錯誤程式碼
    參考execve()。
範例
    /* 執行ls -al /etc/passwd execlp()會依PATH 變數中的/bin找到/bin/ls */
#include<unistd.h>
main()
{
execlp(“ls”,”ls”,”-al”,”/etc/passwd”,(char *)0);
}
執行
    -rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
 


    
execv(執行檔案)
相關函式
    fork,execl,execle,execlp,execve,execvp
表頭檔案
    #include<unistd.h>
定義函式
    int execv (const char * path, char * const argv[ ]);
函式說明
    execv()用來執行引數path字串所代表的檔案路徑,與execl()不同的地方在於execve()只需兩個引數,第二個引數利用陣列指標來傳遞給執行檔案。
返回值
    如果執行成功則函式不會返回,執行失敗則直接返回-1,失敗原因存於errno 中。
錯誤程式碼
    請參考execve()。
範例
    /* 執行/bin/ls -al /etc/passwd */
#include<unistd.h>
main()
{
char * argv[ ]={“ls”,”-al”,”/etc/passwd”,(char*) }};
execv(“/bin/ls”,argv);
}
執行
    -rw-r--r-- 1 root root 705 Sep 3 13 :52 /etc/passwd
 


    
execve(執行檔案)
相關函式
    fork,execl,execle,execlp,execv,execvp
表頭檔案
    #include<unistd.h>
定義函式
    int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
函式說明
    execve()用來執行引數filename字串所代表的檔案路徑,第二個引數系利用陣列指標來傳遞給執行檔案,
    argv要傳遞給程式的完整引數列表,包括argv[0],它一般是執行程式的名字;最後一個引數則為傳遞給執行檔案的新環境變數陣列。
返回值
    如果執行成功則函式不會返回,執行失敗則直接返回-1,失敗原因存於errno 中。
錯誤程式碼
    EACCES
1. 欲執行的檔案不具有使用者可執行的許可權。
2. 欲執行的檔案所屬的檔案系統是以noexec 方式掛上。
3.欲執行的檔案或script翻譯器非一般檔案。
EPERM
1.程序處於被追蹤模式,執行者並不具有root許可權,欲執行的檔案具有SUID 或SGID 位。
2.欲執行的檔案所屬的檔案系統是以nosuid方式掛上,欲執行的檔案具有SUID 或SGID 位元,但執行者並不具有root許可權


其中只有execve是真正意義上的系統呼叫,其它都是在此基礎上經過包裝的庫函式。
exec函式族的作用是根據指定的檔名找到可執行檔案,並用它來取代呼叫程序的內容,換句話說,就是在呼叫程序內部執行一個可執行檔案。這裡的可執行檔案既可以是二進位制檔案,也可以是任何Linux下可執行的指令碼檔案
函式名與引數的關係: 細看一下,這6個函式都是以exec開頭(表示屬於exec函式組),前3個函式接著字母l的,後3個接著字母v的,我的理解是l表示list(列舉引數),v表示vector(引數向量表) 。它們的區別在於,execv開頭的函式是以"char *argv[]"(vector)形式傳遞命令列引數,而execl開頭的函式採用了羅列(list)的方式,把引數一個一個列出來,然後以一個NULL表示結束。這裡的NULL的作用和argv數組裡的NULL作用是一樣的。
字母p是指在環境變數PATH的目錄裡去查詢要執行的可執行檔案。2個以p結尾的函式execlp和execvp,看起來,和execl與execv的差別很小,事實也如此,它們的區別從第一個引數名可以看出:除execlp和execvp之外的4個函式都要求,它們的第1個引數path必須是一個完整的路徑,如"/bin/ls";而execlp和execvp的第1個引數file可以僅僅只是一個檔名,如"ls",這兩個函式可以自動到環境變數PATH指定的目錄裡去查詢。
字母e是指給可執行檔案指定環境變數。在全部6個函式中,只有execle和execve使用了char *envp[]傳遞環境變數,其它的4個函式都沒有這個引數,這並不意味著它們不傳遞環境變數,這4個函式將把預設的環境變數不做任何修改地傳給被執行的應用程式。而execle和execve用指定的環境變數去替代預設的那些。
返回值 與一般情況不同,exec函式族的函式執行成功後不會返回,因為呼叫程序的實體,包括程式碼段,資料段和堆疊等都已經被新的內容取代,只有程序ID等一些表面上的資訊仍保持原樣。呼叫失敗時,會設定errno並返回-1,然後從原程式的呼叫點接著往下執行
與其他系統呼叫比起來,exec很容易失敗,被執行檔案的位置,許可權等很多因素都能導致呼叫失敗。因此,使用exec函式族時,一定要加錯誤判斷語句。最常見的錯誤: 找不到檔案或路徑,此時errno被設定為ENOENT; 陣列argv和envp忘記用NULL結束,此時errno被設定為EFAULT; 沒有對要執行檔案的執行許可權,此時errno被設定為EACCES。

2、應用 如果一個程序想執行另一個程式,它就可以fork或vfork出一個新程序,然後呼叫任何一個exec函式。 為此,Linux還專門對fork作了優化:通常fork會將呼叫程序的所有內容原封不動的拷貝到新產生的子程序中去,這些拷貝的動作很消耗時間,而如果fork完之後我們馬上就呼叫exec,那這些辛辛苦苦拷貝來的東西就會被立刻抹掉,這看起來非常不划算,於是人們設計了一種"寫時複製(copy-on-write)"技術,使得fork結束後並不立刻複製父程序的內容到子程序,而是到了真正使用時才複製,這樣如果下一條語句是exec,它就不會作無用功了。其實"寫時複製"還是有複製,程序的mm結構、頁表都還是被複制了("寫時複製"也必須由這些資訊來支撐。否則核心捕捉到CPU訪存異常,怎麼區分 這是“寫時複製”引起的,還是真正的越權訪問呢?)。 而vfork就把事情做絕了,所有有關於記憶體的東西都不復制了,父子程序的記憶體是完全共享的。但是這樣一來又有問題了,雖然使用者程式可以設計很多方法來避免父子程序間的訪存衝突。但是關鍵的一點,父子程序共用著棧,這可不由使用者程式控制的。一個程序進行了關於函式呼叫或返回的操作,則另一個程序的呼叫棧 (實際上就是同一個棧)也被影響了。這樣的程式沒法執行下去。所以,vfork有個限制,子程序生成後,父程序在vfork中被核心掛起,直到子程序有了自己的記憶體空間(exec**)或退出(_exit)。並且, 在此之前,子程序不能從呼叫vfork的函式中返回(同時,不能修改棧上變數、不能繼續呼叫除_exit或exec系列之外的函式,否則父程序的資料可能 被改寫)。 儘管限制很多,vfork後馬上exec效率會比fork高不少。
  1. /* exec.c */
  2. #include <unistd.h>
  3. void main(void)
  4. {
  5.     char *envp[] = {"PATH=/tmp", "USER=lingdxuyan", "STATUS=testing", NULL};
  6.     char *argv_execv[] = {"echo", "excuted by execv", NULL};
  7.     char *argv_execvp[] = {"echo", "executed by execvp", NULL};
  8.     char *argv_execve[] = {"env", NULL};

  9.     if (fork() == 0)
  10.         if (execl("/bin/echo", "echo", "executed by execl", NULL) < 0)
  11.             perror("Err on execl");

  12.     if (fork() == 0)
  13.         if (execlp("echo", "echo", "executed by execlp", NULL) < 0)
  14.             perror("Err on execlp");

  15.     if (fork() == 0)
  16.         if (execle("/usr/bin/env", "env", NULL, envp) < 0)
  17.             perror("Err on execle");

  18.     if (fork() == 0)
  19.         if (execv("/bin/echo", argv_execv) < 0)
  20.             perror("Err on execv");

  21.     if (fork() == 0)
  22.         if (execvp("echo", argv_execvp) < 0)
  23.             perror("Err on execvp");

  24.     if (fork() == 0)
  25.         if (execve("/usr/bin/env", argv_execve, envp) < 0)
  26.             perror("Err on execve");
  27. }
由於各個子程序執行的順序無法控制,所以有可能出現一個比較混亂的輸出--各子程序列印的結果交雜在一起,而不是嚴格按照程式中列出的次序。若將程式中fork都改為vfork,則各個exec執行的程式將按序執行。            

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述