mit6.s081 lab1 Xv6 and Unix utilities

chapter 1
system call

1.1 Process and memory

  • int fork()
    创建一个进程,父进程返回子进程的pid,子进程返回0
  • int wait(int *status)
    status获取子进程exit返回的状态,wait返回值为exit的子进程的pid。如果调用wait()的进程有子进程,那么等待一个退出的子进程,如果没有子进程直接返回-1。
  • int exec(char *file, char *argv[])
    加载一个二进制文件并覆盖当前程序的进程内存,二进制文件的格式为ELF,file为二进制文件的地址,argv为程序的执行参数
    mini shell(位于user/sh.c)执行流程
    1.fork创建子进程
    2.子进程exec执行二进制文件
    3.父进程wait等待子进程exit

1.2 I/O and File descriptors
fd 0 - standard input
fd 1 - standard output
fd 2 - standard error

  • read(fd ,buf, n)
    从fd的当前offset读取n bytes并拷贝到buf中,返回读取到的内容长度,当读取完毕时返回值为0
  • write(fd, buf, n)
    从buf中写入b bytes内容到fd中,文件offset的处理与read相同,返回写入内容的长度
  • close(fd)
    释放fd,使得该fd重新可用,新分配的fd总是该进程中未使用的最小fd
  • fd = dup(1)
    复制一个fd,返回值为新的fd,两个fd共享一个文件,当调用fork和dup时会出现两个fd共享一个文件偏移量的情况,即使通过open打开相同文件也不会出现共享文件偏移量的现象

1.3 Pipes
pipe创建一个管道,返回两个fd,其中一个用于写入,另外一个用于读取,当调用read读取管道中的内容时,如果管道中没有数据,那么会等待数据写入管道或者写入端fd被close,当写入端fd被关闭后,read返回0

1.4 File system

  • chdir(path)
    改变当前路径
  • mkdir(path)
    创建一个新目录
  • mknod(path, marjor_dev_num, minor_dev_num)
    创建一个设备文件,设定最大设备号和最小设备号
  • 每个文件对应一个inode,inode相当于文件的metadata,inode中记录了文件的类型(文件或者目录),文件的长度,文件在磁盘上的位置以及文件的链接数量。通过fstat可以获取一个文件对应的inode信息,通过unlink可以释放掉对应的链接,当一个inode中的链接数量为0时inode以及对应的文件就会被删除掉

cat program原理
1.从fd 0读取内容
2.将读取到的内容写入fd 1

Lab1
启动xv6
make qemu
sleep

实现xv6中的sleep程序,sleep可以根据用户设置的停止的ticks来暂停(tick在xv6 kernel中被定义,即一个时间片的时间),需要在文件user/sleep.c中实现

  • sleep的system call 在kermel/sysproc.c中实现(sys_sleep)
  • 在Makefile的UPROGS上加上sleep
#include "kernel/types.h" // 这里定义了int
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
    if (argc != 2) { // 判断输入是否正确
        printf("input error\n");
        exit(0);
    }
        
    int tick_count = atoi(argv[1]);
    sleep(tick_count);
    exit(0);
}
  • 退出qemu
    打开另一个终端,输入命令
killall qemu-system-riscv64 
ping pong

使用pipe实现一个ping-pong程序,使用两个方向不同的pipe进行通信,一开始parent通过pipe发送一个byte到child中,child打印": received ping",然后通过pipe发送一个字节到parent中,然后exit。parent读取到管道中的数据后,打印": received pong",然后exit。在文件user/pingpong.c中实现程序

  • 使用pipe创建管道
  • 使用fork创建子进程
  • 使用read读取管道中的数据
  • 使用getpid获取进程pid
  • 在Makefile的UPROGS中添加pingpong
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]) {
    int p1[2];
    int p2[2];
    pipe(p1);
    pipe(p2);
    if (fork() == 0) {
        char buf[8];
        read(p1[0], buf, sizeof(8));
        printf("%d: received ping\n", getpid());
        write(p2[1], "a", 1);
    }
    else {
        char buf[8];
        write(p1[1], "a", 1);
        read(p2[0], buf, sizeof(buf));
        printf("%d: received pong\n", getpid());
    }
    close(p1[0]);
    close(p1[1]);
    close(p2[0]);
    close(p2[1]);
    exit(0);
}
primes

