6.1810: Operating System Engineering <LEC 1>

课程链接:6.1810 / Fall 2023

一、本节任务 

实验环境:

二、introduction and examples

2.1 open(), read(), write(), close(), dup()

使用 open 打开或创建文件,得到文件描述符 fd,再对 fd 进行 read 或者 write 操作。每个进程都默认打开三个文件描述符:0(standard input),1(standard output),2(standard error),所以当我们想把输入内容复制到输出,可以有如下程序:

// ex1.c: copy input to output.

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

int
main()
{
  char buf[64];

  while(1){
    int n = read(0, buf, sizeof(buf));
    if(n <= 0)
      break;
    write(1, buf, n);
  }

  exit(0);
}

或者使用 open 打开一个文件,向文件内写数据:

// ex2.c: create a file, write to it.

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

int
main()
{
  int fd = open("out", O_WRONLY | O_CREATE | O_TRUNC);

  printf("open returned fd %d\n", fd);

  write(fd, "ooo\n", 4);

  exit(0);
}

close() 系统调用会关闭对应的文件描述符,而 dup() 会复制一个文件描述符,被复制的文件描述符和原来的描述符指向相同的文件。 

2.2 fork(), exec()

使用 fork() 函数可以创建一个子进程,子进程相当于父进程的副本,包括指令和数据。fork 在子进程和父进程中都会返回。在子进程中,fork 返回子进程的 PID。在父进程中,fork 返回 0。如下:

// ex3.c: create a new process with fork()

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

int
main()
{
  int pid;

  pid = fork();

  printf("fork() returned %d\n", pid);

  if(pid == 0){
    printf("child\n");
  } else {
    printf("parent\n");
  }
  
  exit(0);
}

exec() 使用一个可执行文件替代当前进程,相当于让子进程去执行可执行文件的内容,一般 fork() 和 exec() 是一起使用的:

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

// ex5.c: fork then exec

int
main()
{
  int pid, status;

  pid = fork();
  if(pid == 0){
    char *argv[] = { "echo", "THIS", "IS", "ECHO", 0 };
    exec("echo", argv);
    printf("exec failed!\n");
    exit(1);
  } else {
    printf("parent waiting\n");
    wait(&status);
    printf("the child exited with status %d\n", status);
  }

  exit(0);
}

exec 会替换调用进程的内存,但将保留其文件打开表,从而使得调用 exec 来运行新程序可以实现I/O重定向:

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

// ex6.c: run a command with output redirected

int
main()
{
  int pid;

  pid = fork();
  if(pid == 0){
    close(1);
    open("out", O_WRONLY | O_CREATE | O_TRUNC);

    char *argv[] = { "echo", "this", "is", "redirected", "echo", 0 };
    exec("echo", argv);
    printf("exec failed!\n");
    exit(1);
  } else {
    wait((int *) 0);
  }

  exit(0);
}

这里先关掉了标准输出描述符 1,然后使用 open 打开 out 文件,因为 open 会选择最小的可使用的文件描述符,所以文件 out 对应的文件描述符为 1,再使用 exec 执行 echo程序,echo 程序就会把写到到标准输出上的内容写到文件 out 里。

2.3 pipe()

管道 pipe 是作为一对文件描述符公开给进程的一个小的内核缓冲区,一个用于读取,另一个用于写入(其实就是两个文件描述符指向同一个缓冲区,一个用来读,一个用来写)。将数据写入管道的一端,使该数据可以从管道的另一端读取。管道为流程提供了一种通信的方式。

使用 pipe() 系统调用初始化一个管道,两个文件描述符放到数组里面,第一个文件描述符用于读,第二个用于写。使用管道可以让父进程和子进程通信,因为 fork 复制父进程的文件描述符表及其内存,所以子进程与父进程的打开文件表相同,故父子进程可以通过 pipe 的读写两端进行通信:

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

// ex8.c: communication between two processes

int
main()
{
  int n, pid;
  int fds[2];
  char buf[100];
  
  // create a pipe, with two FDs in fds[0], fds[1].
  pipe(fds);

  pid = fork();
  if (pid == 0) {
    // child
    write(fds[1], "this is ex8\n", 12);
  } else {
    // parent
    n = read(fds[0], buf, sizeof(buf));
    write(1, buf, n);
  }

  exit(0);
}

当 pipe 所有的写端被关闭后,使用 read() 系统调用会返回 0。

