【Linux】从 fork() 到 exec():理解 Linux 进程程序替换的魔法

news2024/10/20 18:48:40

1.前言

进程程序替换是指一个进程用另一个新的可执行程序来替换当前正在执行的程序,这个过程通过通过exec系列函数完成。在Linux或UNIX系统中,进程程序替换通常发生在一个进程通过fork()创建了子进程之后,子进程用exec()函数加载和执行另一个程序。
也就是说,进程程序替换就是在不改变进程的PID(进程ID)的情况下,用一个全新的程序来替换当前的内存空间和执行内容。
当程序调用一种exec函数时,该进程的用户空间代码和数据完全被新的程序替换,从新程序的启动例程开始执行。

进程程序替换

2.替换函数

exec函数是一个系列函数,负责替换当前进程的映像。常见的exec函数包括:

#include <unistd.h>
int execl(const char* path,const char* arg,...);
int execlp(const char* file,const char* arg,...);
int execle(const char* path,const char* arg,...,char* const envp[]);
int execv(const char* path,char* const argv[]);
int execvp(const char* file,char* const argv[]);
int execve(const char* path,char* const argv[],char* const envp[]);

2.1 函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不在返回。
  • 如果调用出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。
    使用execl测试
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main()
{
	pid_t id = fork();
	if(id < 0)
	{
		perror("fork failed");
		exit(1);
	}
	if(id == 0)
	{
		//child
		printf("i am a child process,replacing myself with /bin/ls\n");
		execl("/bin/ls","ls",NULL);//使用exec替换为ls程序
		//如果execl执行成功,下行将不会被执行。
		perror("execl failed");
		exit(1);
	}
	else 
	{
		//father
		wait(NULL);//等待子进程结束
		printf("child process finished\n");
	}
	return 0;
}

执行结果:

i am a child process,replacing myself with /bin/ls
a.out  pReplaceTest.c
child process finished

在这个例子中:

  • 父进程创建了一个子进程。
  • 子进程使用execl()来替换自己,将当前的进程映像替换为/bin/ls程序。
  • 替换成功后,子进程开始执行ls程序的代码,将不在执行原理的代码。
  • 父进程调用wait()等待子进程结束,执行ls命令将结果输出到控制台。
    在程序的替换函数,通常不会让父进程去执行,而是交给子进程去执行,因为父进程还需要进行完成它的时,同时也防止替换的程序造成程序崩溃。由于进程间的独立性,即使子进程去执行execl函数时候,替换的也是子进程的代码和数据,而父进程的代码和数据是不会被影响的。
    提问:发生了子进程的程序替换,此时:父进程的代码还是共享的吗?
    回答:

不是,此时发生了写时拷贝,也就是会拷贝一份代码和数据出来,然后子进程再被它的execl还是进行程序替换。

2.2 命名理解

这些函数的名称还是太相似了,为了方便记忆,可以按一下规律记忆一下。

  • l(list):表示参数采用列表。
  • v(vector):参数用数组。
  • p(path):有p自动搜索环境变量PATH。
  • e(env):表示自己维护环境变量。
函数名参数格式是否带路径是否使用当前环境变量
execl列表不是
execlp列表
execle列表不是不是,需要自己组装环境变量
execv数组不是
execvp数组
execve数组不是不是,需要自己组装环境变量

2.3 exec系列函数介绍

execl函数在上文已经介绍,这里就跳过了。

2.3.1 execlp函数

execlp函数和execl函数的区别在于,execlp在第一个参数时候,不需要全路径,只需要写上执行命令的文件名即可,表示你需要执行谁,往后也就是和execl的参数一样。

#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        execlp("ls","ls","-a","-l",NULL);
        exit(1);
    }
    else if(id>0)
    {
        int ret = wait(NULL);
        if(ret<0)
        {
            perror("wait failed");
        }
        else
        {
            printf("wait success\n");
        }
    }
    else
    {
        perror("fork failed");
        exit(1);
    }

	return 0;
}

执行结果

