MIT 6.s081 实验解析——labs1

系列文章目录

MIT 6.s081 实验解析——labs1


文章目录

  • 系列文章目录
  • 测试判断流程
    • sleep
    • pingpong
    • Primes
    • find
    • xargs


测试判断流程

  1. 完成代码后将.c文件放入user文件夹中
  2. 在makefile文件的UPROGS处添加要测试的文件,如要添加的是sleep.c,则写为_sleep。
    MIT 6.s081 实验解析——labs1_第1张图片
  3. 重新编译xv6
make qemu
  1. 退出qemu,在文件夹下输入
./grade-lab-util <文件名>

//以sleep为例
./grade-lab-util sleep

sleep

MIT 6.s081 实验解析——labs1_第2张图片

   第一个实验虽然简单,但可以通过这个程序来了解整个命令的运行过程。我们将sleep命令编译入系统之后,在shell里输入sleep 10,shell接受到这段参数之后,进行判断,这是一个程序调用的指令。

//sh.c对应源码
  case EXEC:
    ecmd = (struct execcmd*)cmd;
    if(ecmd->argv[0] == 0)
      exit(1);
    exec(ecmd->argv[0], ecmd->argv);
    fprintf(2, "exec %s failed\n", ecmd->argv[0]);
    break;

可以看到在shell的内部,将第一个参数作为文件名,第二个参数作为接受参数,通过exec系统调用对应程序,这才进入到我们自己写的sleep程序中运行。注意:是将整个argv[]传给了sleep,包括argv[0]。

sleep程序很简单,当参数不为2时,直接警告参数错误,并exit(1)退出。当参数正确时,用atoi函数将字符串转为int,然后直接使用sleep系统调用,最后exit(0).

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc,char* argv[]){
    if(argc!=2){
        fprintf(2,"error,please input correct time");
        exit(1);
    }
    sleep(atoi(argv[1]));
    exit(0);
}

pingpong

MIT 6.s081 实验解析——labs1_第3张图片

  1. 创建两个管道,分别是父进程通向子进程,子进程通向父进程;f
  2. ork之后,父进程向管道发送一个字节后,wait子进程结束;
  3. 子进程从管道接收到数据后,关闭文件描述符,向另一个管道发送数据,最后退出。
  4. 等待子进程结束后,父进程接收到子进程发送的数据,父进程关闭管道,并退出。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc,char* argv[]){
    int p[2];
    int q[2];
    pipe(p);
    pipe(q);
    if (fork() == 0) {
        char data;
        read(p[0],&data,1);
        fprintf(1,"%d: received ping\n", getpid());
        close(p[0]);
        close(p[1]);
        write(q[1], &data, 1);
        close(q[0]);
        close(q[1]);
        exit(0);
    } else {
        char data;
        write(p[1], "1", 1);
        close(p[0]);
        close(p[1]);
        wait(0);
        read(q[0],&data,1);
        fprintf(1,"%d: received pong\n", getpid());
        close(q[0]);
        close(q[1]);
        exit(0);
    }
}

Primes

MIT 6.s081 实验解析——labs1_第4张图片
MIT 6.s081 实验解析——labs1_第5张图片
这是一个有些复杂的问题,因为涉及到递归,整体思路可以通过下图来表示:
MIT 6.s081 实验解析——labs1_第6张图片

p = 从左邻居中获取一个数
print p
loop:
    n = 从左邻居中获取一个数
    if (n不能被p整除)
        将n发送给右邻居

代码如下:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

__attribute__((noreturn)) 
void primes(int p[2]){
    close(p[1]);
    int first;
    int data;
    if(read(p[0], &first, sizeof(int)) == sizeof(int)){
        fprintf(1,"prime %d\n",first);
        int q[2];
        pipe(q);
        while(read(p[0], &data, sizeof(int)) == sizeof(int)){
            if(data%first){
                write(q[1], &data, sizeof(int));
            }
        }
        close(p[0]);
        close(q[1]);
        if(fork() == 0){
            primes(q);
        }else{
            close(q[0]);
            wait(0);
        }
    }
    exit(0);
}

int main(int argc,char* argv[]){
    int p[2];
    pipe(p);
    for(int i = 2; i <= 35; i++){
        write(p[1], &i, sizeof(int));
    }
    if(fork() == 0){
        primes(p);
    }else{
        close(p[0]);
        close(p[1]);
        wait(0);
    }
    exit(0);
}

在编译过程中可能会遇到这样的问题:
MIT 6.s081 实验解析——labs1_第7张图片
一般报错的函数返回值为void,解决办法是在void前加上__attribute__((noreturn))
在这里插入图片描述

find

MIT 6.s081 实验解析——labs1_第8张图片
这道题我们得先去看ls.c是怎么实现文件的遍历的:

