当前位置: 首页 > news >正文

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

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


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


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

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  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 plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>  
  2. #include <unistd.h>  
  3.   
  4. int main(int argc, char *argv[])  
  5. {  
  6.     // 第一个參数 "ls",没有带路径名。在环境变量 PATH 里寻找这个可运行程序  
  7.     // 其他參数使用方法和 execl() 一样  
  8.     execlp("ls""ls""-a""-l""-h", NULL);  
  9.       
  10.     /* 
  11.     char *arg[]={"ls", "-a", "-l", "-h", NULL}; 
  12.     execvp("ls", arg); 
  13.     */  
  14.       
  15.     perror("execlp");  
  16.       
  17.     return 0;  
  18. }  


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

执行结果:





相关文章:

  • 恶灵传说之老程序员
  • 安装zabbix 遇到的故障
  • 场景案例:多表关联update(用户积分奖励)
  • Node.js+Koa开发微信公众号个人笔记(一)准备工作
  • Linux常见命令(二)
  • Django web project
  • okhttp使用总结
  • IP address could not be resolved: Temporary failure in name resolution
  • java基础系列:(一)初始化与清理
  • tarjan强联通分量(模板)
  • 4.3.4 空值与聚合函数
  • AC日记——矩阵取数游戏 洛谷 P1005
  • 【Docker镜像】docker默认存放路径
  • iOS多线程---NSOperation的常用操作
  • Spring源码-IOC容器(六)-bean的循环依赖
  • 【347天】每日项目总结系列085(2018.01.18)
  • mac修复ab及siege安装
  • springMvc学习笔记(2)
  • swift基础之_对象 实例方法 对象方法。
  • yii2权限控制rbac之rule详细讲解
  • yii2中session跨域名的问题
  • 彻底搞懂浏览器Event-loop
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 服务器之间,相同帐号,实现免密钥登录
  • 警报:线上事故之CountDownLatch的威力
  • 日剧·日综资源集合(建议收藏)
  • 如何用vue打造一个移动端音乐播放器
  • 移动端 h5开发相关内容总结(三)
  • Nginx实现动静分离
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • #考研#计算机文化知识1(局域网及网络互联)
  • #每日一题合集#牛客JZ23-JZ33
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (汇总)os模块以及shutil模块对文件的操作
  • (力扣题库)跳跃游戏II(c++)
  • (论文阅读30/100)Convolutional Pose Machines
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (十六)一篇文章学会Java的常用API
  • (四)鸿鹄云架构一服务注册中心
  • (一)插入排序
  • (转)LINQ之路
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .Net MVC + EF搭建学生管理系统
  • .net mvc 获取url中controller和action
  • .NET上SQLite的连接
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复
  • @JsonSerialize注解的使用
  • @RequestMapping-占位符映射
  • [].shift.call( arguments ) 和 [].slice.call( arguments )