drwxrwxr-x  2 ubuntu ubuntu  4096 Oct 20 14:18 .
drwxr-x--- 11 ubuntu ubuntu  4096 Oct 20 14:18 ..
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 12:39 a.out
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 14:18 execlp
-rw-rw-r--  1 ubuntu ubuntu   519 Oct 20 14:18 execlpTest.c
-rw-rw-r--  1 ubuntu ubuntu   552 Oct 20 12:39 pReplaceTest.c
-rw-rw-r--  1 ubuntu ubuntu     1 Oct 20 14:11 test.c
wait success

2.3.2 execle函数

execle函数比execl函数多一个e,按照上的设定。需要在最后一个参数需要给execle传入自定义的环境变量数组。
它的使用情况:如果你需要给你执行的一个新的程序,加载一些自定义的环境变量给新的程序时候,以可以使用该函数。
exeTest.c文件:打印环境变量的值,这个文件假如自己执行自己的话那么会打印默认的环境变量。假如其他文件使用execle传参给exeTest.c的话,exeTest.c就会执行该execle传递过来的环境变量。

#include <stdio.h>
int main()
{
    extern char** environ;
    int i = 0;
    for(;environ[i];i++)
    {
        printf("%s\n",environ[i]);
    }
    return 0;
}

execle测试

#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        char* const myEnv[] = {
            "MYVAL1 = 123",
            "MYVAL2 = 456",
            "MYVAL3 = 789",
            NULL
        };
        execle("./myexe","./myexe",NULL,myEnv);
        exit(1);
    }
    else if(id>0)
    {
        int ret = wait(NULL);
        if(ret>0)
        {
            printf("wait success\n");
        }
        else
        {
            perror("wait failed\n");
        }
    }
    else 
    {
        perror("fork failed");
        exit(1);
    }

    return 0;
}

结果:

MYVAL1 = 123
MYVAL2 = 456
MYVAL3 = 789
wait success

2.3.3 execv函数

execv和execl函数没什么区别。execv函数把execl的以列形式的传参,变成了以数组的形式的传参。

#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
	if(id < 0)
	{
		perror("fork failed");
		exit(1);
	}
	if(id == 0)
	{
		//child
        char* const argv[]={
            "ls",
            "-a",
            "-l",
            NULL
        };
        execv("/bin/ls",argv);
		//如果execv执行成功,下行将不会被执行。
		perror("execl failed");
		exit(1);
	}
	else
	{
		//father
		wait(NULL);//等待子进程结束
		printf("child process finished\n");
	}
    return 0;
}

执行结果:

drwxrwxr-x  2 ubuntu ubuntu  4096 Oct 20 14:54 .
drwxr-x--- 11 ubuntu ubuntu  4096 Oct 20 14:54 ..
-rwxrwxr-x  1 ubuntu ubuntu 16216 Oct 20 14:54 a.out
-rwxrwxr-x  1 ubuntu ubuntu 16216 Oct 20 14:41 execle
-rw-rw-r--  1 ubuntu ubuntu   672 Oct 20 14:38 execleTest.c
-rwxrwxr-x  1 ubuntu ubuntu 16168 Oct 20 14:18 execlp
-rw-rw-r--  1 ubuntu ubuntu   519 Oct 20 14:18 execlpTest.c
-rw-rw-r--  1 ubuntu ubuntu   575 Oct 20 14:54 execvTest.c
-rw-rw-r--  1 ubuntu ubuntu   162 Oct 20 14:40 exeTest.c
-rwxrwxr-x  1 ubuntu ubuntu 16024 Oct 20 14:40 myexe
-rw-rw-r--  1 ubuntu ubuntu   552 Oct 20 12:39 pReplaceTest.c
-rw-rw-r--  1 ubuntu ubuntu     1 Oct 20 14:11 test.c
child process finished

如此一来,我们就讲完了:l v p e的各个结果。其他函数只需要在此基础上进行配合就是了。
exec函数

