【Linux】进程替换

  🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html

9efbcbc3d25747719da38c01b3fa9b4f.gif

目录

进程程序替换

代码和现象

替换函数 

 替换原理

函数解释 

命名理解 

简易shell 


前言

    💬 hello! 各位铁子们大家好哇。

             今日更新了Linux的进程替换的内容
    🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝

进程程序替换

代码和现象

运行后,发现使用了ls命令,而且打印end的语句也不见了。

exec*函数的作用:让进程通过exec*函数把全新的程序替换到自己对应的代码和数据,然后执行新的程序。

exec*函数执行完毕后,后续的代码不见了,因为被替换了。

替换函数 

其实有六种以exec开头的函数,统称exec*函数:

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

 替换原理

我们自己的代码编译运行后就会变成可执行程序,运行起来后就变成进程。

进程=内核数据结构+代码和数据

替换的意义是:内核数据结构不变,个别属性可能会变,用新程序的代码代替老程序的代码。 

进程的替换没有创建新的进程!所以调用exec*前后该进程的id并未改变。

站在被替换进程的角度,本质就是这个程序由磁盘被加载到内存中了。(冯诺依曼体系)

函数解释 

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1
  • 所以exec*函数只有出错的返回值而没有成功的返回值。

 如上图,没有lss命令,所以替换会失败。如果替换成功,就不会向后继续运行。所以只要继续运行了,就一定是替换失败了。

  1 #include<stdio.h>2 #include<unistd.h>3 #include<stdlib.h>4 #include<sys/types.h>5 #include<sys/wait.h>6 7 int main()8 {9   printf("testexec ... begin!\n");10 11   pid_t id=fork();12   if(id==0)13   {14     sleep(2);15     //child  16     execl("/usr/bin/ls","ls","-l","-a",NULL);                                       17     exit(1);18   }19 20   //father21   int status=0;22   pid_t rid=waitpid(id,&status,0);23   if(rid>0)24   {25     printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));26   }27   printf("testexec ... end!\n");28   return 0;29 }

运行结果如上图, 进程替换成功了,父进程也等待成功。上面是用fork创建子进程,让子进程去替换,让子进程完成任务,而不改变父进程。这也是进程替换的重要意义。

命名理解 

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

以execl为例,path表示要执行的程序的路径。第二个是可变参数,如果我们用ls命令,后面需要选项的话,就用逗号隔开。选项带完后,后面必须加上NULL,这是格式要求。

上面的选项参数的隔开就是l(列表)的体现。

 execv的第一个参数跟上面的一样。v就是vector的意思,所以参数二就是传数组。

带有p的就是 用户可以不传执行文件的路径(但是文件名称要传)。直接告诉exec*,我想执行谁就行。有了p,系统会自动在环境变量PATH中进行查找。

 

 

注意上面的参数1表示我想执行谁,参数2表示我想怎么执行。 二者含义不一样。

用一个可执行程序替换另一个可执行程序: 

当我们想要通过make一下就能生成两个可执行程序,可以通过.PHONY设置一个为目标,把想要生成的可执行文件作为依赖方法,这样就能同时生成两个了。

 

上面是通过一个程序替换另一个程序的例子。 

 有了这个例子的基础,接下来介绍execvpe

 mypragma.cc

  1 #include<iostream>2 #include<unistd.h>3 using namespace std;4 5 int main(int argc,char *argv[],char* env[])6 {7   int i=0;8   for(;argv[i];i++)9   {10     printf("argv[%d]:%s\n",i,argv[i]);11   }12 13   printf("------------------------\n");14   for(i=0;env[i];i++)15   {16     printf("env[%d]:%s\n",i,env[i]);17   }18   printf("------------------------\n");                                                                                                                                      19 20 21   cout<<"hello C++,I am a C++ pragma!"<<getpid()<<endl;22   cout<<"hello C++,I am a C++ pragma!"<<getpid()<<endl;23   cout<<"hello C++,I am a C++ pragma!"<<getpid()<<endl;24   cout<<"hello C++,I am a C++ pragma!"<<getpid()<<endl;25   return 0;26 }

