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);
}
相关推荐
27669582922 小时前
携程旅行 token1005
java·linux·前端·javascript·携程旅行·token1005·携程酒店
墨着染霜华2 小时前
Linux 下查看 Java 服务进程占用(CPU / 内存)并定位具体服务
java·linux·运维
嵌入式吴彦祖2 小时前
Luckfox Pico Ultra W 摄像头(一)
linux
zfoo-framework2 小时前
记录文件描述符达到上限问题解决
linux·运维·服务器
2601_949539452 小时前
15万级家用混动SUV电池与续航技术入门科普
运维·网络
呱呱巨基2 小时前
网络基础概念
linux·网络·c++·笔记·学习
薛定谔的悦3 小时前
BMS Modbus RTU实现:从帧结构到寄存器映射的完整工程
linux·数据库·bms
cui_ruicheng3 小时前
Linux进程控制(下):实现简易 Shell 命令行解释器
linux·运维·服务器
Smile_2542204183 小时前
clickhouse日志疯涨问题
linux·运维·服务器·clickhouse