天天看點

Linux下exec函數族詳解

       對于exec函數族來說,它的作用通俗來說就是使另一個可執行程式替換目前的程序,當我們在執行一個程序的過程中,通過exec函數使得另一個可執行程式A的資料段、代碼段和堆棧段取代目前程序B的資料段、代碼段和堆棧段,那麼目前的程序就開始執行A中的内容,這一過程中不會建立新的程序,而且PID也沒有改變。

一般exec函數族的用途有以下兩種:

       1. 當程序不需要再往下繼續運作時,調用exec函數族中的函數讓自己得以延續下去。

       2. 如果當一個程序想執行另一個可執行程式時,可以使用fork函數先建立一個子程序,然後通過子程序來調用exec函數進而實             現可執行程式的功能。

通過man指令來看一下exec函數族:

       首先exec并不是一個函數名,之是以叫函數族就說明它有很多個不同的函數,但是這些函數的功能是一樣的,隻不過參數不同使用的方式也略不相同。那麼在man指令下看到的exec函數原型是這樣的:

#include <unistd.h>

       extern char **environ;

       int execl(const char *path, const char *arg, .../* (char  *) NULL */);
       int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
       int execle(const char *path, const char *arg, ...
                       /*, (char *) NULL, char * const envp[] */);
       int execv(const char *path, char *const argv[]);
       int execvp(const char *file, char *const argv[]);
       int execvpe(const char *file, char *const argv[],char *const envp[]);           

複制

       它們都是以exec為字首,那麼不同的之後後面的一些字元,l表示指令行參數清單、p表示PATH環境變量、v表示使用參數數組、e使用環境變量數組。其中execvpe和execle一般不常用,下面就以例子來看看具體的用法以及所展示出來的效果是怎麼樣的,便于更好的了解exec函數的作用,先來看一下下面的這個代碼:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        execl("/bin/ls", "ls", "-l", NULL);
        perror("execl");
        exit(1);
}           

複制

       這個程式很簡單,就是用目前的程序調用ls這個可執行程式,并添加了-l參數。由于execl成功調用後這個程序的代碼段都被替換了,自然下面的代碼就不會再執行了,是以也就沒有傳回值了,但是當調用失敗後就會傳回-1并設定errno值。那麼在成功調用後實際上這個程序就變成了ls,然後執行ls -l的指令,因為我們用的是execl函數,是以第一個參數就需要用ls的所在目錄,第二個參數其實沒有實際意義,因為已經指定了ls的所在位置,是以第二個參數随便設定就可以但是不可以沒有,第三個參數就是你所需要的功能,這裡我用-l來舉例,最後用NULL表示結束。那麼運作結果如下:

total 16
-rw-r--r-- 1 charles charles  163 Feb 27 15:49 a.c
-rwxr-xr-x 1 charles charles 8384 Feb 27 15:49 test           

複制

       如果是用execlp,那麼第一個參數就可以不用加ls的路徑了,直接是ls就可以了,因為系統會去PATH中查找。如果是execv的話,後面的參數就要是一個指針數組的形式,可以看下面的代碼:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
        char *argv[] = {"ls", "-l", NULL};
        execv("/bin/ls", argv);
        perror("execl");
        exit(1);
}           

複制

一般的exec函數族的錯誤原因:

1. 找不到檔案或者路徑,此時errno為ENOENT。

2. 數組argv和envp(環境變量數組)沒有以NULL結尾,此時errno為EFAULT。

3. 沒有對應可執行檔案的運作權限,此時errno為EACCES。

       下面用exec函數來實作一個簡單的程式b,我們先寫一個這樣的程式,getchar擷取輸入的小寫字母,然後将其轉換成大寫輸出出來,代碼如下:

#include <stdio.h>
#include <ctype.h>

int main(void)
{
	char s;
	while((s = getchar())!=EOF){
		putchar(toupper(s));
	}
	return 0;
}           

複制

      然後我們再寫一個程式a,它的作用是将一個檔案打開,然後讀取檔案中的内容,然後調用exec函數打開這個轉換大寫字母的程式并将原檔案中的内容輸出。這裡這個程式用到了dup2函數來進行重定向,直接看代碼吧,不難了解。

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

int main(int argc, char *argv[])
{
	if(argc != 2){                     // 接收到兩個參數
		printf("Open error!\n");
		exit(1);
	}
	int fd = open(argv[1], O_RDONLY);  // 以隻讀的方式打開文本檔案
	if(fd < 0){
		perror("open file");
		exit(1);
	}
	dup2(fd, STDIN_FILENO);            // 輸入重定向,使STDIN_FILENO指向fd所指的檔案
	close(fd);
    execv("./upper", "upper", NULL);   // 調用upper可執行檔案
	perror("execl");
	exit(1);
}           

複制

       首先我們編譯第一個程式生成可執行程式upper,然後再建立一個文本檔案text,裡面内容為helloworld,然後我們運作程式a,得到下面的運作結果:

Linux下exec函數族詳解