Linux程式執行緒學習筆記

weixin_30488085發表於2020-04-06

上一篇中我們說到,當啟動一個新程式以後,新程式會複製父程式的大部份上下文並接著執行父程式中的程式碼,如果我們使新程式不執行原父程式的程式碼,轉而執行另外一個程式集中的程式碼,這就相當於啟動了一個新程式。這裡的程式碼我們可以理解成一個可執行程式。

所以,要執行一個新程式,需要最基本的兩步:

1,建立一個可執行程式的環境,也就是程式。

2,將環境中的內容替換成你所希望的,也就是用你希望執行的可執行檔案去覆蓋新程式中的原有映像,並從該可執行檔案的起始處開始執行。 

要做到第一點,非常簡單,fork函式就可以(參考上一篇) ,要做到第二點,則可以利用exec函式族。

 

exec是一族函式的簡稱,包含在<unistd.h>中它們作用都一樣,用一個可執行檔案覆蓋程式的現有映像,並轉到該可執行檔案的起始處開始執行。

原型如下:

int execl(const char *path, const char *arg0, ... /*, (char *)0 */); 

int execlp(const char *file, const char *arg0, ... /*, (char *)0 */); 

int execle(const char *path, const char *arg0, ... /*, (char *)0, 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函式為例,其他的大同小異,其第一個引數path是可執行檔案的路徑,是絕對路徑;從arg0引數開始及後面所有的是你要傳遞給可執行檔案的命令列引數,值得注意的是,arg0是可執行檔案本身(還記得C語言中老師講main函式引數列表時所說的麼),當然,不傳程式本身或傳一些亂七八糟的值並不代表不能通過編譯或不能執行,只不過,如果可執行檔案要用到arg0時會產生一些迷惑;最後有一個註釋/*, (char*)0 */是提醒我們最後一個引數應該傳空字串。如何函式執行成功,則不會有任何返回值,否則返回-1,而具體的錯誤號會被設定在errno,errno是一個全域性變數,用於程式設定錯誤號,跟win32的getLastError函式類似。 

看下面的例子:

#include <stdio.h>    
#include 
<unistd.h>   

int main ()
{
    printf(
"app start...\n");

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

    printf(
"app end\n");
    
    
return 0;
}

我們執行了bin目錄下的ls程式,引數arg0時ls程式本身路徑,arg1為-l,使得其以列表的形式列舉當前目錄,在我的計算機上程式輸出如下:

app start...
total 
12
-rw-r--r-- 
1 zhouyh zhouyh  273 2010-09-06 11:09 temp.c
-rwxr-xr-x 
1 zhouyh zhouyh 7175 2010-09-06 11:09 temp.exe

ls程式執行成功了。但注意到了嗎?沒有輸出“app end”這個字串,原因很簡單,我們沒有新起程式,而是直接用ls程式覆蓋了main函式所在的程式。

 

那我們接下來,試著用fork吧,以免影響原程式。

 

#include <stdio.h>  
#include 
<unistd.h>  

int main ()
{
    printf(
"app start...\n");
    
    
if(fork() == 0)
    {
        execl(
"/bin/ls""/bin/ls""-l", NULL);
    }
        
    printf(
"app end\n");
    
    
return 0;
}

我們用fork建立了一個新程式,當其成功建立後(返回值為0),我們用execl來載入ls程式並執行之。

程式的輸出如下:

app start...
app end
zhouyh@ubuntu:~/Documents$ total 
12
-rw-r--r-- 
1 zhouyh zhouyh  229 2010-09-06 15:59 temp.c
-rwxr-xr-x 
1 zhouyh zhouyh 7211 2010-09-06 16:00 temp.exe

 

程式的所有輸出都OK了,但有一點可能和我們想象的不一樣,那就是“app end”這個字串很早就輸出了而不是在最後,其實這並沒有錯,“app end” 是main函式所在的程式(temp.exe)即將結束時輸出的,而列舉檔案目錄的ls卻完全在另外一個程式中,兩個非同步執行的程式,他們沒有誰先誰後結束可言。

 

如果我們希望所有工作完成之後,即ls也執行玩以後,才輸出“app end”,那麼可以使用wait 以及waitpid函式,這裡簡單說一下wait,具體的會在“Linux程式執行緒學習筆記:程式控制”中講。
pid_t wait (int * status); //包含在 <sys/wait.h> 中

wait函式講當前程式休眠,直到該程式的某個子程式結束或者有特定的訊號來喚醒。如果子程式正常結束,則講子程式的程式id(pid)作為返回值,發生錯誤則返回-1,而status引數講傳出子程式的結束狀態值。
針對剛才的例子,可以參考下面的程式碼:

#include <stdio.h>    //for printf(const char)
#include <unistd.h>   //for fork()
#include <sys/wait.h> //for wait(int* status)

int main ()
{
    printf(
"app start...\n");
    
    
if(fork() == 0)
    {
        execl(
"/bin/ls""/bin/ls""-l", NULL);
    }
    
    
int status;
    wait(
&status);
        
    printf(
"app end\n");
    
    
return 0;
}

 

程式輸出如下:

 

app start...
total 
12
-rw-r--r-- 
1 zhouyh zhouyh  337 2010-09-06 16:22 temp.c
-rwxr-xr-x 
1 zhouyh zhouyh 7247 2010-09-06 16:22 temp.exe
app end

 

好了,現在回過頭來看除execl外的其他幾個函式 :

 

int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);

execlp和execl差不多,但區別在於前者會去系統環境變數查詢file所指的程式的位置,所以如果通過環境變數能找到可執行檔案,則file可以不是絕對路徑了,比如 execlp("ls", "ls", "-l", NULL);

 

int execle(const char *path, const char *arg0, ... /*, (char *)0, char *const envp[]*/); 

與execlp不同的是,其最後一個引數作為你自定義的環境變數引數傳進去,而不是查詢系統環境變數

 

  char *env[] = { "HOME=/usr/home", "LOGNAME=home",(char *)0 };

 execle("/bin/ls", "ls", "-l", NULL, env);

 

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[]);

這三個函式和前面的三個類似,函式名由字尾l變成了v,其表達的含義是引數不再用引數列表傳遞而是用一個引數陣列argv[],當然,陣列最後一個元素也必須是char* 0 

 

名字這麼相近的函式,感覺好容易混淆,那麼就從l,v,p,e 這樣的字尾來區分吧:
l:引數為一個逗號分隔的引數列表,並以char* 0作為列表結尾

v: 引數為字串陣列,陣列的最後一個元素為char* 0

p: 可以通過系統環境變數查詢檔案位置

e:呼叫者顯示傳入環境變數 

 

http://www.cnblogs.com/zhouyinhui/archive/2010/09/07/1819338.html

 

轉載於:https://www.cnblogs.com/xd502djj/archive/2010/09/16/1827689.html

相關文章