Homework1: boot xv6 && Homework2: Shell

Homework1: boot xv6

1.获取xv6源

emiko@emiko-virtual-machine:~/firstlab$ git clone http://github.com/mit-pdos/xv6-public.git
emiko@emiko-virtual-machine:~/firstlab$ cd xv6-public
emiko@emiko-virtual-machine:~/firstlab/xv6-public$ make

2.寻找_start地址,就是内核的入口地址,并设置断点,

emiko@emiko-virtual-machine:~/firstlab/xv6-public$ nm kernel | grep _start
8010a48c D _binary_entryother_start
8010a460 D _binary_initcode_start
0010000c T _start

 设置断点查看堆栈寄存器的内容。(在Lab1中已经说过了就不赘述了)。

================================================================================================

 Homework2: Shell

sh.c源代码函数分析:

parsecmd(buf): 解析命令。buf为输入的命令。

parseline(&s,es): s为buf的起始地址,es为结束地址的下一个地址。

parsepipe(ps, es): 解析管道命令。

parseexec(ps, es):  解析重定向命令。

parseredirs: 获取重定向命令中的文件。

gettoken():获取参数变量。可以简单理解为获取命令中除<|>符号外的其他部分。以空格作为分割。

这个实验需要完成的任务如下:

1.下载sh.c文件

2.创建一个t.sh文件(用于测试sh.c)粘贴以下命令。

ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls |  sort | uniq | wc
rm y

测试过程如下:

1.使用gcc sh.c命令,对sh.c进行编译,将会产生一个a.out文件

2.执行./a.out < t.sh将会得到执行结果。

实验任务是补充sh.c中case' ', case <,>,case |,即重定向命令和管道命令。

代码的补充和分析如下:

首先关于case' '下的命令。代码很简单。但在之前,一直照着官方文档里说的,直接执行/bin/ls可以得到结果(就是需要带上目录,不能直接使用ls)试验,但是一直没有成功,感到很疑惑。后来才明白,需要执行execv这个系统调用才能使用这些普通命令。

如果应用程序正常执行完毕,execv永远不会返回;只有当应用程序出错,execv才会返回-1。

case ' ':
    ecmd = (struct execcmd*)cmd;
    if(ecmd->argv[0] == 0)
      _exit(0);
    // Your code here ...
    if(execv(ecmd->argv[0],ecmd->argv) == -1){
      char path[20] = "/bin/";
      strcat(path,ecmd->argv[0]);
      if(execv(path,ecmd->argv) == -1){
        char path1[20]="/usr/bin/";
        strcat(path1,ecmd->argv[0]);
        if(execv(path1,ecmd->argv)==-1){
          fprintf(stderr,"Command %s not found\n",ecmd->argv[0]);
          _exit(0);
        }
      }
    }
   //用注释中的代码替代以上代码,就可以直接通过/bin/cmd执行普通命令
   /* if(execv(ecmd->argv[0],ecmd->argv)==-1){
      fprintf(stderr,"Command %s not found\n",ecmd->argv[0]);
      _exit(0);
    }*/
    break;

 关于重定向命令,代码逻辑是这样:首先rcmd->fd表示的是文件描述符,stdin标准输入流的文件描述符是0,stdout的是1。如果是 > 命令的话,就关闭标准输出流,相反,关于标准输入流。

  case '>':
  case '<':
    rcmd = (struct redircmd*)cmd;
    // Your code here ...
    // stdin's file descriptioner is 0, stdout is 1
    close(rcmd->fd);
    if(open(rcmd->file,rcmd->flags,S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)<0){
      fprintf(stderr,"file %s can not find\n",rcmd->file);
      _exit(0);
    }
    runcmd(rcmd->cmd);
    break;

 在管道命令中,是将前一个命令的输出作为下一个命令的输入。主要是需要理解dup()的作用。结合代码可以理解为:

close(1),在标准输出中,关闭了文件描述符为1的文件,那么就空出来一个表项,dup(p[1])的含义就是把p[1]文件描述符复制到空出来的位置上,从而使得程序向标准输出的内容全部进入到了p[1]所表示的文件中。

case '|':
    pcmd = (struct pipecmd*)cmd;
    // Your code here ...
    if(pipe(p)<0){
    fprintf(stderr,"Falied to create a pipe\n");
    _exit(0);
    }

    if(fork1()==0){
    close(1);
    dup(p[1]);
    close(p[0]);
    close(p[1]);
    runcmd(pcmd->left);
    }

    if(fork1()==0){
     close(0);
     dup(p[0]);
     close(p[0]);
     close(p[1]);
     runcmd(pcmd->right);
    }

    close(p[0]);
    close(p[1]);
    wait(&r);
    wait(&r);
    break;

实验结果:

猜你喜欢

转载自blog.csdn.net/qq_43012789/article/details/107546568