通过fork和pipe实现并发素数筛算法,打印2-35中的素数,在文件user/primes.c中实现
并发primes
并发素数筛算法:

p = get a number from left neighbor
print p
loop:
    n = get a number from left neighbor
    if (p does not divide n)
        send n to right neighbor

mit6.s081 lab1 Xv6 and Unix utilities_第1张图片

  • 注意close pipe产生的fd
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

void child_process(int p[]) {
    int child_p[2];
    close(p[1]);

    int prime;
    int len = read(p[0], &prime, sizeof(int));
    if (len == 0) {
        close(p[0]);
        exit(0);
    }
  
    printf("prime %d\n", prime);
    pipe(child_p);
    int num;
    if (fork() == 0) {
        close(p[0]);
        child_process(child_p);
    }
    else {
        close(child_p[0]);
        while (1) {
            len = read(p[0], &num, sizeof(int));
            if (len == 0) {
                close(p[0]);
                close(child_p[1]);
                wait(0);
                exit(0);
            }
            else {
                if (num % prime != 0) {
                    write(child_p[1], &num, sizeof(int));
                }
            }
        }
        
    }

}

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

实现在目录树中的文件查找,文件的显示路径的前缀与参数中的路径一致,实现在文件user/find.c中

  • 文件路径的访问方式查看ls.c的实现
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.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));
    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) {
        printf("find: cannot open %s\n", path);
        return;
    }
    // 根据fd获取stat
    if (fstat(fd, &st) < 0) {
        printf("find: cannot stat %s\n", path);
        close(fd);
        return;
    }

    switch (st.type)
    {
    case T_FILE:
        break;
    
    case T_DIR:
        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;

            // 不处理.和..目录
            if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
                continue;

            memmove(p, de.name, DIRSIZ);
            p[DIRSIZ] = 0;
            // 根据文件名获取stat
            if(stat(buf, &st) < 0){
                printf("ls: cannot stat %s\n", buf);
                continue;
            }
            if (st.type == T_DIR)
                find(buf, filename);
            else if (st.type == T_FILE) {
                if (strcmp(de.name, filename) == 0) {
                    printf("%s\n", buf);
                }  
            }
    
        }
        break;
    }
    close(fd);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("input error\n");
        exit(0);
    }

    find(argv[1], argv[2]);
    exit(0);
}
xargs

xargs的作用为将标准输入作为参数,放到xargs后面的命令的后面,例如

echo hello too | xargs echo bye实际上是在执行echo bye hello too
输出:bye hello too

在文件user/xargs.c文件中实现
xargs实现思路为:
(1)获取标准输入中的信息,读取标准输入中的每行数据,将每行数据作为一个参数
(2)使用一个char* exec_args[MAXARG]作为exec传入参数,先将传入的参数放入exec_args中,注意argv中第一个参数为xargs,所要执行的参数为第二个参数
(3)声明一个buf用于保存标准输入中的参数,读取标准输入,如果读取到’\n’将该字符变为0(标识字符串结束),将每行数据放到exec_args中
(4)使用fork和exec函数执行真正的命令,其中运行的程序为argv[1],exec中argv参数第一个参数也必须为程序名,然后才为传入的参数

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

int main(int argc, char *argv[]) {
    char buf[512];
    char *exec_argv[MAXARG] = {0};
    int argv_idx = 0;

    for (int i = 1; i < argc; i++) {
        exec_argv[argv_idx++] = argv[i];
    }
    
    int buf_idx = 0;
    int len = 0;

    while (1) {
        int begin_idx = buf_idx;
        while (1) {
            len = read(0, &buf[buf_idx], 1);

            if (len == 0 || buf[buf_idx] == '\n') {
                buf[buf_idx] = 0;
                break;
            }

            buf_idx++;
        }

        if (len == 0)
            break;

        if (buf_idx == begin_idx)
            continue;

        buf[buf_idx++] = 0;    
        
        exec_argv[argv_idx++] = buf + begin_idx;
    }
    

    if (fork() == 0) {
        exec(argv[1], exec_argv);
        exit(0);
    }
    else {
        wait(0);
    }
    exit(0);
} 
运行结果

mit6.s081 lab1 Xv6 and Unix utilities_第2张图片

你可能感兴趣的:(unix,linux,windows)