MIT 6.S081 Lab: Xv6 and Unix utilities

lab1 实验目的:熟悉xv6系统和它的系统调用

一. 实验前准备

按计划,先阅读下面三项

  1. 介绍: https://pdos.csail.mit.edu/6.828/2020/lec/l-overview.txt

    大体意思是:6.S081专注于系统调用接口的设计和实现。
    我们将研究UNIX(Linux,Mac,POSIX和xv6)。
    让我们看看程序如何使用系统调用接口。

  2. 例子: https://pdos.csail.mit.edu/6.828/2020/lec/l-overview/;

    实例 给了多个.c文件,上面的链接中有对应的英文解释,自己看的时候可以对应翻译到.c文件
    另外,推荐https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081,大佬应该是看了视频课程,并做了翻译,很强;

  3. read chapter 1 of the xv6 book

    推荐使用知云文献翻译,中英文对照阅读

  4. 接下来就是进行实验:作业链接:https://pdos.csail.mit.edu/6.828/2020/labs/util.html

二. 知识点

  1. UNIX约定:fd 0是“标准输入”,1是“标准输出”

  2. FD可以做一个指向“管道”的引用,就像对文件一样引用
    系统调用pipe()创建一对FD
    从第一个FD读取
    从第二个FD写入

  3. 单词

    amusement [ə’mjuzmənt] 娱乐

    assignment [ə’saɪnmənt] 任务,作业

附提问解答:

  1. 学生提问:为什么父进程在子进程调用exec之前就打印了“parent waiting”?

    Robert教授:这里只是巧合。父进程的输出有可能与子进程的输出交织在一起,就像我们之前在fork的例子中看到的一样,只是这里正好没有发生而已。并不是说我们一定能看到上面的输出,实际上,如果看到其他的输出也不用奇怪。我怀疑这里背后的原因是,exec系统调用代价比较高,它需要访问文件系统,访问磁盘,分配内存,并读取磁盘中echo文件的内容到分配的内存中,分配内存又可能需要等待内存释放。所以,exec系统调用背后会有很多逻辑,很明显,处理这些逻辑的时间足够长,这样父进程可以在exec开始执行echo指令之前完成输出。这样说得通吧?

  2. 学生提问:子进程可以等待父进程吗?

    Robert教授:Unix并没有一个直接的方法让子进程等待父进程。wait系统调用只能等待当前进程的子进程。所以wait的工作原理是,如果当前进程有任何子进程,并且其中一个已经退出了,那么wait会返回。但是如果当前进程没有任何子进程,比如在这个简单的例子中,如果子进程调用了wait,因为子进程自己没有子进程了,所以wait会立即返回-1,表明出现错误了,当前的进程并没有任何子进程。
    简单来说,不可能让子进程等待父进程退出。

  3. 学生提问:当我们说子进程从父进程拷贝了所有的内存,这里具体指的是什么呢?是不是说子进程需要重新定义变量之类的?

    Robert教授:在编译之后,你的C程序就是一些在内存中的指令,这些指令存在于内存中。所以这些指令可以被拷贝,因为它们就是内存中的字节,它们可以被拷贝到别处。通过一些有关虚拟内存的技巧,可以使得子进程的内存与父进程的内存一样,这里实际就是将父进程的内存镜像拷贝给子进程,并在子进程中执行。
    实际上,当我们在看C程序时,你应该认为它们就是一些机器指令,这些机器指令就是内存中的数据,所以可以被拷贝。

  4. 学生提问:如果父进程有多个子进程,wait是不是会在第一个子进程完成时就退出?这样的话,还有一些与父进程交错运行的子进程,是不是需要有多个wait来确保所有的子进程都完成?

    Robert教授:是的,如果一个进程调用fork两次,如果它想要等两个子进程都退出,它需要调用wait两次。每个wait会在一个子进程退出时立即返回。当wait返回时,你实际上没有必要知道哪个子进程退出了,但是wait返回了子进程的进程号,所以在wait返回之后,你就可以知道是哪个子进程退出了。

三. 作业

  1. 实现sleep

    Implement the UNIX program sleep for xv6; your sleep should pause for a user-specified number of ticks. (A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip.) Your solution should be in the file user/sleep.c. 我的代码应该写到 user/sleep.c里面

    提示:
    a. Look at some of the other programs in user/ to see how you can obtain the command-line arguments passed to a program. If the user forgets to pass an argument, sleep should print an error message. 我可以看看user下的其它文件,看看是怎么获取命令行参数,记得对空参数的处理

    The command-line argument is passed as a string; you can convert it to an integer using atoi (see user/ulib.c).
    Use the system call sleep (see user/usys.S and kernel/sysproc.c). sleep功能最终应该调用 系统调用sleep

    Make sure main calls exit() in order to exit your program. 调用exit()确保自己的程序最后要退出

    Add the program to UPROGS in Makefile and compile user programs by typing make fs.img. 把自己的程序写到makefile里的UPROGS=,并且使用make fs.img 进行编译

    Look at Kernighan and Ritchie’s book The C programming language (second edition) (K&R) to learn about C. 跟着大佬的书学习C语言

    // user/sleep.c
    #include "kernel/types.h"
    #include "user/user.h"
    
    int main(int argc, char *argv[])
    {
         
         
        if (argc <= 1)
        {
         
         
            printf("usage: sleep seconds!\n");
            exit(0);
        }
    
        int nsleep = atoi(argv[1]);
        sleep(nsleep);
        exit(0);
    }
    

    在util分支下,新建属于自己的util分支,新建user/sleep.c文件,它编译之后,生成sleep文件;
    xv6启动之后,shell输入sleep,就是运行上面编译后的文件;所以sleep 10这样的命令行参数,使用main函数的入参就可以了;
    这里argc(入参的数量)包含sleep本身,所以argv[0] = "sleep", argv[1] = "10";
    然后根据提示,调用atoisleep(int n)即可;这个sleep 参数在正常linux下应该是多少s,这里的参数是tick,解释为时钟,sleep 10感觉并没有10s,先不做深究;
    测试的时候,可以运行./grade-lab-util sleep进行测试,下面例子的也是一样;

  2. 实现pingpong

    Write a program that uses UNIX system calls to ``ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent sends by writing a byte to parent_fd[1] and the child receives it by reading from parent_fd[0]. After receiving a byte from parent, the child responds with its own byte by writing to child_fd[1], which the parent then reads. Your solution should be in the file user/pingpong.c.

    用一对管道(管道是单向的,一个进程只处理管道读或者写端,不能同时处理读或者写,不然没人知道你写的数据是要发给另一个进程还是发给你自己;所以这里需需要pipe()两次使用两个管道)实现ping-pong操作,

    #include "kernel/types.h"
    #include "user/user.h"
    
    int main(int argc, char *argv[])
    {
         
         
        int parient_fds[2], child_fds[2];
        // create a pipe, with two FDs in fds[0], fds[1].
        pipe(parient_fds);
        pipe(child_fds);
        int pid;
        pid = fork();
        char buff[100];
        if(0 == pid)
        {
         
         
            // 子进程 这里不关闭不用的文件描述符,本身这个程序使用不多,就没关,等程序退出时自行处理
            //printf("child send ping\n");
            write(parient_fds[1], "ping\n", 5);
            read(child_fds[0], buff, sizeof(buff)); // 阻塞,等待父进程写入pang
            printf("3: received %s", buff);// 写入pong的时候已经加\n,所以这里打印的时候,不用加\n
            exit(0);
        }else
        {
         
         
            // 父进程
            read(parient_fds[0], buff, sizeof(buff))
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值