(一)6.828 Operating System HW1: shell

1.GCC编译


  gcc 最初是“GUN C Compiler”的简称,可以被当作一个C语言的编译器,发展到今天也可以编译其他语言。

  编译分为四个阶段:

  •   预处理
  •   编译
  •   汇编
  •   链接

  在使用GCC编译的时候,可以使用 gcc test.c -o test命令一次完成编译,也可以分四步完成编译:

  1. gcc -E test.c -o test.i   //需要使用 -o 将预处理后的结果输出到 test.i (仍然是 C 代码),否则只将预处理后结果输出到屏幕。
  2. gcc -S test.i         //将预处理后的代码进行反汇编,生成汇编代码
  3. gcc -c test.s         //将汇编代码编译成目标文件,即二进制代码。-c 可以直接把 C/C++ 代码编译成机器代码。
  4. gcc test.o -o test      //将中间文件链接成可执行文件

2.sh.c整体结构


  sh.c的代码如上图所示,main函数()使用getcmd()函数获得命令行字符串后,使用parsecmd()对字符串进行解析,最后返回解析结果给runcmd()函数执行。需要注意的是解析管道命令的函数parsepipe()是一个递归函数。

  runcmd()函数是整个sh.c的“驱动器”,让我们在shell中输入的命令得到执行。这一部分函数也是Homework1中需要补全的函数,解析命令行字符串和命令结构等其他部分已经写好了。

  runcmd()函数实现了基础的三种类型命令:

  '   '  : execcmd 可执行程序命令

  ' | '  : pipecmd 管道命令

  ' > ' 或者 ' < ' :redircmd 重定向命令

了解重定向命令管道线命令可以参考:http://billie66.github.io/TLCL/book/chap07.html,有明晰简介的解释

3.补全runcmd()


execv描述

int execv(const char *path, char *const argv[]);

The execv(), execvp(), and execvP() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program.  The first argument, by convention, should point to the file name associated with the file being executed.  The array of pointers must be terminated by a NULL pointer.

linux access讲解

  讲解转载自https://blog.csdn.net/jinmie0193/article/details/79875662

函数:

#include<unistd.h>

int access(const char* pathname, int mode);

参数介绍:

  •     pathname 是文件的路径名+文件名
  •     mode:指定access的作用,取值如下

        F_OK 值为0,判断文件是否存在
        X_OK 值为1,判断对文件是可执行权限
        W_OK 值为2,判断对文件是否有写权限
        R_OK 值为4,判断对文件是否有读权限
 
注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK
返回值:成功0,失败-1

execcmd 可执行程序命令调用

  目标:

You may want to change the 6.828 shell to always try /bin, if the program doesn't exist in the current working directory, so that below you don't have to type "/bin" for each program. If you are ambitious you can implement support for a PATH variable.

  解决:

  补充代码思路就是判断当前目录下是否有能够执行的文件,没有就去"/bin/"目录底下找,找到就执行,没找到打印错误。

case ' ':
    ecmd = (struct execcmd*)cmd;
    if(ecmd->argv[0] == 0)
      exit(0);
    // fprintf(stderr, "exec not implemented\n");
    // Your code here ...

    // open file  
    if(access(ecmd->argv[0],F_OK)==0){
      execv(ecmd->argv[0],ecmd->argv);
    }
    // exec command
    else{
      const char* binPath = "/bin/";
      int pathLen = strlen(binPath) + strlen(ecmd->argv[0]);
      char *accessPath = (char *)malloc((pathLen+1)*sizeof(char));
      strcpy(accessPath,binPath);
      strcat(accessPath,ecmd->argv[0]);
      // printf("%s\n",accessPath);
      // try open file under bin fileHolder
      if(access(accessPath,F_OK)==0){
        execv(accessPath,ecmd->argv);
      }else{
        // bin does cotain this kind of command
        fprintf(stderr, "exec cmd can not implemented\n");
      }
    }
    break;

open描述

  open成功打开文件返回文件描述符,失败返回-1。

NAME

     open -- open files and directories

SYNOPSIS

     open [-e] [-t] [-f] [-F] [-W] [-R] [-n] [-g] [-j] [-h] [-s sdk]

          [-b bundle_identifier] [-a application] file ... [--args arg1 ...]

int fd = open(const char *pathname,int flags,mode_t mode);

  • 参数1(pathname):即将要打开的文件路径,例如:“a.txt”当前目录下的a.txt文件
  • 参数2(flags):flags分为两类(主类,副类)。主类:O_RDONLY 以只读方式打开   /   O_WRONLY 以只写方式打开   /O_RDWR 以可读可写方式打开
  • 副类:
  • O_CREAT 如果文件不存在则创建该文件
  • O_EXCL 如果使用O_CREAT选项且文件存在,则返回错误消息
  • O_NOCTTY 如果文件为终端,那么终端不可以调用open系统调用的那个进程的控制终端
  • O_TRUNC 如果文件已经存在泽删除文件中原有数据
  • O_APPEND 以追加的方式打开
  • 主副可以配合使用,例如:O_RDWR|O_CREAT|O_TRUNC
  • 参数3(mode)
  • mode:如果文件被新建,指定其权限未mode
  • mode是八进制权限码,0777表示文件所有者   该文件用户组     其他用户都有可读可写可执行权限

