【MIT 6.S081】2020, 实验记录(1),Lab: Xv6 and Unix utilities

目录

    • 实验准备
    • Tasks
      • [Task 1: Boot xv6](#Task 1: Boot xv6)
      • [Task 2: sleep](#Task 2: sleep)
      • [Task 3: pingpong](#Task 3: pingpong)
      • [Task 4: primes](#Task 4: primes)
      • [Task 5: find](#Task 5: find)

实验准备

这个 lab 用来学习尝试如何通过 system call 来实现常见的 shell 命令行程序,比如 lssleepxargs 等。

可以使用 docker 搭建实验环境:

shell 复制代码
docker pull debian:bullseye
docker run -it --name mit6.s081 -d debian:bullseye /bin/bash

然后按照实验官网的介绍,在 git 下载源代码并启动。

Tasks

Task 1: Boot xv6

这个 task 就是在 git 上下载源代码,并启动 xv6 系统:

shell 复制代码
$ make qemu

成功后就可以看到 OS 成功启动。

Task 2: sleep

本 task 以及接下来的 task,都是在 /user 目录下实现用户级程序。

这个 task 新建一个 user/sleep.c,并通过调用 system call 来实现睡眠的功能,代码较为简单:

c 复制代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

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

    int ticks = atoi(argv[1]);
    sleep(ticks);  // 系统调用的 sleep,声明在 `user/user.h` 中
    exit(0);
}

完成后在 Makefile 中的 UPROGS 里添加上 sleep。

测试:

shell 复制代码
./grade-lab-util sleep

Task 3: pingpong

通过该 task 学会 fork 一个子进程,并实现父进程与子进程的通信。

我们需要实现 fork 一个子进程,并让父进程向子进程发送一个字节,然后子进程再向父进程发送一个字节。

关键技术细节:

  • 使用 pipe 函数建立用于进程间通信的通道:
c 复制代码
int fds[2];
pipe(fds);

这里 fds[0] 用于 read,fds[1] 用于 write,每个进程在使用完某个 fd 后需要 close 掉 fd。

  • read() 函数的行为:在另一方未关闭写的 fd 时,自己读取且没有更多消息时会阻塞住;而在另一方关闭了写的 fd 且自己没有更多消息可以读时,会立刻返回 0.

代码实现:

c 复制代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"


int
main(int argc, char *argv[])
{
    int fds[2], pid, n, xstatus;
    enum { BYTE_SZ=1 };
    if (pipe(fds) != 0) {
        printf("pipe() failed\n");
        exit(1);
    }
    pid = fork();
    if (pid > 0) {  // parent proc
        // 向子进程发送一个字节
        char parent_buf[BYTE_SZ];
        if (write(fds[1], "x", BYTE_SZ) != BYTE_SZ) {
            printf("pingpong oops 1\n");
            exit(1);
        }
        // 等待子进程结束
        wait(&xstatus);
        if (xstatus != 0) {
            exit(xstatus);
        }
        // 读取子进程发送过来的字节
        while ((n = read(fds[0], parent_buf, BYTE_SZ)) > 0) {
            if (parent_buf[0] != 'x') {
                printf("pingpong oops 2\n");
                exit(1);
            }
            printf("%d: received pong\n", getpid());
            close(fds[0]);
            close(fds[1]);
            exit(0);
        }
    } else if (pid == 0) {  // child proc
        // 等待读取父进程发送的字节
        char child_buf[BYTE_SZ];
        while ((n = read(fds[0], child_buf, BYTE_SZ)) > 0) {
            if (child_buf[0] != 'x') {
                printf("pingpong oops 2\n");
                exit(1);
            }
            printf("%d: received ping\n", getpid());
            // 向父进程发送一个字节
            if (write(fds[1], "x", BYTE_SZ) != BYTE_SZ) {
                printf("pingpong oops 2\n");
                exit(1);
            }
            close(fds[0]);
            close(fds[1]);
            exit(0);
        }
    } else {
        printf("fork() failed\n");
        exit(1);
    }
    exit(0);
}

测试:

Task 4: primes

这里需要使用 fork 子进程和进程间通信的技术实现 2~35 以内质数的筛选。

思路如下:

大致就是:

  • 第一个进程将 2~35 写入管道
  • 然后 fork 第二个进程,逐个从管道中读出数字,再将符合条件的数字送入下一个管道给第三个进程
  • 第三个进程类似...

代码实现如下:

c 复制代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"


#define INT_SIZE (sizeof(int))
#define MAX_LIMIT  35
#define READ_END 0
#define WRITE_END 1


int
main(int argc, char *argv[])
{
    int fds1[2], fds2[2], n, p, xstatus, pid;
    int *left_fds = fds1;  // 左通道
    int *right_fds = fds2; // 右通道
    if (pipe(left_fds) != 0 || pipe(right_fds) != 0) {
        printf("pipe() failed\n");
        exit(1);
    }
    // feed numbers
    for (int i = 2; i < MAX_LIMIT; ++i) {
        write(left_fds[WRITE_END], &i, INT_SIZE);
    }
    close(left_fds[WRITE_END]);
    int first_loop = 1;
    while (1) {
        int is_success = read(left_fds[READ_END], &n, INT_SIZE);  // read from left
        if (is_success == 0) {   // if read end
            close(left_fds[READ_END]);
            close(right_fds[READ_END]);
            close(right_fds[WRITE_END]);
            if (first_loop != 1) {
                wait(&xstatus);
                exit(xstatus);
            } else {
                exit(0);
            }
        }
        // 如果是第一次进入 Loop,则需要打印 prime 并创建一个子进程
        if (first_loop == 1) {
            pid = fork();
            if (pid == 0) {  // child proc
                first_loop = 1;
                close(left_fds[READ_END]);
                close(left_fds[WRITE_END]);
                int *temp = left_fds;
                left_fds = right_fds;
                right_fds = temp;
                if (pipe(right_fds) != 0) {
                    printf("pipe() failed\n");
                    exit(1);
                }
                close(left_fds[WRITE_END]);
                continue;
            } else if (pid > 0) {  // parent proc
                first_loop = 0;
                p = n;
                printf("prime %d\n", p);
                continue;
            } else {
                printf("fork() failed\n");
                exit(1);
            }
        } else {
            if ((n % p) != 0) {
                write(right_fds[WRITE_END], &n, INT_SIZE);
            }
        }
    }
}

测试:

Task 5: find

相关推荐
兵慌码乱17 小时前
面向桌面端的资产管理系统分层架构设计与核心模块实现
python·系统架构·sqlite·pyqt5·数据库设计·桌面应用开发·mvc架构
LDR0067 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
坏孩子的诺亚方舟7 天前
FPGA系统架构设计实践15_高云Arora V系列时钟体系
fpga开发·系统架构
Luminous.7 天前
C语言--day30
c语言·开发语言
玖玥拾7 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
謓泽7 天前
C语言不是语法,是通往机器的地图。
c语言·开发语言
不会C语言的男孩7 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
桥田智能7 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
2601_951643887 天前
C语言长文整理,关键字和数据类型
c语言·数据类型·关键字·嵌入式开发·格式化输出
m0_547486667 天前
《C#语言程序设计与实践》 全套PPT课件
c语言·c#·c语言程序设计