MIT6.S081 Lab Utilities

Lab Utilities

本章实验学习xv6的一些softerware tools的实现,学习系统调用。
学习进程、文件相关的系统调用使用。

sleep

使用sleep系统调用,暂停指定的ticks(一个tick代表两次时钟中断的时间间隔)。
使用exit系统调用终止当前进程。

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

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

    sleep(atoi(argv[1]));

    exit(0);
}

pingpong

使用read和write系统调用利用管道进行进程间通信。
使用close系统调用关闭不用的管道端。
管道read是阻塞式的,除非对端有数据写入,或者所有指向对端的文件描述符被close掉然后read返回0。

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

int
main(int argc, char *argv[])
{
    int pipe1[2], pipe2[2];
    char buf[1];

    if(pipe(pipe1) < 0 || pipe(pipe2) < 0){
        fprintf(2, "pingpong: pipe failed\n");
        exit(1);
    }

    if(fork() == 0){
        close(pipe1[1]);
        close(pipe2[0]);

        read(pipe1[0], buf, 1);
        printf("%d: received ping\n", getpid());
        write(pipe2[1], "1", 1);
    } else {
        close(pipe1[0]);
        close(pipe2[1]);

        write(pipe1[1], "1", 1);
        read(pipe2[0], buf, 1);
        printf("%d: received pong\n", getpid());
    }

    exit(0);
}

primes

参考素数筛在论文中的实现。
地址的理解:write时,将i的地址传入,长度设为4,则write写入的是从i所在地址的长度为4字节的字符串,本质是将i的值以字符串形式传出。
在pipeline中,可以借鉴xv6的shell设计的思路(Chapter1 1.3),做I/O重定向,这样做的好处是将main和pipeline做了解耦,不需要将管道作为参数传递并且处理,直接使用fd=0的描述符进行读取。
使用dup系统调用和wait系统调用。
及时close不用的管道端口,否则程序会有问题(我这里是不停的打印0,很难调试出原因,应该是内存有限)。

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

void
pipeline()
{
    int p[2];
    int prime;

    if (read(0, &prime, 4) == 0) {
        exit(0);
    }
    printf("prime %d\n", prime);

    pipe(p);

    if (fork() == 0) {
        close(0);
        dup(p[0]);
        close(p[0]);
        close(p[1]);

        pipeline();
    } else {
        close(p[0]);

        int number;
        while (read(0, &number, 4) == 4) {
            if (number % prime != 0) {
                write(p[1], &number, 4);
            }
        }

        close(p[1]);

        wait((int*)0);
    }
    exit(0);
}

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

    pipe(p);

    if (fork() == 0) {
        close(0);
        dup(p[0]);
        close(p[0]);
        close(p[1]);

        pipeline();
    } else {
        close(p[0]);

        for (int i = 2; i <= 35; i++) {
            write(p[1], &i, 4);
        }
        close(p[1]);

        wait((int *)0);
    }
    exit(0);
}

find

目录本质上是一个文件,type是T_DIR,该文件对应的数据是一系列目录项,每个目录项dirent由名称和inode索引组成。
找该给定目录下的所有目录项,除了当前目录和上级目录,如果目录项是目录,递归查找(给定目录找文件);如果目录项是文件,对比文件名称,相同则输出。
inode number为0的目录项不可用(Chapter 8.11)。
使用fstat系统调用,熟悉dirent和stat结构体。

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

char*
fmtname(char *path)
{
    char *p;

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

    return p;
}

void
find(char path[], char file[])
{
    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:
        if (strcmp(fmtname(path), file) == 0)
            printf("%s\n", path);
        break;

    case T_DIR:
        if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
            fprintf(2, "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;

            find(buf, file);
        }
        break;
    }
    close(fd);
}

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

    find(argv[1], argv[2]);

    exit(0);
}

xargs

从标准输入读取行,并每行运行一次命令。
之所以从标准输入读,是因为shell实现了pipelines,通过创建管道链接pipelines左端和右端。来自左端的结果,输出至fd=1,这里管道的写端重定向到fd=1。右端的读端重定向到fd=0。
所以左端的结果需要成为右端的参数。
使用exec系统调用,失败才回到xargs的main。

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

int
main(int argc, char *argv[])
{
    char *arguments[MAXARG], buf[512];

    if(argc < 2) {
        fprintf(2, "Usage: xargs command\n");
        exit(1);
    }

    if (argc + 1 > MAXARG) {
        fprintf(2, "xargs: argument too much\n");
        exit(1);
    }

    for (int i = 1; i < argc; i++)
        arguments[i-1] = argv[i];

    char *p = buf;
    while (read(0, p, 1) == 1) {
        if (*p == '\n') {
            *p = '\0';
            arguments[argc-1] = buf;

            if (fork() == 0) {
                exec(argv[1], arguments);

                fprintf(2, "exec %s failed\n", argv[1]);
                exit(0);
            } else {
                wait((int*)0);
            }

            p = buf;
        } else {
            p++;
        }
    }

    exit(0);
}

Code

Code:Lab util

原文地址:https://www.cnblogs.com/seaupnice/p/15775089.html