4. 总结

  • 进程程序替换是指用一个新的可执行程序替换当前进程的内存空间和执行内容,但进程ID不变。
  • 常用的替换函数是 exec 系列函数(如 execl()execvp())。
  • 它常用于父进程通过 fork() 创建子进程后,子进程用 exec() 替换为新的程序来执行指定任务。
  • 替换后的进程将完全抛弃原来的代码和数据,并开始执行新加载的程序。

如果我文章对你有所帮助的话,点个关注吧~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219493.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP(HyperText Transfer Protocol)协议

前言 HTTP作为应用层协议&#xff0c;定义了客户端与服务器之间的通信规则&#xff0c;使得浏览器或其他客户端程序能够请求并获取Web服务器上的超文本信息。 在分布式、协作式的超媒体信息系统中&#xff0c;HTTP协议扮演着核心角色&#xff0c;它支持了信息的组织、检索和呈现…

ChatGPT01-preivew体验报告:内置思维链和多个llm组合出的COT有啥区别呢?丹田与练气+中学生物理奥赛题测试,名不虚传还是名副其实?

一个月前&#xff0c;o1发布的时候&#xff0c;我写了篇文章介绍 逻辑推理能力堪比博士生&#xff0c;OpenAI发布全新AI模型系列&#xff1a; o1 - 大模型或许进入新阶段&#xff0c;还翻译了官方的介绍 解密OpenAI o1是如何让LLMs获得逻辑推理能力的 - CoT * RL&#xff0c;也…

Ribbon客户端负载均衡策略测试及其改进

文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下&#xff0c;是否具有故障转移的功能&a…

学习 UE5 的一些前置操作总结

随着 Unity, Godot 这些引擎都玩抽象&#xff0c;主动捅自己一刀后&#xff0c;UE5 的风头不可谓不盛&#xff0c;本着多学一点免得失业的思路方针&#xff0c;咱也研究了一下 UE5 引擎&#xff0c;然后发现想要开始使用 UE5 &#xff0c;包含了很多前置操作&#xff0c;这里总…

Java项目-基于springboot框架的家具商城系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

使用Yolov10和Ollama增强OCR

1. 训练自定义 Yolov10 数据集 利用物体检测增强 OCR 的第一步是在数据集上训练自定义 YOLO 模型。YOLO&#xff08;只看一遍&#xff09;是一种功能强大的实时对象检测模型&#xff0c;它将图像划分为网格&#xff0c;使其能够在一次前向传递中识别多个对象。这种方法非常适合…

AI大模型开发架构设计(14)——基于LangChain大模型的案例架构实战

文章目录 基于LangChain大模型的案例架构实战1 LangChain 顶层架构设计以及关键技术剖析LangChain 是什么?LangChain的主要功能是什么&#xff1f;LangChain 顶层架构设计LangChain 典型使用场景&#xff1a;QA 问答系统LangChain 顶层架构设计之 Model I/OLangChain 顶层架构…

No.17 笔记 | XXE漏洞:XML外部实体注入攻击

1. XXE漏洞概览 XXE&#xff08;XML External Entity&#xff09;是一种允许攻击者干扰应用程序对XML输入处理的漏洞。 1.1 XXE漏洞比喻 想象XML解析器是一个听话的机器人&#xff0c;而XXE就是利用这个机器人的"过分听话"来获取不应该获取的信息。 1.2 XXE漏洞危…

基于SSM汽车零部件加工系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;员工管理&#xff0c;经理管理&#xff0c;零件材料管理&#xff0c;产品类型管理&#xff0c;产品信息管理&#xff0c;产品出库管理&#xff0c;产品入库管理 员工账号功能包括&#xff1a;系统首页…

spring-cloud-alibaba-nacos-config2023.0.1.*启动打印配置文件内容

**背景&#xff1a;**在开发测试过程中如果可以打印出配置文件的内容&#xff0c;方便确认配置是否准确&#xff1b;那么如何才可以打印出来呢&#xff1b; spring-cloud-alibaba-nacos-config 调整日志级别 logging:level:com.alibaba.cloud.nacos.configdata.NacosConfigD…

