Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

news2024/11/29 10:22:21

上篇文章:Linux操作系统2-进程控制2(进程等待,waitpid系统调用,阻塞与非阻塞等待)-CSDN博客

本篇代码Gitee仓库:Linux操作系统-进程的程序替换学习 · d0f7bb4 · 橘子真甜/linux学习 - Gitee.com

本篇重点:进程替换

目录

一. 什么是进程替换?

二. 进程替换函数常用的函数 

2.1 execl 

a 进程替换覆盖指定位置后面的代码

b 进程替换不会影响父进程 

2.2 execlp 

 2.3 execv/execvp

2.4 execle 

 2.5 execve 系统调用

 三. 进程替换总结

四. 进程替换可以执行任何后端语言!


一. 什么是进程替换?

        我们知道,使用fork函数可以创建子进程。我们创建子进程的目的是什么?

1 让子进程执行父进程的一部分代码(比如执行父进程处于磁盘中的代码)

2 我们希望让子进程执行一个全新的进程,去完成一个不同的功能

        我们让子进程去执行新程序的数据和代码 -> 进程的程序替换

二. 进程替换函数常用的函数 

常见的函数如下:

#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 execl 

a 进程替换覆盖指定位置后面的代码

int execl(const char *path, const char *arg, ...);    

//path    用于找到程序(程序的路径,ls的路径是 /usr/bin/ls)
//arg     这个命令是怎么执行的(比如ls命令的执行方式就是单独的 ls)
//...     这个命令需要的参数是什么(比如ls可以带参数 -a -l -i 等)

//返回值,失败返回-1,并且设置错误码

我们使用execl去替换一个 ls命令。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("process is running!\n");

    //第一个参数,要执行哪个路径,就填这个路径
    //第二个参数,程序怎么执行,就怎么填
    //后面的参数,执行这个程序需要带的参数,就一个一个地填这些参数
    //最好使用NULL结尾
    int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
    
    //使用系统调用或者涉及底层的C库函数,需要判断错误返回
    perror("execl");


    //由于执行execl之后,代码被覆盖,以下的代码不会被运行!
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    return 0;
}

编译运行结果如下:

 

        execl之后的代码没有被执行的原因是:进程替换的时候没有创建新进程,而是将指定的程序和代码加载到指定的位置

        这样会覆盖后面的代码,所以后面printf就不会执行了!

b 进程替换不会影响父进程 

        进程替换会覆盖指定位置后面的代码,不过我们在子进程中使用进程替换不会影响父进程的代码和数据。

       这个原因是父子进程之间有写实拷贝,由于父子进程之间的写实拷贝,一旦子进程尝试写入,OS就会给子进程开辟一份空间用于保存子进程的数据和代码。这样一来,子进程进程替换就不会影响父进程。

测试代码:

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

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
        perror("execl");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果: 

 

2.2 execlp 

int execlp(const char *file, const char *arg, ...);

p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径

测试代码:

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

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径
        execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);
        perror("execl");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

 

 2.3 execv/execvp

//v:vector:可以将所有的执行参数放入数组,进行统一传入,不用可变参数传参 
int execv(const char *path, char *const argv[]);    


// v:vector p:文件名
int execvp(const char *file, char *const argv[]);  

execv 举例:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execv("/usr/bin/ls", argv_);
        
        perror("execv");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

 

 execvp 加上了p:我们只需给出名字,会去环境变量中自动查找

测试代码:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execvp("ls", argv_);
        
        perror("execvp");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execvp("ls", argv_);
        
        perror("execvp");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果

 

2.4 execle 

int execle(const char *path, const char *arg, ...,char *const envp[]);    //e:传入自定义环境变量,

e:传入自定义环境变量

我们定义另一个C程序mybin,让这个程序打印环境变量。然后我们在子进程中替换为mybin

这样我们的子进程就能够打印我们在execle函数中输入的环境变量了

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        // 加入命令行参数env
        execle("./mybin", "./mybin", NULL, env_);

        perror("execle");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

运行结果如下:

 我们换成系统环境变量

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        // 加入命令行参数env
        extern char** environ;
        execle("./mybin", "./mybin", NULL, environ);

        perror("execle");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

运行结果: 

