【Linux】进程替换与shell的模拟实现

news2025/1/23 12:16:43

目录

一、进程替换

1.1 进程替换的概念

1.2 替换函数

二、命令行解释器-Shell

2.1 shell的实现与运行

2.2 步骤讲解


一、进程替换

1.1 进程替换的概念

当我们使用 fork 函数创建子进程后,父子进程各自执行父进程代码的一部分。那如果创建的子进程想要执行一个全新的程序呢?那我们便可以通过进程替换来实现该功能。

程序替换:是通过特定的接口,加载到磁盘上的一个权限的程序(代码和数据),加载到调用的进程的地址空间中。

子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新进程替换,从新程序的启动历程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

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

函数解释

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

命名理解

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

 事实上,只有 execve 是真正的系统调用,其它五个函数最终都调用 execve,所以 execve 在 man手册第2节,其他函数在第3节,这些函数之间的关系如下图所示:

首先我们来介绍 execl 函数。

其中第一个参数 path 为路径,我们需要传入路径+目标文件名;第二个参数 arg,是可变参数列表,我们可以向其中传入不定的参数;其中 arg 中的参数,我们在命令行上是如何执行代码的,就在其参数中如果传参,注意:最后一个参数必须是NULL,表示传参结束。

结果:(没执行程序结束处,转而执行了 ls 命令)

 

因为程序替换会替换掉当前调用替换接口的进程,所以这些程序替换接口的调用往往配合着进程创建(fork)一起使用。

为什么要创建子进程?

为了不影响父进程,我们想让父进程聚焦在读取数据,解析数据,指派进程执行代码的功能!如果不创建新的子进程,那么被替换的进程只能是当前运行的父进程;如果创建了,替换的进程就是子进程,而不影响父进程。

说了这么多,我们可以举一个配合使用的代码例子:

 运行结果:

 然后我们再来看看 execv 函数是如果调用的

该函数要求第二个参数传入指针数组,数组中存放着调用的命令以及选项。

运行结果:

接下来是 execlp 函数的使用

其中这个p表示,该函数会自己再环境变量PATH中进行查找,不用告知路径。

结果如下:

execvp 函数的使用:

第一个参数会自动在环境变量中进行查找,第二个参数传入指针数组即可。

关于执行 execle 函数

首先我们要先实现一个功能:使用C/C++程序,如果调用其他语言的可执行程序。

我们首先编写一个python脚本,然后使用进程替换接口进行调用:

结果如下: 

关于execle的第三个参数 envp[] ,即环境变量,应该如何传递呢?

第一个参数传入要执行文件的路径,第二个参数为可变参数列表,第三个参数为环境变量,可以是自己设置的,也可以是主函数中传入的。

代码如下(myproc、mycmd、makefile):

结果:

二、命令行解释器-Shell

学习了fork和程序替换的众多接口,我们便可以来实现一个Linux中的命令行解释器shell。

其运行原理就是通过让子进程执行命令,父进程等待&&解析指令。

2.1 shell的实现与运行

这里先把代码附上,然后我们来逐步讲解

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define NUM 1024
#define SIZE 32
#define SEP " "    
char cmd_line[NUM];
char* g_argv[SIZE];
int main()
{
    //0.命令行解释器,一定是一个常驻内存的进程,不退出
	while (1)
	{
        //1.打印出命令提示信息  [root@localhost myshell]#
		printf("[root@localhost myshell]#");
		fflush(stdout);       
		sleep(1);
		memset(cmd_line, '\0', sizeof(cmd_line));
        //2.获取用户的键盘输入[输入的为各种指令和选项 "ls -a -l "]
		if (fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)
		{
			continue;
		}
		cmd_line[strlen(cmd_line) - 1] = '\0';
		//3.命令行字符串解析:"ls -a -l "  -> "ls"  "-a"  "-l"
		g_argv[0] = strtok(cmd_line, SEP);
		//给ls加颜色
		if (strcmp(g_argv[0], "ls") == 0)
		{
			g_argv[index++] = "--color=auto";
		}
		while (g_argv[index - 1])
		{
			g_argv[index++] = strtok(NULL, SEP);
		}
        //4.父进程执行内置命令
		if (strcmp(g_argv[0], "cd") == 0)
		{
			if (g_argv[1] != NULL)chdir(g_argv[1]);
		}
        //5.fork(),子进程执行任务
		pid_t id = fork();
		if (id == 0)
		{
			printf("下面功能是子进程执行的:\n");
			execvp(g_argv[0], g_argv);
			exit(1);
		}
		int status = 0;
		pid_t ret = waitpid(id, &status, 0);
		if (ret > 0)
			printf("exit code:%d\n", WEXITSTATUS(status));
	}
	return 0;
}

