Linux一学就会——编写自己的shell

编写自己的shell

进程程序替换

替换原理

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

在这里插入图片描述

替换函数

其实有几种以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[]);

解释
exec是函数替换的开头,后面跟的都是多加的功能:
l:list的简写,表示参数采用列表。
p:path的简写,就是自动搜索并添加环境变量。可以使用环境变量PATH,无需写全路径。
v:vector的简写,是可以用参数数组。
e:environment的简写,就是环境变量。就是带e都要自己组装环境变量,而且是数组形式传入。

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

在这里插入图片描述
可变参数
我们刚刚可以看到int execl(const char *path, const char *arg, …);
比如:

int func(int, ... )  {
   .
   .
   .
}
 
int main() {
   func(2, 2, 3);
   func(3, 2, 3, 4);
}

函数 func() 最后一个参数写成省略号,即三个点号(…),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
使用宏 va_end() 来清理赋予 va_list 变量的内存。

也就是说可变参数是放在传入参数最后,放在中间必须在输入结束之后再输入一个NULL,而且可变参数和前面放的参数类型一致。
exec调用举例:

#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}

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

在这里插入图片描述

开始写自己的shell

用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左
向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结
束。在这里插入图片描述
每当输入一个命令时,bash就会创建一个子进程来实现的要的命令进程,上述就是ls,等待子进程退出,主进程继续等待命令输入和读取命令,再创建子进程等…

第一步创建一个界面然后让他一直死循环

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include  <ctype.h>

#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{
    memset(command, 0x00, MAX_CMD);
    printf("[Tomshell$]#");
    fflush(stdout);
    if (scanf("%[^\n]%*c", command) == 0)
    {
        getchar();
        return -1;
    }
    return 0;
}
int main(int argc, char *argv[])
{
    while (1) // shell主循环
    {
        myshell_face()}
    return 0;
}

当然这个界面是可以输入命令的,但是你怎么输入都没用。

接下来是解析你输入的命令了。
把刚刚输入的命令行分析出来,比如遇到空格就会再次push_back命令行数组,当有空格就跳过空格,知道遇到NULL为止。

char **do_parse(char *command)
{
    int argc = 0;
    static char *argv[32];
    char *ptr = command;
    while (*ptr != '\0')
    {
        if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格
        {
            argv[argc++] = ptr;
            while ((!isspace(*ptr)) && (*ptr) != '\0')//#include  <ctype.h>   isspace检测是否遇到空格
            {
                ptr++;              //如果不是空格就一直读取命令,直到遇到空格
            }
        }
        else
        {
            while (isspace(*ptr))//如果命令前几个是空格就消除空格
            {
                //*ptr = '\0';//这句就不用加了
                ptr++;
            }
        }
    }
    argv[argc] = NULL;
    return argv;
}

解析完之后返回的是命令行参数数组指针

开始创建子进程并且用execvp替换子进程。

int do_exec(char *command)//进程替换函数=》用的就是exec
{
    char **argv = {NULL};
    int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。
    if (pid == 0)
    {
        argv = do_parse(command);
        if (argv[0] == NULL)
        {
            exit(-1);
        }
        execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)
    }                          // 可以把exec当作call(goto)函数,exit当作return函数。
    else
    {
        waitpid(pid, NULL, 0);
    }
    return 0;
}

这样就可以在子进程实现命令行进程了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include  <ctype.h>

#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{
    memset(command, 0x00, MAX_CMD);
    printf("[Tomshell$]#");
    fflush(stdout);
    if (scanf("%[^\n]%*c", command) == 0)
    {
        getchar();
        return -1;
    }
    return 0;
}
char **do_parse(char *command)
{
    int argc = 0;
    static char *argv[32];
    char *ptr = command;
    while (*ptr != '\0')
    {
        if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格
        {
            argv[argc++] = ptr;
            while ((!isspace(*ptr)) && (*ptr) != '\0')//#include  <ctype.h>   isspace检测是否遇到空格
            {
                ptr++;              //如果不是空格就一直读取命令,直到遇到空格
            }
        }
        else
        {
            while (isspace(*ptr))//如果命令前几个是空格就消除空格
            {
                //*ptr = '\0';//这句就不用加了
                ptr++;
            }
        }
    }
    argv[argc] = NULL;
    return argv;
}
int do_exec(char *command)//进程替换函数=》用的就是exec
{
    char **argv = {NULL};
    int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。
    if (pid == 0)
    {
        argv = do_parse(command);
        if (argv[0] == NULL)
        {
            exit(-1);
        }
        execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)
    }                          // 可以把exec当作call(goto)函数,exit当作return函数。
    else
    {
        waitpid(pid, NULL, 0);
    }
    return 0;
}
int main(int argc, char *argv[])
{
    while (1) // shell主循环
    {
        if (myshell_face() < 0)
            continue;
        do_exec(command);
    }
    return 0;
}

