自己写一个shell2008年04月23日 星期三 11:07看了falcon的一篇文章《Linux命令行上程序执行的那一刹那》,网址oss.lzu.edu/blog/blog.php? do_showone/tid_1543.html,结合其他收集到资料,终于将练习二基本完成了,下面是练习过程中的一些收获(其中可能有些理解错误,望各位指正批评^_^):
练习要求: 编写一个简易的shell,除了能执行一些普通命令外,还要能够处理含有特殊符号(<、>、|、&)的命令;
1.、什么是shell?
1.1、首先看一下baidu的解释:
文字操作系统与外部最主要的接口就叫做shell。shell是操作系统最外面的一层。shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,并且处理各种各样的操作系统的输出结果。
shell提供了你与操作系统之间通讯的方式。这种通讯可以以交互方式(从键盘输入,并且可以立即得到响应),或者以shell script(非交互)方式执行。shell script是放在文件中的一串sh
ell和操作系统命令,它们可以被重复使用。本质上,shell script是命令行命令简单的组合到一个文件里面。
Shell基本上是一个命令解释器,类似于DOS下的command。它接收用户命令(如ls等),然后调用相应的应用程序。较为通用的shell有标准的Bourne shell (sh)和C shell (csh)。
............
从上面的解释,我们可以看出:shell其实就是一个命令行解释器,是用户与内核交互的重要机制。
shell有很多类型,在此不再详述。但大多数的Linux都以bash(Bourne Again Shell)作为缺省的shell,可查看/etc/passwd文件。
1.2、shell的工作原理(shell的工作内容):
笼统的说,shell是从用户接收一个命令行,解析该命令行,然后调用操作系统指令(包括用户所写的程序)。
具体的工作步骤如下:
*打印提示符;
*得到命令行;
*解析命令:区分出命令行中的命令名和参数(如果有参数的话);
*查文件:shell为每个用户提供了一组环境变量,如可根据PATH环境变量的内容 来查命令文件;
*准备参数;
*执行命令。
通过命令:
$ ps
PID TTY TIME CMD
10028 pts/0 00:00:00 bash
10612 pts/0 00:00:00 ps
我们看到当前命令行接口实际上是一个程序,那就是/bin/bash,它打印提示符,接收用户输入的命令,分析命令序列并执行然后返回结果,完成交互。现在,我们就是要写一个类似bash的程序,使其完成相同的工作。
2 /bin/bash是怎么启动起来的?
2.1 /bin/bash
2.1.1 通过CTRL+ALT+F1(F1~F6)切换到一个普通终端,我们应该都记得:首先我们要根据提示“XXX login:”,输入用户名以及接下来的密码。当我们输入了正确的上述信息时,就会登录到字符型界面(命令行接口)。其实,/bin/bash就是由/bin/login程序启动的,就是/bin/login程序提示”XXX login:”的。
2.1.2 那么:/bin/login怎么知道是启动/bin/bash, 而不是其他的sh呢?
实际上,/bin/login会检查/etc/passwd文件,就是在这个文件中,包含了用户名、密码和该用户的登录shell。其中,登录shell就作为用户登录后的命令行程序(下例中的/bin/bash)。
如: may:x:1000:1000:may,,,:/home/may:/bin/bash
由此可知,我们可以写一个自己的shell,然后用自己写的shell的路径替代上句中的/bin/bash,作为我们的登录shell,这样系统再次启动时,就可以直接启动我们自己的shell了; 另外一种方法也可以使内核启动后执行它:在lilo或grub的启动参数上设置init=/path/to/your/own/shell就可以了^_^。
2.1.3 /bin/login又是如何打印”XXX login:” 提示符的呢?
使用strace命令,跟踪程序(/bin/login)的执行。在终端,切换到超级用户:
#strace -f -o strace.out /bin/login ;(-f: 跟踪子进程的创建; -o: 跟踪结果的输出文件)
#cat strace.out | grep /bin/bash
10802 execve("/bin/bash", ["-bash"], [/* 10 vars */]) = 0
... ...
打开strace.out文件,我们可以看到到底执行了哪些文件,读取了哪些文件。从上述结果看出:/bin/login程序用execve调用了/bin/bash命令,并且/bin/login只是在子进程中调用/bin/bash的,因为在启动/bin/bash后,/bin/login并没有退出。
2.2 接下来的问题:/bin/login是怎么启动的?
使用strace命令跟踪getty命令的执行过程,可看到是getty程序调用了execve系统调用执行了/bin/login程序。注意,此处getty是直接在主进程中执行了/bin/login,并不是创建新的子进程来执行的,因此/bin/login程序将把getty的进程空间替换掉。
2.3 又是谁启动了/sbin/getty呢?
是init进程。它是linux系统启动后的第一个进程,负责进行linux系统的一些初始化工作,这些初始化工作的配置是根据/etc/inittab来做的。/etc/inittab的语法比较简单,在此不详述。另外,需要说明的是,现在的一些linux系统里已经没有/etc/inittab文件了,那么init该根据什么来进行初始化工作呢?可参考我前面转载的文章: Ubuntu 7.10里面没有/etc/inittab文
件了[转]。
init程序是“万物之王”,是所有进程的父进程(或祖父进程....)。通过fork()函数调用,可以创建子进程来执行一些命令。我们可以发现/sbin/getty运行时,init并没有退出,因此可判断出getty是由init用fork调用创建子进程后,才通过execve来执行的。
至此,我们推断出了/bin/bash的启动过程。可以通过ps和pstree命令来查看实际情况。ps打印进程信息;pstree打印调用关系。
$ps -ef | egrep “/sbin/init | /sbin/getty | bash | /bin/login”
$pstree | egrep “init|getty|\-bash|login”
init-+-NetworkManager---2*[{NetworkManager}]
      |-5*[getty]
      |-gnome-terminal-+-bash-+-grep
      |-login---bash
