Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】进程替换

【Linux】进程替换

作者头像
秦jh
发布于 2024-10-01 00:00:43
发布于 2024-10-01 00:00:43
18400
代码可运行
举报
文章被收录于专栏:c语言,c++c语言,c++
运行总次数:0
代码可运行

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

前言

💬 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命令,所以替换会失败。如果替换成功,就不会向后继续运行。所以只要继续运行了,就一定是替换失败了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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   //father
 21   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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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       NULL
 18     };
 19 
 20     char* const envp[]=
 21     {
 22       (char*)"HAHA=1111",
 23       (char*)"HEHE=2222",
 24       NULL
 25     };
 26     printf("child pid:%d\n",getpid());
 27     sleep(2);
 28     execvpe("./mypragma",argv,envp);
 43     exit(1);
 44   }
 46   //father
 47   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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  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 512
 10 #define ZERO '\0'
 11 #define SEP " "
 12 #define NUM 32
 13 #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 : output
 54 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         // child
 92         execvp(gArgv[0], gArgv);
 93         exit(errno);
 94     }
 95     else
 96     {
 97         // fahter
 98         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 }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Linux】进程控制
在 Linux 中 fork 函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。我们在前面的学习中也遇到过,所以在此简单介绍一下。
YoungMLet
2024/03/01
2030
【Linux】进程控制
【linux】自主shell编写
现在完成对命令行输出的编写,我们目标是将变量名放到一个输出型参数commandline中,这里需要一个函数snprintf:
用户11029103
2025/02/25
2640
【linux】自主shell编写
【linux】进程等待与进程替换
任何子进程,在退出的情况下,一般必须要被父进程进行等待。进程在退出的时候,如果父进程不管不顾,退出进程,状态Z(僵尸状态),内存泄漏
用户11029103
2024/11/16
2290
【linux】进程等待与进程替换
【Linux系统编程】八、进程程序替换
​ 将磁盘中指定的程序加载到内存中,让指定的进程进行执行。不论是哪种后端语言写的程序,exec* 类的函数都可以调用。
利刃大大
2025/02/23
730
【Linux系统编程】八、进程程序替换
【Linux】进程实践项目 —— 自主shell编写
不管前方的路有多苦,只要走的方向正确,不管多么崎岖不平,都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》
叫我龙翔
2024/04/02
1610
【Linux】进程实践项目 —— 自主shell编写
【Linux】开始学习进程替换吧!
送给大家一句话: 人生中有些事,你不竭尽所能去做,你永远不知道你自己有多出色。—— 尾田荣一郎《海贼王》
叫我龙翔
2024/03/30
1430
【Linux】开始学习进程替换吧!
【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
前言:在Linux的浩瀚宇宙中,Shell脚本无疑是连接用户与系统之间的桥梁,它赋予了用户强大的自动化处理能力,使得繁琐的重复性任务变得轻松高效。对于每一位Linux爱好者、系统管理员或是开发人员而言,掌握Shell脚本编写技能无疑是一项不可或缺的宝贵财富
Eternity._
2024/09/21
2320
【Linux进程】Linux Shell编程实战:构建简易脚本示例与技巧详解
Linux探秘坊-------9.进程控制
当进程调⽤⼀种exec函数时,该进程的 ⽤⼾空间代码和数据完全被新程序替换 ,从新程序的启动例程开始执⾏。调⽤exec并不创建新进程,所以调⽤exec前后该进程的 id并未改变。
hope kc
2025/03/22
730
Linux探秘坊-------9.进程控制
初识Linux · 自主Shell编写
本文介绍是自主Shell编写,对于shell,即外壳解释程序,我们目前接触到的命令行解释器,有bash,还有SSH,对于今天模拟实现的Shell编写,我们模拟的是bash,以及需要的预备知识前文已经介绍了,进程的多方面的知识,在自主Shell编写里面比较重要的是进程程序替换,进程终止,进程等待,进程状态什么的,都是自主Shell编写里面的辅助知识罢了。
_lazy
2024/10/16
1300
初识Linux · 自主Shell编写
【Linux】进程控制,手搓简洁版shell
数据在默认不修改的情况下是共享的,不各自拷贝一份是因为父子进程间的数据大部分是重复的,一般只有少量数据需要修改,因为各自拷贝一份浪费空间。
_小羊_
2024/12/20
1040
【Linux】进程控制,手搓简洁版shell
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
在linux中fork函数时非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程
用户10925563
2024/06/04
8260
【Linux】Linux进程控制>进程创建&&进程终止&&进程等待&&进程程序替换
【Linux系统】探索进程等待与程序替换的奥秘
在 Linux 操作系统的世界里,进程是程序运行的动态实体,它们如同一个个忙碌的工作者,承载着系统中各种任务的执行。无论是系统服务的稳定运行,还是用户程序的交互响应,都离不开进程的支持。深入理解进程的生命周期,包括创建、终止、等待以及程序替换等关键环节,对于掌握 Linux 系统编程和开发高性能应用程序至关重要。
suye
2025/05/07
1110
【Linux系统】探索进程等待与程序替换的奥秘
Linux进程控制【进程程序替换】
子进程 在被创建后,共享的是 父进程 的代码,如果想实现自己的逻辑就需要再额外编写代码,为了能让 子进程 执行其他任务,可以把当前 子进程 的程序替换为目标程序,此时需要用到 Linux 进程程序替换相关知识
北 海
2023/07/01
3140
Linux进程控制【进程程序替换】
Linux:进程控制(二.详细讲解进程程序替换)
进程程序替换是指在运行过程中将一个进程的地址空间中的代码、数据和堆栈等内容完全替换为另一个程序的代码、数据和堆栈的过程。这个过程通常是由操作系统提供的 exec 系列函数来实现的:
是Nero哦
2024/05/30
2720
Linux:进程控制(二.详细讲解进程程序替换)
【Linux】进程控制:理解什么是进程创建,进程终止,进程等待 | 进程替换
可以发现,错误码为0时,代表代码正常执行完毕,所以我们平时主函数里的return 都是return 0
aosei
2024/01/23
4330
【Linux】进程控制:理解什么是进程创建,进程终止,进程等待 | 进程替换
Linux-程序替换
  在Linux系统中,进程程序替换是一种重要的操作,通过进程程序替换,程序可以更新自己的代码和数据,让进程富有动态性和灵活性,话不多说,开始今天的话题。
