1. 程式人生 > >Linux系統編程——進程替換:exec 函數族

Linux系統編程——進程替換:exec 函數族

keyword [] 基礎上 p s sdn bin asset processor include

在 Windows 平臺下。我們能夠通過雙擊運行可運行程序。讓這個可運行程序成為一個進程;而在 Linux 平臺。我們能夠通過 ./ 運行,讓一個可運行程序成為一個進程。


可是,假設我們本來就執行著一個程序(進程),我們怎樣在這個進程內部啟動一個外部程序。由內核將這個外部程序讀入內存,使其執行起來成為一個進程呢?這裏我們通過 exec 函數族實現。


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

[cpp] view plaincopy技術分享技術分享
  1. #include <unistd.h>
  2. int execl(const char *path, const char *arg, ...);
  3. int execlp(const char *file, const char *arg, ...);
  4. int execle(const char *path, const char *arg, ..., char * const envp[]);
  5. int execv(const char *path, char *const argv[]);
  6. int execvp(const char *file, char *const argv[]);
  7. 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() 指定的可運行程序。必須帶上路徑名。

[cpp] view plaincopy技術分享技術分享
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. int main(int argc, char *argv[])
  4. {
  5. // 第一個參數 "ls"。沒有帶路徑名,在環境變量 PATH 裏尋找這個可運行程序
  6. // 其他參數使用方法和 execl() 一樣
  7. execlp("ls", "ls", "-a", "-l", "-h", NULL);
  8. /*
  9. char *arg[]={"ls", "-a", "-l", "-h", NULL};
  10. execvp("ls", arg);
  11. */
  12. perror("execlp");
  13. return 0;
  14. }


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;
}

執行結果: 技術分享




Linux系統編程——進程替換:exec 函數族