MIT 6.828 操作系统课程系列1 Utilities#

https://pdos.csail.mit.edu/6.828/2022/labs/util.html

写代码#

1. sleep#

  • 判断一下参数。把第一个参数转成int。再调sleep(system call)即可。

  • 改好代码后运行make qemu即可编译并运行xv6内核。如果编译有错会报错并停止。
    内核运行起来后就会进入命令行界面,跟一般linux相似,但是很简陋,缺少各种功能。
    ctrl-a再按x退出内核。

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

int
main(int argc, char *argv[])
{
    if(argc < 2){
        fprintf(2, "arg error\n");
    }
    int ticks = atoi(argv[1]);

    sleep(ticks);
    exit(0);
}

2. pingpong#

  • 起两个pipe。fork。父子进程收发一下消息即可。

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

int
main(int argc, char *argv[])
{
    char buf[1024];

    int fds[2];
    if(pipe(fds) < 0){
        printf("pipe() failed\n");
        exit(1);
    }

    int pid = fork();
    if(pid < 0){
        printf("fork failed\n");
        exit(1);
    }
    if(pid == 0){
        // child
        read(fds[0], buf, 1024);

        printf("%d: received ping\n", getpid());

        write(fds[1], buf, 1);
    } else {
        write(fds[1], "x", 1);

        read(fds[0], buf, 1024);
        printf("%d: received pong\n", pid);
    }

    close(fds[0]);
    close(fds[1]);

    exit(0);
}

3. primes#

  • 没仔细看原理。直接起一串进程,不断把一串数字传下去。第一个一定是质数,把它的倍数筛掉,其余的传到下一个进程。

  • fork后不用的pipe要关掉,否则read会永远block。

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

int pipe_id = 0;
char buf[8];
int fds[40][2];

int chile_func(){
    if(pipe_id > 35)
        return 0;

    // recv
    int data[40];
    memset(data, 0, sizeof(data));

    close(fds[pipe_id][1]); // close write

    int n = 0;
    int data_count = 0;

    while(1){
        n = read(fds[pipe_id][0], buf, 4);
        if(n == 0)
            break;

        data[data_count] = 0;
        data[data_count] |= buf[0] << 24;
        data[data_count] |= buf[1] << 16;
        data[data_count] |= buf[2] << 8;
        data[data_count] |= buf[3];

        data_count++;
    }

    close(fds[pipe_id][0]); // close read

    if(data_count <= 0)
        return 0;

    // work
    printf("prime %d\n", data[0]);

    if(data_count <= 1)
        return 0;

    pipe_id++;

    if(pipe(fds[pipe_id]) < 0){
        printf("pipe() failed\n");
        exit(1);
    }
    
    int pid = fork();
    if(pid < 0){
        printf("fork failed\n");
        exit(1);
    }
    if(pid == 0){
        // child
        chile_func();
        exit(0);
    } else {
        close(fds[pipe_id][0]); // close read

        for(int i = 1; i < data_count; ++i){
            if(data[i] % data[0] != 0){
                buf[0] = (data[i] >> 24) & 0xFF;
                buf[1] = (data[i] >> 16) & 0xFF;
                buf[2] = (data[i] >> 8) & 0xFF;
                buf[3] = data[i] & 0xFF;
                write(fds[pipe_id][1], buf, 4);
            }
        }
    }

    close(fds[pipe_id][1]); // close write
    wait(0);
    return 0;
}

int main(int argc, char *argv[])
{
    if(pipe(fds[pipe_id]) < 0){
        printf("pipe() failed\n");
        exit(1);
    }

    int pid = fork();
    if(pid < 0){
        printf("fork failed\n");
        exit(1);
    }
    if(pid == 0){
        // child
        chile_func();
        exit(1);
    } else {
        // parent
        close(fds[pipe_id][0]); // close read

        for(int i = 2; i <= 35; ++i){
            buf[0] = (i >> 24) & 0xFF;
            buf[1] = (i >> 16) & 0xFF;
            buf[2] = (i >> 8) & 0xFF;
            buf[3] = i & 0xFF;
            write(fds[pipe_id][1], buf, 4);
        }
    }

    close(fds[pipe_id][1]); // close write
    wait(0);
    exit(0);
}

4. find#

  • 直接抄ls.c。把文件夹的分支改成递归find即可。

  • fmtname里把空格填充去掉可得到干净的文件名。

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

/*
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device

struct stat {
  int dev;     // File system's disk device
  uint ino;    // Inode number
  short type;  // Type of file
  short nlink; // Number of links to file
  uint64 size; // Size of file in bytes
};

*/

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));
  //memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
  memset(buf+strlen(p), 0, DIRSIZ-strlen(p));
  return buf;
}

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

    //printf("find %s in %s\n", file_name, path);

    if(fstat(fd, &st) < 0){
        fprintf(2, "find: cannot stat %s\n", path);
        close(fd);
        return;
    }

    switch(st.type){
        case T_DEVICE:
        case T_FILE:
            if(strcmp(file_name, fmtname(path)) == 0)
                //fprintf(1, "%s\n", path);
                printf("%s\n", path);

            break;

        case T_DIR:
            //printf("dir %s\n", path);
            
            if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
                printf("find: 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("find: cannot stat %s\n", buf);
                    continue;
                }

                //printf("check %s\n", buf);

                if(strcmp(".", fmtname(buf)) == 0 || strcmp("..", fmtname(buf)) == 0)
                    continue;

                //printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
                find(buf, file_name);
            }
            break;
    }
    close(fd);
}

int main(int argc, char *argv[])
{
    find(argv[1], argv[2]);
    exit(0);
}


5. xargs#

  • 有很多细节。

  • 得把find.c的输出写到stdout的1。之前输入到stderr的2了,半天取不到数据。或者直接用printf。

  • 管道传参数时,xargs从stdin的0取数据。碰到空格或\n时形成一个参数。

  • fork一下,子进程里exec执行即可。

  • 拼装exec的argv时最后要有一项0。

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

// sh < xargstest.sh

int main(int argc, char *argv[])
{
    char wtf;
    char buf[256];
    memset(buf, 0, sizeof(buf));

    char *new_argv[32];

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

    new_argv[i] = buf;
    new_argv[i + 1] = 0;

    while(read(0, &wtf, 1)){
        if(wtf != '\n' && wtf != ' '  && wtf != 0) {
            buf[strlen(buf)] = wtf;
            //printf("%s\n", buf);
            continue;
        }

        //printf("got %s\n", buf);

        int pid = fork();
        if(pid < 0){
            printf("fork failed\n");
            exit(1);
        }
        if(pid == 0){
            // child

            exec(argv[1], new_argv);
            exit(0);
            
        } else {
            wait(0);
            memset(buf, 0, sizeof(buf));
        }

        //sleep(30);
    }

    return 0;
}

创建一个time.txt填一个数字
make clean
make grade
通过全部测试