Linux 執行新程式:execve() 函式
系統呼叫execve()函式作用
在Linux程式中,通過呼叫execve(),程序能夠以全新程式來替換當前執行的程式。再次過程中,將丟棄舊有程式,程序的棧.資料以及堆段會被新程式所替換。這個 exec 函式族就提供了一個在程序中啟動另一個程式執行的方法。
它根據指定的檔名或目錄名找到可執行檔案,並用它來代替當前程序的執行映像。也就是說,exec呼叫並沒有生成新程序,一個程序一旦呼叫 exec函式,它本身就“死亡”了,系統把程式碼段替換成新程式的程式碼,放棄原有的資料段和堆疊段,併為新程式分配新的資料段與堆疊段,惟一保留的就是程序的 ID。也就是說,對系統而言,還是同一個程序,不過執行的已經是另外一個程式了。
execve()函式原型
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
- filename:包含準備載入當前程序空間的新程式的路徑名。既可以是絕對路徑,又可以是相對路徑。
- argv[]:指定了傳給新程序的命令列引數,該陣列對應於c語言main函式的argv引數陣列,格式也相同,argv[0]對應命令名,通常情況下該值與filename中的basename(就是絕對路徑的最後一個)相同。
- 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 之外,程序還保持了許多原有的特徵,主要有:
- 當前工作目錄
- 根目錄
- 建立檔案時使用的遮蔽字
- 程序訊號遮蔽字
- 未決警告
- 和程序相關的使用處理器的時間
- 控制終端
- 檔案鎖
exec族
有 6 個以 exec 開頭的函式族,他們之間的語法有細微的差別,函式原型如下所示:
- 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, *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結尾。