Java爬虫:获取商品销量详情API返回值的实战指南

在数字化时代&#xff0c;数据已成为电商运营的核心。商品销量数据不仅反映了市场的需求和趋势&#xff0c;还能为商家提供决策支持。通过Java爬虫技术&#xff0c;我们可以高效地获取这些数据&#xff0c;从而深入分析商品的市场表现。 为何选择Java爬虫获取销量数据 自动化处…

股票与基金资料收集

声明&#xff1a;本内容是网上资料的收集与整理而成&#xff0c;不定时更新。仅供参考&#xff0c;不构成任何投资建议。 目录&#xff1a; 一、股票 1、黄金交叉和死亡交叉 2、技术指标 3、T、TR、THR含义 二、基金 平准基金 一、股票 1、黄金交叉和死亡交叉 “黄金交…

【C++_string类练习】仅仅反转字母

题目链接&#xff1a;仅仅反转字母 解题思路&#xff1a; 这种反转字符的题目我第一个想到的方法就是&#xff1a;双指针 一个指针在前start&#xff0c;一个指针在后back&#xff0c; 如果指针所指向的位置的值是字母&#xff0c;那么两个指针位置的值就进行交换&#xff0…

P2-3与P2-4.【C语言基本数据类型、运算符和表达式】第三节与第四节

讲解视频&#xff1a; P2-3.【基本数据类型、运算符和表达式】第三节 P2-4.【基本数据类型、运算符和表达式】第四节 目录 必备知识与理论 任务实施 必备知识与理论 C语言中把除了控制语句和输入输出以外的几乎所有的基本操作都作为运算符处理。 其运算符和表达式数量之多&a…

以简单组合优化为例讨论计算复杂性

此为课题组所指导本科生和低年级硕士生学习组合优化问题汇报 所用教材&#xff1a;北京大学屈婉玲教授《算法设计与分析》 课程资料&#xff1a;https://www.icourse163.org/course/PKU-1002525003 承诺不用于任何商业用途&#xff0c;仅用于学术交流和分享 更多内容请关注课题…

centOS实用命令

一、查看进程&#xff0c;端口占用 netstat命令(window和linux通用&#xff0c;细节不同) 查看端口占用(linux) netstat -ano |grep 8080查看端口占用(window) netstat -ano |findstr 8080ps命令 可以直接使用ps aux查看所有用户的进程信息 一些参数 参数解释-p根据进程P…

【git】如何快速准确的回退(revert)已经合并(merge)主分支(master)的新提交代码

文章目录 前言一、merge模式二、回滚步骤总结 前言 我们在做一些需求&#xff0c;正常流程经过开发&#xff0c;测试到最后和代码上线。但是有时候就会发生一些小插曲&#xff0c;比如产品说老板说某某某你的代码要延后上线&#xff01;&#xff01;或者你写的不合格预发环境出…

(成功解决)ubuntu22.04不小心更新成了atzlinux12.7.1,右上角出现红色错误符号

文章目录 &#x1f315;问题&#x1f315;查看系统版本&#x1f315;为什么更新更成了atzlinux&#x1f315;通过修复依赖关系尝试解决右上角红色错误符号&#x1f315;把源换成ubuntu的源&#x1f315;删除atzlinux源和自定义的第三方源&#x1f315;重新创建/etc/os-release文…

AJAX——服务端响应 JSON 数据

网页文件中&#xff1a; js 文件中&#xff1a; 本文分享到此结束&#xff0c;欢迎大家评论区相互讨论学习&#xff0c;下一篇继续分享AJAX中AJAX 请求超时与网络异常处理的学习。

吴伟仁《英国文学史及选读》第一二册课后答案PDF

新经典高等学校英语专业系列教材《英国文学史及选读》根据英国文学历史的顺序结合作品选读编写而成&#xff0c;在历史部分&#xff0c;对英国文学史的每个阶段作了简明扼要的概述&#xff0c;而在作品选读部分则尽可能遴选了文学史上的重要作家和重要作品。教材内容丰富&#…