void
ls(char *path)
{
  char buf[512], *p;
  int fd;
  struct dirent de;
  struct stat st;

  if((fd = open(path, 0)) < 0){
    fprintf(2, "ls: cannot open %s\n", path);
    return;
  }//若无法打开文件则打印错误

  if(fstat(fd, &st) < 0){
    fprintf(2, "ls: cannot stat %s\n", path);
    close(fd);
    return;
  }//若找不到文件描述符对应的文件,则打印错误

  switch(st.type){
  case T_FILE:
    printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
    break;//如果是普通的文件,标准化名称后直接打印信息

  case T_DIR:
    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
      printf("ls: path too long\n");
      break;
    }
    strcpy(buf, path);
    p = buf+strlen(buf);
    *p++ = '/';
    while(read(fd, &de, sizeof(de)) == sizeof(de)){
      if(de.inum == 0)
        continue;
      memmove(p, de.name, DIRSIZ);
      p[DIRSIZ] = 0;
      if(stat(buf, &st) < 0){
        printf("ls: cannot stat %s\n", buf);
        continue;
      }
      printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
    }
    break;
  }
  close(fd);
}
  1. 根据path找到对应的文件,确保这个文件存在。
  2. 如果这是个普通文件,则直接打印他的文件信息。
  3. 如果这是个目录文件(相当于文件夹,底下还有很多其他的文件),则去read这个目录文件(会逐个返回该目录文件下的文件,通过struct dirent),将各个文件贴到path后面,一一打印。

这个思路可以用在find程序上

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"

void find(char *path, char *name){
    char buf[512], *p;
    int fd;
    struct dirent de;
    struct stat st;

    if((fd = open(path, 0)) < 0){
        fprintf(2, "find: cannot open %s\n", path);
        return;
    }//若无法打开文件则打印错误

    if(fstat(fd, &st) < 0){
        fprintf(2, "find: cannot stat %s\n", path);
        close(fd);
        return;
    }//若找不到文件描述符对应的文件,则打印错误

    if(st.type != T_DIR){
        fprintf(2, "find: it is not a DIR");
        close(fd);
        return;
    }//若找不到文件描述符对应的文件,则打印错误

    if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
        printf("find: path too long\n");
        return;
    }
    strcpy(buf, path);
    p = buf+strlen(buf);
    *p++ = '/';
    while(read(fd, &de, sizeof(de)) == sizeof(de)){
      if(de.inum == 0)
        continue;
      memmove(p, de.name, DIRSIZ);
      p[DIRSIZ] = 0;//字符串结束标志
      if(stat(buf, &st) < 0){
        printf("find: cannot stat %s\n", buf);
        continue;
      }
      if(st.type == T_DIR && strcmp(".",p) != 0 && strcmp("..",p) != 0){
        find(buf,name);
      }else if(strcmp(name,p) == 0){
        printf("%s\n", buf);
      }
    }
    close(fd);
}

int
main(int argc, char *argv[])
{
  if(argc != 3){
    fprintf(2, "find: the number of parameter is wrong ");
    exit(1);
  }
  find(argv[1],argv[2]);
  exit(0);
}

输入参数是path和文件name,流程差不多:

  1. 根据path找到对应的文件,确保这个文件存在。
  2. 如果这是个普通文件,则直接打印他的文件信息。
  3. 如果这是个目录文件(相当于文件夹,底下还有很多其他的文件),则去read这个目录文件(会逐个返回该目录文件下的文件,通过struct dirent),递归调用find。

注意点:

1.题目要求不能在.和…中递归,意思是碰到路径是 .和…的要进行判断排除。
2.不能在递归程序最后写exit(0)!!!因为exit操作相当于整个程序退出,如果是在递归第二层exit,并不会返回上一层,而是会直接退出!!!

xargs

MIT 6.s081 实验解析——labs1_第9张图片
这道题一开始我并没有看懂他的意思,xargs的实际效果是,将xargs后面跟的参数作为基础参数,然后接受标准输入的参数拼接在基础参数后面执行,遇到\n说明是两条指令,则分开执行。(通过管道过来的参数,也相当于标准输入过来的,因为中间经过了I/O重定向)

    #include "kernel/types.h"
    #include "user/user.h"
    #include "kernel/param.h"
    int main(int argc, char *argv[]){
        char *p[MAXARG];
        int i;
        for(i=1; i < argc; i++)
            p[i-1] = argv[i];//把参数都放进p
        p[argc-1]  = malloc(512);//在最后一个参数后面分配空间
        p[argc] = 0;//在空间后面+0,表示结束
        while(gets(p[argc-1],512)){ //gets函数一次读取一行
            if(p[argc-1][0] == 0)break; //已读完
            if(p[argc-1][strlen(p[argc-1])-1]=='\n'){
                p[argc-1][strlen(p[argc-1])-1] = 0;
            } //该函数会将末尾换行保留,故需去掉换行符。
        	if(fork()==0) 
                exec(argv[1],p);
    	    else
    	        wait(0);
        }
        exit(0);
    }

你可能感兴趣的:(MIT,6.s081,xv6,操作系统,C,学习,笔记)