[linux进程控制]进程替换

news2025/1/19 17:18:22

文章目录

  • 1.进程替换的概念和原理
  • 2.如何完成进程替换
    • 2.1exec系列函数
      • 加载器的底层系统调用接口
      • 基于execve的封装--库函数
    • 2.2 int execl(const char *path, const char *arg, ...);
      • 1.在当前进程进行替换
      • 2.在子进程进行替换
    • 2.3 int execv(const char *path, char *const argv[]);
    • 2.4 int execlp(const char *file, const char *arg, ...);
    • 2.5 int execvp(const char *file, char *const argv[]);
    • 2.6 int execle(const char *path, const char *arg, ..., char * const envp[]);
      • 1.Makefile: 一次生成多个可执行程序
      • 2.子进程中执行自己写的C/C++程序
        • 2.1自己写的C/C++程序
        • 2.2执行自己写的C/C++程序
      • 3.子进程中执行自己写的py/sh程序
      • 4. int execle(const char *path, const char *arg, ..., char * const envp[]);
        • 4.1自己写的C/C++程序
        • 4.2执行自己写的C/C++程序
    • 2.7 int execvpe(const char *file, char *const argv[], char *const envp[]);

1.进程替换的概念和原理

之前讲过fork()函数的用法:

  1. 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程不处理客户端请求,生成子
    进程来处理这个请求。
  2. 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。==>进程替换

fork()之后 父子进程各自执行父进程代码的一部分 如果子进程想执行一个另外的/全新的程序要怎么做呢?

本来父子进程共享代码和数据 (数据写时拷贝) 子进程怎么去拥有自己的代码呢?

进程替换完成这个功能: 通过特定的接口 把磁盘上一个全新的程序(代码和数据)加载到子进程的地址空间中

当进程替换后 都会发生什么呢?

  1. 新程序的代码和数据加载到内存
  2. 进程替换并没有创建新的进程 所以该进程的pid并不改变
  3. 原进程即被替换的进程的PCB/进程地址空间/页表没有释放 新程序的PCB/进程地址空间/页表没有被创建 而是将原进程的PCB/进程地址空间/页表的内容完全替换成新程序的PCB/进程地址空间/页表的内容 页表左侧替换成新程序对应的虚拟地址 页表右侧替换成新程序的物理地址
  4. 旧进程的用户空间代码和数据完全被新程序替换 从新程序的启动例程开始执行

如何完成这一系列操作?

操作系统的接口exec函数: 加载新程序代码和数据到内存 建立新的映射关系

2.如何完成进程替换

2.1exec系列函数

加载器的底层系统调用接口

#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);

在这里插入图片描述

基于execve的封装–库函数

	   #include <unistd.h>
       extern char **environ;
       
       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 execvpe(const char *file, char *const argv[], char *const envp[]);

在这里插入图片描述

在这里插入图片描述

命名理解

l(list) : 参数采用列表形式
v(vector) : 参数用数组形式
p(path) : 自动搜索环境变量PATH
e(env) : 自己维护环境变量

2.2 int execl(const char *path, const char *arg, …);

1.在当前进程进行替换

 int execl(const char *path, const char *arg, ...);
 execl: l--list 像结点一样将参数传入可变参数列表
 path: 路径+目标文件名
 arg: 可变参数列表 可以传入多个参数(个数不定)
 调用时:NULL结尾 表示参数传递完毕
  1. execl()函数: 函数调用成功后 当前的代码和数据完全被新程序的代码和数据替换
  2. 函数调用失败后会返回-1(执行原代码 对原代码无影响) 表示调用新程序失败 调用成功没有返回值 因为调用成功后当前代码和数据完全被替换 而execl()就在其中

代码演示

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

int main()
{
    printf("当前进程开始运行\n");

    //execl("/usr/bin/ls", "ls", NULL);
    execl("/usr/bin/ls", "ls", "-l", "-a", "-i", NULL);
    //execl("/usr/bin/top", "top", NULL);
    //execl("/usr/bin/ls", "ls", "--color=auto", "-l", NULL);

    ls是一个用C语言写的程序 他也有main函数 实际上 这里的传参就是把参数传给了ls程序里的main函数 这也与我们之前讲到的命令行参数的空间布局相呼应 
    exit(1);
    printf("当前进程结束!\n");
    return 0;
}

在这里插入图片描述

