您的位置:首页 > 游戏 > 手游 > 广东省app开发公司_大米包装设计_方象科技服务案例_营销方案怎么写?

广东省app开发公司_大米包装设计_方象科技服务案例_营销方案怎么写?

2025/3/9 23:35:58 来源:https://blog.csdn.net/2303_80828380/article/details/144160296  浏览:    关键词:广东省app开发公司_大米包装设计_方象科技服务案例_营销方案怎么写?
广东省app开发公司_大米包装设计_方象科技服务案例_营销方案怎么写?

目录

一、为什么要进行进程替换:

二、进程替换的原理:

三、exec家族:

1、execl:

2、execlp:

3、execv:

4、execvp:

5、execle和execve

​编辑

putenv:


一、为什么要进行进程替换:

当父进程创建子进程之后,这两个进程后面的代码是共享的,但是更多的场景下是给子进程执行分配任务,让子进程执行它自己的任务,这个时候就需要进程替换了,让别的进程替换子进程,但是这个替换并没有创建PCB

程序替换的目的是让子进程帮我们执行特定任务

二、进程替换的原理:

单进程替换图解:

父进程创建子进程,然后子进程进行程序替换图解:

替换前:

如上,父进程创建子进程,然后子进程的代码和数据继承父进程的代码和数据的,然后即将发生替换的程序加载到内存中,页表中子进程的物理内存映射变大或变小为新进程的大小,
替换后:

替换后会发生写时拷贝,不仅仅是数据,代码也会,
当子进程需要执行不同的代码时,操作系统会将父进程的代码和数据拷贝一份,然后将新的程序加载到内存中,重新建立子进程的页表,并更新子进程页表中的映射关系。这一过程确保了父子进程的代码分离,使得它们互不干扰,写时拷贝技术保证了父子进程在没有对代码和数据进行修改时共享相同的物理内存,一旦发生写操作,就会为子进程分配新的物理空间,并拷贝数据,从而实现进程间的独立性,因此,当子进程进行程序替换时,父进程不受影响,因为替换过程中发生的写时拷贝确保了子进程的独立性

接下来理解两个问题

进程程序替换时,有没有创建新的进程?

当进程程序替换后,子进程的PCB,进程地址空间和页表都没有变,只是物理内存的数据和代码发生写时拷贝,但是这个子进程的pid并没有改变,也就是说,程序替换不创建新进程,只是进行进程的代码和数据的切换,

CPU是如何获取程序的入口地址的?

首先,在Linux中形成的可执行程序并不是杂乱无章的,是有格式的,Linux中这个格式成为ELF,这个可执行程序的最开始有可执行程序的表头,这个表里面有可执行程序的入口地址,我们之前了解到程序加载是惰性加载的,在程序加载到内存中可以先不把它的代码和数据加载到内存中,但一定要把表头加载到内存中,

在这个表里面有可执行程序的入口地址,这样CPU就能够从表中读取到程序的入口地址了

三、exec家族:

子进程替换父进程是用exec系列的函数来进行程序替换

如上,虽然有7个但是只有execve是系统调用接口,其他的都是调用这个接口进行了一次封装,

1、execl:

理解:

参数1:新替换程序的路径,(./当前路径,或者绝对路径如/usr/bin/ls)
参数2:新替换程序的名称,如ls,自己写的程序名
参数3:新替换程序的选项,如-a,-l,但是最后一个要是NULL表示结束
。。。:这是表示可变参数列表,可以传递不一样的参数个数
返回值:替换失败返回-1,替换成功的话子进程后面程序就不执行了,所以返回值也就没意义了

例如我要把子进程替换为ls进程:

execl("/usr/bin/ls","ls","-a","-l",NULL);

在代码中的使用:

int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");int ret = execl("/usr/bin/ls","ls","-a","-l",NULL);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

如上为执行结果:

可以看到execl之后的代码没有被执行,因为这些代码是属于原来子进程的代码,在execl之后这些代码就被替换了,原本的代码也就肯定不会执行

为什么,父进程能够等待得到已经被替换的进程呢?
这是因为虽然进程被替换了,但是并没有创建新的进程,替换后这个进程依然是父进程的子进程,pid也没有变,那么就可以正常等待

2、execlp:

理解:


