之前同学提起,能区分得开xv6与JOS吗?才发现真不知道,赶紧查了查:

1.extended inspection of xv6, a traditional O/S
xv6,一个传统的操作系统的扩展的分析
2.Lab: JOS, a small O/S for x86 in an exokernel style
实验:JOS,为x86平台所写的一个小型的微内核操作系统
3.x86泛指一系列基于Intel 8086且向后兼容的中央处理器指令集架构
谢谢马如风

主要得明白文件描述符、重定向、管道、runcmd()、parsecmd()

作业内容:补全shell代码

什么是shell呢?

Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell的本质。
Shell 程序本身的功能是很弱的,比如文件操作、输入输出、进程管理等都得依赖内核。我们运行一个命令,大部分情况下 Shell都会去调用内核暴露出来的接口(系统调用),这就是在使用内核,只是这个过程被 Shell 隐藏了起来
谢谢仁兄

xv6 shell的执行流程:

  1. shell 执行 getcmd 获得用户输入的命令
  2. shell 执行 fork 创建一个shell进程的copy,然后shell进入wait状态
  3. shell 执行 runcmd 运行用户的命令
  4. runcmd函数调用exec系统调用加载适当的函数如:echo
  5. 在函数(echo) 的结束,有exit系统调用返回shell,shell从wait中退出
    谢谢卖萌的弱渣

命令有5类:

执行命令 符号: “ ”
重定向命令 > 或者 <
列表命令,也就是多个命令 符号是分号 ;
管道命令,需要先建立管道 符号是 |
返回命令(这个不知道是干嘛的),符号是 &

整个函数的核心函数有两个,一个是parsecmd,一个是runcmd
当读入命令的时候,首先进行解析
parsecmd的核心函数是parseline
这个函数是递归的,执行流程:

首先执行parsepipe,检测是否有管道命令,有的话建立管道连接
检测命令中是否有&,也就是返回命令,有的话用parsepipe的返回值生成一个新的backcmd
检测是否有;,也就是是否有多条命令分别要执行,有的话,递归调用parseline,将所有的命令分别解析之后连接起来

谢谢bdhmwz

关键与难点:弄清主要几个函数的意义与用法,尤其是参数的意义。还得懂文件描述符

1.涉及Shell 命令

ls > y 把ls结果输出到文件y中
cat cat命令的用途是连接文件或标准输入并打印。这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用一次显示整个文件:cat filename从键盘创建一个文件:cat > filename 只能创建新文件,不能编辑已有文件将几个文件合并为一个文件:cat file1 file2 > file
uniq uniq命令常用语报告或者消除文件中的重复内容,一般与sort命令结合使用
wc wc命令的功能为统计指定文件中的字节数、字数、行数, 并将统计结果显示输出ls > y
cat < y | sort | uniq | wc > y1
功能:把当前目录ls的结果存到y中,然后读取y的内容,然后将y的内容排序,去掉重复,然后统计字数,行数.并把结果保存到y1

什么是Unix Pipeline
举个例子: ls -1 | grep p | more 只列出来还有字母p的当前目录下的文件
Pipeline 使用”|“来区分多个命令,从左到右,前一个命令的结果是后一个命令的输入
谢谢仁兄

2.int execv(const char *progname, char *const argv[]);execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID没有改变。

  • progname: 被执行的应用程序。
  • argv: 传递给应用程序的参数列表, 注意,这个数组的第一个参数应该是应用程序名字本身,并且最后一个参数应该为NULL,不参将多个参数合并为一个参数放入数组。
  • 返回值:如果应用程序正常执行完毕,那么execv是永远不会返回的;当execv在调用进程中返回时,那么这个应用程序应该出错了(可能是程序本身没找到,权限不够…), 此时它的返回值应该是-1,具体的错误代码可以通过全局变量errno查看,还可以通过stderr得到具体的错误描述字符串:

谢谢卖萌的弱渣:
3. extern char strchr(char *str,char character) 从字符串str中寻找字符character第一次出现的位置。参数说明:str为一个字符串的指针,character为一个待查找字符。 所在库名:#include <string.h>

4.open 函数原型:int open(const char *pathname, int flags, mode_t mode);

  • flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR.
  • mode: 创建文件时设定的其他用户权限: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
  • 返回值:调用成功时返回一个文件描述符fd;调用失败时返回-1,并修改errno

5.dup 函数原型: int dup(int oldfd); 函数说明: create a copy of the file descriptor oldfd。dup出来的新fd共享之前fd的offset

6.int gettoken(chasr **ps, char *es, char **q, char **eq) // 把地址ps到es的字符串中的变量找到,并存到q到eq的地址去