我们来看看运行结果:

 好的,接下来我们来分析myshell。

2.2 步骤讲解

1. 打印命令提示符

在linux中,我们用户是这样输入的

 这种实现是基于printf'打印出用户和盘符后,调用了fflush立即刷新了缓冲区,然后我们在后面继续输入实现的,然后调用sleep函数让其打印稍显自然,不要立即弹出。

printf("[root@localhost myshell]#");
fflush(stdout);       //立即刷新缓冲区
sleep(1);

2.获取用户输入

我们将用户输入的数据存放到一个全局变量数组中,使用fgets函数进行接受(getline、gets也行),因为用户最有的输入会有一个\n,所以我们还要对该\n进行删除。

//2.获取用户的键盘输入[输入的为各种指令和选项 "ls -a -l "]
memset(cmd_line, '\0', sizeof(cmd_line));
if (fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)
{
	continue;
}
//清除用户输入的\n
cmd_line[strlen(cmd_line) - 1] = '\0';

3.命令行字符串解析

接受了字符串后我们就要对其进行解析,例如"ls -a -l "  -> "ls"  "-a"  "-l"。 

就是将其拆分为字串,然后存放到一个指针数组中。我们就可以使用 strtok 函数来切割。

其中SEP为" ",因为strtok第二个参数是以什么字符串进行切割,所以传入" "。

strtok函数第一次切割传入字符串,第二次以及后续切割直接传入NULL即可。

//3.命令行字符串解析:"ls -a -l "  -> "ls"  "-a"  "-l"
g_argv[0] = strtok(cmd_line, SEP);
//给ls加颜色
if (strcmp(g_argv[0], "ls") == 0)
{
	g_argv[index++] = "--color=auto";
}
while (g_argv[index - 1])
{
	g_argv[index++] = strtok(NULL, SEP);
}

4.内置命令

内置命令就是让父进程(shell)自己执行的命令,其本质就是shell中的一个函数调用。如果用户输入cd ..,就是改变当前 myshell 的路径,让子进程进行切换路径是无法让所在路径进行切换的,所以这里我们要进行额外的处理。

if (strcmp(g_argv[0], "cd") == 0)
{
	if (g_argv[1] != NULL)chdir(g_argv[1]);
}

其中 chdir 是一个系统调用,传入路径,就可以让当前进程进入该路径。

5.fork()子进程执行

接下来就是子进程执行的部分,我们可以直接使用fork创建出一个子进程,然后用 execvp 通过调用环境变量,因为输入的指令本来就以及被添置进了环境变量,然后我们传入我们切割好的指针数组,就可以很好的进行进程替换。

然后使用waitpid让父进程阻塞等待子进程执行完毕。

pid_t id = fork();
if (id == 0)
{
	printf("下面功能是子进程执行的:\n");
	execvp(g_argv[0], g_argv);
	exit(1);
}
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if (ret > 0)
printf("exit code:%d\n", WEXITSTATUS(status));

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UG/NX二次开发Siemens官方NXOPEN实例解析—2.4 File2Points

列文章目录 UG/NX二次开发Siemens官方NXOPEN实例解析—2.1 AssemblyViewer UG/NX二次开发Siemens官方NXOPEN实例解析—2.2 Selection UG/NX二次开发Siemens官方NXOPEN实例解析—2.3 Selection_UIStyler UG/NX二次开发Siemens官方NXOPEN实例解析—2.4 File2Points 前言 随着…

