命令解释器Sh的编写

linux实验自己做的报告后面附有源代码,图片有点问题显示不了,后面补上



                                                                                                                 

 摘要:通过编写一个sh解释器,我们需要了解shell是如何运行一个程序的,以及如何能够连续的执行程序的。通过研究我们得出以下结论:shell通过fork创建进程,用exec在进程中运行用户指定的程序,最后shell用wait等待新进程的结束。这样一个简单的sh解释器就做成了。

1      研究背景

本文主要研究linux操作系统下的通过编写一个sh解释器以此来了解进程通信问题,在研究开始之前我们必须了解一些基础知识才能够进行下面的工作,比如linux中的shell是什么?在讨论shell之前我们先来讨论一下计算机的运作状况。举个例子来说当你要计算机输出音乐的时候,你的计算机需要什么东西呢?硬件:当然就是需要你的硬件“声卡芯片”这个设备。内核管理:操作系统的内核支持这个芯片组,当然还需要提供芯片的驱动程序。应用程序:需要用户输入发生声音的命令。这就是一个输出声音的基本步骤也就是你必须输入一个命令后硬件才能通过你的命令执行工作。那就是kernel的控制工作。我们必须通过shell将我们输入的命令与内核通信,好让内核来控制硬件准确无误的工作。shell的功能只是提供用户操作系统的一个接口,可以调用包括man,chmod ,chown, vi,fdisk,mkfs等命令[[1]]。(鸟哥  2010)普通意义上的shell就是可以接受用户输入命令的程序。它之所以被称作shell是因为它隐藏了操作系统低层的细节。同样的Unix下的图形用户界面GNOME和KDE,有时也被叫做“虚拟shell”或“图形shell”。shell是一个管理和运行程序的程序[[2]]。

2      研究内容

在学习操作系统中有个很重要的概念——进程。从本质上来说,进程就是一个正在执行的一个程序。每个进程都有自己的地址空间,也就是一组内存地址,从某个最小值(通常是0)到某个最大值,进程可以读写其中的内容[[3]]。地址空间中包括,程序,数据和它的栈。在类Unix系统中我们可以通过键入命令ps 查看进程,键入ps -la 获取更多细节显示如下:(在UNIX操作系统得到的结果)

 

$ps -la

F  S UID  PID  PID  C  PRI  NI  SZ WCHAN  TTY   CMD

000 S  504 1779  1731 0  69  0    1086  do_sel   pts/0    gv

 

S 的一列表示各个进程的状态。S列的值为R说明ps对应的进程正在运行,其他为S说明正在睡眠状态每个进程都属于相应的由UID列指明用户ID.每个进程都有一个ID(PID),同时有一个父进程ID(PPID).标记PRI和NI的列分别是进程的优先级和niceness级别。内核根据这些值来决定什么时候运行进程。一个进程可以增加到niceness级别,就像排队的时候插队。一个进程有大小,这由SZ列来表示,这个数据表示进程占用内存的大小。WCHAN列显示进程进程睡眠的原因。例子中显示的睡眠原因是等待输入。do_sel代表内核的地址。ADDR和F已经不能再用了,只是为了能够兼容它们。运行ps就像透过显微镜看一滴池塘水。能看到各式各样的进程在系统中运行。一个进程不必占连续的一段内存空间,进程也想内存一样可以被分成很多小块[[4]]。

那么进程是如何被创建的呢?通过处理机创建PCB(含有程序,必要数据),由PCB创建进程。本课题需要研究的命令解释器是什么?命令解释器有csh,ksh,zsh,bash,它们的最初来源为sh,而本文意在实现它来了解操作系统对程序的调用机制。在用户登入系统的时候,同时启动一个shell,它以终端作为标准的输入输出。一般来说它首先会显示一个系统提示符,如美元符号,提示用户shell正在等待接受一条命令。

有了以上的基础知识后下面自己动手实现一个Unix Shell .

常用的shell有以下三个主要功能:

1 运行程序

2管理输入输出

3 可编程

例如在命令行输入:

fantasy@fantasy-HP-ProBook-4436s:~$grep lp /etc/passwd

lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin

 

fantasy@fantasy-HP-ProBook-4436s:~$date

 

fantasy@fantasy-HP-ProBook-4436s:~$ls -l /etc > etc.listing

2014年06月 07日 星期六 15:36:34 CST

 

(Ubuntu 14.01 上显示)

运行程序:grep date ls 都是很普通的程序,通过shell 载入到内存运行,可以说shell是一个程序启动器。

管理输入输出:shell并不仅仅是输入程序,使用<,>和 | 符号可以将输入输出重定向。这样告诉shell讲文件输入到一个文件或者进程

编程:在shell中可以使用if … then (判断)for(循环) 来进行编程

shell 是如何运行程序的?

1 用户键入a.out ;

2shell 建立一个新的进程来运行这个程序

2 shell 将程序从磁盘载入;

3程序在它的进程中运行结束。

伪代码描述shell 的执行过程

 

  while(!end_of_input)

    get command

    excute commad

   wait for command to finish

 

fantasy@fantasy-HP-ProBook-4436s:/$ls

bin    dev  initrd.img  lost+found  opt  run   sys  var

boot   etc  lib         media       proc sbin  tmp  vmlinuz

cdrom  home lib64       mnt         root srv   usr

 

fantasy@fantasy-HP-ProBook-4436s:/$ps

  PID TTY         TIME CMD

 3500 pts/0   00:00:00 bash

 4179 pts/0   00:00:00 ps

shell读入新的一行输入建立一个新进程,在这个进程中运行,并等待结束

通过上面的操作我们知道要写一个shell,我们需要学习如何运行一个程序,如何建立一个进程,等待exit();

下面就来解决这三个问题