7.int peek(char **ps, char *es, char *toks) //判断从地址ps到es的字符串是否含有toks里面的字符

8.char *mkcopy(char *s, char *es) // s指向需要拷贝的字符串头,es指向需要拷贝的字符串结尾. 这个函数拷贝从s到es的字符串,然后返回拷贝的地址。

9.struct cmd* parsecmd(char *s) // 解析命令把buffer里的命令包装成可执行的数据结构struct cmd

其他函数:

int chdir(const char * path); 用户将当前的工作目录改变成以参数路径所指的目录。

char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。errnum – 错误号,通常是 errno,是个整型

代码补全

case ’ ':
 ecmd = (struct execcmd*)cmd;if(ecmd->argv[0] == 0)_exit(0);//The hard part is figuring out what execv and its parameters mean//So that I can modify the pathif(execv(ecmd->argv[0],ecmd->argv)==-1){char mypath[20]="/bin/";strcat(mypath,ecmd->argv[0]);if(execv(mypath,ecmd->argv)==-1){strcpy(mypath,"/usr/bin/");// I write "/user/bin/" and wrong...strcat(mypath, ecmd->argv[0]);if(execv(mypath,ecmd->argv)==-1){fprintf(stderr, "Command %s can't find\n", ecmd->argv[0]);_exit(0);}}}
case ‘<’:
case ‘>’:
 rcmd = (struct redircmd*)cmd;close(rcmd->fd);if(open(rcmd->file, rcmd->flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)<0){fprintf(stderr, "file %s can't find\n", rcmd->file);_exit(0);}runcmd(rcmd->cmd);break;
case ‘|’:

最难的最有用的就是管道命令了,可以很深刻的理解文件描述符,也能非常好的明白文件重定向

文件描述符是一个int型数,它就像一个文件的索引一样,或者是一个文件指针。按我的理解,每个文件都会有一个自己的fd,用于被其他文件使用,还会有一张fd表,用于记录与其他文件的关系

谢谢 sky_Mata的图以及介绍

这位大哥说的很好,图也很清晰,但是我还是不明白怎么写完这个管道命令,于是我又读了别人的代码,画出了我的理解:
于是我写出了这段代码

pcmd = (struct pipecmd*)cmd;//fprintf(stderr, "pipe not implemented\n");// Your code here ...if(pipe(p)<0){fprintf(stderr,"Fail to create a pipe\n");_exit(0);}if(fork1()==0){//child one:read from stdin(0), write to the right end of pipe(p[1])close(1);dup(p[1]);close(p[0]);close(p[1]);runcmd(pcmd->left);}if(fork1()==0){//child two:read from the left end of pipe(p[0]), write to stdout(1)close(0);dup(p[0]);close(p[0]);close(p[1]);runcmd(pcmd->right);}close(p[0]);close(p[1]);wait(&r); //the position of wait I have a little confusionwait(&r);break;

自问自答

1.为什么我又没有写ls,cat,却可以exec它们,它们就是系统调用接口吗?
答:我认为这些是linux封装好的命令,不是系统调用,但是它们会调用很多系统调用来实现自己的功能。

2.为什么明明cmd结构体只有个type,赋值给其他结构体redircmd、execcmd等却可以把其他参数补全?
答:我认为在main()函数中的runcmd(parsecmd(buf))中,执行parsecmd后就已经根据cmd->type参数返回了相对应的结构体类型,通过struct cmd *这种基类结构体指针来存是个很聪明的选择

3.分析命令的过程还得仔细弄清楚!如“echo “6.828 is cool” > x.txt"从输入到运行下来,是怎么样的?
首先getcmd,将整条命令存到buf里,buf=“echo “6.828 is cool” > x.txt”
然后判断是不是cd命令,这里不是
然后fork1创建一个子进程,子进程里通过parsecmd将buf解析赋值给execcmd结构体
最后执行runcmd(cmd)
(其实parsecmd内部具体怎么搞得我还是比较模糊)

4.fork?
fork就是当前进程创建一个子进程,然后父进程等待子进程的执行。
子进程和父进程有着相同的文件描述符,数据也相同,只是存储的内存不同。
fork在子进程和父进程中都会有返回值,在父进程中返回子进程的pid,子进程中返回0。

5.每执行一条命令都会开辟一个进程吗?
答:不是吧,得fork才会创建新进程。main函数中fork了,管道命令中fork了两次。

6.parseredir在哪里引用了?
答:终于找到了,在parseexec()函数里的第八行处用到了

主要参考

用到的函数介绍
详细介绍
chapter 0介绍
关于管道的介绍

