【xv6操作系统实验】01.操作系统接口

理论准备

操作系统为程序员提供了进程、内存和文件的抽象,如果程序员需要操作它们,就需要用到操作系统提供的接口。从而,使用这些接口进行编程又被称为系统编程。

为操作系统设计接口绝非易事:一方面,接口不能太大,以保证它的实现没有bug;另一方面,用户往往希望接口的功能足够强大。计算机界的两位大师——Ken Thompson和Dennis Ritchie,他们设计了著名的Unix操作系统,其中包含了一套简单实用的操作系统接口,xv6参考了Unix的接口设计。

以下是xv6提供的所有接口:(如无特殊说明,函数正常结束时返回0,出现错误时返回-1)

系统调用 功能描述
int fork() 创建一个子进程,并返回子进程的PID.
int exit(int status) 退出当前进程;进程退出时的状态会传给wait(). 此函数不会返回。
int wait(int *status) 等待子进程退出;子进程退出时的状态会被写入*status; 返回子进程的PID.
int kill(int pid) 终止指定PID的进程。成功杀死进程时返回0,出现错误时返回-1.
int getpid() 返回当前进程的PID.
int sleep(int n) 暂停n个时钟周期。
int exec(char *file, char*argv[]) 加载并运行一个给定了参数的可执行文件;仅当出现错误时函数才会返回。
char *sbrk(int n) 将进程的内存扩大n个字节。返回新内存的首地址。
int open(char *file, int flags) 打开文件;flags指定打开方式为读或写;返回一个文件描述符(fd, file descriptor).
int write(int fd, char *buf, int n) 从buf向fd指定的文件写入n个字节;返回n.
int read(int fd, char *buf, int n) 从fd指定的文件向buf读入n个字节;返回读取的字节数;如果读到文件末尾,返回0.
int close(int fd) 关闭fd指定的文件。
int dup(int fd) 返回一个新的文件描述符,它与fd使用的是同一个文件。
int pipe(int p[]) 创建一个通道(pipe), 将用于读/写的文件描述符写入p[0]和p[1].
int chdir(char *dir) 改变当前的工作目录
int mkdir(char *dir) 新建文件夹
int mknod(char  *file, int, int) 新建一个设备文件(device file)
int fstat(int fd, struct stat *st) 将一个已打开文件的信息写入结构体*st.
int stat(char *file, struct stat *st) 将一个有名字文件的相关信息写入结构体*st.
int link(char *file1, char *file2) 为文件file1创建别名file2
int unlink(char *file) 删除文件

具体实现

在本次实验中,用户需要使用以上系统接口实现一些简单的功能,具体实验要求参见此处

警告

以下代码实现仅供参考,本人不保证其正确性。

sleep【简单】

通过sleep n指定程序休眠的时间。具体实现很简单:首先解析命令行,获取休眠的时间,之后调用sleep即可. 参考实现如下:

user/sleep.c
#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 ticks\n");
        exit(1);
    }

    char* tick_str = argv[1];
    for (int i = 0; tick_str[i] != '\0'; i++) {
        if (!(tick_str[i] >= '0' && tick_str[i] <= '9')) {
            fprintf(2, "error: %s is not a integer\n", tick_str);
            exit(1);
        }
    }

    sleep(atoi(tick_str));
    exit(0);
}

提示

main函数中,需要调用exit终止程序。

pingpong【简单】

需要通过fork克隆一个子进程,并通过pipe创建管道,通过正确设置管道的读/写端,实现父子进程间的数据传输。参考实现如下:

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


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

    if (fork() > 0) {
        // parent process
        close(p1[0]);
        write(p1[1], "c", 1);
        close(p1[1]);

        close(p2[1]);
        char str[2];
        str[1] = '\0';
        read(p2[0], str, 1);
        printf("%d: receive pong %s\n", getpid(), str);
        close(p2[0]);
    }
    else {
        // child process
        close(p1[1]);
        char buf[2];
        buf[1] = '\0';
        read(p1[0], &buf, 1);
        printf("%d: receive ping %s\n", getpid(), buf);
        close(p1[0]);

        close(p2[0]);
        write(p2[1], "s", 1);
        close(p2[1]);
    }

    exit(0);
}

