6.S081 lab1 Xv6_utilities

pipe结构如下

#define PIPESIZE 512

struct pipe {
  struct spinlock lock;
  char data[PIPESIZE];
  uint nread;     // number of bytes read
  uint nwrite;    // number of bytes written
  int readopen;   // read fd is still open
  int writeopen;  // write fd is still open
}; 

在pipe(fd)后,fd[2]数组已不再是一般的有两个元素的int型数组,调用write函数写入fd[1]中的数据都会传到管道并存入缓冲区

1. Sleep函数

(1) 实验目的

实现一条指令,使xv6可以暂停相应参数的ticks(tick为xv6的一个时间单位),输入格式为sleep 10。

(2) 实验步骤

要求在user/sleep.c中实现,故新建sleep.c文件。在user.h文件中有可以调用的函数,根据要求,此处只需要根据输入格式“sleep 第一个参数”调用atoi函数和sleep函数,第一个参数为sleep的ticks。由于是按char*格式传入的参数,需要调用atoi函数来转换成int型参量。

(3) 实验代码

//sleep fuction
#include "kernel/types.h"
#include "user/user.h"

int atoi(const char *s);

void main(int argc, char *argv[]){
    if(argc == 1)
        printf("Error! Please input the number of ticks.\n");
    else if(argc == 2){
        int num;
        num = atoi(argv[1]);
        sleep(num);
    }
    else{
        printf("Error! You input too much argument or no argument.\n");
    }
    exit(0);
}  

2. Pingpong函数

(1) 实验目的

在进程之间创建管道实现通信,使父进程向子进程发送ping,如果接收成功则子进程输出“: received ping”,子进程向父进程发送png,如果接收成功则父进程输出“: received pong”。

(2) 实验步骤

Pipe函数定义如下
int pipe(int fd[2]);
分别建立两个有两个元素的int型数组fd_pa[2]和fd_ch[2]作为父进程和子进程之间通信的管道,其中[0]用于读取,[1]用于写入。Pipe函数调用由于fork( )在子进程中返回值为0,而在父进程中返回值为子进程的pid(大于0),则可以由此区分情况,分别调用write( )和read( )进行写入和读取即可。输出时要求输出相应进程的pid,调用getpid( )即可。
此处子进程中应当先read到fd_pa之中的“ping”之后再向fa_ch进行write,不然出现奇怪的bug。

(3) 实验代码

//pingpong fuction
#include "kernel/types.h"
#include "user/user.h"
// #include "kernel/pipe.c"

void main(int argc, char* argv){
    int fd_pa[2], fd_ch[2];//written by parent and child
    char buf[5];
    if(pipe(fd_pa) == -1 || pipe(fd_ch) == -1){
        printf("Fail to create pipe\n");
    }
    if(fork() > 0){
        write(fd_pa[1], "ping", strlen("ping"));
        read(fd_ch[0], buf, strlen("pong"));
        printf("%d: received %s\n", getpid(), buf);
    }
    else if(fork() == 0){
        read(fd_pa[0], buf, strlen("ping"));
        printf("%d: received %s\n", getpid(), buf);
        write(fd_ch[1], "pong", strlen("ping"));
    }
    else if(fork() == -1){
        printf("Fail to create process\n");
    }
    exit(0);
}  

3. Primes函数

(1) 实验目的

通过pipe( ) 和fork( )来设置管道,以实现埃氏筛法来寻找不大于35的素数,输入为”primes”

(2) 实验步骤

筛法
思路即如图所示,每一次选取管道中第一个数作为base,剩下的数如果可以整除base则被筛掉,余下的数进行下一次筛,直到只剩下一个数。每次的base即为筛得的质数。

pipe
首先将大于等于2且小于等于35的整数输入管道中(在调用pipe函数之后,fd实际是形成了一个pipe的结构,写入fd[1]的数据实际会以char型写入data中),然后关闭写入端,防止不同进程之间同时写入。
子进程中,调用read( )读取base之后(read函数读取过的数据会被自动从data中删除掉),对管道中剩下的数分别进行检验,筛掉合数,对剩下的不能整除base的数作为新的数组调用prime函数,递归直到只剩1个数,最终即得所有的质数。
本实验中,对于各进程需要注意wait(0)的位置,不然可能会导致某一进程提前结束进而导致shell的$符随机出现并因此无法通过测试。

(3) 实验代码

//primes fuction
#include "kernel/types.h"
#include "user/user.h"

int num = 35;

