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);
}
相关推荐
pt10433 小时前
网络自动化Python课程:Cisco PyATS网络自动化测试框架
运维·自动化
Johny_Zhao4 小时前
网络安全等级保护测评实施方案
linux·网络·人工智能·网络安全·信息安全·云计算·等保测评·系统运维·itsm
小袁拒绝摆烂4 小时前
Jenkins部署经验
运维·jenkins
落羽的落羽4 小时前
【AI】快速理解AI应用的相关名词概念
linux·c++·人工智能·python·计算机网络·算法
刚入门的大一新生5 小时前
Linux-进程控制
linux·运维·服务器·c++
上火的金鱼妹5 小时前
K8S基础组件作用和关系整理
linux·运维·服务器·kubernetes
Vcaker5 小时前
Linux学习25-harbor私有仓库部署
linux·运维·学习
刃神太酷啦5 小时前
Redis 进阶核心:持久化 (RDB/AOF)、事务与主从复制全解析----《Hello Redis!》(5)
linux·c语言·数据库·c++·redis·缓存·bootstrap
编程的一拳超人5 小时前
DeepSeek Harness Linux/macOS/Windows 本地下载、启动与模型配置指南
linux·windows·macos·deepseek
醉颜凉5 小时前
网络安全必学:粘性MAC地址(Sticky MAC)原理与应用全解析
运维·服务器·网络·安全·web安全