testexec.c 

  7 int main()8 {9   printf("testexec ... begin!\n");10   11   pid_t id=fork();12   if(id==0)13   {14     char* const argv[]=15     {16       (char*)"mypragma",17       NULL18     };19 20     char* const envp[]=21     {22       (char*)"HAHA=1111",23       (char*)"HEHE=2222",24       NULL25     };26     printf("child pid:%d\n",getpid());27     sleep(2);28     execvpe("./mypragma",argv,envp);43     exit(1);44   }46   //father47   int status=0;48   pid_t rid=waitpid(id,&status,0);49   if(rid>0)50   {51     printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));52   }53   printf("testexec ... end!\n");54   return 0;55 }

编译运行testexec.c程序后,就会用mypragma程序替换。里面的execvpe,参数1是要替换的文件名,参数2表示怎么执行,参数3就是环境变量。参数2和参数3都会被传到替换文件中。所以运行结果如下图:

对于main函数的子进程,它的父进程main本身也是bash的子进程,所以可以通过环境变量的第三方指针extern char** environ 获取系统的环境变量。

所以,参数3的意义就是整体替换所有的环境变量。

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在 man手册第3节。 之所以弄那么多接口,主要是为了支持不同的应用场景。

简易shell 

  1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 #include <errno.h>5 #include <unistd.h>6 #include <sys/types.h>7 #include <sys/wait.h>8 9 #define SIZE 51210 #define ZERO '\0'11 #define SEP " "12 #define NUM 3213 #define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)14 15 16 // 为了方便,我就直接定义了17 char cwd[SIZE*2];18 char *gArgv[NUM];19 int lastcode = 0;20 21 void Die()22 {23     exit(1);24 }25 26 const char *GetHome()27 {28     const char *home = getenv("HOME");29     if(home == NULL) return "/";30     return home;31 }32 33 const char *GetUserName()34 {35     const char *name = getenv("USER");36     if(name == NULL) return "None";37     return name;38 }                                                                                                                                                                              39 const char *GetHostName()40 {41     const char *hostname = getenv("HOSTNAME");42     if(hostname == NULL) return "None";43     return hostname;44 }45 // 临时46 const char *GetCwd()47 {48     const char *cwd = getenv("PWD");49     if(cwd == NULL) return "None";50     return cwd;51 }                                                                                                                                                                              52 53 // commandline : output54 void MakeCommandLineAndPrint()55 {56     char line[SIZE];57     const char *username = GetUserName();58     const char *hostname = GetHostName();59     const char *cwd = GetCwd();60 61     SkipPath(cwd);62     snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd+1);63     printf("%s", line);64     fflush(stdout);65 }66 67 int GetUserCommand(char command[], size_t n)68 {69     char *s = fgets(command, n, stdin);70     if(s == NULL) return -1;71     command[strlen(command)-1] = ZERO;72     return strlen(command); 73 }74 75 76 void SplitCommand(char command[], size_t n)77 {78     (void)n;79     // "ls -a -l -n" -> "ls" "-a" "-l" "-n"80     gArgv[0] = strtok(command, SEP);81     int index = 1;82     while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意写成=,表示先赋值,在判断. 分割之后,strtok会返回NULL,刚好让gArgv最后一个元素是NULL, 并且while判断结束83 }84                                                                                                                                                                                85 void ExecuteCommand()86 {87     pid_t id = fork();88     if(id < 0) Die();89     else if(id == 0)90     {91         // child92         execvp(gArgv[0], gArgv);93         exit(errno);94     }95     else96     {97         // fahter98         int status = 0;99         pid_t rid = waitpid(id, &status, 0);
100         if(rid > 0)
101         {
102             lastcode = WEXITSTATUS(status);
103             if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
104         }
105     }
106 }
107 
108 void Cd()
109 {
110     const char *path = gArgv[1];
111     if(path == NULL) path = GetHome();
112     // path 一定存在
113     chdir(path); //更改当前的工作路径
114 
115     // 刷新环境变量
116     char temp[SIZE*2];//临时缓冲区
117     getcwd(temp, sizeof(temp)); //得到当前进程的绝对路径 
118     snprintf(cwd, sizeof(cwd), "PWD=%s", temp);//
119     putenv(cwd); // 导入新的环境变量
120 }
121 
122 int CheckBuildin()
123 {
124     int yes = 0;
125     const char *enter_cmd = gArgv[0];
126     if(strcmp(enter_cmd, "cd") == 0)
127     {
128         yes = 1;
129         Cd();
130     }
131     else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0)
132     {
133         yes = 1;
134         printf("%d\n", lastcode);
135         lastcode = 0;
136     }
137     return yes;
138 }
139 
140 
141 int main()
142 {
143     int quit = 0;
144     while(!quit)
145     {
146         // 1. 我们需要自己输出一个命令行
147         MakeCommandLineAndPrint();
148 
149         // 2. 获取用户命令字符串
150         char usercommand[SIZE];
151         int n = GetUserCommand(usercommand, sizeof(usercommand));
152         if(n <= 0) return 1;
153 
154         // 3. 命令行字符串分割. 
155         SplitCommand(usercommand, sizeof(usercommand));
156 
157         // 4. 检测命令是否是内建命令
158         n = CheckBuildin();
159         if(n) continue;
160         // 5. 执行命令
161         ExecuteCommand();
162     }
163     return 0;
164 }

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频美颜SDK与直播美颜工具API是什么?计算机视觉技术详解

