【Shell】mksh运行分析

news2024/11/19 18:35:02

mksh运行分析

Shell

shell,壳子,即操作系统的壳子。这层壳子套在操作系统上,为用户提供与操作系统的交互手段。
操作系统的交互方式一般有,图形化交互(GUI)和命令行交付(CLI,command-line interface)。

  • 套在操作系统上的壳子
    在这里插入图片描述

Android系统中使用了一款叫mksh的shell程序,用于交互式的命令解释器。

  • init.rc中定义了名为"console"的service,service对应的可执行程序是sh这个二进制,这个二进制程序由 exteranl/mksh/Android.bp定义。
service console /system/bin/sh
    class core
    console
    disabled
    user shell
    group shell log readproc
    seclabel u:r:shell:s0
    setenv HOSTNAME console

mksh

mksh是一款开源的命令解释器(shell),aosp中的源码路径是external/mksh,编译后会在/system/bin下生成 "sh"可执行程序,init.rc中配置了开机启动这个二进制程序。当"sh"启动后,终端工具上就会出现我们常知的命令解释器,可以输入shell命令进行操作。

  • 如果在rc中注释掉/system/bin/console,就无法通过命令行形式操作系统。

接下来以mksh接受终端输入命令(如:ls)的角度分析mksh的源码。

  • 启动入口:main.c, main入口中调用函数main_init初始化mksh的运行环境,如果初始化没有问题,默认走shell函数。
int
main(int argc, const char *argv[])
{
	int rv;
	Source *s;
	struct block *l;

	if ((rv = main_init(argc, argv, &s, &l)) == 0) {
		if (as_builtin) {
			rv = c_builtin(l->argv);
		} else {
			shell(s, 0);
			/* NOTREACHED */
		}
	}
	return (rv);
}
  • main.c: shell函数,根据函数的注释可以了解到,mksh通过这个函数解释从外部设备输入的命令,并且返回结果。这个函数中,通过while(1)循环监听command,当有command输入时,调用complime函数解析commnand.
/*
 * run the commands from the input source, returning status.
 */
int
shell(Source * volatile s, volatile int level)
{
// 省略
while (/* CONSTCOND */ 1) {
		if (trap)
			runtraps(0);

		if (s->next == NULL) {
			if (Flag(FVERBOSE))
				s->flags |= SF_ECHO;
			else
				s->flags &= ~SF_ECHO;
		}
		if (interactive) {
			j_notify();
			set_prompt(PS1, s);
		}
		t = compile(s, sfirst, true);
	// 省略
	}
}
  • sync.c: compile->yyparse->c_list->andor->pipeline->get_command->tpeek->yylex,上述为函数调用关系。
struct op *
compile(Source *s, bool skiputf8bom, bool doalias)
{
	nesting.start_token = 0;
	nesting.start_line = 0;
	herep = heres;
	source = s;
	if (skiputf8bom)
		yyskiputf8bom();
	yyparse(doalias);
	return (outtree);
}

static void
yyparse(bool doalias)
{
	int c;

	ACCEPT;

	outtree = c_list(doalias ? ALIAS : 0, source->type == SSTRING);
	c = tpeek(0);
	if (c == 0 && !outtree)
		outtree = newtp(TEOF);
	else if (!cinttype(c, C_LF | C_NUL))
		syntaxerr(NULL);
}

static struct op *
c_list(int sALIAS, bool multi)
{
	struct op *t = NULL, *p, *tl = NULL;
	int c;
	bool have_sep;

	while (/* CONSTCOND */ 1) {
		p = andor(sALIAS);
		// 省略
	}
}

static struct op *
andor(int sALIAS)
{
	struct op *t, *p;
	int c;

	t = pipeline(0, sALIAS);
	if (t != NULL) {
		while ((c = token(0)) == LOGAND || c == LOGOR) {
			if ((p = pipeline(CONTIN, sALIAS)) == NULL)
				syntaxerr(NULL);
			t = block(c == LOGAND? TAND: TOR, t, p);
		}
		REJECT;
	}
	return (t);
}

static struct op *
pipeline(int cf, int sALIAS)
{
	struct op *t, *p, *tl = NULL;

	t = get_command(cf, sALIAS);
	if (t != NULL) {
		while (token(0) == '|') {
			if ((p = get_command(CONTIN, sALIAS)) == NULL)
				syntaxerr(NULL);
			if (tl == NULL)
				t = tl = block(TPIPE, t, p);
			else
				tl = tl->right = block(TPIPE, tl->right, p);
		}
		REJECT;
	}
	return (t);
}