相比于临时文件,管道的一大优势就是会自动清理自己。管道可以通过任意长的数据量。

三、Lab util: Unix utilities

安装实验工具链:

sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu 

clone 并进入实验仓库:

git clone git://g.csail.mit.edu/xv6-labs-2023
cd xv6-labs-2023

编译并运行 xv6:

make qemu

成功运行会打印如下信息:

xv6 kernel is booting

hart 2 starting
hart 1 starting
init: starting sh
$

下面开始完成实验。 

3.1 Sleep(easy)

实现一个用户 sleep 函数,很简单,调用系统函数 sleep 就行: 

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

/*
 * usage: sleep <tick>
 * to sleep for some tick
 */

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

        int ticks = atoi(argv[1]);


        sleep(ticks);

        exit(0);
}

3.2 pingpong(easy)

实现父进程和子进程之间交换一个字节,使用前面的 pipe 即可实现父子进程的通信,因为 pipe 不是全双工的,所以这里我们用两个管道:

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

int main(int argc, char *argv[])
{
        int pid, n;
        int p1[2], p2[2];
        char buf[10];

        pipe(p1);
        pipe(p2);

        pid = fork();
        if (pid ==  0)
        {
                /* child */
                close(p1[1]); // close pipe1's write port
                close(p2[0]);
                n = read(p1[0], buf, 1);
                if (n == 1)
                {
                        fprintf(1, "%d: received ping\n", getpid());
                }
                write(p2[1], buf, 1);
        }
        else
        {
                /* parent */
                close(p1[0]);
                close(p2[1]);
                write(p1[1], "a", 1);
                n = read(p2[0], buf, 1);
                if (n == 1)
                {
                        fprintf(1, "%d: received pong\n", getpid());
                }
        }
        exit(0);
}

3.3 primes(hard)

实现Sieve质数算法,大致流程如下图,每个进程打印第一个输入的质数,然后使用第一个质数筛选一遍剩下的数(即不可被第一个质数整除),这样到达最后一个进程即可打印全部质数:

代码:

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


void run_process(int rd_pipe_fd)
{
        int pipes[2];
        int num, n;
        n = read(rd_pipe_fd, &num, sizeof(num));

        /* 判断是否有输入 */
        if (n > 0)
        {
                /* 打印第一个质数 */
                fprintf(1, "prime %d\n", num);
                pipe(pipes);

                int pid = fork();

                if (pid > 0)
                {
                        int num1;
                        /* 筛选一遍输入的数 */
                        while ((n = read(rd_pipe_fd, &num1, sizeof(num1))) != 0)
                        {
                                if (num1 % num != 0)
                                        write(pipes[1], &num1, sizeof(num1));
                        }
                        close(pipes[1]);
                        close(rd_pipe_fd);
                        run_process(pipes[0]);

                        int status;
                        wait(&status);
                        exit(0);
                }
        }
        else
        {
                close(rd_pipe_fd);
                exit(0);
        }

        return;
}

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

        pipe(pipes);

        for (i = 2; i <= 35; i++)
        {
                write(pipes[1], &i, sizeof(i));
        }
        close(pipes[1]);

        run_process(pipes[0]);

        exit(0);
}

 3.4 find (moderate)

实现 find 命令:

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.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 *filename)
{
        int fd;
        char buf[512], *p;
        struct stat st;
        struct dirent dt;

        /* open file */
        if ((fd = open(path, O_RDONLY)) < 0)
        {
                fprintf(2, "cannot open %s\n", path);
                return;
        }

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

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

                case T_DIR:
                        strcpy(buf, path);
                        p = buf + strlen(buf);
                        *p++ = '/';
                        while ((read(fd, &dt, sizeof(dt))) == sizeof(dt))
                        {
                                if ((dt.inum == 0) || (strcmp(dt.name, ".") == 0) || (strcmp(dt.name, "..") == 0))
                                        continue;
                                memmove(p, dt.name, DIRSIZ);
                                p[DIRSIZ] = 0;
                                find(buf, filename);
                        }
                        break;
        }
        close(fd);

}

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

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

        exit(0);
}

3.5 xargs (moderate)

因为不是所有指令都能直接读取标准输入作为参数,所以需要 xargs 进行转换:

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