最终成果:
在这里插入图片描述

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

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

相关文章

视觉震撼的数据可视化示例

众所周知&#xff0c;数据可以非常强大——当你真正理解它告诉你什么时。 数据和信息可视化(数据可视化或信息可视化)是对大量复杂的定量、定性数据、信息进行设计和创建易于沟通、易于理解的图形或视觉表示的实践&#xff0c;在静态、动态或交互式视觉项目的帮助下&#xff0…

存储网络架构——DAS、NAS、SAN、分布式组网架构

目录 DAS直连式存储 NAS网络附加存储 SAN存储 存储区域网络 分布式存储组网 DAS直连式存储 DAS遇到的挑战 NAS网络附加存储 向主机提供文件服务&#xff1b;文件系统由存储设备维护&#xff0c;用户访问文件系统&#xff0c;不直接访问底层存储 拥有所有主机上文件与底层存储空…

JS案例分析-某国际音x-tt-params参数分析

今天我们要分析的网站是&#xff1a;https://www.tiktok.com/selenagomez?langen&#xff0c;参数名字叫x-tt-params。 先来抓个包 这个接口是用户视频列表url&#xff0c;参数叫x-tt-params&#xff0c;该接口中还有其他参数像msToken&#xff0c;X-Bogus&#xff0c; _sig…

【51单片机】点亮一个LED灯(看开发板原理图十分重要)

&#x1f38a;专栏【51单片机】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【The Right Path】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 目录 &#x1f354;基础内容 &#x1f3f3…

项目集的定义及管理

一、什么是项目集 项目集是相互关联且被协调管理的项目、子项目集和项目集活动&#xff0c;以便获得分别管理所无法获 得的效益。 以项目集的形式管理项目、子项目集及项目集活动能确保项目集组件的战略和工作计划根据各组 件的成果做出相应调整&#xff0c;或者按照发起组织的…

洞车系统常见问题解决指南

洞车常见问题解决指南 1.研发脚本处理问题1.1 WMS出库单无法审核1.2 OMS入库单无法提交&#xff0c;提示更新中心库存失败1.3 当出现OMS下发成功WMS/TMS/DMS还没有任务的情况时处理方案1.4 调度波次生成或者添加任务系统异常1.5 东鹏出库单部分出库回传之后要求重传1.6 更新订单…

不会前端,怎么快速打造属于自己的个人博客?

个人博客 简介提前准备 一、初始化vuepress项目二、页面配置首页配置顶部配置顶部导航栏路由配置侧边导航栏配置 三、打包部署四、数据统计插槽自定义插槽配置整体结构页面效果 项目地址 简介 主要教大家如何快速搞一个属于自己的博客网站&#xff0c;特别是一些不怎么会前端的…

信息安全保障