参数1:这个和execl不同的是这个就只需要传执行的文件名就行了,路径会自动在环境变量中找
参数2:新替换程序的名称,如ls,自己写的程序名
参数3:新替换程序的选项,如-a,-l,但是最后一个要是NULL表示结束
。。。:这是表示可变参数列表,可以传递不一样的参数个数
返回值:替换失败返回-1,替换成功的话子进程后面程序就不执行了,所以返回值也就没意义了

这个就可以看做execl的升级版,也就是不需要手动传路径,会自动在环境变量中找

例如我要把子进程替换为ls进程:

execlp("ls","ls","-a","-l",NULL);

注意:
这虽然有两个ls,但是含义是不同的第一个参数是要执行程序的名字,第二个参数是可变参数列表

在代码中的使用:

int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");int ret = execlp("ls","ls","-a","-l",NULL);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

执行结果和execl是一样的

3、execv:

理解:

参数1:新替换程序的路径,(./当前路径,或者绝对路径如/usr/bin/ls)
参数2:这个是传一个 指针数组,里面成员就是类似于execl后面的参数

例图:(差不多就是下面这个意思)

int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");char* const myargv[] = { "ls" , "-a" , "-l" , NULL };int ret = execv("/usr/bin/ls",myargv);if(ret == -1){printf("进程替换失败\n");}printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

执行结果和execl是一样的

4、execvp:

char* const myargv[] = { "/usr/bin/ls" , "-a" , "-l" , NULL };int ret = execv("/usr/bin/ls",myargv);

这个和execl升级成execlp是一样的,就只是从自己在第一个参数中给路径变为只需传执行的文件名就行了,路径会自动在环境变量中找

5、execle和execve

理解:

参数1,参数2和返回值是一样的,就是多了一个环境参数,envp就是你自己设置的环境变量

putenv:

首先了解一下putenv函数:
这就是一个创建环境变量

putenv("环境变量=数值")

作用:在当前进程下创建一个环境变量并继承给子进程,并不会影响父进程

exec系列带e的函数中,就可以传环境变量:

int main()
{putenv("MYENV=6666666");pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");execlp("./other","other",NULL);		printf("子进程替换进程后\n");exit(1);}else{printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

上面是bash的环境变量,下面是上述程序的运行结果,所以可以通过putenv来增加环境变量

也可以使用自己的环境变量,这个时候就需要使用execle或者execve了:

int main()
{pid_t id = fork();if(id < 0){printf("进程创建失败\n");exit(1);}else if(id == 0){//子进程printf("子进程替换进程前\n");char* const myenv[] = {"MYENV1=123","MYENV2=456","MYENV3=789",NULL};char* const myargv[] = {"other","-a","-b",NULL};execve("./other",myargv,myenv);		printf("子进程替换进程后\n");exit(1);}else{//父进程printf("父进程正常执行\n");pid_t ret = waitpid(id,NULL,0);if(ret == id){printf("等待成功\n");}}return 0;
}

如上,其中自己所写的other进程就是打印命令行参数和环境变量

int main(int argc,char* argv[],char* env[])
{cout<<"命令行参数"<<endl;for(int i = 0;argv[i];i++){cout<<i<<":"<<argv[i]<<endl;}cout<<"环境变量表"<<endl;for(int i = 0;env[i];i++){cout<<i<<":"<<env[i]<<endl;}return 0;
}

这样,在调用之后就可以打印出自己所传的命令行参数,和自己建立的环境变量表

想要理解好带e的函数,就需要先理解好exec在bash中的作用:

首先,在bash中,所有进程都是bash的子进程,那么这些子进程的命令行参数是从哪儿来的呢?就是通过exec系列的函数进行传参的,把第二个参数的选项作为命令行参数传给第一个参数指向的可执行性程序,所以在Linux中,当进程启动的时候就是父进程创建一个子进程,然后这个子进程的数据和代码替换为所执行的命令,exec系列的函数就是起到了类似于加载的作用

exec也可以加载脚本

比如下面写一个Python的脚本语言,

接着在子进程中调用脚本语言:

这样就能够运行脚本语言

所以,尽管Python和cpp是两种语言,但是竟然可以进行跨语言调用,这是为什么呢?

其实所有语言运行起来都是进程,只要是进程就可以被调用,所以,只要这个语言运行起来变成进程,那么就可以使用exec系列函数进行调用!!!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com