Linux(十三)fork + exec进程创建

一、进程创建

在了解进程创建的步骤前,让我们先通过实例观察一下。大家可以跟小编一起,在终端中执行3次ps -f命令,观察一下。

通过上图,我们可以发现,3次ps -f的父进程(PPID)都是一样的(图上为3520),又可以发现这个PPID和bash的PID是一样的;那我们得出结论;执行了3次ps -f,ps -f的父进程的ID都是一样的,即是bash。

实际上,Linux上这个bash就是不断的复制自身,然后把复制出来的用exec替换成想要执行的程序(比如ps);运行ps,发现ps是bash的一个子进程;原因就是bash把自己复制一份,然后替换成ps;

在这里,就会用到我们之前学习的写时拷贝技术了。那我们回顾一下写时拷贝技术的定义"就是fork的时候,子进程直接把父进程的页表复制过来,子进程发生写入(修改)的时候才分配内存复制,然后进行相应的页表修改"。

在替换这步就体现了写时拷贝的意义,如果全部都要替换,那么最开始的复制是没有意义的;注意,用了写时拷贝就只复制了几个页表的映射,内容还没有复制,然后执行了替换exec。(即下图)

通过上面的观察和推导,我们就可以了解到在Linux新的进程的产生过程(也就是进程创建)

fork:复制进程

exec 系列: 将当前进程替换成另外一个进程

二、进程替换exec系列介绍

小编之前一直在和大家强调一定要学会使用帮助手册,现在就是大家可以查看帮助手册的时候啦!因为我们要学习一个新的知识啦!

以发现,使用exec需要添加相应的头文件,且exec是一个系列,里面包含了6种库函数,在这里呢,小编只和大家学习前5个,因为最后一个execvpe不通用。大家也可以通过小编在图片上的标注,了解每个库函数的使用方法。

1、execl

我们通过代码来演示一下吧。

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main()
{
    printf("main pid =%d\n",getpid());
    execl("/usr/bin/ps","ps","-f",(char*)0);
    printf("hello!\n");
    printf("execl error!\n");
    exit(0);
}

在这里,我们要注意,原来的程序替换成了ps程序,但PCB没有改变,但是PCB里面的有些值被修改了,比如PCB中程序的名字换成了新的名字。(./main -> ps -f)

那大家和小编一起思考几个问题;

(1)新的进程从哪里执行?

新的进程从主函数的第一行开始执行,也就是ps程序的主函数的第一行代码开始执行,这

个和fork方法不一样,fork返回以后,从fork所在位置开始执行;所以可以直接在execl下面打印一个失败,如果成功就根本不会执行到这里;

所以,在上面的代码中就有了这两句

printf("hello!\n");

printf("execl error!\n");

(2)如果将execl里面的第二个参数改为"xxxy",程序还能否执行?

execl("/usr/bin/ps","ps","-f",(char*)0);

改为execl("/usr/bin/ps","xxxy","-f",(char*)0);

通过运行截图,我们可以发现可以正常运行,只是程序命令改为了xxxy

(3)如果将execl里面的第一个参数改为"xxxy",程序还能否执行?

execl("/usr/bin/ps","ps","-f",(char*)0);

改为execl("/usr/bin/xxxy","ps","-f",(char*)0);

我们通过运行截图可以发现,execl是没有成功执行的,因为打印了接下来的错误提示。

通过(2)(3),我们就知道只要execl第一个参数不出错,第一个参数如果出错了,你就找不到这个程序了;那么就运行不成功了;

(4)去掉\n,会发生什么?

printf("main pid =%d\n",getpid());

改为 printf("main pid =%d",getpid());
(5)ps也是由ps.c编译为ps可执行程序的,同理cp也是;

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main()
{
    printf("main pid =%d\n",getpid());
    execl("/usr/bin/cp","cp","yma.c","ymb.c",(char*)0);
 
    printf("execl error!\n");
 
    exit(0);
}

2、execlp

只给文件名,不需要给文件路径,因为它可以去环境变量PATH所指的位置去搜索;

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main()
{
    printf("main pid =%d\n",getpid());
    //execl("/usr/bin/ps","ps","-f",(char*)0);
    execlp("ps","ps","-f",(char*)0);
    printf("hello!\n");
    printf("execlp error!\n");
    exit(0);
}

3、execle

和execl参数一样,只是多了最后一个环境变量;

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main(int argc,char *argv[],char*envp[])
{
    printf("main pid =%d\n",getpid());
    //execl("/usr/bin/ps","ps","-f",(char*)0);
    //execlp("ps","ps","-f",(char*)0);
    execle("/usr/bin/ps","ps","-f",(char*)0,envp);
    printf("hello!\n");
    printf("execle error!\n");
    exit(0);
}

4、execv

execv是把参数都放在了一个数组中,然后把这个数组传递进去即可;

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main(int argc,char *argv[],char*envp[])
{
    printf("main pid =%d\n",getpid());
    //execl("/usr/bin/ps","ps","-f",(char*)0);
    //execlp("ps","ps","-f",(char*)0);
    //execle("/usr/bin/ps","ps","-f",(char*)0,envp);
    char *myargv[]={"ps","-f",0};
    execv("/usr/bin/ps",myargv);
    printf("hello!\n");
    printf("execv error!\n");
    exit(0);
}

5、execvp

第一个参数只要文件名,不要路径;

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
 
int main(int argc,char *argv[],char*envp[])
{
    printf("main pid =%d\n",getpid());
    //execl("/usr/bin/ps","ps","-f",(char*)0);
    //execlp("ps","ps","-f",(char*)0);
    //execle("/usr/bin/ps","ps","-f",(char*)0,envp);
    char *myargv[]={"ps","-f",0};
    //execv("/usr/bin/ps",myargv);
    execvp("ps",myargv);
    printf("hello!\n");
    printf("execvp error!\n");
    exit(0);
}
相关推荐
fengkai45458 小时前
3、Kubernetes的Namespace&Contexts&Pod&YAML格式
运维·容器·kubernetes·k8s·namespace·yaml·pod
2401_868534788 小时前
局域网络中信息安全方案设计及攻击防范技术(制造企业园区网)
服务器·网络
戴西软件8 小时前
制造研发中的AI视觉与仿真:戴西工程图纸智能审图智能体、戴西3DViz_CAE AI集成与戴西3DViz_CAD AI集成
大数据·运维·人工智能·3d·自动化·制造·rpa
苏宸啊8 小时前
Reactor反应堆模式
linux
javaDocker8 小时前
1800 亿科技投入砸下去,多数机构平的只是服务器,不是能力
运维·服务器·人工智能·科技·研发效能·金融科技
服务器硬件项目经理9 小时前
超节点修仙录 目 录
服务器·硬件工程·硬件
骇客野人9 小时前
Linux 全套实操方案(含目录详解、文件传输、日志、文件规范、环境变量、Java 应用部署运维)
java·linux·运维
_菜鸟果果9 小时前
高德地图 JS API 2.0 在国产 Linux/ARM 电脑上渲染失败 的排查与解决
linux·开发语言·javascript
GeW9 小时前
飞行汽车交付阿联酋,真正先“起飞”的是低空数据基座——红帽Linux与数据库高可用为什么成了低空运营的隐形跑道
linux
eleven_4u9 小时前
RocketMQ相关问题
java·运维·中间件