【xv6学习之HW1】shell

本文深入讲解了Shell编程的基础知识,包括使用stat()函数获取文件信息、execv()函数执行shell命令以及open()函数进行文件操作。同时,文章还探讨了如何解析和执行复杂的Shell命令,如管道和重定向。
               

第一次的作业就把我难倒了。在此十分感谢JasonLeaster,没有这一系列的博文,我可能就要放弃了。希望能坚持下去,虽然比较难,但是我肯定能学到很多东西。

传送在此:http://blog.youkuaiyun.com/cinmyheart/article/details/45122619

不会的东西比较多,记录的东西也就比较多,比较杂。记下来先,慢慢熟悉。


看了JasonLeaster写的代码,差不多能理解个大概,也就真的是大概了。。

1、stat()函数与access()函数:获取文件信息。(参考的Jason的源代码,他使用了access()函数判断权限值,在我的代码里,删掉了此部分)

stat既有命令也有同名函数,用来获取文件Inode里主要信息(即文件类型、文件权限、创建/修改/访问时间等)。

struct stat  {      dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/      ino_t       st_ino;     /* inode number -inode节点号*/    mode_t      st_mode;    /* 文件的类型和存取的权限*/      nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/      uid_t       st_uid;     /* user ID of owner -user id*/      gid_t       st_gid;     /* group ID of owner - group id*/      dev_t       st_rdev;    /* device ID (if special file) -设备号,针对设备文件*/      off_t       st_size;    /* total size, in bytes -文件大小,字节为单位*/      blksize_t   st_blksize; /* blocksize for filesystem I/O -系统块的大小*/      blkcnt_t    st_blocks;  /* number of blocks allocated -文件所占块数*/      time_t      st_atime;   /* time of last access -最近存取时间*/      time_t      st_mtime;   /* time of last modification -最近修改时间*/      time_t      st_ctime;   /* time of last status change - */  };  

其中, st_mode该成员描述了文件的类型和权限两个属性。

st_mode是个32位的整型变量,不过现在的linux操作系统只用了低16位。特征位的定义如下:


<sys/stat.h>头文件定义如下:

#define S_IFMT     00170000     文件类型的位遮罩#define S_IFSOCK    0140000     socket#define S_IFLNK     0120000     符号链接(symbolic link)#define S_IFREG     0100000     一般文件#define S_IFBLK     0060000     区块设备(block device)#define S_IFDIR     0040000     目录#define S_IFCHR     0020000     字符设备(character device)#define S_IFIFO     0010000     先进先出(fifo)#define S_ISUID     0004000     文件的(set user-id on execution)位#define S_ISGID     0002000     文件的(set group-id on execution)位#define S_ISVTX     0001000     文件的sticky位#define S_IRWXU     00700       文件所有者的遮罩值(即所有权限值)#define S_IRUSR     00400       文件所有者具可读取权限#define S_IWUSR     00200       文件所有者具可写入权限#define S_IXUSR     00100       文件所有者具可执行权限#define S_IRWXG     00070       用户组的遮罩值(即所有权限值)#define S_IRGRP     00040       用户组具可读取权限#define S_IWGRP     00020       用户组具可写入权限#define S_IXGRP     00010       用户组具可执行权限#define S_IRWXO     00007       其他用户的遮罩值(即所有权限值)#define S_IROTH     00004       其他用户具可读取权限#define S_IWOTH     00002       其他用户具可写入权限#define S_IXOTH     00001       其他用户具可执行权限#define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)  #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)  #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)  #define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)  #define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)  #define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)  #define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK) 

首先S_IFMT是一个掩码,它的值是0170000, 可以用来过滤出前四位表示的文件类型。因此可得如上几个用来判断文件类型的宏定义。

Permission属性区域的bit0~bit8,也即st_mode字段的最低9位,代表文件的许可权限,它标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。用法类似。


stat() 函数:int stat(const char * file_name,struct stat *buf);

头文件: #include<sys/stat.h>

#include<unistd.h>

stat()用来将参数file_name所指的文件状态,复制到参数buf所指的结构中。

执行成功则返回0,失败返回-1,错误代码存于errno。

一个使用例子见:huangshanchun(包含access()函数的用法)