用户11029129
2024/06/04
2140
Linux-程序替换
【Linux程序设计】之进程控制&守护进程
这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的。
马三小伙儿
2018/09/12
2.1K0
Linux:进程替换
        为什么要有进程替换呢???比方说我们想用fork创建一个子进程去帮助我们完成一个工作,这个工作我们需要封装成一个函数去使用,但难道我们每次都要自己写一个函数吗?或者说子进程一定要用我父进程的代码吗?  难道不可以是我们之前已经写好的一个可执行程序,当我想去执行的时候直接让子进程用一下不就可以了,但是因为操作系统不相信任何人,所以我们也必须要有一些系统调用接口来完成这个工作。
小陈在拼命
2024/10/15
3580
Linux:进程替换
【Linux】进程控制&实现自主shell
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
小志biubiu
2025/04/01
1160
【Linux】进程控制&实现自主shell
【Linux】进程理解与学习-程序替换
进程可以通过程序替换的方式来执行一个全新的程序,具体的做法则是通过对应的程序替换的几个系统调用函数来实现,下面先来看一下程序替换的现象,根据这个现象来分析程序替换实现的原理。
诺诺的包包
2023/04/04
1.1K0
【Linux】进程理解与学习-程序替换
推荐阅读
相关推荐
【Linux】进程控制
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档