Linux进程控制(五)之做一个简易的shell
文章目录
- 做一个简易的shell
- 预备知识
- 代码实现
- 运行结果
做一个简易的shell
重谈Shell
shell是操作系统的一层外壳程序,帮我们用户执行指令,
获取到指令后,交给操作系统,操作系统执行完后,把执行结果通过shell交给用户。
shell大部分执行命令时,要创建子进程(fork)。
shell/bash本身也是一个进程,执行指令的时候,本质就是自己创建子进程执行的。
考虑下面这个与shell典型的互动:
[root@localhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[root@localhost epoll]# psPID TTY TIME CMD3451 pts/0 00:00:00 bash3514 pts/0 00:00:00 ps
用下图的时间轴来表示事件的发生次序。
其中时间从左向右。
shell由标识为sh的方块代表,它随着时间的流逝从左向右移动。
shell从用户读入字符串"ls"。
shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。
然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。
所以要写一个shell,需要循环以下过程:
-
获取命令行
-
解析命令行
-
建立一个子进程(fork)
-
替换子进程(execvp)
-
父进程等待子进程退出(wait)
根据这些思路,和我们前面的学的技术,就可以自己来实现一个shell了。
在继续学习新知识前,我们来思考函数和进程之间的相似性
exec/exit就像call/return
一个C程序有很多函数组成。
一个函数可以调用另外一个函数,同时传递给它一些参数。
被调用的函数执行一定的操作,然后返回一个值。
每个函数都有他的局部变量,不同的函数通过call/return系统进行通信。
这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。
Linux鼓励将这种应用于程序之内的模式扩展到程序之间。
一个C程序可以fork/exec另一个程序,并传给它一些参数。
这个被调用的程序执行一定的操作,然后通过exit(n)来返回值。
调用它的进程可以通过wait(&ret)来获取exit的返回值。
预备知识
命令函的本质就是字符串。先输出后输入。
主机名、用户名和路径都是可以通过系统调用获取的。
但是,我们也可以使用环境变量获得。使用getenv()。
读取一行
Linux系统会打开三个输入输出流(所以我们可以从stdin里面读取输入)
分割字符串strtok
子进程执行普通命令exec*,因为有argv,所以带v、带p。
编写cd指令时,需要用到chdir和getcwd。
chdir是改变当前的工作目录
getcwd是获取当前的工作目录
把一个字符串格式化到指定处
导入环境变量的时候要注意的问题!
代码实现
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#include <assert.h>#include <string.h>#include <sys/types.h>#define LEFT "["#define RIGHT "]"#define LALEL "$"#define DELIM " \t"#define LINE_SIZE 1024#define ARGC_MAX 32#define EXIT_CODE 33int last_code=0;int quit=0;char commandline[LINE_SIZE];char* argv[ARGC_MAX];char pwd[LINE_SIZE];//在shell需要维护//自定义环境变量表//自定义本地变量表(一段缓冲区)char myenv[LINE_SIZE];//也可以malloc空间//或者弄成一个二维数组const char*getusername(){return getenv("USER");}const char*gethostname(){return getenv("HOSTNAME");}void getpwd(){getcwd(pwd,sizeof(pwd));}void interact(char*cline,int size){getpwd();printf(LEFT"%s@%s %s"RIGHT""LALEL" ",getusername(),gethostname(),pwd);//输入最好不要用scanf,因为它是以空格结尾//fgets可以读取一行,读取成功就为读取字符串的地址,失败返回NULLchar*s=fgets(cline,size,stdin);//sizeof是否需要-1呢//不需要,-1是防御性编程,防止\0不够放了。后面要用到系统级别的io就要考虑是否-1//fgets不可能为null,不输入,按了回车之后,也算是有字符。//输入了命令,一敲回车就会换行,但是我们不想要换行。assert(s!=NULL);//assert在debug的情况下起效果(void)s;//抵消编译器的报警//"abcd\n\0"cline[strlen(cline)-1]='\0';}int splitstring(char*_argv[],char*cline){int i=0;_argv[i++]=strtok(cline,DELIM);while(_argv[i++]=strtok(NULL,DELIM));return i-1;}void normalexcute(char*_argv[]){pid_t id=fork();if(id<0){perror("fork fail!");return;}else if(id==0){execvp(_argv[0],_argv);exit(EXIT_CODE);//替换失败我们就可以拿到子进程的退出信息。//cd echo等内建命令不能由子进程执行//子进程cd 与父进程无关!}else{int status=0;pid_t rid=waitpid(id,&status,0);if(rid==id){last_code=WEXITSTATUS(status); }}}int buildcommand(char*_argv[],int _argc){if(_argc==2&&strcmp(_argv[0],"cd")==0){chdir(_argv[1]);getpwd();sprintf(getenv("PWD"),"%s",pwd);return 1;}//export也是内建命令//如果让子进程执行export,那么子进程的环境变量会添加,//但是与父进程无关,export导环境变量是给调用的进程导入。 else if(_argc==2&&strcmp(_argv[0],"export")==0){strcpy(myenv,_argv[1]);putenv(myenv);//只是修改了一个环境变量表的指针,让该指针指向了环境变量。//但是环境变量是在argv里面的,所以,下一次argv的就会改变,//所以环境变量指针指向的内容也会改变//没有直接拷贝到系统的环境变量表中//而是把argv里面的信息导入了,argv指向的内容一直都会变化。//所以我们需要一个不变的区域。return 1;}else if(_argc==2&&strcmp(_argv[0],"echo")==0){if(strcmp(_argv[1],"$?")==0){printf("%d\n",last_code);last_code=0;}else if(*_argv[1]=='$'){char*val=getenv(_argv[1]+1);if(val) printf("%s\n",val);}else{printf("%s\n",_argv[1]);}return 1;}//给文件加颜色!if(strcmp(_argv[0],"ls")==0){_argv[_argc++]="--color";_argv[_argc]=NULL;}return 0;}int main(){while(!quit){//1.//2.交互问题,获取命令行interact(commandline,sizeof(commandline));//printf("echo :%s\n",commandline);//字符串分割 要有每个字串的地址 "ls -a -l" -> "ls" "-a" "-l"//保存在agrv(字符串数组里)//分割字串strtok 调用一次只能切割一个子串//3.字符串分割问题,解析命令行int argc=splitstring(argv,commandline);if(argc==0)continue;// for(int i=0;argv[i];i++)// {// printf("[%d]:%s\n",i,argv[i]);// }//4.指令的判断//判断是否是内建命令,如果是就要让父进程执行相应的指令//内建命令本质就是shell内部的一个函数int n=buildcommand(argv,argc);//5.普通命令的执行if(!n)normalexcute(argv);}return 0;}
运行结果
结论:
所以,当我们进行登陆的时候,系统就是要启动一个shell进程
我们shell本身的环境变量是从哪里来的?
.bash_profile
.bashrc
/etc/bashrc
我们所用的环境变量都是从配置文件读的。
当用户登陆的时候,shell会读取用户目录下的 .bash_profile 文件,里面保存了导入环境变量的方式!
shell的环境变量最终是从环境里来的。
相关文章:
Linux进程控制(五)之做一个简易的shell
文章目录 做一个简易的shell预备知识代码实现运行结果 做一个简易的shell 重谈Shell shell是操作系统的一层外壳程序,帮我们用户执行指令, 获取到指令后,交给操作系统,操作系统执行完后,把执行结果通过shell交给用户…...
Apache Kafka全栈技术解析
目录 第一章 Kafka概述与核心价值 1.1 消息队列的演进与Kafka的诞生 1.2 Kafka的核心应用场景 1.3 Kafka生态全景图 第二章 Kafka核心概念与架构解析 2.1 核心概念深度剖析 2.2 Kafka架构设计精要 第三章 Kafka环境搭建与配置 3.1 单机部署实战 3.2 集群部署最佳实践 …...
结合 Flink/Spark 进行 AI 大数据处理(实时数据 + AI 推理的应用场景)
随着企业对实时智能决策的需求日益增强,将 Flink / Spark 等流批计算框架 与 大模型推理能力相结合,正在成为 AI 工业化落地的重要实践路径。本篇文章将深入介绍如何将 AI 模型集成到大数据流处理系统中,实现实时感知、智能判断与自动反馈。 1. 为什么需要“实时数据 + AI 推…...
开发PDF时,如何比较 PDF 文件
在 PDF 论坛上,“如何比较 PDF 文件”是一个经常被提到的问题。在开始之前,重要的是要明确你想要比较的内容是什么。 不同的 PDF 文件可能看起来一样吗? 是的,可能。不同的 PDF 创建工具可能会生成在视觉上完全相同的页面&#x…...
自动提取pdf公式 ➕ 输出 LaTeX
# 创建打包脚本的主内容 script_content """ from doc2x.extract_formula import extract_formula_imgs from pix2text import Pix2Text from PIL import Image import osdef main():pdf_path "your_file.pdf" # 将你的PDF命名为 your_file.pdf 并…...
abaqus二次开发python程序集
abaqus二次开发python程序集 1、设置字体背景色等2、读取模态频率并写入 csv 文件3、在两个窗口快速对比各价模态 1、设置字体背景色等 # _*_ coding:UTF-8 _*_from abaqusConstants import* def fontsize(sessionNone):#设置字体session.viewports[Viewport: 1].viewportAnno…...
高级java每日一道面试题-2025年3月23日-微服务篇[Nacos篇]-如何使用Nacos进行服务发现?
如果有遗漏,评论区告诉我进行补充 面试官: 如何使用Nacos进行服务发现? 我回答: 在Java高级面试中讨论如何使用Nacos进行服务发现时,可以从多个角度深入探讨,包括基本概念、配置步骤、代码示例以及高级特性。以下是综合了多种信息的详细回…...
k8s核心资源对象一(入门到精通)
本文将深入探讨Kubernetes中的核心资源对象,包括Pod、Deployment、Service、Ingress、ConfigMap和Secret,详细解析其概念、功能以及实际应用场景,帮助读者全面掌握这些关键组件的使用方法。 一、pod 1 pod概念 k8s最小调度单元,…...
了解 DeepSeek R1
了解DeepSeek R1 R1探索纯强化学习是否可以在没有监督微调的情况下学会推理的能力。 ‘Aha’ Moment 这种现象有点类似于人类在解决问题时突然意识到的方式,以下是它的工作原理: 初始尝试:模型对解决问题进行初始尝试识别:识别…...
【C语言】大小端字节序和字节序判断
前言: 在上章介绍了整形在内存的储存,了解了原码,反码,补码,知道了整数在内存的储存一般是补码,解决了负数相加的问题。 那么在本章为大家讲解一下大小端字节序。 一那字节序是什么呢? 字节…...
DrissionPage移动端自动化:从H5到原生App的跨界测试
一、移动端自动化测试的挑战与机遇 移动端测试面临多维度挑战: 设备碎片化:Android/iOS版本、屏幕分辨率差异 混合应用架构:H5页面与原生组件的深度耦合 交互复杂性:多点触控、手势操作、传感器模拟 性能监控:内存…...
ARM 汇编启动代码详解:从中断向量表到中断处理
ARM 汇编启动代码详解:从中断向量表到中断处理 引言 在嵌入式系统开发中,ARM 处理器(如 Cortex-A 系列)的启动代码是系统初始化和运行的基础。启动代码通常包括中断向量表的创建、初始化硬件状态(如关闭缓存和 MMU&a…...
笔试专题(七)
文章目录 乒乓球筐(哈希)题解代码 组队竞赛题解代码 删除相邻数字的最大分数(线性dp)题解代码 乒乓球筐(哈希) 题目链接 题解 1. 两个哈希表 先统计第一个字符串中的字符个数,再统计第二个字…...
React基础知识(一)
文章目录 概念特点React基本使用hello_react案例虚拟DOM的两种创建方式使用jsx创建使用js创建 虚拟DOM和真实DOM React jsxXMLjsx语法规则作用基本语法规则js语句和js代码babel.js作用 模块与组件模块组件 React面向组件编程函数式组件类组件 概念 react是一个将数据渲染为Htm…...
红黑树(Red-Black Tree)核心知识点与面试高频问题
红黑树(Red-Black Tree)核心知识点与面试高频问题 一、红黑树的核心性质 红黑树是一种自平衡的二叉搜索树,通过以下规则确保平衡性: 节点颜色:每个节点是红色或黑色。 根节点:根必须是黑色。 叶子节点&a…...
SpringBoot整合SSM
一、SpringBoot整合SSM SpringBoot整合SpringSpringBoot整合SpringMVCSpringBoot整合MyBatis(主要) 步骤一:创建SpringBoot工程,添加druid依赖 <!-- todo 1 添加druid连接池依赖--> <dependency><groupId>co…...
set/multiset容器
1.概念 所有元素会在插入时自动排序 set/multiset属于关联式容器,底层结构是用二叉树实现。 set不允许重复元素,multiset允许重复元素。 2. set构造和赋值 set<T> st; set(const set &st);// 拷贝构造函数 set& operator(const set &a…...
vim 编辑器 使用教程
Vim是一款强大的文本(代码)编辑器,它是由Bram Moolenaar于1991年开发完成。它的前身是Bill Joy开发的vi。名字的意义是Vi IMproved。 打开vim,直接在命令行输入vim即可,或者vim <filename>. Vim分为四种模式&a…...
去中心化固定利率协议
核心机制与分类 协议类型: 借贷协议(如Yield、Notional):通过零息债券模型(如fyDai、fCash)锁定固定利率。 收益聚合器(如Saffron、BarnBridge):通过风险分级或博弈论…...
Python高阶函数-filter
1. 基本概念 filter() 是Python内置的高阶函数,用于过滤序列中的元素。它接收一个函数和一个可迭代对象作为参数,返回一个迭代器,包含使函数返回True的所有元素。 filter(function, iterable)2. 工作原理 惰性计算:filter对象是…...
hive/doris查询表的创建和更新时间
hive查询表的创建和更新时间: SELECT d.NAME AS database_name, t.TBL_NAME AS table_name, FROM_UNIXTIME(t.CREATE_TIME) AS create_time, FROM_UNIXTIME(tp.PARAM_VALUE) AS last_ddl_time FROM metastore.TBLS t JOIN metastore.DBS d ON t.DB_ID d.DB_ID JOIN…...
40常用控件_WindowFrame的影响
window frame 的影响 如果 widget 作为一个窗口(带有标题栏,最小化,最大化,关闭按钮),那么在计算尺寸和坐标的 时候就有两种算法.包含 window frame 和 不包含 window frame. 其中x(),y0,frameGeometry(), pos(),move() 都是按照包含 window frame 的方式来计算 的. 其中 geome…...
PCB 赋能机器人技术革新:核心功能与前沿趋势
一、智能控制中枢的异构集成 采用 20 层刚挠结合板架构,搭载 NVIDIA Jetson AGX Orin SoC(100TOPS 算力),集成 64 位 ARMv8 内核与 32GB 内存,实现多模态传感器数据融合与实时决策。板载 128MB DDR4 缓存支持 μs 级响…...
unity 环形UI菜单实现方法2
在项目中需要一个环形UI并且循环往复的效果,这个方法思路为提前预设好位置,让UI根据坐标预设的移动,然后使用mask遮罩达到循环往复效果的目的。 下图分别分为了三个列表 第一个列表poslist是提前预设的位置 第二个列表为背景暂时不用看 第三个…...
Redis进阶--主从复制
目录 一、引言 二、介绍 三、解决问题 四、配置主从复制 1.复制 全量复制: 部分复制: 实时复制: 五、总结 一、引言 本篇文章将继续介绍Redis中的主从复制机制 二、介绍 主从复制是在分布式系统中实现的,希望有多个服务器…...
Redisson分布式锁:原理、使用
1. Redisson简介 Redisson是一个基于Redis的Java客户端库,提供了丰富的分布式对象和服务(如分布式锁、信号量、Map等)。其核心优势在于简化分布式锁的实现,并解决了原生Redis分布式锁的常见问题(如死锁、误删…...
Java设计模式之外观、享元、组合模式《三国争霸:模式风云录》
第一章:乱世起(外观初现) 黄巾余孽张角三兄弟操控"混沌子系统",各地流民不堪996劳役。观国隐士诸葛孔明出山,在博望坡构建首个"军师智脑": /*** 外观模式:军师智…...
设计模式之解释器模式:原理、实现与应用
引言 解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言的文法表示,并提供一个解释器来解释该语言中的句子。解释器模式适用于需要解析特定语法规则的场景,如正则表达式、SQL解析等。本文将深入…...
redis itheima
缓存问题 核心是如何避免大量请求到达数据库 缓存穿透 既不存在于 redis,也不存在于 mysql 的key,被重复请求 public Result queryById(Long id) {String key CACHE_SHOP_KEYid;// 1. redis & mysqlString shopJson stringRedisTemplate.opsFo…...
AF3 OpenFoldDataModule类setup方法解读
AlphaFold3 data_modules 模块的 OpenFoldDataLoader 类 setup 方法用于设置数据集的关键部分,负责根据不同的模式(训练、验证或预测)生成和初始化相应的数据集。 源代码: def setup(self, stage=None):# Most of the arguments are the same for the three datasets data…...
服务器报错:xxx/libc.so.6: version `GLIBC_2.32‘ not found
/lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.32 not found (required by ./aima-sim-app-main) 解决思路 根据错误信息,您的应用程序 aima-sim-app-main 和 libmujoco.so.3.1.6 库依赖于较新的 GNU C Library (glibc) 版本(如 GLIBC_2.32, GLIBC…...
FPGA状态机设计:流水灯实现、Modelsim仿真、HDLBits练习
一、状态机思想 1.概念 状态机(Finite State Machine, FSM)是计算机科学和工程领域中的一种抽象模型,用于描述系统在不同状态之间的转换逻辑。其核心思想是将复杂的行为拆解为有限的状态,并通过事件触发状态间的转移。 2.状态机…...
机试题——最少乘坐公交次数
题目描述 春节将近,小明想在节日期间逛一逛城里的 ( N ) 个著名景点。所有景点都能通过坐公交到达。需要设计一种公交路线方案,让小明能最快地逛完所有景点。 输入描述 第一行:一个整数 ( N ),表示景点数量,满足 ( …...
防孤岛保护装置在分布式光伏并网中的应用
什么是光伏的“孤岛效应” 孤岛islanding 包含负荷和电源的部分电网,从主网脱离后继续孤立运行的状态。孤岛可分为非计划性孤岛和计划性孤岛。 孤岛效应的危害 当电网侧停电检修,若并网光伏电站的逆变器仍在继续供电,维修人员不一定…...
记一次gitlab服务器负载过高问题处理
服务器上进程 /var/opt/gitlab/gitlab-rails/etc/unicorn.rb /opt/gitlab/embedded/service/gitlab-rails/config.ru 进程服务器cpu占用过高应该怎么处理 tail -f /var/log/gitlab/gitlab-rails/production.log调整 Unicorn 配置:unicorn.rb 是 Unicorn 服务器的配…...
LiT and Lean: Distilling Listwise Rerankers intoEncoder-Decoder Models
文章:ECIR 2025会议 一、动机 背景:利用LLMs强大的能力,将一个查询(query)和一组候选段落作为输入,整体考虑这些段落的相关性,并对它们进行排序。 先前的研究基础上进行扩展 [14,15],…...
【项目日记】高并发服务器项目总结
生活总是让我们遍体鳞伤, 但到后来, 那些受伤的地方一定会变成我们最强壮的地方。 -- 《老人与海》-- 高并发服务器项目总结 模块关系图项目工具模块缓冲区模块通用类型模块套接字socket模块信道Channel模块多路转接Poller模块 Reactor模块时间轮Tim…...
P1332 血色先锋队(BFS)
题目背景 巫妖王的天灾军团终于卷土重来,血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团,以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围,现在他们将主力只好聚集了起来,以抵…...
systemd 与 SysVinit
1. 什么是 systemd 和 SysVinit? systemd 和 SysVinit 都是 Linux 的初始化系统(init system),用于管理系统启动、服务、进程和日志。 比较项SysVinitsystemd启动方式逐步启动(串行)并行启动(…...
PythonWeb项目-Django+vue白酒数据推荐系统功能介绍
❥(^_-) 上千个精美定制模板,各类成品Java、Python、PHP、Android毕设项目,欢迎咨询。 ❥(^_-) 程序开发、技术解答、代码讲解、文档,💖文末获取源码+数据库+文档💖 💖软件下载 | 实战案例 💖文章底部二维码,可以联系获取软件下载链接,及项目演示视频。 本项目源…...
AWS Bedrock:开启企业级生成式AI的钥匙【深度解析】
引言:生成式AI的工业革命需要新基建 根据Gartner预测,到2026年超过80%的企业将在生产环境中部署生成式AI。而AWS Bedrock作为首个企业级生成式AI托管平台,正在重塑AI工业化落地的技术范式。本文将深入解构其技术架构与商业价值。 一、Bedroc…...
网络安全应急响应-文件痕迹排查
在Windows系统的网络安全应急响应中,文件痕迹排查是识别攻击行为的关键步骤。以下是针对敏感目录的详细排查指南及扩展建议: 1. 临时目录排查(Temp/Tmp) 路径示例: C:\Windows\TempC:\Users\<用户名>\AppData\L…...
【玩转全栈】—— Django 连接 vue3 保姆级教程,前后端分离式项目2025年4月最新!!!
本文基于之前的一个旅游网站,实现 Django 连接 vue3,使 vue3 能携带 CSRF Token 发送 axios 请求给后端,后端再响应数据给前端。想要源码直接滑倒底部。 目录 实现效果 解决跨域 获取 csrf-token 什么是 csrf-token ? CSRF攻击的…...
网络安全·第一天·IP协议安全分析
本篇博客讲述的是网络安全中一些协议缺陷以及相应的理论知识,本博主尽可能讲明白其中的一些原理以及对应的防卫措施。 学习考研408的同学也能进来看看,或许对考研有些许帮助(按照考研现在的趋势,年年都有新题目,本文当…...
TensorFlow深度学习实战——字符嵌入、子词嵌入、句子嵌入和段落嵌入
TensorFlow深度学习实战——字符嵌入、子词嵌入、句子嵌入和段落嵌入 0. 前言1. 字符嵌入2. 字词嵌入3. 句子嵌入和段落嵌入相关链接 0. 前言 在自然语言处理中,嵌入 (Embedding) 技术是将文本转化为数值向量的核心方法,使计算机能够理解和处理语言中的…...
剖析AI与5G:是夸大其词,还是时代变革的引擎?-优雅草卓伊凡
剖析AI与5G:是夸大其词,还是时代变革的引擎?-优雅草卓伊凡 在当今科技飞速发展的时代,AI与5G无疑是两大备受瞩目的焦点。近日,一个引人深思的问题浮出水面:“AI是不是被夸大了,就像当年的5G一样…...
Python Cookbook-5.7 在增加元素时保持序列的顺序
任务 你需要维护一个序列,这个序列不断地有新元素加入,但始终处于排序完毕的状态这样你可以在任何需要的时候检查或者删除当前序列中最小的元素。 解决方案 假设有一个未排序的列表,比如: the_list [903, 10, 35, 69, 933, 485, 519, 37…...
Ubuntu18系统安装
1、虚拟机安装 请参照之前的《虚拟机安装centos7-NAT网络模式安装》与《虚拟机安装centos7-桥接模式》 2、启动虚拟机进入系统的欢迎界面 选择English后并点击回车键(Enter) 3、进入安装页面 选择Countinue without updating并点击回车键(Enter) 4、键盘配置 选择Done并点击…...
【AIGC】零样本学习方法综述(TPAMI 2023 研究综述)
出版日期为2022年7月18日;工作由国家自然科学基金资助6217616年6061732011和61976141,部分由广东基础和应用基础研究基金资助2022A1515010791,部分由深圳大学自然科学基金会稳定支持计划资助号20200804193857002,部分由SZU的跨学科…...
Redis持久化之AOF
AOF(Append Only File)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用解决了数据持久化的实时性。 1.使用AOF 开启AOF需要设置配置文件: appendonly yes…...