当前位置: 首页 > news >正文

[Linux入门]---进程替换

文章目录

  • 1.进程替换原理
  • 2.进程替换函数
    • 2.1execl函数
    • 2.2execlp函数
    • 2.3execv函数
    • 2.4execvp函数
    • 2.5execle函数
    • 2.6execve函数
    • 2.7跨语言调用程序
  • 3.总结

1.进程替换原理

一个程序替换的函数:

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);

示例1:

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

在这里插入图片描述

示例2:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);execl("/usr/bin/ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述

从上面的代码运行结果,示例1中可以看出父进程使用execl程序替换函数,会直接替换程序的代码和数据;示例2中,父进程创建子进程使用execl程序替换函数,要替换子进程的代码和数据的时候,触发只读权限写实拷贝,OS会重新开辟空间存放替换新程序的代码和数据!

问题1:在两个示例中,为什么没有执行以下代码语句?

 printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());

程序替换成功之后,exec*后续的代码不会被执行;替换失败呢?才可能执行后续代码。exec*函数,只有失败返回值,没有成功返回值!!

问题2:CPU是如何得知程序的入口地址?
Linux中形成的可执行程序,是有格式的,有一个描述信息的表,该表里面就描述了当前可能程序分了哪些段,包括代码段,数据段等,可执行(入口)地址就是表头地址!CPU把可执行程序加载进来的时候,首先获得表头地址(可执行程序地址),便可以执行程序!

问题3:程序替换有没有创建新的进程?
程序替换没有创建新进程,只是进行进程的程序代码和数据的替换工作!

替换原理总结

fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
在这里插入图片描述

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.1execl函数

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