Python开发的编译神器PyCharm----测试从业来编写Python脚本最钟意的工具

目录 前言&#xff1a; 一、PyCharm简介 二、PyCharm下载与安装 1、下载 2、安装 三、PyCharm新增Python项目 步骤1、新增 步骤2、路径配置 步骤3、环境选择 步骤4、 项目运行 四、画圣诞树 前言&#xff1a; 本文将为大家介绍PyCharm下载安装与初步的使用&#xff0c;初…

外汇天眼:美国11月独栋房屋建设跌至两年半低点!

美国商务部周二公布了这份令人沮丧的报告&#xff0c;此前周一有消息称&#xff0c;12月份房屋建筑商信心连续第12个月出现创纪录的下滑。美国11月独栋房屋建设跌至两年半低点&#xff0c;未来建筑许可大幅下滑&#xff0c;因抵押贷款利率上升继续打压楼市活动。 具体数据显示&…

(十九)Vue之组件和模块概念

文章目录传统编程组件化编程模块模块化组件化Vue学习目录 上一篇&#xff1a;&#xff08;十八&#xff09;Vue之生命周期 传统编程 一个html引入大量的CSS、JS文件&#xff0c;使得结构混乱、代码复用率低 传统方式编写应用存在的问题&#xff1a; 1.依赖关系混乱、不好维护…

某讯滑块验证码反汇编分析-第三章

某讯滑块验证码反汇编分析-第三章collect明文处理vData明文处理collect明文处理 上一篇已经跟到明文的拼接函数&#xff0c;接下来看看get函数的返回值&#xff0c;对什么进行拼接。 在这一次运行中&#xff0c;第一次get返回的是一个10&#xff0c;是怎么计算出来的呢&#x…

[XCTF]red_green(难度2)

得到一个乱码的图片&#xff0c;查看信息&#xff0c;联系“red_green” 目录 前言 一、题目重述 二、解题思路 1.套路来一遍 2.过程中注意到以下两点&#xff1a; &#xff08;1)stegsolve图层中只有red/green&#xff0c;与题目暗合 &#xff08;2)zsteg查看隐写信息发…

springboot+jsp实验室管理系统fu1ju-

目录 第一章 绪论 5 1.1 研究背景 5 1.2系统研究现状 5 1.3 系统实现的功能 6 1.4系统实现的特点 6 1.5 本文的组织结构 6 第二章开发技术与环境配置 7 2.1 Java语言简介 7 2.2JSP技术 8 2.3 MySQL环境配置 8 2.4 MyEclipse环境配置 9 2.5 mysql数据库介绍 9 2.6 B/S架构 9 第三…

2022年度安徽省职业院校技能大赛中职组“网络搭建与应用”赛项竞赛试题

2022年度安徽省职业院校技能大赛 中职组“网络搭建与应用” 赛项竞赛 &#xff08;总分1000分&#xff09; 竞赛说明 一、竞赛内容分布 “网络搭建与应用”竞赛共分三个部分&#xff0c;其中&#xff1a; 第一部分&#xff1a;网络搭建及安全部署项目&#xff08;500分&…

SpringCloud-Feign远程调用

&#x1f341;Feign的介绍 Feign是一个声明式的http客户端&#xff0c;官方地址: https://github.com/0penFeign/feign作用就是帮助我们优雅的实现http请求的发送&#xff0c;解决上面提到的问题。 &#x1f341;定义和使用Feign客户端 1.引入依赖 在order-service服务的po…

【PyTorch Geometric】工具包安装部署 过程记录(图模型学习 需要安装的工具包);图神经网络 工具包安装过程记录

目录 部署过程下载别的版本试试部署过程 在常规的安装过程中,若直接通过pip,老师讲 大概率无法成功安装。比较正规的安装方法是,下载好工具包,手动安装。 首先通过 Jupyter Notebook打开配置文档: 有些步骤在该文档中。 点击上图中的超链接,进入对应的 GitHub。 进去…