//埃氏筛法寻找质数
void primes(int *array, int num){
    if(num == 1){
        printf("prime %d\n", *array);
        return ;
    }
    int fd[2];
    if(pipe(fd) == -1){
        printf("Fail to create pipe\n");
        return ;
    }
    int base, temp;
    base = *array;
    printf("prime %d\n", base);

    if(fork() == 0){
        int i;
        for(i = 0; i < num; ++i){
            temp = array[i];
            write(fd[1], &temp, sizeof(int));
        }
        exit(0);
    }

    close(fd[1]);

    if(fork() == 0){
        int ans = 0;
        while(read(fd[0], &temp, sizeof(int)) != 0){
            //每读出一段数据,被读出的数据会被从pipe中清除
            if(temp % base != 0){
                *array = temp;
                array += 1;
                ans++;
            }
        }
        primes(array - ans, ans);
        wait(0);
        exit(0);
    }
    wait(0);
}

void main(int argc, char* argv){
    int array[num - 1];;
    int i;
    for(i = 0; i < num - 1; ++i){
        array[i] = i + 2;
    }
    primes(array, num - 1);
    wait(0);
    exit(0);
}  

4. Find函数

(1) 实验目的

在指定路径及其子文件夹内寻找指定名称的文件,格式为

find <path> <filename>

(2) 实验步骤

题目中提示查看user/ls.c来了解怎样读取目录,发现ls.c实现的功能和题目要求的很相似,只缺少对文件名称进行比对后输出这一功能,故先将ls.c的代码copy至find.c中之后进行修改即可。

Ls.c的代码中,调用open( )获取相应的文件描述符并输入fd,调用fstat( )函数是st得到相应的文件属性,如果是文件,则输出,如果是文件夹,则在路径后加’/’和该文件夹下的文件名或者文件夹名,再调用该函数。

具体修改如下:在st.type是文件而不是文件夹的时候,调用strcmp函数对该文件的文件名与寻找的filename进行对比,如果一样则输出(此处需要先调用fmtname( )将路径名转化为文件名)。题目要求不输出“.”和“…”,故当de.name是“.”和“…”时,跳过

(3) 实验代码

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

char* fmtname(char *path){
    static char buf[DIRSIZ + 1];
    char *p;

    // Find first character after last slash.
    for(p = path + strlen(path); p >= path && *p != '/'; p--);
    p++;

    // Return blank-padded name.
    if(strlen(p) >= DIRSIZ)
        return p;
    memmove(buf, p, strlen(p) + 1);
    // memset(buf + strlen(p), ' ', DIRSIZ - strlen(p));
    return buf;
}

void find(char *path, char* filename){
    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;
    }

    switch(st.type){
    case T_FILE:
        // printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
        if(strcmp(fmtname(path), filename) == 0){
            printf("%s\n", path);
        }
        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 || strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 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);
            find(buf, filename);
        }
    break;
    }
    close(fd);
}

void main(int argc, char* argv[]){
    if(argc != 3){
        printf("Error! You input too much argument or no argument.\n");
        exit(0);
    }
    find(argv[1], argv[2]);
    exit(0);
}  

5. xargs函数

(1) 实验目的

在xv6中实现xargs指令,使前一条指令的输出由标准输入转化为命令行参数来作为后面指令的参数(之一)。此处要求实现的xargs指令前只有一个参数。

(2) 实验步骤

xrags
以上图为例,如果一个指令以这样的形式输入命令行,则‘|’前的输出”hello too”会被存在0(缓冲区)中。

实现xargs时,只需要在其后指令(echo)的参数后缀上前面的输出,即从0中read出来并作为最后一个参数即可。然后调用fork ( )创建子进程并调用exec( )来执行“xargs”其后的指令即可。

(3) 实验代码

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

void main(int argc, char *argv[]) {
    if(argc < 2) {
        printf("Error! You input too few arguments.\n");
        exit(0);
    }
    if(argc + 1 > MAXARG) {
        printf("Error! You input too many arguments.\n");
        exit(0);
    }
    char *command = argv[1];
    char *argument[MAXARG], buf[512];
    int i;

    for (i = 1; i < argc; i++) {
        argument[i - 1] = argv[i];
    }
    argument[argc] = 0;

    while(1) {
        i = 0;
        while(1) {
            int n = read(0, &buf[i], 1);
            if(n == 0 || buf[i] == '\n') 
                break;
            i++;
        }
        if(i == 0) 
            break;
        buf[i] = 0;
        argument[argc - 1] = buf;
        if(fork() == 0) {
            exec(command, argument);
            exit(0);
        } 
        else{
            wait(0);
        }
    }
    exit(0);
}  

6.S081 lab1 Xv6_utilities
http://zqizhang.github.io/2021/10/20/6.S081_lab1/
作者
Wang Xun
发布于
2021年10月20日
许可协议