primes【中等/困难】

通过筛法筛选质数,不过是基于进程间通信的实现,每筛选出一个质数,都会有一个新的进程被创建。参考实现如下:

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

void hoare(int prime, int *p)
{
    printf("prime %d\n", prime);
    close(p[1]);

    int triggered = 0;
    int value;
    int p2[2];
    pipe(p2);
    while (read(p[0], &value, 1) != 0)
    {
        if (value % prime != 0)
        {
            if (triggered == 0)
            {
                triggered = 1;
                if (fork() == 0)
                {
                    hoare(value, p2);
                }
                else
                {
                    close(p2[0]);
                }
            }
            else
            {
                write(p2[1], &value, 1);
            }
        }
    }
    close(p2[1]);
    close(p[0]);
}

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

    if (fork() == 0)
    {
        // child process
        hoare(2, p);
    }
    else
    {
        // parent process
        close(p[0]);
        for (int i = 3; i <= 35; i++)
        {
            write(p[1], &i, 1);
        }
        close(p[1]);
    }

    while (wait(0) >= 0) {
        ;
    }

    exit(0);
}

find【中等】

find用于在文件系统中查找与指定名称匹配的文件,通过系统接口了解如何遍历文件系统。参考实现如下:

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


void find_file(const char* dirpath, const char* filename) {
    int fd = open(dirpath, 0);
    if (fd < 0) {
        fprintf(2, "can't open %s\n", dirpath);
        exit(1);
    }
    
    struct dirent de;
    
    char buf[512], *p;
    if (strlen(dirpath) + 1 + DIRSIZ + 1 > sizeof(buf)) {
        fprintf(2, "find: path too long\n");
        exit(1);
    }
    strcpy(buf, dirpath);
    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;
        struct stat st;
        if (stat(buf, &st) < 0) {
            printf("cannot stat %s\n", buf);
            continue;
        }
        switch (st.type) {
            case T_DEVICE:
            case T_FILE:
                if (strcmp(de.name, filename) == 0) {
                    printf("%s\n", buf);
                }
                break;
            case T_DIR:
                find_file(buf, filename);
                break;
        }
    }
    close(fd);
}


int main(int argc, char* argv[]) {
    if (argc != 3) {
        fprintf(2, "Usage: find directory filename\n");
        exit(1);
    }

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

xargs【中等】

这里需要实现的是命令xargs的简化版本。参考实现如下:

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


int getline(char* buf) {
    int n_read = 0;
    char ch;
    while (read(0, &ch, 1) != 0) {
        if (ch == '\n') {
            buf[n_read] = '\0';
            return n_read;
        }
        else {
            buf[n_read] = ch;
            n_read += 1;
        }
    }
    return n_read;
}


int main(int argc, char* argv[]) {
    char* argbuf[MAXARG];
    for (int i = 0; i < argc; i++) {
        argbuf[i] = (char*)malloc(sizeof(char) * (strlen(argv[i+1]) + 1));
        strcpy(argbuf[i], argv[i+1]);
    }

    char buf[512];
    int n_read = 0;
    while ((n_read = getline(buf)) != 0) {
        int n_token = argc - 1;

        if (n_read >= 511) {
            fprintf(2, "error: line too long\n");
            exit(1);
        }
        buf[n_read] = ' ';
        n_read += 1;
        buf[n_read] = '\0';

        char tmp[512];
        int j = 0;
        for (int i = 0; i < n_read; i++) {
            char ch = buf[i];
            if (ch == ' ' || ch == '\r') {
                tmp[j] = '\0';
                j = 0;
                argbuf[n_token] = (char*)malloc(sizeof(char) * (strlen(tmp) + 1));
                strcpy(argbuf[n_token], tmp);
                n_token += 1;
                if (n_token >= MAXARG) {
                    fprintf(2, "error: argumens too long\n");
                    exit(1);
                }
            }
            else {
                tmp[j] = ch;
                j += 1;
            }
        }

        argbuf[n_token] = 0;
        if (fork() == 0) {
            exec(argbuf[0], argbuf);
        }
        wait(0);
    }
    exit(0);
}

 

posted @ 2024-06-22 21:43  overxus  阅读(47)  评论(0编辑  收藏  举报