1 一个程序如何运行另一个程序?

使用execvp使一个程序得以在另一个程序中运行

例如运行 ls -l,一个程序调用execvp(“ls”,arglist).

Unix 如何运行一个程序?

1 将指定的程序复制到调用它的进程

2将指定的字符串数组作为argv[]传给这个程序

3运行这个程序

需要注意的是内核将新程序载入到当前进程,替代当前进程代码和数据。

通过调用execvp()我们就能写第一个版本的带提示符的shell了,程序命名为psh1.c(见附录)程序在Unbuntu14.01下运行成功

fantasy@fantasy-HP-ProBook-4436s:~$./a.out

Arg[0]?ls 

Arg[1]?-l

Arg[2]?more01

Arg[3]?

-rwxrwxr-x 1fantasy fantasy 9035  5月 25 11:38 more01

这样我们就模拟了shell的基本功能了,但是需要注意的是我们现在只能执行一个命令,按空格退出了,那么shell是如何连续的执行的呢?由基础知识我们只道,只要再新建一个进程就可以解决!

如何创建新的进程?

调用fork()

调用fork后系统的状态,当控制转移到内部fork是,内核:

1分配新的内存块和内核数据结构。

2复制原来的进程到新的进程。

3向运行进程添加新的进程。

4将控制返回给两个进程[[5]]。

可以这样建立一个进程

mypid =getpid();    //获取当前的pid

printf(“beforemy pid  %d”,mypid);

ret_fork=fork();

printf(“aftermy fork %d”,ret_fork);

系统调用fork正是解决了shell只能运行一条命令这个问题,使用fork我们能够根据其返回值判断子进程和父进程,然后在利用execvp来执行用户指明的程序

父进程如何等待子进程的退出?

我们可以调用wait等待子进程的结束。

图1.3所示wait是如何工作的,fork创建的子进程与父进程同步,遇到wait将父进程挂起,并取得子进程结束时返回给exit的值

 

 

 

 

 

 

 

 

 

 


                   图表 1 wait暂停父进程直到子进程结束

 

wait还有个功能就是告诉父进程子进程是如何结束的。一个进程以三种方式结束,成功,失败,死亡,成功exit(0),失败exit返回一个非0的值,程序也可以被一个信号杀死,wait返回结束的子进程的PID给父进程。那么父进程如何知道子进程以何种方式退出的呢?

在父进程调用子进程的时候会传给wait一个整型变量地址,用来保存退出时的状态,等子进程调用exit,会把整数值给这个变量。

上面从shell是如何运行一个程序的层层深入,shell通过fork创建进程,用exec在进程中运行用户指定的程序,最后shell用wait等待新进程的结束。

Unix shell都是使用下面这个模型,建立psh2.c(程序见附录)

 

exit

 

main

 

exec

 

 

 

 

 

wait

 

fork

 

read

 

write

 

时间

 

 

 

 

 

 

 

 

 

图表2 shell的fork() exec() wait() 循环

 

 

3      总结

通过编写sh解释器,查阅了很多资料,很多东西都与操作系统的知识相关,要了解进程,使用shell脚本编程,我们是需要去了解其中的原理,知道子进程和父进程是如何切换的,还有shell到底是什么,通过自己做一个简单的shell然后再和系统自己的shell对比虽然基本功能实现了,但是还是有很大的差距。自己能够实现也是很开心的一件事情。在做这个调查研究学习时,使我体会到了,要深入理解一个东西不仅仅去看表面,要真正的去编写代码,这样才能完全自己去掌握,这样才能不仅仅停留在理论上。

 

 

 

 

附录

 

Psh2.c   C语言实现(psh1类似)

 

#include<stdio.h>

#include<signal.h>

#include<stdlib.h>

#include<malloc.h>

 

#defineMAXARGS   20   //文件名最大字节数

 

#defineARGLEN    100  //最大输入长度

 

/*

 * 执行子程序

 */

voidexecute(char *arglist[]){

intpid,exitstatus;

    switch(pid){

       case -1:

           perror("fork failed");

           exit(1);

       case 0:

           execvp(arglist[0],arglist);

           perror("execvp failed");

           exit(1);

       default:

            while(wait(&exitstatus) != pid)

                ;

            printf("child exited withstatus %d,%d\n",exitstatus>>8,exitstatus&0377);

     }

}

/*

 *  处理字符串去掉'\n'

 */

char*makestring(cahr *buf){

   char *cp;

   buf[strlen(buf)-1]= '\0';

   cp = (char*)malloc(strlen(buf)+1);

   if(cp == NULL){

    fprinf(stderr,"no memory");

    exit(1);

   }

   strcpy(cp,buf);

   return cp;

}

int main(){

 

  char *arglist[MAXARGS+1];  //字符串数组

  int numargs;

  char argbuf[ARGLEN];

 

   numargs = 0;

   while( numargs < MAXARGS ){

     printf("arg[%d]?",numargs);

     if(fgets(argbuf,ARGLEN,stdin) &&*argbuf != '\n')

        arglist[numargs++] =makestring(argbuf);

     else{

        if(numargs >0){

            arglist[numargs] = NULL;

            execute(arglist);

            numargs = 0;

        }

     }

   }

}

 


参考文献



[[1]] [EB/OL].http://zhwikipedia.org/wiki/Unix_shell

[[2]]鸟哥.鸟哥的Linux私房菜 基础学习篇(第三版)[M].北京:人民邮电出版社,2010

[[3]] AbrahamSilberschatz Operating Systems:Design and Implementation(3rdEdition) ,1985

[[4]] bruce molay.Unix/Linux编程实践[M].北京:清华大学出版社 ,2004

[[5]]汤小丹.计算机操作系统(第三版)[M].西安:西安电子科技大学出版社,2007

 



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值