MIT6.828学习之homework2:shell相关推荐

  1. MIT6.828学习之Lab1

    一.概要 了解x86汇编语言,学会用QEMU模拟x86环境,学会用GDB调试 BIOS负责执行基本的系统初始化,从某些适当的位置(如 floppy disk(软盘), hard disk, CD-RO ...

  2. MIT6.828学习之homework9:Barriers

    在本作业中,我们将探讨如何使用pthread库提供的条件变量来实现barrier.barrier是应用程序中的一个点,在这个点上,所有线程都必须等待,直到所有其他线程也到达该点.条件变量是一种序列协调 ...

  3. MIT6.828课程学习初步

    MIT6.828课程学习初步 MIT6.828课程是1门比较好的操作系统原理课程,通过动手实践xv6操作系统来熟悉原理. [ 课程网站 ] 原先是想从Linux内核开始看起,但是看了一段时间,由于都是 ...

  4. Mit6.S081学习记录

    Mit6.S081学习记录 前言 一.课程简述 二.课程资源 1,课程主页 2,参考书 3,实验环境 三.学习过程 Mit6.S081-实验环境搭建 Mit6.S081-GDB使用 Mit6.S081 ...

  5. MIT6.828课程JOS在macOS下的环境配置

    本文将介绍如何在macOS下配置MIT6.828 JOS实验的环境. 写JOS之前,在网上搜寻JOS的开发环境,很多博客和文章都提到"不是32位linux就不好配置,会浪费大量时间在配置环境 ...

  6. MIT6.828——LAB1:Booting a PC

    MIT6.828--LAB1:Booting a PC Part1:PC Bootstrap 练习1: 熟悉X86汇编语言 The PC's Physical Address Space 电脑的物理地 ...

  7. Nginx学习笔记3:Shell脚本检测Nginx服务状态

    前言 nginx 服务启动后,我们需要对其服务状态进行监控,今天学习过程中了解到一段非常有用的小脚本,不仅可以监控Nginx,也可以用来监控其他服务 脚本 A=`ps -C nginx –no-hea ...

  8. 史上最牛最强的linux学习笔记 10.shell基础

    史上最牛最强的linux学习笔记 10.shell基础 写在最前面: 本文是基于某站的视频学习所得,第一个链接如下: https://www.bilibili.com/video/BV1mW411i7 ...

  9. MIT6.828 32位操作系统笔记(3)----系统的启动和初始化

    MIT EDU 6.828 实验源代码 分类 MIT6.828 32位操作系统实验笔记 实验完善代码 LAB2-4下载链接 提取码:79t8 系统的启动过程 物理内存的分布 首先分析PC 开机以后的默 ...

最新文章

  1. SpringDataRedis对Redis的数据类型的常用操作API的使用代码举例
  2. 2016-04-29 二分查找的面试题
  3. Cracer渗透-windows基础(系统目录,服务,端口,注册表)
  4. 让Team Foundation Server/TFS自动记住用户名密码解决方案
  5. 智慧交通day03-车道线检测实现02-1:相机校正
  6. word图片填充之后图片颜色变灰了_一文读懂如何快速实现网页页面变灰?
  7. 专业测试工程师浅谈功能测试用例模板设计
  8. 计算机二级access数据库考试题型,2016最新计算机二级Access数据库试题及答案
  9. python求方程的根_python计算方程式根的方法
  10. 用excel将有规律的数据随机打乱
  11. 重置网络命令win7
  12. mysql 建复合索引_mysql 建立复合索引
  13. android格式化SD卡,获取其它程序的缓存大小,清理数据
  14. redis多实例常见部署方法和使用
  15. 条形码入门指南(八):二维条形码
  16. Python(Tuirtle库)简单动画--升旗
  17. mysql及格率70以上_五个 SQL 查询性能测试题,只有 40% 及格率,你敢来挑战吗?| 原力计划...
  18. 华为系大数据专家傅一航老师--沪师经纪刘建
  19. D. Vessels(并查集+模拟)
  20. 国内首家中外合资人寿保险公司--中宏保险获批筹建陕西分公司

热门文章

  1. 7. 重磅硬核 | 一文聊透对象在JVM中的内存布局,以及内存对齐和压缩指针的原理及应用
  2. 2018-12-22-jekyll-theme-H2O
  3. 电大考试计算机应用基础考试试题,电大计算机应用基础网络教育统考考试(2013真题卷)...
  4. 数仓可视化,低代码开发平台
  5. 人生百味-10:顺势而为(外圆)与自我坚持(内方)
  6. 【手拉手 带你准备电赛】解答小课堂——为什么要使能时钟
  7. java工程师怎么找兼职,快来看鸭~
  8. Android真香系列,再也不用数据线就能真机调试啦
  9. 产品经理是做什么的?
  10. centos7加载磁盘