execl替换函数,参数path中,/usr/bin/ls作为路径传参,需要找到可执行程序的位置;execl替换函数以链表的方式,把功能选项连接起来,则是要确定如何执行可执行程序!

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替换函数execl("/usr/bin/ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述

使用命令行参数指令:

#ls -a -l

指令运行结果:
在这里插入图片描述
简记:execl替换函数,arg参数与命令行参数传递一致,命令行参数怎么写,在execl函数中就怎么传!只不过空格换逗号,加NULL

2.2execlp函数

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

示例:

int execl(const char *path, const char *arg, ...);
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替换函数execlp("ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述
execlp替换函数,函数中名中p指的是环境变量PATH,指令的默认路径,执行进程时系统会去默认路径寻找,执行系统提供的指令时不需要带上地址;参数file中,ls作为文件名传参,需要在默认路径下找到对应的可执行程序;"ls","-l","-a",NULLexecl替换函数以链表的方式,把功能选项连接起来,则是要确定如何执行可执行程序!
在这里插入图片描述

2.3execv函数

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

execv替换函数名中的v是指vector(数组),把可执行程序的执行选项功能放在数组中,在数组中以NULL为结尾!参数path,把可执行程序的位置作为参数传递;参数argv,把自定义选项功能数组作为参数传递!
示例:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//数组参数char* const myargv[]={"ls","-a","-l",NULL};//替换函数execv("/usr/bin/ls",myargv);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述

2.4execvp函数

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

execvp替换函数名中的v是指vector(数组),把可执行程序的执行选项功能放在数组中,在数组中以NULL为结尾;函数中名中p指的是环境变量PATH,执行系统程序时不需要带路径;参数file,把可执行程序的位置作为参数传递;参数argv,把自定义选项功能数组作为参数传递!
示例:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//数组参数char* const myargv[]={"ls","-a","-l",NULL};//替换函数execvp("ls",myargv);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述

2.5execle函数

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

execle替换函数名是在‘execl’函数基础上,新增加了envp参数。可以传递‘environ’环境变量参数,libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时 要用extern声明。因为环境变量通常具有全局属性,子进程继承父进程的环境变量;也可以自己设置自定义新的环境变量数组,传递给子进程!
示例1:

//ohtherExe.cpp
#include <iostream>
using namespace std;int main(int argc, char *argv[], char *env[])
{cout << argv[0] << " begin running" << endl;cout << "这是命令行参数: \n";for(int i=0; argv[i]; i++){cout << i << " : " << argv[i] << endl;}cout << "这是环境变量信息: \n";for(int i = 0; env[i]; i++){cout << i << " : " << env[i] << endl; }cout<<"xxxxxxxxxxxxxxxxx"<<endl;  return 0;
}
//mycommand.c
//新增extern char** environ;//替换函数execle("./otherExe","otherExe","-a","-b","c",NULL,environ);

代码运行结果为:
在这里插入图片描述
示例2:

//mycommand.c
//新增部分
char *const myenv[] = {"MYVAL=1111","MYPATH=/usr/bin/XXX",NULL
};
//替换函数为
execle("./otherExe", "otherExe", "-a", "-w", "-v", NULL, myenv);

代码运行结果为:
在这里插入图片描述

2.6execve函数

int execve(const char *path, char *const argv[], char *const envp[]);

execve替换函数名中的v是指vector(数组),把可执行程序的执行选项功能放在数组中,在数组中以NULL为结尾;envp参数,传递环境变量为参数。
示例:

    char* const myargv[]={"otherExe","-a","-l",NULL};char* const myenv[]={"MYVAL=111111","MYPATH=/usr/bin/xxx",NULL};extern char** environ;

代码运行的结果为:
在这里插入图片描述
注意:
libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头文件中,所以在使用时要用extern声明;我们如果想给子进程传递环境变量,有两种方式:①新增环境变量,在父进程中使用putenv函数自己设置的环境变量;②彻底替换(覆盖)环境变量,使用带“e”选项的exec函数(execle/execve函数),传递自己设置环境变量数组;

2.7跨语言调用程序

在上面的例子中,我们都是使用我们自己写的mycommand.c程序调用系统的程序(指令),那我们是否可以调用自己所写的程序呢?答案是当然可以!
示例1:
python代码

#!/usr/bin/python3print("hello Python!")
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替换函数execl("/usr/bin/python3", "python3", "test.py", NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}

代码运行结果如下:
在这里插入图片描述
为什么我们可以用C语言程序调用Python可执行程序?
无论什么语言的可执行程序运行起来之后变成了进程,便可以通过在子进程中使用exec系列函数替换达到调用的效果!

3.总结

exec系列函数如果调用成功,则加载新的程序从启动代码开始执行不再返回。如果调用出错则返回-1,所以exec函数只有出错的返回值,而没有成功的返回值。
②命名理解记忆

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

在这里插入图片描述
③只有execve函数是真正的系统调用,其它五个函数最终都调用execve函数,所以execve在man手册 第2节,其它函数在man手册第3节。这些函数之间的关系如下图所示:
在这里插入图片描述

相关文章:

[Linux入门]---进程替换

文章目录 1.进程替换原理2.进程替换函数2.1execl函数2.2execlp函数2.3execv函数2.4execvp函数2.5execle函数2.6execve函数2.7跨语言调用程序 3.总结 1.进程替换原理 一个程序替换的函数&#xff1a; #include <unistd.h> int execl(const char *path, const char *arg,…...

Java语言程序设计基础篇_编程练习题*18.9 (以逆序输出一个字符串中的字符)

目录 题目&#xff1a;*18.9 (以逆序输出一个字符串中的字符) 代码示例 输出结果 题目&#xff1a;*18.9 (以逆序输出一个字符串中的字符) 编写一个递归方法&#xff0c;使用下面的方法头在控制台上以逆序显示一个字符串: public static void reverseDisplay(String value…...

IT英语每日积累

IT词汇积累 前言今日学习1. be synonymous with2.handle something3.modify4.optionally5. generate6,sandby7.interrupt8.emphasize9.croodinate10.splitting and merging11.shard12.per13.consecutively14.synchronization15。unbounded 前言 这里给出的是本人在生活和学习中…...

QML学习二:Qt启用qml文件实时预览编辑,以及打印日志到控制台

开发环境:Qt 6.5.3 LTS 1、Qt 6.5.3 LTS 2、Pyside6 3、Python 3.11.4 效果如下,右侧更改的代码可以实时反映到左侧的设计器中。 Qt启用qml文件实时预览编辑,以及打印日志到控制台 一、打开Qt Designer插件二、qml和Python文件打印输出到控制台总结Qt Creator版本如下:…...

JVM面试真题总结(四)

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 列举常用的垃圾收集器&#xff0c;并简要说明其特点 Serial收集器…...

P1352 没有上司的舞会

~~~~~ P1352 没有上司的舞会 ~~~~~ 总题单链接 思路 ~~~~~ 设 d p [ u ] [ [ 0 / 1 ] dp[u][[0/1] dp[u][[0/1] 表示第 u u u 个点 [ 不选 / 选 ] [不选/选] [不选/选] 的最大值。 ~~~~~ d p [ u ] [ 1 ] dp[u][1] dp[u][1] 只能用 d p [ v ] [ 0 ] dp[v][0] dp[v][0] 来更…...

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来&#xff0c;一站式有声阅读平台听书系统 &#x1f31f; 开篇&#xff1a;遇见未来&#xff0c;从“智听”开始 在这个快节奏的时代&#xff0c;你是否渴望在忙碌的间隙&#xff0c;找到一片属于自己的宁静角落&#xff1f;是否梦想着能随时随地&#xff0c;沉浸在知…...

2024 第七届“巅峰极客”网络安全技能挑战赛初赛 Web方向 题解WirteUp

EncirclingGame 题目描述&#xff1a;A simple game, enjoy it and get the flag when you complete it. 开题&#xff0c;前端小游戏&#xff0c;红点出不去就行 直接玩通关了 看看如何不玩也能拿到flag&#xff0c;flag存储在后端php文件内&#xff0c;前端找不到。 看一下…...

论文阅读笔记《面向集群协同的两点相对定位技术》

邓廷祥,任鹏,程甲,等.面向集群协同的两点相对定位技术[J].兵工学报,2023,44(S2):22-34. 摘要 无人机精确定位的三个难题&#xff1a; GNSS难以提供稳定准确的位置信息、难以部署辅助锚点、传统的相对定位方法大多存在节点数量限制。 本文针对上述问题&#xff0c;提出了一种GN…...

RK3566/RK3568 Android 11 无操作自动隐藏导航栏、底部上拉显示导航栏

概述 总目录:RK3566/RK3568 Android 11 定制大全 在系统服务中增加无操作自动隐藏导航栏方法,在上层app动态调用无操作自动隐藏导航栏方法,系统会在5秒无操作后自动隐藏导航栏,隐藏导航栏后从底部上拉可显示导航栏,设备关机和重启后也能继续生效。 创建全局变量 1.定义…...

四、Django模型

Model Model (模型) 简而言之即数据模型&#xff0c;是一个Django应用的核心。模型不是数据本身&#xff08;比如数据表里的数据), 而是抽象的描述数据的构成和逻辑关系。 每个Django的模型(model)实际上是个类&#xff0c;继承了models.Model。每个Model应该包括属性(字段)&…...

Telephony SS

1、补充业务概述 SS是补充服务的缩写,它包含呼叫转移、呼叫限制、呼叫等待、线路识别(CLIR)等。在IMS(IP多媒体子系统)网络中,它使用XCAP(XML配置访问协议)协议通过UT接口操纵SS数据。该协议允许授权用户在连接到IMS或连接到非IMS网络(例如公共互联网)时操纵与服务相…...

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h> #include <stdlib.h> void shellSort(int data[], int n){// 划分的数组&#xff0c;例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量&#xff0c;换值int temp;…...

C++(一)----C++基础

1.C的发展史 C语言诞生后&#xff0c;很快普及使用&#xff0c;但是随着编程规模增大且越来越复杂&#xff0c;并且需要高度的抽象和建模时&#xff0c;C语言的诸多短板便表现了出来&#xff0c;为了解决软件危机&#xff0c;上世纪八十年代&#xff0c;计算机界提出了oop&…...

C 语言面试题大汇总之华为面试题

文章目录 1. 局部变量能否和全局变量重名?2. 如何引用一个已经定义过的全局变量?3. 全局变量可不可以定义在可被多个.C 文件包含的头文件中?为什么?4. 请写出下列代码的输出内容5. static 全局变量与普通的全局变量有什么区别?static 局部变量和普通局部变量有什么区别?s…...

Java:面向对象

继承 继承是一种面向对象编程&#xff08;OOP&#xff09;特性&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;继承另一个类&#xff08;称为父类或基类&#xff09;的属性&#xff08;如方法和字段&#xff09;。通过继承&#xff0c;子类可以复用父类的代…...

【区块链 + 基层治理】腾讯未来社区:区块链业主决策系统 | FISCO BCOS应用案例

腾讯未来社区是腾讯推出的智慧社区综合解决方案&#xff0c;致力于形成“互联网 社区”一站式解决方案&#xff0c;打造智慧社 区健康生态。为了解决物业管理领域的痛点&#xff0c;构建围绕居民、物业、政府和商业四个角色为核心的良好生态&#xff0c;以 信息平台及工具为纽…...

【Rust练习】13.数组

练习题来自&#xff1a;https://practice-zh.course.rs/compound-types/array.html 1 fn main() {// 使用合适的类型填空let arr: __ [1, 2, 3, 4, 5];// 修改以下代码&#xff0c;让它顺利运行assert!(arr.len() 4); }显然这个数组的长度是5. fn main() {// 使用合适的类…...

直流负载技术介绍

直流负载技术是一种用于控制和调节电力系统运行状态的重要技术。它主要通过对电力系统中的直流负载进行有效的管理和控制&#xff0c;以保证电力系统的稳定运行&#xff0c;提高电力系统的运行效率&#xff0c;降低电力系统的运行成本。 直流负载技术主要包括直流负载的检测、…...

FPGA低功耗设计

FPGA低功耗设计 文章目录 FPGA低功耗设计前言一、功耗类型1.1 动态功耗1.2 静态功耗1.3 浪涌功耗 二、系统级低功耗设计2.1 **多电压技术&#xff1a;**即工作频率、电压和功耗的关系2.2 系统时钟分配&#xff1a;2.3 软硬件划分2.4 p 或单元库选择 三、RTL级别低功耗设计3.1 并…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...