为什么在xshell下执行ls 就会有颜色 而在我们的程序里执行ls 还要加"--color=auto"才会有颜色?

因为在bash进程里给ls取了别名 自动就会加"--color=auto"

2.在子进程进行替换

在子进程进行进程替换的目的

  1. 不影响父进程 让父进程聚焦在读取数据,解析数据,指派子进程执行代码
  2. 让子进程被替换的目的是去执行新的程序而不是执行父进程的不同代码 所以让子进程进行替换的目的就是让父进程仍然执行他自己的代码 让子进程执行一个新的程序 并且父进程不受影响

对子进程进行程序替换的理解

  1. 子进程被替换前 父子进程代码共享 数据写时拷贝
  2. 当子进程发生进程替换 会有一份新的程序(代码+数据)完全覆盖子进程原有内容 此时会发生写时拷贝 即当子进程发生进程替换 子进程在物理内存中将会有自己的单独空间(新程序)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0);
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
     return 0;
}

2.3 int execv(const char *path, char *const argv[]);

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

#define NUM 16
int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            char *const _argv[NUM] = {
                (char*)"ls",
                (char*)"-a",
                (char*)"-l",
                (char*)"-i",
                NULL
            };
            //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
            execv("/usr/bin/ls", _argv);
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0); 
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

2.4 int execlp(const char *file, const char *arg, …);

自动在环境变量PATH中查找文件名的路径

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

#define NUM 16
int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            char *const _argv[NUM] = {
                (char*)"ls",
                (char*)"-a",
                (char*)"-l",
                (char*)"-i",
                NULL
            };
            //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
            //execv("/usr/bin/ls", _argv); 
            
            execlp("ls", "ls", "-a", "-l", NULL);
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0); 
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

2.5 int execvp(const char *file, char *const argv[]);

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

#define NUM 16
int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
             printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            char *const _argv[NUM] = {
                (char*)"ls",
                (char*)"-a",
                (char*)"-l",
                (char*)"-i",
                NULL
            };
            //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
            //execv("/usr/bin/ls", _argv); //和上面的execl只有传参方式的区别
            //execlp("ls", "ls", "-a", "-l", NULL);
            execvp("ls", _argv);
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0); 
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

2.6 int execle(const char *path, const char *arg, …, char * const envp[]);

1.Makefile: 一次生成多个可执行程序

在这里插入图片描述

2.子进程中执行自己写的C/C++程序

2.1自己写的C/C++程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("can not execute!\n");
        exit(1);
    }

    printf("获取环境变量: VIRTUALENV: %s\n", getenv("VIRTUALENV"));

    if(strcmp(argv[1], "-a") == 0)
    {
        printf("hello a!\n");
    }
    else if(strcmp(argv[1], "-b") == 0)
    {
        printf("hello b!\n");
    }
    else
    {
        printf("default!\n");
    }

    return 0;
}
2.2执行自己写的C/C++程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUM 16

//const char *fileaddr = "/home/lhr/linux/procreplace/cmd";
const char *fileaddr = "./cmd";

int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            char *const _argv[NUM] = {
                (char*)"ls",
                (char*)"-a",
                (char*)"-l",
                (char*)"-i",
                NULL
            };
            execle(fileaddr, "cmd", "-a", NULL);
            //execle(fileaddr, "cmd", "-a", NULL);

            //execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
            //execv("/usr/bin/ls", _argv); //和上面的execl只有传参方式的区别
            //execlp("ls", "ls", "-a", "-l", NULL);
            //execvp("ls", _argv);
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0); 
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

3.子进程中执行自己写的py/sh程序

test.py
#! /usr/bin/python3.6
print("hello Python")
print("hello Python")
print("hello Python")

test.sh
#! /usr/bin/bash
echo "hello shell!"
echo "hello shell!"
echo "hello shell!"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUM 16

int main(int argc, char*argv[], char *env[])
{
       pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            在命令行下: python test.py === ./test.py (自动调用解释器)
            //execlp("./test.py", "test.py", NULL);
            //execlp("bash", "bash", "test.sh", NULL);
            //execlp("python", "python", "test.py", NULL);
            py/sh/java: 有解释器 在解释器(也是一个程序)内编译执行对应程序
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0); 
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

4. int execle(const char *path, const char *arg, …, char * const envp[]);

