[linux进程控制]进程替换

文章目录

  • 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[]);

同上类比

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于JNI实现调用C++ SDK

基于JNI实现调用C SDK 背景分析解决实践 背景 上篇文章总结了几种Java项目调用C/C SDK项目方法&#xff0c;在逐一实践、踩坑后&#xff0c;最终还是敲定采用 JNI 方式进行实现。在文章开始的过程&#xff0c;会先大概讲讲笔者遇到的情况&#xff0c;因为封装方式需要根据实际…

Debian12配置ssh服务器

Debian12配置ssh服务器 安装ssh-server sudo apt install openssh-server启动ssh sudo systemctl start ssh启用ssh sudo systemctl enable ssh查看ssh状态 sudo systemctl status ssh可以看到有enabled和running字样 说明ssh启用成功 连接到服务器 # username是你的用…

React立即更新DOM

正常情况下&#xff0c;react会等待set完毕后再进行页面渲染&#xff0c;所以在set时无法拿到更新后的dom import { useRef, useState } from "react"export default () > {const div useRef(null)const [count, setCount] useState(0)const btnClick () >…

备战春招——12.3 算法

哈希表 哈希表主要是使用 map、unordered_map、set、unorerdered_set、multi_&#xff0c;完成映射操作&#xff0c;主要是相应的函数。map和set是有序的&#xff0c;使用的是树的形式&#xff0c;unordered_map和unordered_set使用的是散列比表的&#xff0c;无序。 相应函数…

【PUSDN】java中easyexcel导入导出带有图片的Excel(main方法方式)

简述 java中easyexcel导入导出带有图片的Excel&#xff08;main方法方式&#xff09;&#xff0c;web方式详见另一篇 由于电脑音频问题&#xff0c;视频暂时没有解说声音&#xff0c; 回头重新补上 前情提示 如果有任何疑问、需求、技术支持&#xff0c;欢迎点赞&#xff0…

微机原理——定时器8253(8254)学习2应用与设计

目录 简要说明 用户扩展的定时计数器应用举例 1 8254作测量脉冲宽度 2 8254作定时 3 8254作分频 4 8254同时用作计数与定时 硬件设计 ​编辑软件设计 微机系统中定时计数器应用举例 5 计时器设计 硬件设计 软件设计 6 发生器设计 硬件设计 软件设计 简要说明 定…

springboot数据源配置

springboot数据源配置 数据层解决方案——持久化技术 内置持久化解决方案——jdbcTemplate 内置数据库 H2一般用于测试环境&#xff0c;配置profiels&#xff0c;只在开发阶段使用&#xff0c;让他在上线的时候不走这里就可以了 要使用内嵌的数据库H2,要先导入jar包

C++ day51 买卖股票最佳时期

题目1&#xff1a;309 买卖股票的最佳时机含冷冻期 题目链接&#xff1a;买卖股票的最佳时机含冷冻期 对题目的理解 prices[i]表示第i天股票的价格&#xff0c;尽可能多地完成更多的交易&#xff0c;不能同时进行多笔交易&#xff0c;卖出股票后&#xff0c;第二天无法买入股…

Hisat-Trinity-PASA等组学分析流程

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 详细教程请访问&#xff1a; 组学分析流程 本期分析流程 Hisat2-SamtoolsTrinity_GG_denovoPASA … 本期教程文章 题目&#xff1a;Genomic insights into local adaptation and future climate-induced vu…

C++ 基础篇

目录 C开发概述 C特点 C跨平台的原因 C编译器 C库 操作系统API C基本概念 注释 变量 常量 两种定义常量方式的区别 表示符命名规则 常见的关键字 数据类型 整型 浮点数 字符型 转义字符 字符串型 布尔类型 运算符 算术运算符 赋值运算符 比较运算符 逻…

三极管在数字电路中的应用