文章目录 信息安全保障基础基本概念信息安全定义广义和狭义的信息安全问题信息安全问题的根源和特征情报威胁和态势感知信息安全保障基础信息安全属性信息安全视角 信息安全发展阶段通信安全计算机安全信息系统安全信息安全保障网络安全空间 信息安全保障新领域工业控制系统(IS…

【虹科案例】使用 TCP 分析测量握手时间

如何使用 Allegro Network 万用表的 TCP 分析确定握手时间 握手需要多少时间&#xff1f; 在图 1 中&#xff0c;您可以在虹科Allegro 网络万用表的 TCP 统计数据中看到过去 10 分钟的客户端握手次数。在这里&#xff0c;您可以清楚地看到在指定时间段内有延长的响应时间。但…

ChatGPT探索系列之五:讨论人工智能伦理问题及ChatGPT的责任

文章目录 前言一、安全二、隐私和道德三、我们应该做什么总结 前言 ChatGPT发展到目前&#xff0c;其实网上已经有大量资料了&#xff0c;博主做个收口&#xff0c;会出一个ChatGPT探索系列的文章&#xff0c;帮助大家深入了解ChatGPT的。整个系列文章会按照一下目标来完成&am…

给定一个文本文件,每行是一条股票信息,写程序提取出所有的股票代码

问题&#xff1a;给定一个文本文件&#xff0c;每行是一条股票信息&#xff0c;写程序提取出所有的股票代码。其中&#xff0c;股票代码规则是&#xff1a;6 位数字&#xff0c; 而且以.SH 或者.SZ 结尾。 文件内容示例&#xff1a; 2020-08-08;平安银行(000001.SZ);15.55;2940…

如何用ChatGPT做品牌联名方案策划?

该场景对应的关键词库&#xff08;15个&#xff09;&#xff1a; 品牌、个人IP、社交话题、联名策划方案、调研分析、市场影响力、资源互补性、产品体验、传播话题、视觉形象设计、合作职权分配、销售转化、曝光目标、宣发渠道、品牌形象 提问模板&#xff08;1个&#xff09;…

kubernetes项目部署

目录 ​一、容器交付流程 二、k8s平台部署项目流程 三、在K8s平台部署项目 一、容器交付流程 容器交付流程通常分为四个阶&#xff1a;开发阶段、持续集成阶段、应用部署阶段和运维阶段。 开发阶段&#xff1a;开发应用程序&#xff0c;编写Dockerfile; 持续集成阶段&#…

很佩服的一个Google大佬,离职了。。

这两天&#xff0c;科技圈又有一个突发的爆款新闻相信不少同学都已经看到了。 那就是75岁的计算机科学家Geoffrey Hinton从谷歌离职了&#xff0c;从而引起了科技界的广泛关注和讨论。 而Hinton自己也证实了这一消息。 提到Geoffrey Hinton这个名字&#xff0c;对于一些了解过…

Spring Cloud学习笔记【分布式配置中心-Config】

文章目录 SpringCloud Config概述概述传统方式弊端主要功能与GitHub整合配置 Config服务端配置与测试服务端配置(即Gitee上的配置文件)Config Demo配置Spring Cloud Config访问规则 Config客户端配置与测试bootstrap.yml说明Config客户端 Demo配置 SpringCloud Config概述 概述…

无需公网IP 使用SSH远程连接Linux CentOS服务器【内网穿透】

文章目录 视频教程1. Linux CentOS安装cpolar2. 创建TCP隧道3. 随机地址公网远程连接4. 固定TCP地址5. 使用固定公网TCP地址SSH远程 本次教程我们来实现如何在外公网环境下&#xff0c;SSH远程连接家里/公司的Linux CentOS服务器&#xff0c;无需公网IP&#xff0c;也不需要设置…

深入理解Java虚拟机——垃圾收集器

1.前言 在前面我们已经说过了垃圾收集算法&#xff0c;那么现在我们要讲的垃圾收集器&#xff0c;实际上就是对垃圾收集算法的实践。 首先我们先看一张图&#xff0c;这张图可以帮助我们了解各款经典垃圾收集器之间的关系&#xff1a; 图中的垃圾收集器所在的区域代表了它是属…

【三十天精通Vue 3】第二十六天 Vue3 与 TypeScript 最佳实践

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: 三十天精通 Vue 3 文章目录 引言一、为什么使用TypeScript?二、Vue 3和TypeScript的基础2.1 安装TypeScript2.2 配置TypeScript2.3 Vue 3中使用TypeScript

Java多线程基础概述

简述多线程&#xff1a; 是指从软件或者硬件上实现多个线程并发执行的技术。 具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程&#xff0c;提升性能。 正式着手代码前&#xff0c;需要先理清4个概念&#xff1a;并发&#xff0c;并行&#xff0c;进程&#…

TinyJAMBU的制动原理——一种轻量化的认证密码

关于TinyJAMBU的定义和介绍在另一篇博文已经介绍过了&#xff0c;这里只对其动作原理进行描述和说明。 对应的博文链接如下&#xff1a;TinyJAMBU&#xff1a;一种轻量化密码介绍 首先&#xff0c;该密码是一个流密码体系的块密码框架。其加密模式整体上来看是块密码&#xff0…
最新文章