Linux进程间通信(一):匿名管道的原理和使用

文章目录一、前言二、什么是匿名管道&#xff1f;三、匿名管道的原理三、匿名管道的创建四、匿名管道实现数据传输五、匿名管道实现进程控制六、匿名管道特点总结一、前言 &#xff08;在阅读本文前&#xff0c;需要具备Linux基础IO的基本知识&#xff09;  在某些特定情况下…

【Linux】Linux下基本指令(二)

作者&#xff1a;一个喜欢猫咪的的程序员 专栏&#xff1a;《Linux》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 目录 1. Linux基本指令&#xff1a;&#xff08;续&#xff09; 1.1man指令&am…

英国访问学者AV签证超期怎么办?

英国AV签证只给12个月整&#xff0c;关于 AV签证超期怎么办&#xff1f;知识人网访问学者老师和大家聊一聊&#xff1a; 一、呆不满一年(360天)会遇到的问题主要有&#xff1a; 1、无法申请留学回国科研启动基金。 2、需要向CSC返还一定的资助费用。 3、国内一些地方判断一个…

数字化转型如何认清本质少被忽悠:小步快跑看到项目效果再推下一步

这些年&#xff0c;我们见识了太多新概念&#xff1a;数据智能、Data Fabric、数据虚拟化还有最著名的“数据中台”。 然而&#xff0c;跟几年前疯狂追逐这些热词不同&#xff0c;或者说因为已经踩坑踩到晕厥。动辄一个千万的中台项目投入进去&#xff0c;上线后高层看数还是要…

【网络安全】Redis未授权访问查看敏感文件

前言 什么是未授权访问漏洞呢&#xff1f;也就是没有权限&#xff0c;但是可以访问站点或者服务器的漏洞&#xff0c;称之为未授权访问漏洞&#xff0c;这个属于高危漏洞。比如我们常见的路由器未授权访问等等。 一、准备阶段 service httpd.service start 开启apache服务se…

【文档编辑】积累

文章目录WordExcel填充表格样式函数Markdownsublime搜索jsonhexWord Word 2016 撰写论文(1): 公式居中、编号右对齐 Excel 填充 EXCEL excel中运用ctrlD、ctrlenter、ctrlE批量填充数据 表格样式 Excel技法&#xff1a;三招实行Excel隔行填色 函数 COUNTA 函数 step1&…

最小生成树算法:Kruskal 与 Prim算法

Ⅰ. 最小生成树 连通图中的每一棵生成树&#xff0c;都是原图的一个极大无环子图&#xff0c;即&#xff1a;从其中删去任何一条边&#xff0c;生成树就不再连通&#xff1b;反之&#xff0c;在其中引入任何一条新边&#xff0c;都会形成一条回路。 若连通图由 n 个顶点组成&…

向前主动防御 | 云蜜罐年底感恩回馈活动

安全事件频发&#xff0c;防御能力备受考验2022年接近尾声&#xff0c;回顾全年&#xff0c;数据泄露、网络攻击、漏洞发现等各个层面呈爆发态势&#xff0c;无论在数量还是影响面上&#xff0c;均超过以往任何年度。信息泄露创历史新高&#xff0c;2022年仅上半年泄露或被盗的…

计算机组成原理——期末复习题

113、计算机系统如何进行多级划分&#xff1f;这种分级观点对计算机设计会产生什么影响&#xff1f;答案&#xff1a;计算机系统通常由五个以上不用的级组成&#xff0c;具体如下&#xff1a; 第1级是微程序设计级或逻辑电路级&#xff0c;该级由硬件直接执行&#xff1b; 第…

异步代码处理

在Spring中&#xff0c;实现异步调用主要有三种方式&#xff1a; 方式一&#xff1a;注解方式 要开启异步支持&#xff0c;首先得在Spring Boot入口类上加上EnableAsync注解&#xff1a; SpringBootApplication EnableAsync public class DemoApplication {public static voi…