static struct op *
get_command(int cf, int sALIAS)
{
// 省略
switch (tpeek(cf)) {
		// 省略
}

#define tpeek(cf)	((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
  • lex.c: yylex->getsc_bn->getsc__->getsc_line->xread。
#define o_getsc()	(*source->str != '\0' && *source->str != '\\' && \
			    !backslash_skip ? *source->str++ : getsc_bn())

#define getsc()		getsc_r((unsigned int)(unsigned char)o_getsc())

/* optimised getsc_uu() */
#define	o_getsc_u()	((*source->str != '\0') ? *source->str++ : getsc_uu())

int
yylex(int cf)
{
    // 省略
	if (cf & ONEWORD)
	    // 省略
	else if (cf & LETEXPR) {
        // 省略
	} else {
		/* normal lexing */
		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
		do {
			c = getsc();
		} while (ctype(c, C_BLANK));
		if (c == '#') {
			ignore_backslash_newline++;
			do {
				c = getsc();
			} while (!ctype(c, C_NUL | C_LF));
			ignore_backslash_newline--;
		}
		ungetsc(c);
	}
    // 省略
}

static int
getsc_bn(void)
{
	int c, c2;

	if (ignore_backslash_newline)
		return (o_getsc_u());

	if (backslash_skip == 1) {
		backslash_skip = 2;
		return (o_getsc_u());
	}

	backslash_skip = 0;

	while (/* CONSTCOND */ 1) {
	    // 调用的是 getsc_uu
		c = o_getsc_u();
		if (c == '\\') {
			if ((c2 = o_getsc_u()) == '\n')
				/* ignore the \newline; get the next char... */
				continue;
			ungetsc_i(c2);
			backslash_skip = 1;
		}
		return (c);
	}
}

