【Linux系统编程】进程替换:exec 函数族

00. 目录

01. exec函数族

在 Windows 平台下,我们可以通过双击运行可执行程序,让这个可执行程序成为一个进程;而在 Linux 平台,我们可以通过 ./ 运行,让一个可执行程序成为一个进程。

但是,如果我们本来就运行着一个程序(进程),我们如何在这个进程内部启动一个外部程序,由内核将这个外部程序读入内存,使其执行起来成为一个进程呢?这里我们通过 exec 函数族实现。

exec 函数族,顾名思义,就是一簇函数,在 Linux 中,并不存在 exec() 函数,exec 指的是一组函数,一共有 6 个:

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

其中只有 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,失败后从原程序的调用点接着往下执行。

02. 参考示例

2.1 execl函数示例

#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("before exec\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");
	
	return 0;
}

执行结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
before exec

总用量 23K
drwxrwxrwx 1 root root 4.0K 11月  5 15:38 .
drwxrwxrwx 1 root root 4.0K 11月  4 18:05 ..
-rwxrwxrwx 1 root root  576 11月  5 15:38 1.c
-rwxrwxrwx 1 root root  704 11月  5 13:42 1.c.bak
drwxrwxrwx 1 root root 4.0K 11月  3 20:46 4sys
-rwxrwxrwx 1 root root 8.2K 11月  5 15:38 a.out
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

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

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
总用量 23K
drwxrwxrwx 1 root root 4.0K 11月  5 15:40 .
drwxrwxrwx 1 root root 4.0K 11月  4 18:05 ..
-rwxrwxrwx 1 root root  643 11月  5 15:40 1.c
-rwxrwxrwx 1 root root  576 11月  5 15:38 1.c.bak
drwxrwxrwx 1 root root 4.0K 11月  3 20:46 4sys
-rwxrwxrwx 1 root root 8.2K 11月  5 15:40 a.out
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

2.3 execlp() 或 execvp()函数示例

execlp() 和 execl() 的区别在于,execlp() 指定的可执行程序可以不带路径名,如果不带路径名的话,会在环境变量 PATH指定的目录里寻找这个可执行程序,而 execl() 指定的可执行程序,必须带上路径名。

#include <stdio.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	// 第一个参数 "ls",没有带路径名,在环境变量 PATH 里寻找这个可执行程序
	// 其它参数用法和 execl() 一样
	execlp("ls", "ls", "-a", "-l", "-h", NULL);
	
	/*
	char *arg[]={"ls", "-a", "-l", "-h", NULL};
	execvp("ls", arg);
	*/
	
	perror("execlp");
	
	return 0;
}

执行结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
总用量 22K
drwxrwxrwx 1 root root 4.0K 115 15:42 .
drwxrwxrwx 1 root root 4.0K 114 18:05 ..
-rwxrwxrwx 1 root root  354 115 15:42 1.c
-rwxrwxrwx 1 root root  643 115 15:40 1.c.bak
drwxrwxrwx 1 root root 4.0K 113 20:46 4sys
-rwxrwxrwx 1 root root 8.2K 115 15:42 a.out
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

2.4 execle() 或 execve()函数示例

execle() 和 execve() 改变的是 exec 启动的程序的环境变量(只会改变进程的环境变量,不会影响系统的环境变量),其他四个函数启动的程序则使用默认系统环境变量。

#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=TEST", "HOME=/tmp", NULL};
	
	/* ./test:外部程序,当前路径的 test 程序,通过 gcc test.c -o test 编译
		test:这里没有意义
		NULL:给 test 程序传参结束
		env:改变 test 程序的环境变量,正确来说,让 test 程序只保留 env 的环境变量
	 */
	execle("./test", "test", NULL, env);
	
	/*
	char *arg[]={"test", NULL};		
	execve("./test", arg, env);	
	*/
	
	perror("execle");
	
	return 0;
}

外部程序,test.c 示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
	printf("\nin the test fun, after exec: \n");
	printf("USER=%s\n", getenv("USER"));
	printf("HOME=%s\n", getenv("HOME"));
	
	return 0;
}

03. 附录

3.1 参考博客:【Linux系统编程】进程替换:exec 函数族

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值