XV6學習(1) Lab util


正在學習MIT的6.S081,把做的實驗寫一寫吧。

實驗的代碼放在了Github上。

第一個實驗是Lab util,算是一個熱身的實驗,沒有涉及到系統的底層,就是使用系統調用來完成幾個用戶模式的小程序。

Boot xv6 (easy)

啟動XV6,按照文檔執行就ok了。

$ git clone git://g.csail.mit.edu/xv6-labs-2020
$ cd xv6-labs-2020
$ git checkout util
$ make qemu

在XV6中沒有ps命令,而是使用Ctrl+p來查看正在運行的進程。

sleep (easy)

這一個就是仿照已有的程序調用一下sleep的系統調用就行了。

在Unix系統里面,默認情況下0代表stdin1代表stdout2代表stderr。這3個文件描述符在進程創建時就已經打開了的(從父進程復制過來的),可以直接使用。而分配文件描述符的時候是從當前未使用的最小的值來分配,因此可以關閉某個文件描述符再通過opendup將該文件描述符分配給其他文件或pipe來實現輸入輸出重定向。

有一個小坑就是程序執行結束后要用exit(0)來退出,而不是return 0

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

int
main(int argc, char *argv[])
{
  if(argc < 2){
    fprintf(2, "Usage: sleep [time]\n");
    exit(1);
  }

  int time = atoi(argv[1]);
  sleep(time);
  exit(0);
}

pingpong (easy)

這一個實驗就是用pipe打開兩個管道,然后fork出一個子進程,完成要求的操作就行了。

fork函數是一次調用兩次返回的函數,在父進程中返回子進程的pid,在子進程中返回0。

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

int
main(int argc, char *argv[]){
    int p2c[2];
    int c2p[2];
    if(pipe(p2c) < 0){
        printf("pipe");
        exit(-1);
    }
    if(pipe(c2p) < 0){
        printf("pipe");
        exit(-1);
    }
    int pid = fork();
    if(pid == 0){
        // child
        char buf[10];
        read(p2c[0], buf, 10);
        printf("%d: received ping\n", getpid());
        write(c2p[1], "o", 2);
    }else if(pid > 0){
        // parent
        write(p2c[1], "p", 2);
        char buf[10];
        read(c2p[0], buf, 10);
        printf("%d: received pong\n", getpid());
    }
    close(p2c[0]);
    close(p2c[1]);
    close(c2p[0]);
    close(c2p[1]);
    exit(0);
}

primes (moderate)/(hard)

這一個是實現管道的發明者Doug McIlroy提出的計算素數的方法,該方法類似於篩法,不過是用管道和多線程實現的。
管道計算素數
main函數中父進程創建了一個管道,輸入2~35。之后通過prime函數來實現輸出,如果讀取到了超過兩個的數,就創建一個新進程來進行后續處理。

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

void prime(int rd){
    int n;
    read(rd, &n, 4);
    printf("prime %d\n", n);
    int created = 0;
    int p[2];
    int num;
    while(read(rd, &num, 4) != 0){
        if(created == 0){
            pipe(p);
            created = 1;
            int pid = fork();
            if(pid == 0){
                close(p[1]);
                prime(p[0]);
                return;
            }else{
                close(p[0]);
            }
        }
        if(num % n != 0){
            write(p[1], &num, 4);
        }
    }
    close(rd);
    close(p[1]);
    wait(0);
}

int
main(int argc, char *argv[]){
    int p[2];
    pipe(p);

    int pid = fork();
    if(pid != 0){
        // first
        close(p[0]);
        for(int i = 2; i <= 35; i++){
            write(p[1], &i, 4);
        }
        close(p[1]);
        wait(0);
    }else{
        close(p[1]);
        prime(p[0]);
        close(p[0]);
    }
    exit(0);
}

find (moderate)

這一個就是仿照ls.c中的方法,對當前目錄排除掉...后進行遞歸遍歷,同時對路徑名進行匹配,匹配到了就輸出。不過庫中沒有提供strstr函數,只能自己寫了個\(O(n^2)\)的子串匹配算法。

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

void match(const char* path, const char* name){
    //printf("%s %s", path, name);
    int pp = 0;
    int pa = 0;
    while(path[pp] != 0){
        pa = 0;
        int np = pp;
        while(name[pa] != 0){
            if (name[pa] == path[np]){
                pa++;
                np++;
            }
            else
                break;
        }
        if(name[pa] == 0){
            printf("%s\n", path);
            return;
        }
        pp++;
    }
}

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, "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", path, st.type, st.ino, st.size);
            match(path, name);
            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;
                if(de.name[0] == '.' && de.name[1] == 0) continue;
                if(de.name[0] == '.' && de.name[1] == '.' && de.name[2] == 0) continue;
                memmove(p, de.name, DIRSIZ);
                p[DIRSIZ] = 0;
                if(stat(buf, &st) < 0){
                    printf("ls: cannot stat %s\n", buf);
                    continue;
                }
                find(buf, name);
            }
            break;
    }
    close(fd);
}

int
main(int argc, char *argv[]){
    if (argc < 3){
        printf("Usage: find [path] [filename]\n");
        exit(-1);
    }
    find(argv[1], argv[2]);
    exit(0);
}

xargs (moderate)

這一個就是從輸入中構造出新的argc數組,然后用forkexec執行就行了。大部分時間都用在輸入的處理上面了。。庫里面沒有提供readline函數和split,只能自己寫。

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

char* readline() {
    char* buf = malloc(100);
    char* p = buf;
    while(read(0, p, 1) != 0){
        if(*p == '\n' || *p == '\0'){
            *p = '\0';
            return buf;
        }
        p++;
    }
    if(p != buf) return buf;
    free(buf);
    return 0;
}

int
main(int argc, char *argv[]){
    if(argc < 2) {
        printf("Usage: xargs [command]\n");
        exit(-1);
    }
    char* l;
    argv++;
    char* nargv[16];
    char** pna = nargv;
    char** pa = argv;
    while(*pa != 0){
        *pna = *pa;
        pna++;
        pa++;
    }
    while((l = readline()) != 0){
        //printf("%s\n", l);
        char* p = l;
        char* buf = malloc(36);
        char* bh = buf;
        int nargc = argc - 1;
        while(*p != 0){
            if(*p == ' ' && buf != bh){
                *bh = 0;
                nargv[nargc] = buf;
                buf = malloc(36);
                bh = buf;
                nargc++;
            }else{
                *bh = *p;
                bh++;
            }
            p++;
        }
        if(buf != bh){
            nargv[nargc] = buf;
            nargc++;
        }
        nargv[nargc] = 0;
        free(l);
        int pid = fork();
        if(pid == 0){
            // printf("%s %s\n", nargv[0], nargv[1]);
            exec(nargv[0], nargv);
        }else{
            wait(0);
        }
    }
    exit(0);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM