第一次的作业就把我难倒了。在此十分感谢JasonLeaster,没有这一系列的博文,我可能就要放弃了。希望能坚持下去,虽然比较难,但是我肯定能学到很多东西。
传送在此:http://blog.csdn.net/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是个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: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);
返回值:若所有欲核查的权限都通过了检查则返回0 值, 表示成功, 只要有一个权限被禁止则返回-1.int open(const char * pathname, int flags, mode_t mode);
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. void runcmd(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); } } } break; case '>': 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); break; case '|': 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); } int getcmd(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 -1; return 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); } int fork1(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 : 1; return (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; } // Parsing char 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. */ int gettoken(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] = 0; return 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; }