4.1自己写的C/C++程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("can not execute!\n");
        exit(1);
    }

    printf("获取环境变量: VIRTUALENV: %s\n", getenv("VIRTUALENV"));

    if(strcmp(argv[1], "-a") == 0)
    {
        printf("hello a!\n");
    }
    else if(strcmp(argv[1], "-b") == 0)
    {
        printf("hello b!\n");
    }
    else
    {
        printf("default!\n");
    }

    return 0;
}

4.2执行自己写的C/C++程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUM 16

//const char *fileaddr = "/home/lhr/linux/procreplace/cmd";
const char *fileaddr = "./cmd";

int main(int argc, char*argv[], char *env[])
{
        pid_t id = fork();
        if(id == 0)
        {
            //子进程
            printf("子进程开始运行, pid: %d\n", getpid());
            sleep(3);
            char *const _env[NUM] = {
                 (char*)"VIRTUALENV=547993",
                  NULL
            };
        
            char *const _argv[NUM] = {
                (char*)"ls",
                (char*)"-a",
                (char*)"-l",
                (char*)"-i",
                NULL
            };
            execle(fileaddr, "cmd", "-a", NULL, env);
            //execle(fileaddr, "cmd", "-a", NULL, _env);
            
            exit(1);
        }
        else 
        {
            //父进程
            printf("父进程开始运行, pid: %d\n", getpid());
            int status = 0;
            pid_t id = waitpid(-1, &status, 0);
            if(id > 0)
            {
                printf("wait success, exit code: %d\n", WEXITSTATUS(status));
            }
        }
    return 0;
}

2.7 int execvpe(const char *file, char *const argv[], char *const envp[]);

同上类比

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

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

相关文章

业务数据治理体系化实施流程学习总结

目录 一、业务数据治理实施流程 步骤 1&#xff1a;发现问题和制定目标 步骤 2&#xff1a;针对问题进行拆解&#xff0c;设计可衡量的指标 步骤 3&#xff1a;制定解决SOP和检查研发标准规范 步骤 4&#xff1a;推广运营&#xff0c;以拿结果为核心目标 步骤 5&#xff…

SmartSoftHelp8,代码版权保护

1.Html网页前端添加作者开发信息 2. Html网页添加版权信息 3. Css添加作者开发信息 4. JavaScript添加作者开发信息 5. C井后端代码添加作者开发信息 6. Dll内裤添加作者开发信息 7.应用程序添加开发作者信息&#xff0c;著作权&#xff0c;应用版权信息 下载地址&#…

使用Java语言实现字母之间的大小写转换

这个类的作用为实现字母之间的大小写转换&#xff0c;通过加减32来完成。 输入的代码 import java.util.Scanner; public class WordChangeDemo {public static void main(String[] args){try (Scanner in new Scanner(System.in)) {System.out.println("请输入您要进…

tomcat运行项目时,前端页面中文乱码

如图&#xff1a; 解决办法&#xff1a; 在前端页面添加下面代码 <%page language"java" pageEncoding"utf-8"%>再次运行

C++ Easyx 三子棋

目录 思路 框架​编辑 读取操作 数据操作 绘制画面 游戏的数据结构 用二维数组来模拟棋盘格 赢的情况 平局情况 Code 代码细节部分 &#xff08;1&#xff09;初始化棋盘格 &#xff08;2&#xff09; 初始化棋子类型​编辑 事件处理部分 落子 框架内代码的完善 数据处…

如何调用 API | 学习笔记

开发者学堂课程【阿里云 API 网关使用教程:如何调用 API】学习笔记&#xff0c;与课程紧密联系&#xff0c;让用户快速学习知识。 课程地址&#xff1a;阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 如何调用 API 调用 API 的三要素 要调用 API 需要三…

Nginx 具体应用

1 Nginx 1.1 介绍 一款轻量级的 Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。它占有的内存少&#xff0c;并发能力强&#xff0c;中国大陆使用 nginx 的网站有&#xff1a;百度、京东、新浪、网易、腾讯、淘宝等。第一个公开版本发布于…

1.1卷积的作用

上图解释了1∗1卷积如何适用于尺寸为H∗W∗D的输入层&#xff0c;滤波器大小为1∗1∗D&#xff0c;输出通道的尺寸为H∗W∗1。如果应用n个这样的滤波器&#xff0c;然后组合在一起&#xff0c;得到的输出层大小为H∗W∗n。 1.1∗1卷积的作用 调节通道数 由于 11 卷积并不会改…

【vue】vue-slick-carousel插件,实现横向滚动列表手动左右滚动(也可设置为自动滚动)