今天&#xff0c;小编将深入探讨视频美颜SDK与直播美颜工具API的概念及其背后的计算机视觉技术。 一、视频美颜SDK的概念 视频美颜SDK是一套用于开发实时美颜效果的工具集&#xff0c;开发者可以利用它在视频流中实现面部特征的优化。这些SDK通常提供了一系列功能&#xff0c…

计算机毕业设计 基于Hadoop的智慧校园数据共享平台的设计与实现 Python 数据分析 可视化大屏 附源码 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

SpringBoot3脚手架

MySpringBootAPI SpringBoot3脚手架&#xff0c;基于SpringBoot3DruidPgSQLMyBatisPlus13FastJSON2Lombok&#xff0c;启动web容器为Undertow(非默认tomcat)&#xff0c;其他的请自行添加和配置。 <java.version>17</java.version> <springboot.version>3.3…

fastAPI教程:jinja2模板

FastAPI 五、jinja2模板 jinja是python知名web框架Flask的作者开发的⼀个开源的模板系统&#xff0c;起初是仿django模板的⼀个模板引擎DjangoTPL&#xff0c;为Flask提供模板支持&#xff0c;由于其灵活&#xff0c;快速和安全等优点被⼴泛使用。 jinja2是jinja2这个模块的…

erlang学习:Linux命令学习8

shell脚本案例学习 循环求 1-100 的每一步和 —案例 j0 i1 while((i<100)) do j$((ji)) echo $j ((i)) done每 30 s循环判断一次 user 用户是否登录系统 —案例 设置了一个次数&#xff0c;如果循环了五次在user文件中添加user用户&#xff0c;表示用户登录 USERS"u…

nodejs安装及环境配置

一、下载 进入官网https://nodejs.org/en/download/prebuilt-installer下载node.js安装包&#xff0c;选择对应版本的node&#xff0c;这里我选择的是14.21.3版本 二、安装 1、下载完成后&#xff0c;双击“node-v14.21.3-x64.msi”&#xff0c;开始安装Node.js 2、勾选复…

【电机-概述及分类】

文章目录 第1章1-1 电机的定义1-2 电机的构成要素1-3 电机的分类1-3-1 直流电机1-3-1-1 永磁励磁型直流电机1-3-1-2 电磁铁励磁型直流电机 第1章 重新认识电机的体系 电机包括许多种类。换个角度来看&#xff0c;并没有完美的电机&#xff0c;某种电机具有所谓A的优点&#xf…

STM32F1+HAL库+FreeTOTS学习14——数值信号量

STM32F1HAL库FreeTOTS学习13——数值信号量 1. 数值信号量2. 相关API函数2.1 创建计数信号量2.2 获取信号量2.3 释放信号量2.4 删除信号量2.5 获取信号量的计数值 3. 操作实验1. 实验内容2. 代码实现&#xff1a;运行结果 上一期我们学习了二值信号量 &#xff0c;这一期学习计…

