【Linux】进程替换

img

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法…感兴趣就关注我吧!你定不会失望。

本篇导航

  • 1. 进程替换库函数接口
    • execl与execv
    • 如何用makefile同时编译多文件
    • execlp与execvp
    • execle与execvpe
  • 2. 进程替换系统调用接口

在这里插入图片描述# 0. 进程替换概念

我们想要在一个进程中的子进程运行外部程序,就可以用到进程替换的相关接口.

系统对于此提供了exec类的相关接口.

相关接口有6个,不过都具有一定的使用规则.

image-20231116081306274

先来看看单进程版的进程替换

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
int main()
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    execl("/usr/bin/ls","ls","-a","-l",NULL);
    printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    return 0;
}

编译运行会发生什么呢?

image-20231116081848443

执行到execl前的程序是正常运行的,也就是正常打印出了进程PID相关信息.

后来执行到了execl,如我们猜测的一样执行了 ls -al 的指令

但是!!后来的 **printf(“after i am a process,pid:%d,ppid:%d\n”,getpid(),getppid()); **这条指令并未执行.

这是我们没有想到的?这是为什么呢?

回顾我们的标题名,叫做进程替换.显然这里发生了进程的替换.

8df75dddf104585179d4c2eed3338dd

一个文件未被运行时其在磁盘当中,execl把内存当中的ls可执行程序,对内存中的./proce可执行程序进行了一个替换.不创建新的进程,只进行代码和数据的替换

注意!这里并未重新创建一个进程,而是在原有的进程上进行了夺舍,也就是运行了exec*指令后,进程还是那个进程,内容却不再是原来的内容了(肉体仍旧,灵魂改变).

所以替换成功后,整个程序改变了,那么这个函数有返回值嘛

运行成功后数据替换,就算返回了也被换掉,所以执行成功是没有返回值的,执行失败则返回-1

现在我们大概能知道什么是进程替换了.我们在看看在多进程状态下,进程替换会发生什么呢?

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    pid_t ret=fork();
    
    if(ret == 0)
    {
        printf("before i am a child process,pid:%d,ppid:%d\n",getpid(),getppid());   
        execl("/usr/bin/ls","ls","-a","-l",NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    
    else
    {
        int ret=wait(NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

image-20231116084200236

子进程执行了execl指令,完成了进程替换,而父进程照常运行自己剩下的指令.

那么,这几个接口有什么含义呢.以及那个命令为什么要这样写呢?

1. 进程替换库函数接口

exec*相关接口使用需包含两个库

#include<sys/types.h>
#include<sys/wait.h>

其中函数的命名是有一定的规律的

  1. l:list
  2. p:path
  3. v:vector
  4. e:environ

execl与execv

观察六个函数我们发现,前缀要么是 execl,要么是 execv,这具体含义是

l: list 列表 v: vector 数组

也就是

execl("可执行文件所在路径","文件运行时指令","NULL")

文件运行指令在屏幕上输入什么,就在这里输入了什么

  • 例如: ls -al 我们在屏幕上需要输入这两个指令+参数.所以而ls所在路径为 /usr/bin/ls,最后以NULL结尾

    execl("/usr/bin/ls","ls","-al",NULL);
    

同样也可以运行我们自己的可执行程序 并不仅限于C,可以是任何可执行程序!

test.c:

#include<stdio.h>
int main()
{
    printf("hello\n");
    return 0;
}

proce.c:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    pid_t ret=fork();
    
    if(ret == 0)
    {
        printf("before i am a child process,pid:%d,ppid:%d\n",getpid(),getppid());   
        execl("./test","test",NULL); //可以为相对路径也可以为绝对路径
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    
    else
    {
        int ret=wait(NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

image-20231116085803827

如何用makefile同时编译多文件

test:test.c
	gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
	rm -rf proce test

通常我们编译单文件是这样.

Makefile会自顶向下执行检测到的第一个依赖项,然后完成编译,不会编译其他依赖项

也就是如果想要这样编译多文件是没有用的

proce:proce.c
	gcc -o $@ $^ -std=c99
test:test.c
	gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
	rm -rf proce test

我们需要添加一个伪目标:

.PHONY:all
all:proce test

proce:proce.c
	gcc -o $@ $^ -std=c99
test:test.c
	gcc -o $@ $^ -std=c99
.PHONY:clean
clean:
	rm -rf proce test

回到上面,我们看过了L如何使用,那么V呢?

其实这两个差不多,我们需要手动创建一个字符数组,将上面的指令放到数组中即可.最后同样需要以NULL结尾

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    pid_t ret=fork();
    char * myargv[]=
    {
        "ls",
        "-a",
        "-l",
        NULL
    };
    if(ret == 0)
    {
        printf("before i am a child process,pid:%d,ppid:%d\n",getpid(),getppid());   
        // execl("./test","test",NULL);
        execv("/usr/bin/ls",myargv);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    
    else
    {
        int ret=wait(NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

image-20231116090741443

execlp与execvp

这个P的概念是,系统会从默认的**$PATH**中去寻找可执行程序,不需要再写路径.

这里以execlp举例.

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    pid_t ret=fork();
    if(ret == 0)
    {
        printf("before i am a child process,pid:%d,ppid:%d\n",getpid(),getppid());   
        execlp("ls","ls","-a","-l",NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    else
    {
        int ret=wait(NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

**execlp(“ls”,“ls”,“-a”,“-l”,NULL)**这里的第一个ls表示要在PATH中寻找哪个执行程序,后面依然是参数列表.

execle与execvpe

多了一个e的选项:表示可以传递的环境变量列表.

我们用自己创建的程序来验证一下这个信息.

#include<stdio.h>
#include<stdlib.h>
int main(int argc,char * argv[],char * environ[])
{
    for(int i=0;environ[i];i++)
    {
        printf("i -> %d :,environ: %s\n",i,environ[i]);
    }
    printf("end\n");
    return 0;
}
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(int argc,char *argv[])
{
    printf("before i am a process,pid:%d,ppid:%d\n",getpid(),getppid());   
    pid_t ret=fork();
    char *environ[]={
        "hello=123",
        "Linux",
        "execle",
        NULL
    };
    if(ret == 0)
    {
        printf("before i am a child process,pid:%d,ppid:%d\n",getpid(),getppid());   
        execle("./test","test",NULL,environ);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    else
    {
        int ret=wait(NULL);
        printf("after i am a process,pid:%d,ppid:%d\n",getpid(),getppid());
    }
    return 0;
}

image-20231116092051565

现在我们有三种形式向子进程中传入环境变量

  1. 不传入,直接使用全局的环境变量
  2. 在父进程中使用putenv(),追加形式向子进程传入环境变量
  3. 使用自定义的环境变量传参

2. 进程替换系统调用接口

image-20231116125255627

上方提到的六个库函数都是调用此函数

image-20230905164632777

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

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

相关文章

STM32-基本定时器

一、基本定时器的作用 定时触发输出直接驱动DAC。 二、基本定时器的框图 以STM32F103系列为例&#xff0c;具体开发板请查看开发手册。 类别定时器总线位数计数方向预分频系数是否可以产生DMA捕获/比较通道互补输出基本定时器TIM6 / TIM7APB116位向上1~65536可以0无通用定时…

链式前向星

性质 一种邻接表的写法 关键点&#xff1a; 数据结构 // 边 class Edge {int next; // 指向相同起始点的下一条边int to; // 邻接点int w; // 权重 } Edge[] edge new Edge[9]; // edge[cnt]表示编号为cnt的边// 用数组表示 int[] next new int[MAX]; int[] to new int[M…

算法-二叉树-简单-二叉树的遍历

记录一下算法题的学习6 首先我们要回忆一下怎么样遍历一个树&#xff1a; 三种遍历概念 先序遍历&#xff1a;先访问根节点&#xff0c;再访问左子树&#xff0c;最后访问右子树。 后序遍历&#xff1a;先左子树&#xff0c;再右子树&#xff0c;最后根节点。 中序遍历&…

人工智能轨道交通行业周刊-第65期(2023.10.30-11.19)

本期关键词&#xff1a;高铁自主创新、智慧城轨、调车司机、大模型垂直应用、大模型幻觉 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨道…

掌握深度学习利器——TensorFlow 2.x实战应用与进阶

掌握深度学习利器——TensorFlow 2.x实战应用与进阶 摘要&#xff1a;随着人工智能技术的飞速发展&#xff0c;深度学习已成为当下最热门的领域之一。作为深度学习领域的重要工具&#xff0c;TensorFlow 2.x 备受关注。本文将通过介绍TensorFlow 2.x的基本概念和特性&#xff…

蓝桥杯每日一题2023.11.18

题目描述 蓝桥杯大赛历届真题 - C 语言 B 组 - 蓝桥云课 (lanqiao.cn) 题目分析 本题使用搜索&#xff0c;将每一个格子进行初始赋值方便确定是否为相邻的数&#xff0c;将空出的两个格子首先当作已经填好数值为100&#xff0c;此时从第一个格子右边的格子开始搜索&#xff…

HAL库STM32串口开启DMA接收数据

STM32CubeMx的配置 此博客仅仅作为记录&#xff0c;这个像是有bug一样&#xff0c;有时候好使&#xff0c;有时候不好&#xff0c;所以趁现在好使赶紧记录一下&#xff0c;很多地方用到串口接收数据&#xff0c;DMA又是一种非常好的接收方式&#xff0c;可以节约CPU的时间&…

KVM Cloud云平台

项目介绍 KVM Cloud 是一款基于Java实现的轻量级私有云平台&#xff0c;旨在帮助中小企业快速实现计算、存储、网络等资源的管理&#xff0c;让企业拥有自己的云平台&#xff0c;包括但不限于如下功能: 1、基于KVM的VM基础功能(创建、启动、停止、重装、webVNC等功能) 2、使用…

systemverilog:interface中端口方向、Clocking block的理解

1、interface中端口方向的理解 &#xff08;1&#xff09;从testbench的角度看&#xff0c;tb中信号的输入输出方向与interface中信号输入输出方向一致&#xff1a; &#xff08;2&#xff09;从DUT角度看&#xff0c;DUT中信号输入输出方向与interface中信号输入输出方向相反…

基于MS16F3211芯片的触摸控制灯的状态变化和亮度控制(11.17,PWM控制与状态切换)

1.今天做了什么 2.过程思路 看了两天文档才慢慢看懂&#xff0c;有点满了 现在接着前一天的思路&#xff0c;可以通过代码来控制pwm的占空比。我这里采用的是TP0定时器 初步控制pwm的占空比 void LED_PWM_OPEN(void) {//占空比 PWM1-Y-PB2PWM1DH 0X0F;PWM1DL 0X00; //占…

【Linux】20、进程状态:不可中断进程、iowait、僵尸进程、dstat strace pstree

文章目录 一、进程状态1.1 iowait 分析1.2 僵尸进程1.3 小结 短时应用的运行时间比较短&#xff0c;很难在 top 或者 ps 这类展示系统概要和进程快照的工具中发现&#xff0c;你需要使用记录事件的工具来配合诊断&#xff0c;比如 execsnoop 或者 perf top。 讲到 CPU 使用率的…

App测试经典面试题及参考答案

最近整理了一些关于App测试的面试题。 本参照答案是本人在工作实践中总结&#xff0c;仅代表个人观点&#xff0c;如有错误&#xff0c;请谅解。 1、说一些你在测试过程中常用到的adb命名 2、APP测试与web测试的区别&#xff1f; 3、APP闪退有哪些原因造成的&#xff1f; …

解决Kibana初始化失败报错: Unable to connect to Elasticsearch

现象&#xff1a; 原因&#xff1a; docker run生成容器的时候&#xff0c;指定elastic server时指向了localhost 为什么不能是localhost, 因为这个localhost指向的是容器本身的网络&#xff0c;而elastic用的是物理网络&#xff0c;两个网络是隔离的&#xff0c;所以如果kiba…

统计量及抽样分布

1.常用统计量 &#xff08;1&#xff09;样本均值 反映总体X数学期望的信息&#xff0c;是最常用的统计量。 &#xff08;2&#xff09;样本方差 反映总体X方差的信息。 &#xff08;3&#xff09;样本变异系数 反映总体变异系数C的信息&#xff0c;用来刻画离散程度。 &am…

微服务实战系列之Nacos

导语 欢迎来到 “Nacos” 的世界&#xff01; Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单…

C语言基本算法----冒泡排序

原理 冒泡排序就是对一个存放N个数据的数组进行N次扫描&#xff0c;每次把最小或者最大的那个元素放到数组的最后&#xff0c;达到排序的目的。 原理图解 冒泡排序过程分析 冒泡排序的执行过程 冒泡排序总结 在此感谢 冒泡排序法_哔哩哔哩_bilibili 这篇blog是对这位up此视…

ESP32网络开发实例-非接触式水位监测

非接触式水位监测 文章目录 非接触式水位监测1、HC-SR04介绍2、软件准备3、硬件准备4、代码实现在本文中,我们将使用 HC-SR04 超声波传感器和 ESP32 创建一个水位监测网络服务器。 这将是一个非接触式水位测量系统。 首先,我们将介绍HC-SR04 与 ESP32 连接。 使用ESP32对超声…

mac无法向移动硬盘拷贝文件怎么解决?不能读取移动硬盘文件怎么解决

有时候我们在使用mac的时候&#xff0c;会遇到一些问题&#xff0c;比如无法向移动硬盘拷贝文件或者不能读取移动硬盘文件。这些问题会给我们的工作和生活带来不便&#xff0c;所以我们需要找到原因和解决办法。本文将为你介绍mac无法向移动硬盘拷贝文件怎么回事&#xff0c;以…

RobotFramework之如何使用数据驱动(十二)

学习目录 引言 数据驱动是什么&#xff1f; 非驱动方式测试案例 通过添加Template模板的方式&#xff0c;实现数据驱动 将参数放在变量文件中&#xff0c;实现数据驱动 引言 大家平时在写接口或者UI自动化用例的时候&#xff0c;是否遇到这种情况&#xff1a; 写了很多条…

在 C# 程序中注入恶意 DLL

为什么 Windbg 附加到 C# 程序后&#xff0c;程序就处于中断状态了&#xff1f;它到底是如何实现的&#xff1f;其实简而言之就是线程的远程注入&#xff0c;这一篇就展开说一下。 实现原理 1. 基本思路 WinDbg 在附加进程的时候&#xff0c;会注入一个线程到 C# 进程 中&…