一、认识三极管 三极管拥有3个引脚&#xff0c;分别对应3个级&#xff1a;基极(Base)、发射极&#xff08;Emitter&#xff09;、集电极(Collector)&#xff0c;如下图所示&#xff1b;下图横向左侧的是基极&#xff0c;带箭头的那个引脚就是发射极&#xff0c;另一个就是集电…

从零开始搭建博客网站-----登陆页面

登录按钮以及背景图设置 安装element-plus和css插件 npm install element-plus --save npm install sass --save npm install sass-loader --save在main.js里引用 寻找背景图存入assets文件下&#xff0c;并且在Login.vue里设置背景图和登录按钮 设置的背景图的大小没有起…

基于ResNet18网络完成图像分类任务

目录 1 数据处理 1.1 数据集介绍 1.2 数据读取 1.3 构造Dataset类 2 模型构建 3 模型训练 4 模型评价 5 模型预测 6 什么是预训练模型和迁移学习 7 比较“使用预训练模型”和“不使用预训练模型”的效果。 总结 在本实践中&#xff0c;我们实践一个更通用的图像分类任务…

物联网开发(一)新版Onenet 基础配置

onenet新创建的账号&#xff0c;没有了多协议接入&#xff0c;只有新的物联网开放平台 第一讲&#xff0c;先给大家讲一下&#xff1a;新版Onenet 基础配置 创建产品 产品开发-->创建产品 产品的品类选择个&#xff1a;大致符合你项目的即可&#xff0c;没有影响 选择智…

构建第一个ArkTS应用(纯HarmonyOS应用)

1. 安装开发工具 在华为开发者官方上下载HarmonyOS应用专用的开发工具&#xff0c;链接地址&#xff1a;HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 要想使用开发工具让项目跑起来&#xff0c;需要10G的磁盘空间。开发工具需要的磁盘空间为2.36G&#xff1b;SDK需…

Python笔记1.2(with open() as file和open()、logging、os、shutil、glob、decode和encode)

Python笔记1.1&#xff08;字符串日期转换、argparse、sys、overwrite、eval、json.dumpsloads、os.system(cmd)、zfill、endswith、深浅拷贝&#xff09; Python笔记2&#xff08;函数参数、面向对象、装饰器、高级函数、捕获异常、dir&#xff09; Python笔记1.2 13、with …

Linux(12):磁盘配额(Quota)与进阶文件系统管理

磁盘配额&#xff08;Quota&#xff09;的应用与实作 Quota 的一般用途&#xff1a; 针对 www server &#xff0c;例如:每个人的网页空间的容量限制&#xff1b; 针对 mail server&#xff0c;例如:每个人的邮件空间限制。 针对 file server&#xff0c;例如:每个人最大的可用…

Python 爬虫 之scrapy 框架

文章目录 常用的命令开始爬虫请求与响应让控制台只输出想要的信息创建一个py 文件来帮忙运行爬虫 工作原理图实战 常用的命令 Scrapy是一个用于爬取网站数据的Python框架&#xff0c;以下是一些常用的Scrapy命令&#xff1a; 开始的时候 用 cd 进入你想创建scrapy 的文件夹 &a…

Java中各种数据类型之间的转换

低类型向高类型自动进行转换&#xff0c;高类型向低类型的准换会丢失数据&#xff0c;整数到字符类型的转换将获取对应编码的字符。 进行高精度向低精度的强制类型准换时&#xff0c;需要将想要转换成的数据类型加一个括号()。 如何完成自动转换呢&#xff1f; 转换前的数据类…

Linux 下命令行启动与关闭WebLogic的相关服务

WebLogic 的服务器类型 WebLogic提供了三种类型的服务器&#xff1a; 管理服务器节点服务器托管服务器 示例和关系如下图&#xff1a; 对应三类服务器&#xff0c; 就有三种启动和关闭的方式。本篇介绍使用命令行脚本的方式启动和关闭这三种类型的服务器。 关于WebLogic 的…