当然我们也能够在 命令行参数获取环境变量

 2.5 execve 系统调用

上面的exec函数都是对execve系统调用的封装

//2号手册,这个才是真正的系统调用(上面的都是函数封装)
int execve(const char *path, char *const argv[], char *const envp[]);

 

 

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        // 注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        char *const argv_[] = {"./mybin"};
        execve("./mybin", argv_, env_);

        perror("execve");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果: 

 三. 进程替换总结

我们知道程序必须加载到内存中才能执行。这是由冯诺依曼计算机体系结构决定的。

在Linux中就是通过exec*函数来加载程序的!

那么是exec函数先加载还是main函数先执行?

exec函数先加载,然后main函数再执行

我们可以发现exec函数的参数和main函数的参数很像!

所以是exec函数先加载路径,参数,环境变量在执行main函数 

四. 进程替换可以执行任何后端语言!

myCppBin.cc

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello C++!" << endl;
    return 0;
}

mytest.c

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        // 注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        char *const argv_[] = {"./myCppBin"};

        //执行C程序
        //execve("./mybin", argv_, env_);

        //执行C++
        execve("./myCppBin", argv_, env_);


        perror("execve");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

 

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

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

相关文章

Java函数式编程+Lambda表达式

文章目录 函数式编程介绍纯函数Lambda表达式基础Lambda的引入传统方法1. 顶层类2. 内部类3. 匿名类 Lambda 函数式接口&#xff08;Functional Interface&#xff09;1. **函数式接口的定义**示例&#xff1a; 2. **函数式接口与Lambda表达式的关系**关联逻辑&#xff1a;示例&…

DI依赖注入详解

DI依赖注入 声明了一个成员变量&#xff08;对象&#xff09;之后&#xff0c;在该对象上面加上注解AutoWired注解&#xff0c;那么在程序运行时&#xff0c;该对象自动在IOC容器中寻找对应的bean对象&#xff0c;并且将其赋值给成员变量&#xff0c;完成依赖注入。 AutoWire…

自动化运维(k8s)之微服务信息自动抓取:namespaceName、deploymentName等全解析

前言&#xff1a;公司云原生k8s二开工程师发了一串通用性命令用来查询以下数值&#xff0c;我想着能不能将这命令写成一个自动化脚本。 起初设计的 版本一&#xff1a;开头加一条环境变量&#xff0c;执行脚本后&#xff0c;提示输入&#xff1a;需要查询的命名空间&#xff0c…

[Python/网络安全] Git漏洞之Githack工具基本安装及使用详析

前言 本文仅分享Githack工具基本安装及使用相关知识&#xff0c;不承担任何法律责任。 Git是一个非常流行的开源分布式版本控制系统&#xff0c;它被广泛用于协同开发和代码管理。许多网站和应用程序都使用Git作为其代码管理系统&#xff0c;并将其部署到生产环境中以维护其代…

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统

解决水库安全监测难题 长期无外接电源 低功耗设备智能化监测系统 国内某水库安全监测项目需要监测点分散&#xff0c;且无外接供电。项目年限为4年&#xff0c;不允许使用太阳能电板。因此&#xff0c;我们需要设备具备低功耗且内置电池的功能。为了满足客户的要求&#xff0c;…

蓝桥杯c++算法秒杀【6】之动态规划【上】(数字三角形、砝码称重(背包问题)、括号序列、组合数问题:::非常典型的必刷例题!!!)

下将以括号序列、组合数问题超级吧难的题为例子讲解动态规划 别忘了请点个赞收藏关注支持一下博主喵&#xff01;&#xff01;&#xff01;! ! ! ! &#xff01; 关注博主&#xff0c;更多蓝桥杯nice题目静待更新:) 动态规划 一、数字三角形 【问题描述】 上图给出了…

AD软件如何快速切换三维视图,由2D切换至3D,以及如何恢复

在Altium Designer软件中&#xff0c;切换三维视图以及恢复二维视图的操作相对简单。以下是具体的步骤&#xff1a; 切换三维视图 在PCB设计界面中&#xff0c;2D切换3D&#xff0c;快捷键按住数字键盘中的“3”即可切换&#xff1b; 快捷键ctrlf&#xff08;或者vb快捷键也…