static int
getsc_uu(void)
{
	Source *s = source;
	int c;

	while ((c = ord(*s->str++)) == 0) {
		/* return 0 for EOF by default */
		s->str = NULL;
		switch (s->type) {
		case SEOF:
			s->str = null;
			return (0);

		case SSTDIN:
		case SFILE:
			getsc_line(s);
			break;
		// 省略
    }
}

static void
getsc_line(Source *s)
{
    // 省略
#ifndef MKSH_NO_CMDLINE_EDITING
	if (have_tty && (
#if !MKSH_S_NOVI
	    Flag(FVI) ||
#endif
	    Flag(FEMACS) || Flag(FGMACS))) {
		int nread;
        
		nread = x_read(xp);
    }
}
    
  • edit.c : x_read ->x_emacs->x_e_getc->x_getc。x_getc这个函数使用了STDIN_FILENO,STDIN_FILENO表示标准输入设置,即从标准输入设备循环读取输入的命令,当遇到回车将解释输入的命令。
/*
 * read an edited command line
 */
int
x_read(char *buf)
{
	int i;

	x_mode(true);
	modified = 1;
	if (Flag(FEMACS) || Flag(FGMACS))
		i = x_emacs(buf);
#if !MKSH_S_NOVI
	else if (Flag(FVI))
		i = x_vi(buf);
#endif
	else
		/* internal error */
		i = -1;
	editmode = 0;
	x_mode(false);
	return (i);
}

static int
x_emacs(char *buf)
{
   // 省略
	while (/* CONSTCOND */ 1) {
		x_flush();
		if ((c = x_e_getc()) < 0)
			return (0);
	}
	// 省略
}

static int
x_e_getc(void)
{
	int c;

	if (unget_char >= 0) {
		c = unget_char;
		unget_char = -1;
		return (c);
	}

#ifndef MKSH_SMALL
	if (macroptr) {
		if ((c = (unsigned char)*macroptr++))
			return (c);
		macroptr = NULL;
	}
#endif

	return (x_getc());
}

static int
x_getc(void)
{
#ifdef __OS2__
	return (_read_kbd(0, 1, 0));
#else
	char c;
	ssize_t n;

	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
		if (trap) {
			x_mode(false);
			runtraps(0);
#ifdef SIGWINCH
			if (got_winch) {
				change_winsz();
				if (x_cols != xx_cols && editmode == 1) {
					/* redraw line in Emacs mode */
					xx_cols = x_cols;
					x_init_prompt(false);
					x_adjust();
				}
			}
#endif
			x_mode(true);
		}
	return ((n == 1) ? (int)(unsigned char)c : -1);
#endif
}
  • misc.c: blocking_read的定义如下。
ssize_t
blocking_read(int fd, char *buf, size_t nbytes)
{
	ssize_t ret;
	bool tried_reset = false;

	while ((ret = read(fd, buf, nbytes)) < 0) {
		if (!tried_reset && errno == EAGAIN) {
			if (reset_nonblock(fd) > 0) {
				tried_reset = true;
				continue;
			}
			errno = EAGAIN;
		}
		break;
	}
	return (ret);
}
  • 函数调用时序图:
    请添加图片描述

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

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

相关文章

虚拟机栈

虚拟机栈简介虚拟机栈的出现背景内存中的栈与堆虚拟机栈基本内容虚拟机栈的特点虚拟机栈的异常设置栈内存大小栈的存储单位栈中存储什么&#xff1f;栈运行原理栈帧的内部结构局部变量表认识局部变量表关于Slot的理解Slot代码示例Slot的重复利用静态变量与局部变量的对比补充说…

oracle安装教程

1安装和创建用户 1.1.安装以及常见问题 oracle安装教程 安装中 system密码改成root oracle卸载&#xff0c;除此之外清除C:\Program Files内的oracle 需要准备oracle安装包和plsql界面化操作工具 重装报错bug&#xff1a; plsql操作 plsql注册码 product code: ke4tv8t5jtxz…

java动态代理-面向切面代码样例

1.测试入口import java.lang.reflect.Proxy;/*** 面向切面测试* author epsoft-hy**/ public class test {public static void main(String[] args) {Class<?>[] cls {UserDao.class};//接口一个String classpath"util.aop.UserDaoImp";//访问类路径test2(tes…

【学vue跟玩一样】快速学会常用指令及如何理解生命周期

一&#xff0c;内置指令1.v-textv-bind:&#xff0c;单向绑定解析表达式v-model :双向数据绑定v-for:遍历数组/对象/字符串v-on :绑定事件监听,可简写为v-if :条件渲染(动态控制节点是否存存在)v-else :条件渲染(动态控制节点是否存存在)v-show:条件渲染(动态控制节点是否展示)…

3. Rstudio【可视化导入】数据

b站课程视频链接&#xff1a;https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99元买了&#xff0c;感觉不错&#xff09;&#xff1a;https://ke.qq.com/course/3707827#term_id103855009 &nbsp&#xff1b; 本笔记…

SigmaStar空板烧录

一、硬件连接 在官方给的SDK文档中&#xff0c;有描述如何使用ISP TOOL给空板烧录程序&#xff0c;但坑的是&#xff0c;文档中并没有写这个上位机工具需要配合Mstar专门的烧录工具来使用&#xff0c;烧录工具如下图所示。 上图中有三根连接线&#xff0c;示意图如下。目标板的…

HTML与CSS基础(九)—— 综合项目(CSS样式补充、项目前置认知、项目结构搭建)

目标能够在网页中使用 精灵图 能够使用 背景大小属性 &#xff0c;设置背景图片的大小 能够认识 CSS书写顺序&#xff0c;提高代码专业性和浏览器渲染性能 能够使用的专业方式完成 项目结构搭建 和 基础公共样式 能够应用已学技术知识 完成小兔鲜儿项目一、项目样式补充目标&am…

URL中绝对路径与相对路径拼接问题总结

URL中绝对路径与相对路径拼接问题总结1. 基础环境2. 测试2.1 总结2.2 本地文件url1. 基础环境 这里用nginx的docker镜像构建了一个服务&#xff0c;步骤如下&#xff1a; sudo docker pull nginxsudo docker run -d --name nginx01 -p 8083:80 nginx nginx发布的根目录是/us…

Redis安装及常用数据类型介绍

1、redis介绍1.1、redis是一个开源的key-value存储系统1.2、和Memcached类似&#xff0c;它支持存储的value类型相对很多&#xff0c;包括String、list、set、zset以及hash类型。1.3、这些数据类型都支持push/pop、add/remove及取交集并集及更丰富的操作&#xff0c;而且这些操…

LeetCode[264]丑数II

难度&#xff1a;中等题目&#xff1a;给你一个整数 n&#xff0c;请你找出并返回第 n个 丑数 。丑数 就是只包含质因数 2、3和/或 5的正整数。示例 1&#xff1a;输入&#xff1a;n 10输出&#xff1a;12解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组…

【NKOJ三校 初中新年大联欢DIV2】考试游记

目录 看不见的手ljj的方块零它来了宝石转换ljj的距离零它走了彩蛋 题目 【A NKOJ-P8629】 看不见的手 40pts 考试思路&#xff1a; 这道题一看数据范围就知道是O(n)O(n)O(n)的结论题&#xff0c;考试的时候脑子抽筋偏分输出1得了40pts&#xff1b; 正解&#xff1a; 他说…

VISTA -MIT开源基于数据驱动的自动驾驶仿真引擎

引言 VISTA 是MIT开源的一个基于数据驱动的用于自动驾驶感知和控制的仿真引擎。VISTA API提供了一个接口&#xff0c;用于将真实世界的数据集转换为具有dynamic agents、sensor suites、task objectives的虚拟环境。 用过 Unreal Engine 或者 Gazebo的仿真引擎的同学都知道&…

【Kafka】八股文梳理

什么是消息中间件&#xff1f; 消息中间件是基于队列与消息传递技术&#xff0c;在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统。 消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流&#xff0c;并基于数据通信来进行分布式系统的集成。…

Android Native开发系列之C/C++代码调试

Android Native开发系列之C/C代码调试 引言 在做Android native层开发的时候&#xff0c;需要经常调试C/C代码&#xff0c;相较而言通过打日志的方式太不方便了。有两种方式可以进行底层代码的调试。 利用Android studio自带的Debugger进行调试。利用LLDB VSCode进行代码调试…

SAP灵活工作流条件增强

前置 灵活工作流模板中可以设置条件&#xff0c;用来在场景配置中判断是否启动流程或者是否执行该节点 除了流程模板中设置&#xff0c;也可以通过增强设置更为灵活的条件判断,对应增强点SWF_PROCESS_WORKFLOW_CONDITION 增强实施步骤参考 创建增强点实施 复制示例类 为增强设…

深入聊一聊vue3中的reactive()

在vue3的开发中&#xff0c;reactive是提供实现响应式数据的方法。日常开发这个是使用频率很高的api。这篇文章笔者就来探索其内部运行机制。小白一枚&#xff0c;写得不好请多多见谅。 调试版本为3.2.45 什么是reactive? reactive是Vue3中提供实现响应式数据的方法. 在Vue2中…

【Dash搭建可视化网站】项目9:智能选股器(结合行业、换手率、量比与60日k线图选择目标股票)制作步骤详解

【Dash搭建可视化网站】项目9&#xff1a;智能选股器&#xff08;结合行业、换手率、量比与60日k线图选择目标股票&#xff09;1 项目效果图2 项目架构3 文件介绍和功能完善3.1 assets文件夹介绍3.2 app.py和index.py文件完善3.3 layoutleft.py文件完善3.4 api.py和api.ipynb文…

【数据结构趣味多】栈和队列(详细解析)

目录 1.1 栈的定义 1.2 栈的模拟实现&#xff08;顺序栈&#xff09; push()方法 pop()方法 peek()方法 size()方法 栈模拟实现的全代码 1.3顺序栈和链栈的对比 2.队列 2.1队列的定义 2.1队列的模拟实现&#xff08;单链表&#xff09; offer()函数 poll()函数 p…

ABB AC500 PLC 与西门子 S7-300 CPU DP 通讯

硬件连接 将一个 AC500 CPU&#xff08;本例 PM583&#xff09; 左侧插入以 DP 通讯扩展模块 CM572&#xff0c;使用 DP 通讯电缆连接 CM572 与 314-2DP CPU 的 DP 通讯口。连接示意图如下&#xff1a; AC500 DP 主站配置 安装 DP 从站设备描述文件 从西门子官方网站下载 CP…

认识二叉树

hi&#xff0c;代噶候。今天带大家认识一下二叉树&#xff0c;这个二叉树在我看来确实很有难度&#xff0c;但是不要怕&#xff0c;&#xff0c;鲁迅先生曾经说过&#xff0c;真正的勇士敢于面对惨淡的人生&#xff0c;敢于正视淋漓 的鲜血&#xff0c;下面让我们开始吧&#x…