stat指令: $ stat xxx

利用stat命令查看文件xxx的各种属性。


对于accsee()函数:

功 能: 确定文件的访问权限。即,检查某个文件的存取方式,比如说是只读方式、只写方式等。如果指定的存取方式有效,则函数返回0,否则函数返回-1。

#include <unistd.h>int access(const char *pathname, int mode);

返回值:若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1,并且设置errno。

按实际用户ID和实际组ID测试,跟踪符号链接

参数mode:
R_OK 是否有读权限
W_OK 是否有写权限
X_OK 是否有执行权限

F_OK 测试一个文件是否存在

可以通过 | 判断多个权限是否同时满足。

由于access()只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve()执行时则会失败。



2、execv()函数

权限问题搞清楚了之后就是shell的执行函数。在该练习里面,我们实际使用的是 execv() 函数来运行shell命令。

头文件:#include <unistd.h>

定义函数:int execv (const char * path, char * const argv[]);

函数说明:execv()用来执行参数path 字符串所代表的文件路径, 与execl()不同的地方在于execv()只需两个参数, 第二个参数利用数组指针来传递给执行文件。

返回值:如果执行成功则函数不会返回, 执行失败则直接返回-1, 失败原因存于errno 中。



3、open()函数

打开文件操作使用系统调用函数open(),该函数的作用是建立一个文件描述符,其他的函数可以通过文件描述符对指定文件进行读取与写入的操作。

头文件:#include <sys/types.h>    #include <sys/stat.h>    #include <fcntl.h>

定义函数:

    int open(const char * pathname, int flags);

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

返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.
参数 pathname 指向欲打开的文件路径字符串. 下列是参数flags 所能使用的旗标:
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
O_CREAT 若欲打开的文件不存在则自动建立该文件.
O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
O_NDELAY 同O_NONBLOCK.
O_SYNC 以同步的方式打开文件.
O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.

O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。

参数mode 类似以上的mode_t数据类型(但只有12位,使用比较频繁的是低9位),我在该练习里使用的是S_IRWXU。mode参数只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umask)。关于文件权限计算的知识,可以参见老男孩的博文。

#define S_IRWXU     00700       文件所有者的遮罩值(即所有权限值)#define S_IRUSR     00400       文件所有者具可读取权限#define S_IWUSR     00200       文件所有者具可写入权限#define S_IXUSR     00100       文件所有者具可执行权限#define S_IRWXG     00070       用户组的遮罩值(即所有权限值)#define S_IRGRP     00040       用户组具可读取权限#define S_IWGRP     00020       用户组具可写入权限#define S_IXGRP     00010       用户组具可执行权限#define S_IRWXO     00007       其他用户的遮罩值(即所有权限值)#define S_IROTH     00004       其他用户具可读取权限#define S_IWOTH     00002       其他用户具可写入权限#define S_IXOTH     00001       其他用户具可执行权限

MIT6.828 hw1 shell,代码不敢细看,不会的太多,坑越挖越大,以后再细看。代码如下:参考自Jason卖萌的弱渣