学习threejs,使用CubeCamera相机创建反光效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️CubeCamera 立方体相机 二、…

长时间无事可做是个危险信号

小马加入的是技术开发部&#xff0c;专注于Java开发。团队里有一位姓隋的女同事&#xff0c;是唯一的web前端工程师&#xff0c;负责页面开发工作&#xff0c;比小马早两个月入职。公司的项目多以定制化OA系统为主&#xff0c;后端任务繁重&#xff0c;前端工作相对较少。在这样…

Llama模型分布式训练(微调)

1 常见大模型 1.1 参数量对照表 模型参数量发布时间训练的显存需求VGG-19143.68M2014~5 GB&#xff08;单 224x224 图像&#xff0c;batch_size32&#xff09;ResNet-15260.19M2015~7 GB&#xff08;单 224x224 图像&#xff0c;batch_size32&#xff09;GPT-2 117M117M2019~…

Linux 子进程 -- fork函数

子进程 什么是子进程? 子进程指的是由一个已经存在的进程&#xff08;称为父进程或父进程&#xff09;创建的进程. 如: OS (操作系统) 就可以当作是一个进程, 用来管理软硬件资源, 当我点击浏览器, 想让浏览器运行起来时, 实际上是由 OS 接收指令, 然后 OS 帮我们将浏览器运行…

DataLoade类与list ,iterator ,yield的用法

1 问题 探索DataLoader的属性&#xff0c;方法 Vscode中图标含意 list 与 iterator 的区别&#xff0c;尤其yield的用法 2 方法 知乎搜索DataLoader的属性&#xff0c;方法 pytorch基础的dataloader类是 from torch.utils.data.dataloader import Dataloader 其主要的参数如下&…

C++入门——“C++11-lambda”

引入 C11支持lambda表达式&#xff0c;lambda是一个匿名函数对象&#xff0c;它允许在函数体中直接定义。 一、初识lambda lambda的结构是&#xff1a;[ ] () -> 返回值类型 { }。从左到右依次是&#xff1a;捕捉列表 函数参数 -> 返回值类型 函数体。 以下是一段用lam…

【Linux网络编程】第二弹---Socket编程入门指南:从IP、端口号到传输层协议及编程接口全解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Socket 编程预备 1.1、理解源 IP 和目的 IP 1.2、认识端口号 1.2.1、端口号范围划分 1.2.2、理解 &q…

《用Python实现3D动态旋转爱心模型》

简介 如果二维的爱心图案已经无法满足你的创意&#xff0c;那今天的内容一定适合你&#xff01;通过Python和matplotlib库&#xff0c;我们可以实现一个动态旋转的3D爱心模型&#xff0c;充满立体感和动感。# 实现代码&#xff08;完整代码底部名片私信&#xff09; 以下是完…

shell-函数调用进阶即重定向

shell-函数调用进阶 声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷…

【高等数学学习记录】微分中值定理

一、知识点 &#xff08;一&#xff09;罗尔定理 费马引理 设函数 f ( x ) f(x) f(x) 在点 x 0 x_0 x0​ 的某邻域 U ( x 0 ) U(x_0) U(x0​) 内有定义&#xff0c;并且在 x 0 x_0 x0​ 处可导&#xff0c;如果对任意的 x ∈ U ( x 0 ) x\in U(x_0) x∈U(x0​) &#xff0…

【vue-router】vue-router如何实现动态路由

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Web前端技术浅谈CooKieAG网址漏洞与XSS攻防策略

随着互联网技术的飞速发展,Web前端开发已经成为构建网站和应用程序的重要环节。然而,Web前端开发中存在许多安全问题,这些问题不仅会影响用户体验,还可能给企业和个人带来严重的经济损失。但是web前端安全方面技术包含的东西较多&#xff0c;我们这里着重聊一聊关于XSS 的危害与…

关于VNC连接时自动断联的问题

在服务器端打开VNC Server的选项设置对话框&#xff0c;点左边的“Expert”&#xff08;专家&#xff09;&#xff0c;然后找到“IdleTimeout”&#xff0c;将数值设置为0&#xff0c;点OK关闭对话框。搞定。 注意,服务端有两个vnc服务,这俩都要设置ide timeout为0才行 附件是v…