【Shell】mksh运行分析

news/2025/2/13 23:06:23/

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/shclass coreconsoledisableduser shellgroup shell log readprocseclabel u:r:shell:s0setenv 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;elses->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);elsetl = 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_uuc = 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_EDITINGif (have_tty && (
#if !MKSH_S_NOVIFlag(FVI) ||
#endifFlag(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_NOVIelse if (Flag(FVI))i = x_vi(buf);
#endifelse/* 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_SMALLif (macroptr) {if ((c = (unsigned char)*macroptr++))return (c);macroptr = NULL;}
#endifreturn (x_getc());
}static int
x_getc(void)
{
#ifdef __OS2__return (_read_kbd(0, 1, 0));
#elsechar c;ssize_t n;while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)if (trap) {x_mode(false);runtraps(0);
#ifdef SIGWINCHif (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();}}
#endifx_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.ppmy.cn/news/10544.html

相关文章

【Linux】Linux编译器-gcc/g++使用

文章目录1. 什么是gcc/g2. 程序翻译的四个阶段2.1 预处理2.2 编译2.3 汇编2.4 链接3. 函数库3.1 动态库3.1 静态库4. gcc/g的使用1. 什么是gcc/g Linux中的gcc是由GNU推出的一款功能强大的、性能优越的多平台编译器。gcc编译器能将C、C语言源程序和目标程序编译、连接成可执行文…

数据库管理-第五十一期 新年新气象(20230108)

数据库管理 2023-01-08第五十一期 新年新气象1 新年快乐2 旧账3 软硬件对比4 新气象总结第五十一期 新年新气象 1 新年快乐 2023年来了&#xff0c;我也没有第一时间写一篇写文章给大家祝福&#xff0c;第一呢是因为某些原因元旦假期也没咋休息&#xff0c;其次就是因为本周又…

AjaxRedis问题总结

一、JQuery——Ajax实现三大方法的应用及区别&#xff08;get,post,ajax&#xff09; 1. $.get()方法 <script type"text/javascript">$(function () {$("#send").click(function () {$.get("AjaxHandler.ashx", {username: $("#use…

Hive表的创建,删除,修改

TBLPROPERTIES的主要作用是按键-值对的格式为表增加额外的文档说明。Hive会自动增加两个表属性:一个是last_modified_by&#xff0c;其保存着最后修改这个表的用户的用户名﹔另一个是 last_modified_time&#xff0c;其保存着最后一次修改的新纪元时间秒。用户还可以拷贝一张已…

MATLAB循环码编译码实验

标题循环码编译码实验一、实验目的1、掌握循环码编码原理和译码原理2、练习使用Matlab编程实现循环码编码和译码二、实验原理伴随式译码捕错译码三、实验要求1、编程实现码长n15的各种循环码的编码、译码&#xff0c;给出相应的码生成多项式、&#xff08;典型&#xff09;监督…

LeetCode 每日一题 2023/1/2-2023/1/8

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录1/2 1801. 积压订单中的订单总数1/3 2042. 检查句子中的数字是否递增1/4 1802. 有界数组中指定下标处的最大值1/5 1803. 统计异或值在范围内的数对有多少1/6 2180. 统计各位…

Zookeeper

第 1 章 Zookeeper 入门 1.1 概述 Zookeeper 是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的 Apache 项目。1.2特点 1&#xff09;Zookeeper&#xff1a;一个领导者&#xff08;Leader&#xff09;&#xff0c;多个跟随者&#xff08;Follower&#xff09;组成…

牛客竞赛每日俩题 - Day13

目录 洪泛法BFS 26进制计数字符串 洪泛法BFS 红与黑__牛客网 循环接收每组用例&#xff0c;对于每组用例进行如下操作&#xff1a; 找到‘’所在的位置&#xff0c;即起始搜索的点 使用DFS搜索地板中的每块瓷砖&#xff0c;如果是黑色&#xff0c;给计数1&#xff0c;然后像…