首页 > 代码库 > 基于u-boot源码的简单shell软件实现

基于u-boot源码的简单shell软件实现

一、概述

1、shell概念

  Shell(命令解析器),它用于接收用户输入的命令,进行解析,然后调用相应的应用程序,为使用者提供了使用软件的界面。

  shell是操作系统最外面的一层。shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,执行相应的应用程序,并且输出各种各样的处理结果。

2、shell分类

  按照界面类型,可以分为图形界面shell和命令行式shell。

  图形界面shell(Graphical User Interface shell 即 GUI shell),应用最为广泛的 Windows Explorer (微软的windows系列制作系统),还有也包括广为人知的 Linux shell,其中linux shell 包括 X window manger (BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 XFCE。

  命令行式shell(Command Line Interface shell ,即CLI shell),被广泛熟知的有bash / sh / ksh / csh(Unix/linux 系统)、cmd.exe(Windows XP 系统)、command.com(DOS系统)。

3、shell的运行模式

  Shell的运行模式可以分为:交互式和非交互式。

  交互式模式就是shell等待你的输入,并且执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当你签退后,shell也终止了。

  shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与你进行交互,而是读取存放在文件(shell script),并且执行其中的命令。当它读到文件的结尾,shell也就终止了。

二、shell软件功能需求分析

1、本文的开发环境

  本文希望在单片机上运行这个shell程序,单片机可以是STM32、AVR等等,本文的测试是在S3C2440开发板上完成的。用户输入通常选用PC机的键盘,由于硬件运行平台是单片机,就选用串口进行交互数据的传输。软件开发环境选用linux系统下的arm-linux-gcc交叉编译工具链,该工具链功能强大,可配置性较集成开发环境尤其独到之处。

2、本文shell的基本功能要求

  本文目的在于实现一个简单的shell,功能不要求很多,但是要求完全描绘出一个shell的工作原理和shell软件结构。有了这样的软件结构,再向shell里添加其他的功能,就好像做填空题一样。

  简单的shell功能可支持的命令虽然很少,但是也要确定选择哪些命令比较合适。实现这样简单的命令最好不需要增添其他的外设,比如说硬盘、网卡,利用最小的单片机系统就能搞定。这样可以节省工作量,将主要精力集中到shell软件结构上,而不是具体命令的实现上。基于这样的考虑,本文选择echo(回显)、md(内存显示)和hello(打印“hello world”)三个命令作为测试。另外,还必须提供shell必备的命令help或者?(打印所有命令的帮助信息)。而且对于每一个命令,通过“help+命令”的输入打印单个命令的详细信息。

三、shell软件设计方案

  在shell运行期间,无非涉及两个模块。一个模块是等待用户输入,并把用户输入的字符保存起来。另外一个模块,就是把之前保存的用户输入解析出来,并且根据解析到的命令找到对应的函数地址。再根据用户输入命令的参数,将参数传给这个函数地址,进行函数调用。

  shell的核心是如何根据命令字符串,找到对应的函数地址,这是一个算法问题。因为命令字符串相对应着一个函数地址,需要将它们用一个数据结构包含在一起,所以也是一个数据结构的问题。

  有什么样的数据结构,就有什么样的算法,设计数据结构成了关键。一个命令的结构体应该包含这样的内容:命令的名字、命令参数的最大个数、命令对应函数的地址、命令的简要帮助信息、命令的详细帮助信息。

  有了命令数据结构,在设计一个命令的时候,就将这些命令的必要信息提供好,然后将这些信息填充到它对应的结构体变量中。最后,再将所有命名对应的结构体变量保存在一个数据段中。通过编译器产生的参数,我们可以掌握这个数据段的起始地址和结束地址,从而知道命令数据段的长度,进而知道命令的个数。只要我们知道这个数据段的起始地址,因为命令结构体的长度固定,我们就可以准确的遍历整个命令数据段,找到匹配的命令。

  如果设计好这样的命令数据结构,再来思考如何实现根据命令字符串找到对应的函数地址,其实就是一个遍历命令数据段的问题。从命令数据段的开头,拿所要找的命令子妇产依次与命令数据段中的每个命令结构体进行比较,直到找到匹配的那个为止。找到了匹配的命令结构体,也就找到了命令字符串对应的调用函数地址。

四、shell软件实现

1、命令结构体

  根据上一章节的分析,命令结构体的实现代码如下:

struct cmd_tbl_s {
   char       *name;     /* Command Name          */
   int    maxargs;   /* maximum number of arguments  */
  int repeatable;/* autorepeat allowed?*/
                 /* Implementation function  */
   int    (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
   char       *usage;       /* Usage message  (short)    */
   char       *help;     /* Help  message  (long) */
};

   其中,核心成员是命令对应的函数指针cmd,cmd的参数有4个。第一个参数是当前函数指针对应的命令结构体的地址,传递这样的参数的好处是命令函数可以根据这个指针找到命令所有的信息,为程序拓展带来了方便。最后一个参数是一个字符型指针,保存了输入参数的存放地址,以供函数调用。

2、读取输入函数

  读取输入函数名为readline,这个函数就是将用户的输入按照一定的格式存储到一个缓冲区console_buffer中,并且将用户的输入在返回到显示界面中。当用户输入Enter键的时候,这个函数就认为用户输入结束。

3、查找并执行命令的函数

  此函数的函数名为run_cmmand,它通过调用查找命令函数find_cmd,将存入缓冲区console_buffer中的内容逐一与命令数据存储区中命令结构体的命令字符串比较。如果完全匹配,就调用命令结构体对应的函数,并传入参数。

五、shell软件后期完善

  本文实现的简单shell软件,功能还有待补充,比如说使用环境变量保存用户配置、Flash管理等等。但是,由于本文已将shell框架搭好,再向其中添加其他的命令已经变得非常容易。只需要新建一个实现新命令的C源文件,然后将必要的头文件加入,再编写具体的命令实现代码。下一步再将新命令的信息填入到新命令对应的命令结构体变量中,重新编译执行即可。

 

参考资料:《u-boot-1.1.6 源码包》

附本文shell的实现代码:shell.zip