1. 程式人生 > >Linux 執行新程式:execve() 函式

Linux 執行新程式:execve() 函式

系統呼叫execve()函式作用

在Linux程式中,通過呼叫execve(),程序能夠以全新程式來替換當前執行的程式。再次過程中,將丟棄舊有程式,程序的棧.資料以及堆段會被新程式所替換。這個 exec 函式族就提供了一個在程序中啟動另一個程式執行的方法。
它根據指定的檔名或目錄名找到可執行檔案,並用它來代替當前程序的執行映像。也就是說,exec呼叫並沒有生成新程序,一個程序一旦呼叫 exec函式,它本身就“死亡”了,系統把程式碼段替換成新程式的程式碼,放棄原有的資料段和堆疊段,併為新程式分配新的資料段與堆疊段,惟一保留的就是程序的 ID。也就是說,對系統而言,還是同一個程序,不過執行的已經是另外一個程式了。

execve()函式原型

#include <unistd.h>
int execve(const char *filename, char *const argv[],
                  char *const envp[]);
  1. filename:包含準備載入當前程序空間的新程式的路徑名。既可以是絕對路徑,又可以是相對路徑。
  2. argv[]:指定了傳給新程序的命令列引數,該陣列對應於c語言main函式的argv引數陣列,格式也相同,argv[0]對應命令名,通常情況下該值與filename中的basename(就是絕對路徑的最後一個)相同。
  3. envp[]:最後一個引數envp指定了新程式的環境列表。引數envp對應於新程式的environ陣列。

例項1

下面的每個程式碼我都放到了github上,歡迎大家fork/star
GeneralSandman
t_execve.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
int main(int argc,char * argv[]){
    char * argVec[10];
    char * envVec[]={"環境變數1","環境變數2",NULL};
    argVec[0
]=argv[0]; argVec[1]=argv[1]; argVec[2]="引數1"; execve(argv[1],argVec,envVec); printf("the progress can't to here\n"); exit(EXIT_SUCCESS); }

envargs.c
下面的每個程式碼我都放到了github上,歡迎大家fork/star
GeneralSandman

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
extern char ** environ;
int main(int argc,char * argv[]){
    int i=0;
    char ** ep;
    printf("new progress\n");
    printf("引數\n");
    for(i=0;i<argc;i++)
        printf("\t%s\n",argv[i]);
    printf("環境變數:\n");
    for(ep=environ;*ep!=NULL;ep++)
        printf("\t%s\n",*ep);
    printf("new progress over\n");
    exit(EXIT_SUCCESS);
    return 0;
}

執行結果

這裡寫圖片描述

程式說明

執行t_execve程式,會呼叫execve()函式,函式將會呼叫程序argv[1],也就是呼叫envargs,並將自己的環境變數,引數傳遞給他。我們發現,printf(“the progress can’t to here\n”); 永遠不會被執行(除非呼叫execve()失敗),因為呼叫execve(),會轉到新的程序,自己會被“殺死”;

例項2

t_execve2.c
下面的每個程式碼我都放到了github上,歡迎大家fork/star
GeneralSandman

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
int main(int argc,char * argv[],char ** environ){
    pid_t pid;
    printf("the example!!!\n");
    switch(fork()){
    case -1:
        printf("fork error\n");
        exit(EXIT_FAILURE);
    case 0:
        printf("child is running\n");
        printf("child pid=%d, child parent pid=%d\n",getpid(),getppid());
        printf("child uid=%d, child gid=%d\n",getuid(),getpid());
        execve(argv[1],argv,environ);
        printf("child can't in here\n");
        break;
    default:
        wait(NULL);
        printf("parent is running\n");
        break;
    }
    exit(EXIT_SUCCESS);
}

envargs2.c
下面的每個程式碼我都放到了github上,歡迎大家fork/star
GeneralSandman

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
int main(int argc,char * argv[],char ** environ){
    int i;
    printf("running after the execve()\n");
    printf(" pid=%d,  parent pid=%d\n",getpid(),getppid());
    printf(" uid=%d,  gid=%d\n",getuid(),getpid());
    printf("the immage over\n");
    exit(0);
}

執行結果

這裡寫圖片描述

程式說明

執行t_execve2程式,fork()之後,子程序會呼叫execve()函式,而父程序會等待子程序退出(也就是等待envargs2退出)。我們發現:執行新程式保持了原來程序的程序 ID、父程序ID、實際使用者 ID 和實際組 ID。同時還可以看到,當呼叫新的可執行程式後,原有的子程序的映像被替代,不再被執行。

執行新程式後的程序除了保持原來的程序 ID、父程序 ID、實際使用者 ID 和實際組 ID 之外,程序還保持了許多原有的特徵,主要有:

  1. 當前工作目錄
  2. 根目錄
  3. 建立檔案時使用的遮蔽字
  4. 程序訊號遮蔽字
  5. 未決警告
  6. 和程序相關的使用處理器的時間
  7. 控制終端
  8. 檔案鎖

exec族

有 6 個以 exec 開頭的函式族,他們之間的語法有細微的差別,函式原型如下所示:

  1. int execl(const char *path, const char *arg, …)
  2. int execv(const char *path, char *const argv[])
  3. int execle(const char *path, const char *arg, … , char *const envp[])
  4. int execve(const char *path, char *const argv[], char *const envp[])
  5. int execlp(const char *file, const char *arg, …)
  6. int execvp(const char *file, *const argv[])

區別

這裡寫圖片描述

實際上有關exec是一個函式族,包括execle,execlp,execvp,execv,execl但是他們具體的實現都是呼叫execve()之上。它們的區別就在於對路徑名,引數以及環境變數的指定上。下面分別從這三個方面來區分這幾個函式:
- 路徑名:帶p的表示可以通過環境變數PATH去查詢,所以我們可以不用絕對路徑,比如execlp和execvp就可以直接用filename。
- 引數:帶l的execle()和execlp()以及execl()要求在呼叫中以字串形式指定引數。首個引數相當於新程式main中的argv[0],因而通常與filename中的basename相同(就是絕對路徑的最後一個)。
- 環境變數:以e結尾的允許我們通過envp為新程式顯式的指定環境變數,其中envp必須以NULL結尾。