在线相亲系统:新时代的婚恋观与传统习俗的碰撞

随着互联网技术的发展&#xff0c;相亲交友平台已成为年轻人寻找伴侣的新方式。这些平台不仅改变了人们的社交习惯&#xff0c;也反映了当代婚恋观与传统习俗之间的碰撞与融合。开发h17711347205本文将探讨在线相亲系统是如何在尊重传统的基础上&#xff0c;为现代年轻人提供更…

21.1 k8s接口鉴权token认证和prometheus的实现

本节重点介绍 : k8s接口鉴权方式serviceaccount和token的关系手动curl访问metrics接口 k8s对象接口鉴权 以容器基础资源指标为例 对应就是访问node上的kubelet的/metrics/cadvisor接口&#xff0c;即访问https://nodeip:10250/metrics/cadvisor 直接curl访问 会报错&…

第一弹:llama.cpp编译

1.编译llama.cpp命令行&#xff08;电脑版本&#xff09;&#xff1b; 2.交叉编译安卓命令行版本。 一、Llama.cpp是什么&#xff1f; 二、Llama.cpp编译 首先我们尝试编译llama.cpp. 2.1 下载llama.cpp 项目的github地址&#xff1a; https://github.com/ggerganov/llama…

ubuntu18.04 NVIDIA驱动 CUDA cudnn Anaconda安装

1、安装NVIDIA驱动 a.查看推荐驱动 ubuntu-drivers devicesb.打开软件更新&#xff0c;选择相应的显卡 c.重启查看安装情况&#xff0c;输入nvidia-smi 2、安装CUDA 下载链接https://developer.nvidia.com/cuda-toolkit-archive 安装CUDA&#xff1a; sudo bash cuda_11…

完整网络模型训练(一)

文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集&#xff1a; #因为CIFAR10是属于PRL的数据集&#xff0c;所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…

前端工程规范-2:JS代码规范(Prettier + ESLint)

Prettier 和 ESLint 是两个在现代 JavaScript 开发中广泛使用的工具&#xff0c;它们结合起来可以提供以下作用和优势&#xff1a; 代码格式化和风格统一&#xff1a; Prettier 是一个代码格式化工具&#xff0c;能够自动化地处理代码的缩进、空格、换行等格式问题&#xff0c;…

【C++算法】8.双指针_三数之和

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; 15.三数之和 题目描述&#xff1a; 解法 解法一&#xff1a;排序暴力枚举利用set去重O(n3) 例如nums[-1&#xff0c;0&#xff0c;1&#xff0c;2&#xff0c;-1&…

【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧

文章目录 C模板进阶编程前言第一章: 非类型模板参数1.1 什么是非类型模板参数&#xff1f;1.1.1 非类型模板参数的定义 1.2 非类型模板参数的注意事项1.3 非类型模板参数的使用场景示例&#xff1a;静态数组的实现 第二章: 模板的特化2.1 什么是模板特化&#xff1f;2.1.1 模板…

基于单片机的催眠电路控制系统

** 文章目录 前言一 概要功能设计设计思路 软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主…

Apache DolphinScheduler-1.3.9源码分析(一)

引言 随着大数据的发展&#xff0c;任务调度系统成为了数据处理和管理中至关重要的部分。Apache DolphinScheduler 是一款优秀的开源分布式工作流调度平台&#xff0c;在大数据场景中得到广泛应用。 在本文中&#xff0c;我们将对 Apache DolphinScheduler 1.3.9 版本的源码进…

html+css(如何用css做出京东页面,静态版)

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>京东</title><link rel"stylesheet&q…

AR 眼镜之-蓝牙电话-来电铃声与系统音效

目录 &#x1f4c2; 前言 AR 眼镜系统版本 蓝牙电话 来电铃声 系统音效 1. &#x1f531; Android9 原生的来电铃声&#xff0c;走的哪个通道&#xff1f; 2. &#x1f4a0; Android9 原生的来电铃声&#xff0c;使用什么播放&#xff1f; 2.1 来电铃声创建准备 2.2 来…