可以看出:
*init是5个getty程序(直接启动)、login程序(间接启动该程序)和gnome-terminal的父进程,而后两者是bash的父进程。
*我们运行的grep命令在bash上运行,是bash的子进程。
*init进程ID是1,其父进程ID是0。
2.4 谁启动了/sbin/init呢?
2.4.1 在内核启动时,我们可能会用到Linux内核的一个启动参数init(一般在lilo或grub的配置文件中?)。通过man bootparam可以查看linux内核启动参数的作用,init参数是用来指定内核启动后要启动的第一个程序,如果没有指定该参数,内核则会依次查/sbin/init、/etc/init、/bin/init、/bin/sh,如果不到这几个文件中的任何一个,内核就要恐慌了,并停在那一动不动了。
man 手册解释:
`init=...'
This sets the initial command to be executed by the kernel. If
this is not set, or cannot be found, the kernel will try
/sbin/init, then /etc/init, then /bin/init, then /bin/sh and
panic if all of this fails.
因此,/sbin/init是由linux内核启动的。
2.4.2 那么你肯定又会问内核是怎么启动的呢?呵呵。内核是通过lilo或grub等引导程序启动的,lilo和grub都有相应的配置文件,一般对应/etc.lilo和/boot/grub/menu.lst。通过这些配置文件可以指定内核映像文件、系统根目录所在分区、启动选项标签等信息,从而能够顺利地将内核启动起来。
2.4.3 lilo和grub又是怎么运行起来的?shell创建文件并写入内容
记得主引导扇区MBR吧,一般情况下这里存放着lilo和grub的代码。而谁知道它们存放在这呢?BIOS,通过修改BIOS的默认启动设置,我们可以让系统从光盘、硬盘甚至软盘启动。正是这里的设置让BIOS知道了MBR处的代码需要执行。
2.4.4 BIOS又是如何启动起来的?
加电自检就执行到这里了.
要想了解更多的系统启动的细节,可以看看”man boot-scripts”, 这里讲述了详细的系统启动过程。
3 /bin/bash如何处理用户键入的命令:
兜了一个圈子,我们知道了大概的系统启动过程,知道/bin/bash就是系统启动后运行的一个程序,只不过该程序比较特殊,可以处理用户的请求。那么它到底是如何响应用户请求的呢?
3.1环境变量PATH:
我们在命令行输入命令的时候,并没有将它的绝对路径都写出来,可是依然会得到执行。/bin/bash是如何到这些命令的呢?通过PATH环境变量。linux支持一种策略:shell的环境变量PATH里面存放了程序的一些路径,当shell执行程序的时候,会到这些目录下面查。
3.2 命令的执行顺序:(根据falcon文章中的一系列实验) shell 获取用户命令后,如何相应不同类型的命令呢?
3.2.1 当下面四个中有任意两个或多个重名时:
命令的别名-->函数-->内置命令--->程序
有一段文字解释说 "在shell中的命令,一般用户命令分为内置命令或程序,若命令为程序,则shell会出该程序,然后将控制权交给shell来处理,若为shell内置命令,则由shell直接响应" 。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。