进程基础2 --- 进程源语fork版本变更,进程源语exec

一、FORK的版本变更
1、第一版本fork采用不过问原则, 只要创建出子进程,内核就要以父进程为模板对子进程进行初始化。(0-3G用户空间直接复制 , 3-4G内核空间复制一部分,生成一部分)
第一版的缺陷:内核花费了大量精力对子进程进行初始化,但是如果子进程没有使用任何的继承资源,内核的拷贝与克隆变得毫无意义。(反而变成了系统资源的浪费)

2、第二版vfork , 避免克隆资源没有被使用,产生无意义的克隆开销,vfork决定砍掉克隆拷贝功能, 开发者需要自行填充进程的核心功能 vfork + exec 。(vfork已逐渐被废弃)
第二版的vfork ,调用vfork后,内核创建子进程,但是不会发生继承过程,不会将任何父进程资源拷贝给子进程(0-3G是空的,但是PCB是存在的,3G-4G部分拷贝,自己生成pid),子进程需要重载自定义任务,vfork要结合exec使用。
问题:如果子进程需要继承资源,那么vfork就不可用。
官方提供两版FORK,开发者根据实际需求决定使用哪版,当fork加入读共享写复制后,vfork已经逐渐废弃了。

3、第三版fork , 在第一版基础上进行了重构, 加入了读共享,写复制机制, 可以过问用户是否需要拷贝克隆, 根据用户需求决定。

1)读共享采用映射技术,进程创建初期,将父进程的用户空间映射给子进程,子进程通过映射可以读访问父进程资源。
2)写复制,当子进程不满足读数据尝试修改映射数据时,或者父进程修改了资源数据时,会调用clone模块并断开映射,clone模块将父进程的资源拷贝给子进程。
在这里插入图片描述
什么时候父进程会修改数据?我们创建一父多子模型的时候,用 i 来计算创建到第几个子进程,得到的 i 是不同的,但是如果他们都是读取共享父进程的数据,那 i 应该是一直改变的,而并非固定到创建他们时继承的 i 的值,所以父进程修改资源内容时,也会发生clone,将资源复制一份给子进程。
二、EXEC
1、EXEC 进程功能重载
Vfork 创建出一个进程,我们现在想要这个进程有浏览器的功能,进程调用exec,exce找到指定的浏览器app,并去运行这个app,app被运行就会被创建出一个进程,exec读取浏览器进程0-3G的的核心工作资源内容,并将读取到的写到process的工作部分,process里面0-3G所有内容都会被覆盖,只剩重载后浏览器里面的功能。
所有进程核心工作都是通过0-3G那部分资源内容完成的。
2、exec
1)l 每一个参数是独立的字符串。
2)v 创建一个参数数组,char* , 将参数存储在数组中,直接传数组即可。
3)p 无需传递命令路径, 直接传递命令名即可。
4)e 支持自定义进程环境变量,没有e则用系统默认环境变量。

绝对路径,程序名,程序运行时参数,NULL:exec初始化加载参数到NULL结束。

execl(/usr/bin/firefox,firefox,"www.bilibili.com",NULL)
execlp(firefox,firefox,"bilibili.com",NULL)
char * argv1 = {"firefox","bilibili.com",NULL};
execv(/usr/bin/firefox,argv1);
execvp(firefox,argv1);
execle() //可自定义环境变量
execve() //系统提供的函数接口

3、例子

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

int main()
{
	pid_t pid;
	pid = fork();
	if(pid>0){
		printf("parent process running ...\n");
		while(1)
			sleep(1);
	}else if(pid == 0){
		printf("child process will ecec fireforx ...\n");
		execl("/usr/bin/firefox","firefox","www.baidu.com",NULL);
		printf("123456\n");
	}else{
		perror("fork call failed");
		exit(1);
	}
}

在这里插入图片描述
然后跳转到
在这里插入图片描述

问:为什么没有输出123456?
如果需要子进程完成特定任务要在fork之后,execl之前完成。execl函数运行之后,子进程原有的数据与代码逻辑都会被覆盖,所以不要在execl之后编写代码,因为没有任何意义。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要让子进程执行"ps -a"命令,可以使用fork函数创建一个新的进程,并在子进程中调用exec系列函数执行"ps -a"命令。 首先,使用fork函数创建一个新的子进程,该函数会复制父进程的所有内容给子进程,包括代码、数据、文件描述符等。子进程会继承父进程的环境。 接下来,通过调用exec系列函数在子进程中执行命令。exec函数会将子进程的地址空间替换为新的可执行文件并执行它。ps命令通常在/bin目录下,可以使用execvp函数来执行。 下面是示例代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { pid_t pid = fork(); // 创建子进程 if (pid < 0) { fprintf(stderr, "Fork failed\n"); exit(1); } else if (pid == 0) { // 子进程中 char* args[] = {"ps", "-a", NULL}; // 命令行参数 execvp("ps", args); // 执行ps命令 exit(1); // 如果执行失败,则子进程退出 } else { // 父进程中 wait(NULL); // 等待子进程结束 } return 0; } ``` 在这个示例中,父进程使用fork函数创建一个新的子进程,在子进程中利用execvp函数执行"ps -a"命令。父进程则等待子进程结束,然后程序结束。 执行以上代码,子进程会执行"ps -a"命令,并输出进程的信息。 注意,由于exec函数会取代子进程的地址空间,所以子进程的逻辑需要在exec函数之前定义,exec函数后面的代码不会被执行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值