DESCRIPTION

     The open command opens a file (or a directory or URL), just as if you had double-clicked the file's icon. If no application name is specified, the default application as determined via LaunchServices is used to open the specified files.

     If the file is in the form of a URL, the file will be opened as a URL.

EXAMPLES

     "open '/Volumes/Macintosh HD/foo.txt'" opens the document in the default application for its type (as determined by LaunchServices).

     "open '/Volumes/Macintosh HD/Applications/'" opens that directory in the Finder.

close描述

NAME

     close -- delete a descriptor

SYNOPSIS

     #include <unistd.h>

     int close(int fildes);

DESCRIPTION

     The close() call deletes a descriptor from the per-process object reference table.

     If this is the last reference to the underlying object, the object will be deacti-

     vated.  For example, on the last close of a file the current seek pointer associated

     with the file is lost; on the last close of a socket(2) associated naming information

     and queued data are discarded; on the last close of a file holding an advisory lock

     the lock is released (see further flock(2)).

RETURN VALUES

     Upon successful completion, a value of 0 is returned.  Otherwise, a value of -1 is

     returned and the global integer variable errno is set to indicate the error.

redircmd 重定向命令 

  目标:

The parser already recognizes ">" and "<", and builds a redircmd for you, so your job is just filling out the missing code in runcmd for those symbols. You might find the man pages for open and close useful

Make sure you print an error message if one of the system calls you are using fails.

Make sure your implementation runs correctly with the above test input. A common error is to forget to specify the permission with which the file must be created (i.e., the 3rd argument to open).

  解决:

  根据redircmd->fd来获得文件描述符,文件描述符是一个普通的整数,但通过这个整数操作系统在打开文件表中获得这个文件的相关信息,从而操作文件。

struct redircmd {
  int type;          // < or > 
  struct cmd *cmd;   // the command to be run (e.g., an execcmd)
  char *file;        // the input/output file
  int mode;          // the mode to open the file with
  int fd;            // the file descriptor number to use for the file
};

  补充代码的思路就是,获得文件描述符以后,关闭老的文件I/0,打开重定向命令后面的文件。(注意open命令的第三个参数设置权限,不设置读写文件会有问题)。

  case '>':
  case '<':
    rcmd = (struct redircmd*)cmd;
    // fprintf(stderr, "redir not implemented\n");
    // Your code here ...
    // close old I/O
    close(rcmd->fd);
    if(open(rcmd->file,rcmd->mode,0777)<0){
      fprintf(stderr, "file can not open\n");
      exit(0);
    }

    runcmd(rcmd->cmd);
    break;

dup描述

NAME

     dup, dup2 -- duplicate an existing file descriptor

SYNOPSIS

     #include <unistd.h>

     int dup(int fildes);

     int dup2(int fildes, int fildes2);

RETURN VALUES

     Upon successful completion, the new file descriptor is returned.  Otherwise, a value

     of -1 is returned and the global integer variable errno is set to indicate the error.

pipe描述

A pipe is a small kernel buffer exposed to processes as a pair of file descriptors, one for reading and one for writing. Writing data to one end of the pipe makes that data available for reading from the other end of the pipe. Pipes provide a way for processes to communicate.

The following example code runs the program wc with standard input connected to the read end of a pipe.

     int p[2];

     char *argv[2];

     argv[0] = "wc";

     argv[1] = 0;

     pipe(p);

     if(fork() == 0) {

       close(0);

       dup(p[0]);

       close(p[0]);

       close(p[1]);

       exec("/bin/wc", argv);

     } else {

       write(p[1], "hello world\n", 12);

       close(p[0]);

       close(p[1]);

    }

 

pipecmd 管道命令

  目标:

The parser already recognizes "|", and builds a pipecmd for you, so the only code you must write is for the '|' case in runcmd. You might find the man pages for pipe, fork, close, and dup useful.

Test that you can run the above pipeline. The sort program may be in the directory /usr/bin/ and in that case you can type the absolute pathname /usr/bin/sort to run sort. (In your computer's shell you can type which sort to find out which directory in the shell's search path has an executable named "sort".)

  解决:

  补充代码的原理就是,标准的I/O被关闭后,被dup的管道p的接口替换了。先关闭标准输出,利用dup将left命令的输出流入管道,再关闭标准输入,从管道中读取right命令的输入。

  测试中的 sort等指令在目录/usr/bin/下,需要在case '  ' 下添加代码检索这个目录下的文件。

case '|':
    pcmd = (struct pipecmd*)cmd;
    // fprintf(stderr, "pipe not implemented\n");
    // Your code here ...
    if(pipe(p)<0) fprintf(stderr,"pipe failed\n");
    if(fork1() == 0){
      // close standard output,set output to pipe
      // default standard output file descriptor is 1
      close(1);
      // dup used to connect standard output and pipe
      dup(p[1]);
      close(p[0]);
      close(p[1]);
      runcmd(pcmd->left);
    }
    if(fork1() == 0){
      // close standard input,read input from pipe
      // default standard input file descriptor is 0
      close(0);
      // dup used to connect standard input and pipe
      dup(p[0]);
      close (p[0]);
      close(p[1]);
      runcmd(pcmd->right);
    }
    // close pipeline ,back to standard I/O
    close(p[0]);
    close(p[1]);
    wait(&r);
    wait(&r);
    break;

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值