天天看點

2021-06-29嵌入式學習---exec族函數

exec族函數函數的作用:

我們用fork函數建立新程序後,經常會在新程序中調用exec函數去執行另外一個程式。

當程序調用exec函數時,該程序被完全替換為新程式。

因為調用exec函數并不建立新程序。

(簡單說就是,在執行一個主程式跑到一半的時候,可以調用另一個程式執行,原程式就不執行了)

exec族函數

功能:

  在調用程序内部執行一個可執行檔案。可執行檔案既可以是二進制檔案,也可以是任何Linux下可執行的腳本檔案。

函數族:

  exec函數族分别是:execl, execlp, execle, execv, execvp, execvpe

函數原型:

#include <unistd.h>
extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., 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函數族的函數執行成功後不會傳回,調用失敗時,會設定errno并傳回-1,然後從原程式的調用點接着往下執行。 可以調用perror()函數傳回報錯原因

參數說明:

path:可執行檔案的路徑名字

arg:可執行程式所帶的參數,第一個參數為可執行檔案名字,沒有帶路徑且arg必須以NULL結束

file:如果參數file中包含/,則就将其視為路徑名,否則就按 PATH環境變量,在它所指定的各目錄中搜尋可執行檔案。

exec族函數參數極難記憶和分辨,函數名中的字元會給我們一些幫助:

l : 使用參數清單

p:使用檔案名,并從PATH環境進行尋找可執行檔案

v:應先構造一個指向各參數的指針數組,然後将該數組的位址作為這些函數的參數。

e:多了envp[]數組,使用新的環境變量代替調用程序的環境變量

一、帶l的一類exac函數(l表示list),包括execl、execlp、execle,要求将新程式的每個指令行參數都說明為 一個單獨的參數。這種參數表以空指針結尾。

以execl函數為例子來說明:

//檔案execl.c//gcc編譯成execl
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函數原型:int execl(const char *path, const char *arg, ...);

int main(void)
{
    printf("before execl\n");
    if(execl("./bin/echoarg","echoarg","abc",NULL) == -1)
    {
        printf("execl failed!\n");      
    }
    printf("after execl\n");
    return 0;
}
           
//檔案echoarg.c//gcc編譯成echoarg檔案
#include <stdio.h>

int main(int argc,char *argv[])
{
    int i = 0;
    for(i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n",i,argv[i]); 
    }
    return 0;
}
           

運作結果:

ubuntu:~/test/exec_test$ ./execl
before execl
argv[0]: echoarg
argv[1]: abc
           

我們先用gcc編譯echoarg.c,生成可執行檔案echoarg并放在目前路徑bin目錄下。檔案echoarg的作用是列印指令行參數。然後再編譯execl.c并執行execl可執行檔案。用execl 找到并執行echoarg,将目前程序main替換掉,是以”after execl” 沒有在終端被列印出來。

二、帶p的一類exac函數,包括execlp、execvp、execvpe,如果參數file中包含/,則就将其視為路徑名,否則就按 PATH環境變量,在它所指定的各目錄中搜尋可執行檔案。舉個例子,PATH=/bin:/usr/bin

//檔案execlp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函數原型:int execlp(const char *file, const char *arg, ...);
int main(void)
{
    printf("before execlp\n");
    if(execlp("ps","ps","-l",NULL) == -1)//execlp與execl的差別就是execlp可以不加具體路徑,按PATH環境變量指定的目錄搜尋到ps檔案
    {
        printf("execlp failed!\n");
    }
    printf("after execlp*****\n");
    return 0;
}
           

運作結果:

ubuntu:~/test/exec_test$ gcc execlp.c -o execlp
ubuntu:~/test/exec_test$ ./execlp
before execlp
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R  1048 35976 74920  0  80   0 -  2860 -      pts/4    00:00:00 ps
0 S  1048 74920 74916  0  80   0 -  7579 wait   pts/4    00:00:00 bash
           

三、帶v不帶l的一類exac函數,包括execv、execvp、execve,應先構造一個指向各參數的指針數組,然後将該數組的位址作為這些函數的參數。

如char *arg[]這種形式,且arg最後一個元素必須是NULL,例如char *arg[] = {“ls”,”-l”,NULL};

下面以execvp函數為例說明:

//檔案execvp.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函數原型:int execvp(const char *file, char *const argv[]);

int main(void)
{
    printf("before execlp\n");
    char *argv[] = {"ps","-l",NULL};
    if(execvp("ps",argv) == -1) 
    {
        printf("execvp failed!\n");     
    }
    printf("after execlp*****\n");
    return 0;
}
           

運作結果:

ubuntu:~/test/exec_test$ gcc execvp.c -o execvp
ubuntu:~/test/exec_test$ ./execvp
before execlp****
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R  1048 63491 74920  0  80   0 -  2860 -      pts/4    00:00:00 ps
0 S  1048 74920 74916  0  80   0 -  7579 wait   pts/4    00:00:00 bash
           

四、帶e的一類exac函數,包括execle、execvpe,可以傳遞一個指向環境字元串指針數組的指針。 參數例如char env_init[] = {“AA=aa”,”BB=bb”,NULL}; 帶e表示該函數取envp[]數組,而不使用目前環境。

下面以execle函數為例:

//檔案execle.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函數原型:int execle(const char *path, const char *arg,..., char * const envp[]);

char *env_init[] = {"AA=aa","BB=bb",NULL};
int main(void)
{
    printf("before execle\n");
        if(execle("./bin/echoenv","echoenv",NULL,env_init) == -1)
        {
                printf("execle failed!\n");
        }       
    printf("after execle*****\n");
    return 0;
}
           
//檔案echoenv.c
#include <stdio.h>
#include <unistd.h>
extern char** environ;
int main(int argc , char *argv[])
{
    int i;
    char **ptr;
    for(ptr = environ;*ptr != 0; ptr++)
        printf("%s\n",*ptr);
    return 0;
}
           

運作結果:

ubuntu:~/test/exec_test$ gcc execle.c -o execle
ubuntu:~/test/exec_test$ ./execle
before execle
AA=aa
BB=bb
           

我們先寫一個顯示全部環境表的程式,命名為echoenv.c,然後編譯成可執行檔案放到./bin目錄下。然後再運作可執行檔案execle,發現我們設定的環境變量确實有傳進來。

推薦原文學習參考連接配接

exec函數配合fork函數使用

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

int main()
{
	pid_t pid;
	int data = 0;
	
	while(1){
		printf("input your data\n");
		scanf("%d", &data);
		if(data == 1){       //輸入1,建立程序
			pid = fork();   //建立程序
			if(pid > 0){    //父程序等待子程序
				wait(NULL);
			}else if(pid == 0){  //子程序執行execl函數,運作另一個程式檔案,運作完傳回本程式
				exexl("./bin/echoarg","echoarg","abc",NULL);
			}
		}else{
			printf("do nothing\n");
		}
	}
	return 0;
}
           
//檔案echoarg.c//gcc編譯成echoarg檔案
#include <stdio.h>

int main(int argc,char *argv[])
{
    int i = 0;
    for(i = 0; i < argc; i++)
    {
        printf("argv[%d]: %s\n",i,argv[i]); 
    }
    return 0;
}
           

繼續閱讀