exec函數族

一、exec函數族簡介

(1)fork子進程是爲了執行新程序(fork創建了子進程後,子進程和父進程同時被OS調度執行,因此子進程可以單獨的執行一個程序,這個程序宏觀上將會和父進程程序同時進行)

(2)可以直接在子進程的if中寫入新程序的代碼。這樣可以,但是不夠靈活,因爲我們只能把子進程程序的源代碼貼過來執行(必須知道源代碼,而且源代碼太長了也不好控制),譬如說我們希望子進程來執行ls -la 命令就不行了(沒有源代碼,只有編譯好的可執行程序),而exec族函數可以直接把一個編譯好的可執行程序直接加載運行。

(3)我們有了exec族函數後,我們典型的父子進程程序是這樣的:子進程需要運行的程序被單獨編寫、單獨編譯連接成一個可執行程序(叫hello),(項目是一個多進程項目)主程序爲父進程,fork創建了子進程後在子進程中exec來執行hello,達到父子進程分別做不同程序同時(宏觀上)運行的效果。

 

#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[]);

 

二、execl()函數

函數原型:

int execl(const char *path, const char *arg, ...
                       /* (char  *) NULL */);

函數返回值:

The  exec() functions return only if an error has occurred.  The return value is -1, and errno is set to indicate the error.

成功則不返回值, 失敗返回-1, 失敗原因存於errno中,可通過perror()打印

 

execl代碼示例:

實驗過程:編譯鏈接new.c生成new.o,在execl.c中使用execl函數調用new.o

/*
execl.c
*/

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

#include <sys/types.h>  
#include <sys/wait.h>
#include <stdlib.h>

int main(void)
{
	pid_t pid = -1;
	pid_t ret = -1;
	int status = -1;
	
	pid = fork();
	if (pid > 0)
	{
		// 父進程
		printf("this is parent, 子進程id = %d.\n", pid);
		printf("parent pid = %d.\n", getpid());
		ret = wait(&status);
		
		
		printf("子進程已經被回收,子進程pid = %d.\n", ret);
		printf("子進程已經被回收,子進程status = %d.\n", status);
	}
	else if (pid == 0)
	{
		// 子進程
		execl("./new", "./new", NULL);		// ls -l -a  以NULL結尾表示完成  第一個參數是命令的名字,第二個纔是參數
		return 0;
	}
	else
	{
		perror("fork");
		return -1;
	}
	
	return 0;
}

 

/*
new.c
*/
#include<stdio.h>
#include <unistd.h>

int main(void)
{
	for(int i =0; i<3; i++)
	{
		sleep(1);
		printf("this is child!\r\n");
	}

	return 0;
}

 

三、exec函數族使用細節

(1)execl和execv    這兩個函數是最基本的exec,都可以用來執行一個程序,區別是傳參的格式不同。execl是把參數列表(本質上是多個字符串,必須以NULL結尾)依次排列而成(l其實就是list的縮寫),execv是把參數列表事先放入一個字符串數組中,再把這個字符串數組傳給execv函數。

char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
 
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
 
execv("/bin/ps", ps_argv);

(2)execlp和execvp 這兩個函數在上面2個基礎上加了p,較上面2個來說,區別是:上面2個執行程序時必須指定可執行程序的全路徑(如果exec沒有找到path這個文件則直接報錯),而加了p的傳遞的可以是file(也可以是path,只不過兼容了file。加了p的這兩個函數會首先去找file,如果找到則執行執行,如果沒找到則會去環境變量PATH所指定的目錄下去找,如果找到則執行如果沒找到則報錯)(實驗證明,會先到PATH指定的目錄下面找)

char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};

execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
 
execvp("ps", ps_argv);

(3)execle和execvpe        這兩個函數較基本exec來說加了e,函數的參數列表中也多了一個字符串數組envp形參,e就是environment環境變量的意思,和基本版本的exec的區別就是:執行可執行程序時會多傳一個環境變量的字符串數組給待執行的程序。

char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
 
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
 

 

note:

main函數的原型其實不止是int main(int argc, char **argv),而可以是

int main(int argc, char **argv, char **env)     第三個參數是一個字符串數組,內容是環境變量。

如果用戶在執行這個程序時沒有傳遞第三個參數,則程序會自動從父進程繼承一份環境變量(默認的,最早來源於OS中的環境變量);如果我們exec的時候使用execlp或者execvpe去給傳一個envp數組,則程序中的實際環境變量是我們傳遞的這一份(取代了默認的從父進程繼承來的那一份)

 

#include <stdio.h>

// env就是我們給main函數額外傳遞的環境變量字符串數組
int main(int argc, char **argv, char **env)
{
	int i = 0;
	
	printf("argc = %d.\n", argc);
	
	while (NULL != argv[i])
	{
		printf("argv[%d] = %s\n", i, argv[i]);
		i++;
	}
	
	i = 0;
	while (NULL != env[i])
	{
		printf("env[%d] = %s\n", i, env[i]);
		i++;
	}
	
	return 0;
}

 

使用舉例:

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

int main(void)
{
	pid_t pid = -1;
	pid_t ret = -1;
	int status = -1;
	
	pid = fork();
	if (pid > 0)
	{
		// 父進程
		printf("parent, 子進程id = %d.\n", pid);
	}
	else if (pid == 0)
	{
		// 子進程
		char * const envp[] = {"AA=aaaa", "XX=abcd", NULL};
		execle("hello", "hello", "-l", "-a", NULL, envp);
		
		return 0;
	}
	else
	{
		perror("fork");
		return -1;
	}
	
	return 0;
}

 

可見,使用execle和execvpe可以自己向執行進程傳遞環境變量,但不會繼承Shell進程的環境變量,而其他四個exec函數則繼承Shell進程的所有環境變量。

REF:

https://www.cnblogs.com/mickole/p/3187409.html

https://blog.csdn.net/amoscykl/article/details/80354052

朱有鵬課堂筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章