#include <stdlib.h>#include <unistd.h>#include <stdio.h>#include <fcntl.h>#include <string.h>#include <assert.h>#include <sys/types.h>#include <sys/stat.h>#include <errno.h>  //用于errno变量,debug时可用// Simplifed xv6 shell.#define MAXARGS 10// All commands have at least a type. Have looked at the type, the code// typically casts the *cmd to some specific cmd type.struct cmd {  int type;          //  ' ' (exec), | (pipe), '<' or '>' for redirection};struct execcmd {  int type;              // ' '  char *argv[MAXARGS];   // arguments to the command to be exec-ed};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};struct pipecmd {  int type;          // |  struct cmd *left;  // left side of pipe  struct cmd *right; // right side of pipe};int fork1(void)// Fork but exits on failure.struct cmd *parsecmd(char*);// Execute cmd.  Never returns.voidruncmd(struct cmd *cmd)int p[2]; // used for pipe line in shell  int r;    // return value  struct execcmd *ecmd;  struct pipecmd *pcmd;  struct redircmd *rcmd;  if(cmd == 0)    exit(0);    switch(cmd->type){  default:    fprintf(stderr, "unknown runcmd\n");    exit(-1);  case ' ':    ecmd = (struct execcmd*)cmd;    if(ecmd->argv[0] == 0)        exit(0);    //fprintf(stderr, "exec not implemented\n");    // Your code here ...    if (execv(ecmd->argv[0], ecmd->argv) == -1)  //先在当前目录下搜索执行文件    {  char cmdPath[30] = "/bin/";  strcat(cmdPath, ecmd -> argv[0]);  if(execv(cmdPath, ecmd->argv) == -1//以上执行失败,在/bin/目录下搜索执行文件  {               char cmdPath2[30] = "/usr/bin/";     strcat(cmdPath2, ecmd -> argv[0]);     if(execv(cmdPath2, ecmd->argv) == -1//以上执行失败,在/usr/bin/目录下搜索执行文件     {      fprintf(stderr, "Command %s not found %d\n", ecmd -> argv[0], __LINE__); //都失败,执行文件不存在,结束当前进程         exit(0);     }         }    }    breakcase '>'case '<':    rcmd = (struct redircmd*)cmd;    //fprintf(stderr, "redir not implemented\n");    // Your code here ...    close(rcmd->fd);     //此处的fd已经根据'<'或‘>’设置为0或1    if(open(rcmd->file, rcmd->mode, 0777) < 0)  //open()函数的使用,注意第三个参数的设置,当其产生新文件时起作用    {        fprintf(stderr, "Try to open :%s failed\n", rcmd->file);        exit(0);    }    runcmd(rcmd->cmd);    breakcase '|':    pcmd = (struct pipecmd*)cmd;    //fprintf(stderr, "pipe not implemented\n");    // Your code here ...    if(pipe(p) < 0)    {        fprintf(stderr, "call syscall pipe() failed in line %d\n", __LINE__);        exit(0);    }    if(fork1() == 0)     //pipe的产生    {        close(1);        dup(p[1]);     // 标准输出被赋予fd:p[1]        close(p[0]);          close(p[1]);     // 这样fd表里只剩下标准输入fd:0 和输出fd:p[1]        runcmd(pcmd->left);    }    if(fork1() == 0)    {        close(0);        dup(p[0]);     // 标准输入被赋予fd:p[0]        close(p[0]);        close(p[1]);     // 这样fd表里只剩下标准输出fd:1 和 输入fd:p[0]        runcmd(pcmd->right);    }    close(p[0]);    close(p[1]);    wait();    wait();    break;  }      exit(0);}intgetcmd(char *buf, int nbuf){    if (isatty(fileno(stdin)))    fprintf(stdout, "6.828$ ");  memset(buf, 0, nbuf);  fgets(buf, nbuf, stdin);  if(buf[0] == 0) // EOF    return -1return 0;}int main(int argc, char *argv[]){    static char buf[100];    int fd, r;    // Read and run input commands.    while(getcmd(buf, sizeof(buf)) >= 0)    {        if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ')        {            // Clumsy but will have to do for now.            // Chdir has no effect on the parent if run in the child.            buf[strlen(buf)-1] = 0// chop \n            if(chdir(buf+3) < 0)                fprintf(stderr, "cannot cd %s\n", buf+3);            continue;        }        else if(buf[0] == 'q' && buf[1] == 'u' &&\                buf[2] == 'i' && buf[3] == 't')        {            printf("GoodBye :)\n");            return 0;        }        if(fork1() == 0)            runcmd(parsecmd(buf));        wait(&r);    }    exit(0);}intfork1(void)int pid;    pid = fork();  if(pid == -1)    perror("fork");  return pid;}struct cmd*execcmd(void)struct execcmd *cmd;  cmd = malloc(sizeof(*cmd));  memset(cmd, 0, sizeof(*cmd));  cmd->type = ' 'return (struct cmd*)cmd;}struct cmd*redircmd(struct cmd *subcmd, char *file, int type)struct redircmd *cmd;  cmd = malloc(sizeof(*cmd));  memset(cmd, 0, sizeof(*cmd));  cmd->type = type;  cmd->cmd = subcmd;  cmd->file = file;  cmd->mode = (type == '<') ?  O_RDONLY : O_WRONLY|O_CREAT|O_TRUNC;  cmd->fd = (type == '<') ? 0 : 1return (struct cmd*)cmd;}struct cmd*pipecmd(struct cmd *left, struct cmd *right)struct pipecmd *cmd;  cmd = malloc(sizeof(*cmd));  memset(cmd, 0, sizeof(*cmd));  cmd->type = '|';  cmd->left = left;  cmd->right = right;  return (struct cmd*)cmd;}// Parsingchar whitespace[] = " \t\r\n\v";char symbols[] = "<|>";/* *  Update the inputed command string after we have run a cmd. *  @ps point to the address of the inputed command string. *  This function gettoken() is gona to update this string. */intgettoken(char **ps, char *es, char **q, char **eq){    char *s = NULL;    int ret = 0;    s = *ps;    /*     *  strchr() function returns a pointer to the first occurrence of     *  the character @first_parameter in the @second_paramter     *  This function would return NULL, if the character is not found     */    while(s < es && strchr(whitespace, *s))    {        s++;    }    if(q)    {        *q = s;    }    ret = *s;    switch(*s)    {        case 0:            break;        case '|':        case '<':            s++;            break;        case '>':            s++;            break;        default:            ret = 'a';            while(s < es && !strchr(whitespace, *s) && \                 !strchr(symbols, *s))            {              s++;            }            break;    }    if(eq)    {        *eq = s;    }    while(s < es && strchr(whitespace, *s))    {        s++;    }    *ps = s;    return ret;}/* *  Update the string which @ps point to. *  If *s is not '\0' and we could find *s in toks, return 1,  *  otherwise return 0 */int peek(char **ps, char *es, char *toks)char *s;    s = *ps;  while(s < es && strchr(whitespace, *s))    s++;  *ps = s;  return *s && strchr(toks, *s);}struct cmd *parseline(char**, char*);struct cmd *parsepipe(char**, char*);struct cmd *parseexec(char**, char*);// make a copy of the characters in the input buffer, starting from s through es.// null-terminate the copy to make it a string.char *mkcopy(char *s, char *es)int n = es - s;  char *c = malloc(n+1);  assert(c);  strncpy(c, s, n);  c[n] = 0return c;}struct cmd*parsecmd(char *s)char *es;  struct cmd *cmd;  es = s + strlen(s);  cmd = parseline(&s, es);  peek(&s, es, "");  if(s != es){    fprintf(stderr, "leftovers: %s\n", s);    exit(-1);  }  return cmd;}struct cmd*parseline(char **ps, char *es)struct cmd *cmd;  cmd = parsepipe(ps, es);  return cmd;}struct cmd*parsepipe(char **ps, char *es)struct cmd *cmd;  cmd = parseexec(ps, es);  if(peek(ps, es, "|")){    gettoken(ps, es, 0, 0);    cmd = pipecmd(cmd, parsepipe(ps, es));  }  return cmd;}struct cmd*parseredirs(struct cmd *cmd, char **ps, char *es){    int tok;    char *q, *eq;    while(peek(ps, es, "<>"))    {        tok = gettoken(ps, es, 0, 0);        if(gettoken(ps, es, &q, &eq) != 'a')         {            fprintf(stderr, "missing file for redirection\n");            exit(-1);        }        switch(tok)        {            case '<':                cmd = redircmd(cmd, mkcopy(q, eq), '<');                break;            case '>':                cmd = redircmd(cmd, mkcopy(q, eq), '>');                break;        }    }    return cmd;}struct cmd*parseexec(char **ps, char *es){    char *q, *eq;    int tok, argc;    struct execcmd *cmd;    struct cmd *ret;    ret = execcmd();    cmd = (struct execcmd*)ret;    argc = 0;    ret = parseredirs(ret, ps, es);    while(!peek(ps, es, "|"))    {        if((tok=gettoken(ps, es, &q, &eq)) == 0)            break;        if(tok != 'a')         {            fprintf(stderr, "syntax error\n");            exit(-1);        }        cmd->argv[argc] = mkcopy(q, eq);        argc++;        if(argc >= MAXARGS)         {            fprintf(stderr, "too many args\n");            exit(-1);        }        ret = parseredirs(ret, ps, es);    }    cmd->argv[argc] = 0;    return ret;}




           
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值