完整版Linux命令解释器的设计本科毕业设计.docx
《完整版Linux命令解释器的设计本科毕业设计.docx》由会员分享,可在线阅读,更多相关《完整版Linux命令解释器的设计本科毕业设计.docx(27页珍藏版)》请在冰点文库上搜索。
完整版Linux命令解释器的设计本科毕业设计
Linux命令解释器的设计
摘要:
随着Linux系统使用的越来越广泛,越来越多的人开始深入的研究Linux,特别是对Linuxshell的研究是对Linux研究最主要的部分。
本文主要是研究了对shell的功能的一些认识,而且对shell的列表,管道,输入重定向和输出重定向等命令功能进行了实现,可以对shell命令解释器进行更加全面的认识和充分的了解,而且在shell命令解释器中执行正确的命令,从而对它的原理,方法等在程序中必须用到的知识原理有比较清楚的认识,最后通过对每个功能的详细分析,进而编写出恰当实现各个功能的代码,从而做成一个Shell命令解释器。
并且,在嵌入式领域中,与人们的需求相比,硬件的资源是微不足道的。
这就意味着我们把不多的资源利用完成很多的需求,而嵌入式的设计在PC上编程是有很大不同的,它其实只需要一个很大程序的比较小的一方面就可以满足它的需求,因此我们必须把一些无用的程序删掉来换成空间。
在嵌入式领域中就像传统的Kshell,Cshell,Bourneshell等大型的shell程序就会令硬件方面的东西很少。
因此我们需要一个更符合嵌入式系统中使用的Shell。
关键词:
Shell;程序;设计与实现;嵌入式;Linux
Abstract:
AccompaniedbythepopularizeoftheLinux,moreandmorepeople aredoingdeeplystudyinit.ThestudyofshellisthemostimportantthingwhenstudytheLinux.Itmainlyincludestheunderstandofsomeknowledgeandfunctionofthemicrolinuxshell.Throughthestudyoftheordersintheshelllikelist,pipe,inputredirectandoutputredirect,readerscantousetheseordersaccurayinshellwhichplayanimportantroleinprogramminglater.Codeaccurayandachieveasimpleshellwiththefunctionsaboveafteranalyzingthedetailsofeveryfunctionsmodule.
Moreover,intheembeddedfield,theandprogramminginthetraditionalPC,isverydifferent,anditoftenrequiresonlyasmallpartofalargeprogramonmeetitsneeds,soweexchangeforspace.Intheembeddedfield,asthetraditionalKshell,Cshell,Bourneshellandotherlarge-scaleshellprogramwillmakeourtheshell,thisismyanotherobjectiveofthisdesign.
Keywords:
Shell,Process,Design,Designandachieve,embedded,Linux
前言
shell作为Linux系统的最外层的部分,为使用者提供必备的接口。
Shell作为用户和Linux内核之间的接口程序,起着一个桥梁的作用,假如把Linux内核当做是一个球心,那么shell就是球体的表面。
从shell向Linux发出命令时,内核会对该命令做出相应的解释。
本论文就是模拟一个shell命令解释器,包括:
管道、内部命令、外部命令、重定向命令。
管道是Linux支持的最初UnixIPC其中的一个,具有这一些特点:
管道是半双工的,里面的数据只能单向的流动;当双方需要通信时,要创造出来两个管道;但是只能用于父子进程和兄弟进程之间(具有亲缘关系的进程);其实就是他们自己独自组成一种的文件系统,这个文件系统是独立的:
管道相对于管道两端的进程来说,就是独立的文件,它不是一般的文件,它并不属于一种文件系统,而是自己有自己的特点,独自构成一种文件系统,而且只是在内存中。
当写数据时一个进程向其中一个管道中写的数据会被管道另一头的进程读出,而且写入的数据每次都在管道缓冲区的最后,并且每次都是从缓冲区的首端读数据。
每当执行shell命令行时一般会打开三个标准文件,这三个标准文件就是标准输入文件(stdin),一般对应用户所用的输入键盘;标准输出文件(stdout)和标准错误输出文件(stderr),这两个文件都对应着用户所用的输出屏幕。
进程将从标准输入文件中读取输入数据,将正确的数据输出到标准输出文件,将不正确的信息输出到标准错误文件中。
输入重定向,顾名思义,就是所把命令(或可执行程序)的标准输入重新定向到自己所认定的文件中。
也就是说,输入不一定要来自键盘,可以来自一个自己指定的文件。
输出重定向就是把命令(或可执行程序)的标准输出或标准错误输出重新定向到目标中。
该命令的输出就不会在屏幕上显示,而是写入到自己的目标文件中。
Shell是一个命令解释器,它就是把用户输入的命令进行重新解释而且把这些命令送到内核中。
不止如此,Shell对命令的编辑有自己相对应的编程语言,但是灵活的是用户自身编写由shell命令组成的程序也是允许的。
Linux像windows一样提供了很多可视化界面有窗口、图标和菜单,大部分控制都是通过鼠标来操作。
现在在Linux中稍微流行的窗口管理器就是是KDE。
而shell就像窗口管理器一样,它可以对系统灵活地进行各种管理。
不同的Linux系统的用户有同的界面或Shell,来满足自己相应的Shell需要。
同Linux本身一样,Shell也有多种不同的版本。
目前主要有下列版本的Shell:
BourneShell:
由贝尔尔实验室研发的。
BASH:
是GNU的BourneAgainShell,是GNU操作系统上默认的shell。
KornShell:
是对BourneSHell的拓展,与BourneShell是互相兼容的。
CShell:
是SUN公司Shell的BSD版本。
如果我们是经常使用UMXLinux系统的研发人员,我们对shell的功能会有很深的认识和理解。
1绪论
1.1Shell的定义
Shell是一种特殊的程序,如下图所示。
他是用户与Linux系统内核之间的通道。
内核在系统开机时将内存载入,关机后不再管理系统。
它对进程,内存,文件和通信等都无时无刻的管理着。
内核之外的自己编写的程序以及shell程序都保存在磁盘上,内核将这些程序加载到内存中运行,而且对它们运行完后进行系统清理。
Shell是一个程序,在开机后系统启动。
当用户开机并登陆后,系统不需要等待用户的命令会自动启动一个Shell。
Shell会根据用户输入的命令后来执行命令:
根据命令行的关键字来判断是那种命令,然后执行命令。
当人们输入一组类似的命令是,希望会这些命令会自行的完成。
这样的话,就可以把这些命令行输入到一个文件中,直接执行这个文件就可以完成那些命令的功能。
1.1.1UNIXShell
UNIX系统大都支持3种主流的shell,他们是BourneShell(也称为AT&Tshell)、Cshell(也称为Berkeley)和Kornshell(Bourneshell的一个展集)。
这三种Shell功能是很类似的,如果作为脚本的话,它们的还是有不同的。
Bourneshell是标准的UXINshell,用于系统管理。
大部分系统管理脚本(如rcstart和Stop脚本,shutdown)部是Bourneshell脚本。
BourneShell的使用是非常简练的,而且很严谨,效率很高,它的命令提示符是$。
CShell完善了很多不同的功能,就像查询命令行历史,作业的前后台控制等。
大部分的用户还是更倾向于Cshell,其实管理者们还是倾向于BourneShell。
由于类似的程序,使用BourneShell会效率更高。
Cshell默认的提示符是百分号(%)。
Kornshell是AT&T的DavidKorn开发的,它是Bourneshell的-个扩展集,在增强改进Cshell的基础上,Kornshell添加了更多功能。
Kornshell几乎完全向上兼容Bourneshell,所以老的Bourne程序在Koreshell中运行良好。
Kornshell默认提示符是美元符号($)。
Linux上默认的shell是GUNbash(BourneAgainshell),这是一种增强的Bourneshell,人们可以根据自己的工作环境进行编辑,因为它的功能有一部分还是有局限的,通过自己的编辑可以提高速度。
如果用户想了解自己装的Linux是用的哪些Shell,直接在终端输入命令行:
$catetcshellls;即查看当前发行版本可以使用的shell
Linux的环境中可以使用的Shell都在etcshells文件中。
常用的版本包括bash(Bourneshell),tcsh(TCshell)金和ksh(Kornshell)。
1.2shell的历史
Shell首个标准是在1979年由StephenBourne提出的,并且以该人的名字来作为名字。
BourneShell是建立在Algol的程序的基础上的。
在当时主要是对系统进程的管理比较方便,所以收到了很大用户的支持,但是在交互方面还是有所不足,有待完善。
CShell是由加州大学伯克利分校开发的,跟Bourneshell发行的不同,它是跟操作系统同步发布的。
它的主要的开发人是BillJoy。
Cshell就是完善了Bourneshell的不足,是用户与系统的交互更加的方便。
而且,对于大型机的使用,有扩展了其他的功能,起到了很大的帮助,但是在小型机的使用还是效率很低。
而且,即使在大型机上,它的速度也不如Bourneshell。
由于不同版本的Shell发布,人们开始有选择的使用Shell,对于不同的Shell有自己的见解。
20世纪80年代中期,AT&T的DavidKorn推出了Kornshell。
其实Kornshell是对Bourneshell的一个拓展集,它在UNIX系统下可以运行的留长,还能在OS2.VMS和DOS上运行的很顺利。
Kornshell的优点就是可以不止可以再UNIX下使用,还可以在其它的系统下使用。
由于Kornshell是对Bourneshell的拓展,由学习了Cshell的优点,所以用户数很多。
随着Linux的发展,BourneAgainshell(bash)开始流行起来。
Bash是Linux操作系统默认上的shell。
它的设计符合了IEEPOSIXP1003.2IS09945.2shell和工具标准。
bash又增加了很多新的功能:
命令行历史与编辑、目录栈、作业控制、函数、别名、数组、整数运算(底数可以是2-64),还增加了一些Kornshell的功能,如扩展的元字符,select循环和let命令等。
1.3shell的职责
Shell的主要功能就是根据用户的输入的命令行,来判断这是哪种命令,并且通过关键字来分解,比如空格,如果判断出来有不能识别的字符,shell会自动替换该字符。
Shell处理IO和后台进程,然后shell会自动搜索该命令,并开始执行。
Shell的另外的重要的作用就是对用户环境进行设定,这往往是刚开始就是初始化的时候就已经进行了。
在初始化的文件中有很多变量,根据不同的程序来设置相应的变量,所以用户在使用起来还是比较方便的。
KornBashshell和CTCshell还提供了其他的专门的功能:
历史添加、别名、设置内置变量可以阻止用户对文件进行损坏或不小心退出,告诉用户作业已经结束。
Shell还能用作编程语言来使用。
2开发环境和开发工具介绍
2.1开发环境--Ubuntu
Ubuntu比较受欢迎的原因就是,它是完全像社会人们开放的,并且希望人们对系统进行完善并且是一些编程爱好者们共同开发的系统,所以Ubuntu目前是并将永远是免费的。
但是,它还是需要成本的,它并不是无拘无束的可以自由的使用,使用的前提是对于社会来说,它是有益的,并不是恶意的使用它,人们可以随时下载,更正编写自由软件的权利。
所以,它在技术层面上是所有人的结晶,是让很多开发者的各种想法融于一身的软件。
所以该软件随着时间的推移,使用起来会更加的方便和高效。
这就是自由软件的优点,也是大家都推崇的原因。
2.2开发工具-GCC
GNUC编译器是一个非常优越的编译器,它在Linux等系统的运行是非常重要的,可以说,这个编译器是效率很高的编译器,也是大多数开源系统的使用的最多的编译器。
最初,GCC被认为是GNUCComplier的缩写。
经过10多年的发展,GCC不仅支持C语言,还支持Ada、C++、Java、ObjectiveC、Pascal、COBOL等开发语言。
但是Gcc已经不只是一个但单纯的编译器了,人们已经将它的功能扩展到很多的语言上。
GCC也变成GNUCompilerCollection(即GNU编译器族)的缩写。
目前,GCC在人们的努力下,在所有的硬件平台上的使用都很方便,而且编译的过程也是十分简练,使用时可以通过自己的控制来灵活的使它在任意一个编译阶段停止,由于Unix的可移植性较高,几乎所有的Unix系统上都能看到GCC编译器,所以随着时间的流逝,人们会更加利用这个编译器,会更加的完善编译器。
3模拟shell设计
3.1简单shell设计思想
当我们知道Shell是如何使用的,我们也就可以开始编写一个自己的shell编译器了,起初编写时可以从简单的入手,最初的shell编写的思维可以分成下而几个方法:
a)用户在终端输入命令行,然后从而获取命令的内容。
b)判断是内部命令还是外部命令。
c)根据命令的不同,来调用相应的程序。
对于外部命令执行相应的文件。
d)循环执行上面一个过程。
内部命令:
内部命令由shell程序实现。
如exit、cd、pwd、fgbg等等,其实Linux的内部命令并不是那么多,并且很多的内部命令我们是几乎不用的,所以实现简单的内部命令还是比较少的。
外部命令:
外部命令其实不像我们想的很复杂,其是一个独立的应用,可以单独的是一个执行文件,也可以都集成在一个文件中的不同函数中。
但是对于外部命令的编写还是比较复杂的。
简单的shell流程图如图2.1所示:
3.2复杂shell设计思想
我们要从简单的开始入手,然后开始慢慢的了解和学习复杂的shell,刚开始要对命令进行分析然后判断前后台调度(&)、重定向(>>、<、>)和管道(l)等,最后才判断命令是什么属性,才能继续往后面执行。
3.2.1作业及作业前后调度实现方法
在系统中,进程的运行的状态是可以切换的。
有一些我们不太使用的但是又不得不运行的程序就可以放到后台来运行,这样就可以不用在屏幕上有很多程序了,后台的程序也可以称作是后台作业。
但是有一些作业是我们必须要自己看到运行的结果,这就是前台作业。
图2.2就描述了各种进程和作业在操作系统中运行的一些状态。
当执行如下两条命令时:
$proc1|proc2&$proc3|proc4|proc5,进程会分为三个进程组。
图2.3为命令执行时它们进程状态的不同,其中procl和proc2属于同一个后台进程组中的进程,二proc3、proc4、proc5被Shell放到同一个前台进程组,在他们当中,每一个进程都是这个进程组的组进程,Shell调用wait等待它们运行结束。
一旦运行完成,shell就就调用tcsetpgrp函数将本身自己放到前台来等待下一步命令是什么操作。
3.2.2进程组、会话与终端
一个对话期,有它自己对应的控制的终端(controllingterminal)。
在控制终端与对话期中间会建立一个进程,这个进程就叫做控制进程(controllingprocess)。
图2.4为它们之间是互相怎样作用的。
在一个对话期当中,有几个进程组称为前台进程组,还有个至少一个的在后台运行的进程组,前台进程组负责对输入进行接收。
Shell中的对于作业的前后台的切换就是前台进程组和后台进程组的切换。
如果一个命令中带有一些&符号的进程组那这个就是后台进程组。
当对话期中仅仅有唯一一个控制终端,那么就有一个前台进程组,其它的任何进程组都是作为后台的进程组,不管什么时候进行作业的中断,都会是信号退出和中断。
会话、进程组与进程的之间的关系如图2.5所示。
3.2.3命令行
用户一般在输入命令时,都会在提示符后面,那么这一行命令就叫做“命令行字符串”,shell用一个数组保存字符串,当这个作业运行完成后,shell才会释放保存的空间,也包括后台的作业和挂起的作业。
这个标识符是来标记储存该作业的数据结构的作用,每个命令行字符串的内容都包含在该数据结构中,所以说,这个数据结构还是很重要的。
当该作业运行完成后,shell就删掉数据结构,释放空间。
标识符可以循环使用。
但是如果这些命令行字符串是内部命令,那么就没有必要建立该作业的数据结构,因为本来就已经在shell程序中了,没必要在多此一举了。
3.2.4&、bg、fg等信号的思想
与本系统的任务有关的命令(内部命令):
jobs、fg、bg等。
1.&被用到一个命令的最后,表示禁止命令在后台执行。
2.ctrl+z可以将一个前台的命令暂停并且将该命令放到后台。
3.jobs查看当前有多少在后台运行的命令。
4.wait%作业号则是使该作业不再继续运行,只有后台作业执行完成后才可以。
也可以后跟一个进程号作为参数。
无&:
指一个前台作业
前台作业,该作业是终端用户进行交互命令和接受信号,在前台执行
将制定的作业号存储到该指针的链表中
作业数处理numProgs、runningProgs(加)
有&:
表示为后台作业(可以有多个)
后台作业,该作业与终端交互命令和接受信号,但是在后台执行
将制定的作业号存储到该指针的链表中
作业数处理numProgs、runningProgs(加)
后台的运行机制与前台的运行机制的不同就是前台要等待子进程的完成从而才可以停止父进程的操作。
后台运行时可以在父进程中进行操作。
fg命令:
将后台进程发送信号切换到前台来,即用tcsetpgrp获取终端判断进程是否退出并阻塞父进程,调用waitpid等待。
处理作业数numProgs.runningProgs,stopedProgs(减),正常退出时Jobid=0
bg命令:
让后台作业仍在后台运行,但是父进程继续运行,处理作业数numProgs,runningProgs,stopedProgs(减),正常退出时Jobid=0。
jobs命令:
对作业的状态进行判断并且输出该状态的信息,如running,done,stoppedrunning的含义:
runningProgs>0
stopped的含义:
runninoProgs=stoppedProgs
done的含义:
runningProgs=0
3.2.5管道
管道是Linux支持的最初UnixIPC形式之一,具有这一些特点:
管道是半双工的,管道当中的内容是从一个方向向另一个方向进行;所以当两方开始通信时,要把两个管道创建起来;然而它仅仅可以用于父进程和子进程或者两个兄弟进程之间(具有亲缘关系的进程);独自组成一个相互没有关联的文件系统:
其实管道可以看做是一个文件,但是与其它普通文件不同的是它并不属于一种文件系统,而是自己有自己的特点,独自构成一种文件系统,而且只是在内存中。
数据的读出与写入:
一个进程只能向一个管道中的一头写入数据,管道另一头的进程会读出这个数据内容,这些写入的数据每次都会放在管道缓冲区的最后,但是读数据时必须要从从缓冲区的头部开始读数据。
在输入命令时要用“|”将两个命令分开,系统就会自动从左边的命令的输出当做右边的命令的输入。
当我们一直使用管道命令时,第二个命令的输出同样会作为第三个命令的输入,以此类推,管道方便了我们一直输入相同的命令的,可以直接调用相应的文件来查看命令。
管道相关函数简介
编写程序时,管道的两端,用fd[0]以及fd[1]来表示,管道的两端只能有固定的作用,不能混用。
就是说管道的一端只能用于读的作用,用fd[0]表示,将其称为读端;同样,管道的另一端则只能用于写的作用,由fd[1]来表示,将其称为写端。
但是如果当我们从管道的fd[1]进行读取数据时,或者向管道fd[0]用于输入数据的作用,那么就会发生错误,管道是严格的按照相应的规则进行的。
一般文件的大多数的IO函数都可以当做用于管道的函数,如close、read、write等等头文件:
#include进程将从标准输入文件中读取输入数据,将正确的数据输出到标准输出文件,将不正确的信息输出到标准错误文件中。
一个程序命令后可能还跟有元字符“<”或“>”,它们是重定向符,而在重定向符号后面还跟着一个文件名。
在“<”的情况下,程序的输入被重定向到一个指定的文件中。
在“>”的情况下,程序的输出被重定向到一个指定的文件中。
如果输入文件不存在,则认为是出现了错误。
4Shell的实现
4.1数据结构
在这次设计中,首先是建立了循环数组和链表数组,都用于history命令,用数组来保存以前曾经输入的命令字符。
对链表的操作必须首先把作业用链表存储起来。
首先定义链表的节点:
typedefstructNODE{
pid_tpid;进程号
charcmd[100];命令名
charstate[10];作业状态
structNODE*link;下一节点指针
}NODE;
NODE*()
{
intfd,n,i;
charbuf[80];
if((fd=open("myshell_profile",O_RDONLY,660))==-1){
printf("initenvironvariableerror\n");
exit
(1);
}
while(n=line(fd,buf)){
getenviron(n,buf);
}
envhis.start=0;
envhis.end=0;
(n,buf)。
line(fd,buf)的作用是读取行的信息到buf中。
getenviron(n,buf)的主要作用是读取line(fd,buf)读取的命令,然后用冒号分隔开buf中的信息,将命令各自放在envpath[]中,等待后面查找命令时在envpath[]中寻找命令。
这样命令前期初始化工作就已经完成了。
然后初始化history命令中链表的头指针和尾指针,将envhis.start和envhis.end的值置为0,同样将jobs命令中的头指针和尾指针分别指为空,;i++){
if(