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);
}
相关推荐
大树886 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质6 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush46 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5206 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz6 小时前
Maven依赖冲突
java·服务器·maven
Inhand陈工7 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智7 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩8 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
shushangyun_8 小时前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化