需求&#xff1a;图片列表横向滚动的时候&#xff0c;隐藏原始滚动条&#xff0c;通过左右箭头控制滚动条往左右按一定的步长移动。 el-carousel走马灯一滚动就是一屏&#xff0c;不适合我的需求 在npm官网搜vue-slick-carousel&#xff0c;查看更详细的配置 vue-slick-caro…

matlab 路面裂缝检测识别系统设计GUI

1、内容简介 略 23-可以交流、咨询、答疑 2、内容说明 路面裂缝检测识别系统设计GUI 基于matlab的GUI设计路面裂缝检测识别系统 matlab、GUI、直方图、裂缝检测、二值化 3、仿真分析 4、参考论文 略 链接&#xff1a;https://pan.baidu.com/s/1AAJ_SlHseYpa5HAwMJlk…

十五届海峡两岸电视主持新秀大会竞赛流程

海峡两岸电视主持新秀会是两岸电视媒体共同举办的一项活动&#xff0c;旨在为两岸年轻的电视主持人提供一个展示才华的舞台&#xff0c;促进两岸文化交流和青年交流。本届新秀会是第十二届海峡两岸电视艺术节的重要活动之一。本次竞赛赛制流程如下&#xff1a; &#xff08;1&…

遥感方向期刊总结

开眼看期刊~ 期刊分区信息搜索网址中国科学院文献情报中心期刊分区表升级版科研通期刊查询&#xff1a; 遥感领域常见期刊Nature CommunicationsRemote Sensing of EnvironmentProceedings of the National Academy of Sciences &#xff08;PNAS&#xff09;ISPRS Journal of …

SHAP(六):使用 XGBoost 和 HyperOpt 进行信用卡欺诈检测

SHAP&#xff08;六&#xff09;&#xff1a;使用 XGBoost 和 HyperOpt 进行信用卡欺诈检测 本笔记本介绍了 XGBoost Classifier 在金融行业中的实现&#xff0c;特别是在信用卡欺诈检测方面。 构建 XGBoost 分类器后&#xff0c;它将使用 HyperOpt 库&#xff08;sklearn 的 …

Mybatis 的操作(要结合上个博客一起)续集

Mybatis 是一款优秀的 持久性 框架,用于简化 JDBC 的开发 持久层 : 指的就是持久化操作的层,通常指数据访问层(dao),是用来操作数据库的 简单来说 Mybatis 是更简单完成程序和数据库交互的框架 Mybatis 的写法有两种 : 1.xml 2.注解 这两者各有利弊,后面进行总结 Mybati…

使用Java语言实现变量互换

一、 java运算 通过异或运算符实现两个变量的互换 import java.util.Scanner;public class ExchangeValueDemo {public static void main(String[] args){try (Scanner scan new Scanner(System.in)) {System.out.println("请输入A的值&#xff1a;");long A sca…

Kafka 的特点和优势

Apache Kafka 作为一款分布式流处理平台&#xff0c;以其独特的特点和卓越的优势成为实时数据处理领域的瑰宝。本文将深入研究 Kafka 的各项特点和优势&#xff0c;并通过详实的示例代码展示其在不同场景下的强大应用。 高吞吐量和水平扩展 Kafka 的设计注重高吞吐量和水平扩…

9-4 函数输入信息,函数输出信息

#include<stdio.h>struct student{char name[10];int num;char score[3]; }stu[5]; //结构体输入信息int main(){void input(struct student stu[]);void print(struct student stu[]);input(stu);print(stu);return 0; }void input(struct student stu[5]) { int i,j;fo…

JVM虚拟机:JVM参数之标配参数

本文重点 本文我们将学习JVM中的标配参数 标配参数 从jdk刚开始就有的参数&#xff0c;比如&#xff1a; -version -help -showversion

万界星空科技灯具行业MES介绍

中国是LED照明产品最大的生产制造国&#xff0c;如今&#xff0c;我国初步形成了包括LED外延片的生产、LED芯片的制备、LED芯片的封装以及LED产品应用在内的较为完超为产业链&#xff0c;随着LED照明市场渗诱率的快速警升&#xff0c;LED下游应用市场将会越来越广阔。这也将推动…

30、LCD1602

LCD1602介绍 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff0c;还可以有8个自定义字符 显示容量&#xff1a;162个字符&#xff0c;每个字符为5*7点阵 …