int main(int argc, char *argv[])
{
        if (argc < 2)
        {
                fprintf(2, "Usage: xargs <command> ...");
                exit(1);
        }

        char buf[512], *p;
        int n, i;

        p = buf;

        /* read from the standard input */
        while ((n = read(0, p, sizeof(char))) > 0)
        {
                if (*p == '\n')
                {
                        char *args[MAXARG];
                        *p = '\0';

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

                        char *p2 = buf;
                        char *ch_p;
                        while ((ch_p = strchr(p2, ' ')) != 0 && ch_p < buf + strlen(buf))
                        {
                                *ch_p = '\0';
                                args[i-1] = p2;
                                i++;
                                p2 = ch_p + 1;
                        }
                        args[i-1] = p2;

                        if (fork() == 0)
                        {
                                exec(args[0], args);
                        }
                        wait(0);

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

        exit(0);
}

最后所有 test 均通过: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/152641.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神器推荐丨不可错过的10个3D模型素材库

如果你是一位设计师&#xff0c;那么你一定知道3D模型素材库对你的工作有着不可或缺的重要性。不论是创新的产品设计&#xff0c;惊艳的视觉特效&#xff0c;还是生动的角色建模&#xff0c;无不需要从各类3D模型素材库中选择适合的素材&#xff0c;来完成你的设计。那么&#…

《C++避坑神器·十六》函数默认参数和占位参数

C中函数是可以给默认参数的 注意点&#xff1a; &#xff08;1&#xff09;一旦某个参数设置为默认参数&#xff0c;那跟着后面的所有参数都必须设置默认参数 &#xff08;2&#xff09;函数的声明和定义只能有一个可以设置默认参数&#xff0c;两个都设置会报错 int f1(int a…

站群服务器 CentOS 搭建socks5多IP代理服务器详细教程,12个步骤教会你!

准备工作 首先要保证服务上能正常使用wget tar make vim&#xff0c;如果正常就直接进入【第一步】 #安装wget的命令 yum install wget#安装tar解压工具 yum install -y tar#安装make的命令 yum groupinstall "Development Tools"#安装vim的命令 yum install…

PDF/X、PDF/A、PDF/E:有什么区别,为什么有这么多格式?

PDF 是一种通用文件格式&#xff0c;允许用户演示和共享文档&#xff0c;无论软件、硬件或操作系统如何。多年来&#xff0c;已经创建了多种 PDF 子类型来满足各个行业的不同需求。让我们看看一些最流行的格式&#xff1a;PDF/X、PDF/A 和 PDF/E。 FastReport .net下载 PDF/X …

蓝桥杯每日一题2023.11.16

蓝桥杯大赛历届真题 - C 语言 B 组 - 蓝桥云课 (lanqiao.cn) 题目描述 对于此代码&#xff0c; 注释解释如下&#xff1a;答案&#xff1a;f(a,k1,m-j,b)&#xff1b; 在这里插入代码片#include <stdio.h> #define N 6 #define M 5 #define BUF 1024 void f(int a[], in…

软件外包开发设计文档

软件设计文档是在软件开发过程中编写的一个关键文档&#xff0c;用于记录系统的设计和结构。设计文档通常包含以下内容&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.引言&#xff08;Introductio…

Skywalking流程分析_8(拦截器插件的加载)

前言 在之前的文章中我们将&#xff0c;静态方法、构造方法、实例方法的增强逻辑都分析完毕&#xff0c;但在增强前&#xff0c;对于拦截类的加载是至关重要的&#xff0c;下面我们就来详细的分析 增强插件的加载 静态方法增强前的加载 //clazz 要修改的字节码的原生类 Sta…

Elasticsearch:运用向量搜索通过图像搜索找到你的小狗

作者&#xff1a;ALEX SALGADO 你是否曾经遇到过这样的情况&#xff1a;你在街上发现了一只丢失的小狗&#xff0c;但不知道它是否有主人&#xff1f; 了解如何使用向量搜索或图像搜索来做到这一点。 通过图像搜索找到你的小狗 您是否曾经遇到过这样的情况&#xff1a;你在街…

linux查看资源占用情况常用命令

1. 查看 CPU 使用情况&#xff1a; top这个命令会显示系统中当前活动进程的实时信息&#xff0c;包括 CPU 使用率、内存使用率等。按 q 键退出。 2. 查看内存使用情况&#xff1a; free -m这个命令显示系统内存的使用情况&#xff0c;以兆字节&#xff08;MB&#xff09;为…

SpringCloud Alibaba组件入门全方面汇总(上):注册中心-nacos、负载均衡-ribbon、远程调用-feign

文章目录 NacosRibbonFeignFeign拓展 Nacos 概念&#xff1a;Nacos是阿里巴巴推出的一款新开源项目&#xff0c;它是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos致力于帮助用户发现、配置和管理微服务&#xff0c;它提供了一组简单易用的特性集&am…

五金信息展示预约小程序的作用是什么

五金行业所覆盖的产品很广&#xff0c;如灯具、浴具、门窗、工具等都是人们生活所需或常用到的&#xff0c;而五金行业规模也是连年上涨&#xff0c;市场呈现多品牌多门店多区域扩展的趋势。 虽然市场规模大&#xff0c;但同样问题不少&#xff0c;接下来我们来看看几个痛点。…

PyTorch:框架的自动微分机制

近年来&#xff0c;深度学习技术的迅猛发展已经改变了许多行业&#xff0c;其中框架的自动微分机制在深度学习领域扮演了重要的角色。PyTorch作为一款深度学习框架&#xff0c;在自动微分方面具有独特的优势和特点。本文将深入探讨PyTorch框架的自动微分机制&#xff0c;包括其…

一文搞懂GPU的概念、工作原理,以及与CPU的区别

中午好&#xff0c;我的网工朋友。 最近GPTs热度很高啊&#xff0c;你们都用上了吗&#xff1f; ChatGPT到现在热度仍不减&#xff0c;人工智能还在快速发展&#xff0c;这都离不开高性能、高算力的硬件支持。 如果以英伟达A100GPU的处理能力计算&#xff0c;运行ChatGPT将需…

kubernetes集群编排——etcd

备份 从镜像中拷贝etcdctl二进制命令 [rootk8s1 ~]# docker run -it --rm reg.westos.org/k8s/etcd:3.5.6-0 sh 输入ctrlpq快捷键&#xff0c;把容器打入后台 获取容器id [rootk8s1 ~]# docker ps 从容器拷贝命令到本机 docker container cp c7e28b381f07:/usr/local/bin/etcdc…

【Java 进阶篇】JQuery 案例:下拉列表选中条目左右移动,打破选择的边界

在前端的舞台上&#xff0c;下拉列表是常见的用户交互元素&#xff0c;但有时候我们想要更多的交互体验。通过巧妙运用 JQuery&#xff0c;我们可以实现下拉列表中选中条目的左右移动功能&#xff0c;为用户提供更加灵活的选择方式。本篇博客将深入研究 JQuery 中实现这一功能的…

vue项目如何防范XSS攻击?

场景&#xff1a; 前后端交互的过程中&#xff0c;前端使用v-html或者{{}}渲染时&#xff0c;网页自动执行其恶意代码&#xff0c;如页面弹窗、跳转到钓鱼网站等 解决方案&#xff1a; 先说解决方式&#xff0c;其原理下文解释. 由于我是vue项目所以用的是vue-dompurify-html这…

PDF文件中更改 PDF 文本颜色的最有效解决方案

PDF 是最常用的文档类型之一&#xff0c;也是商业中使用的首选文档。在工作中&#xff0c;我们经常需要修改PDF的文本内容&#xff0c;转换格式&#xff08;如PDF转Word&#xff0c;PDF转Excel等&#xff09;&#xff0c;合并PDF&#xff0c;以达到更好的工作效果。 然而&…

统信UOS_麒麟KYLINOS上使用SSH远程工具Termius

原文链接&#xff1a;统信UOS/麒麟KYLINOS上使用SSH远程工具Termius hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇在统信UOS/麒麟KYLINOS上使用SSH远程工具Termius的文章&#xff0c;Termius是一个功能强大的ssh工具&#xff0c;支持Linux x86平台、windows、maco…

安装包管理工具-Yarn

一、介绍与安装 1.1 介绍 Yarn是一款功能包管理工具&#xff0c;与npm(npm:Node.js 的包管理器 npm,是目前最流行的Node.js 的包管理器。)类似。有着FAST(快速的), RELIABLE( RELIABLE 可信赖的), AND SECURE DEPENDENCY MANAGEMENT(安全依赖关系管理)的特点。 Yarn官网 1.2…

QGIS之二十四安装插件

1、从菜单栏中找到插件 2、搜索插件 从搜索框中搜索插件&#xff0c;如“cesium" 3、安装插件 4、查看插件 安装好的插件从这边可以看到&#xff0c;当然&#xff0c;其它插件可能在其它位置 5、已安装插件 可以查看已安装的插件
最新文章