6.1810: Operating System Engineering <LEC 1>

news2024/9/19 10:55:59

课程链接: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.coloradmin.cn/o/1212851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

绩效考核管理项目|记录1

项目用C#winformSQL Server写的&#xff0c;现在记录一下学习到的新东西。 winform工具 splitContainer&#xff1a;分割出两个容器&#xff0c;能添加面板之类的工具 treeview&#xff1a;展示标签页的分层集合&#xff08;用户管理、基数管理......&#xff09;&#xff0…

现货黄金职业交易员怎么使用技术分析?

职业的交易员每天要处理很多不同的信息&#xff0c;其中只一部分是涉及技术指标。在这一部分处理技术分析的时间里&#xff0c;只能再分出少之又少的时间给技术指标。那职业交易员会利用做技术指标做什么呢&#xff1f;下面我们就来讨论一下。 识别行情。技术指标的主要作用就是…

codemirror怎么高亮指定文本

先上图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/c070cde11a314c45a859ccb0ca7f41c7.png 产品经理的需求是编辑框中用了占位符的话&#xff0c;占位符中的变量高亮&#xff0c;简单吧&#xff01;还有一个附加要求&#xff0c;必须是外部定义的变量&#xff0c;不…

单区域OSPF配置

配置命令步骤&#xff1a; 1.使用router ospf 进程ID编号 启用OSPF路由 2.使用network 直连网络地址 反掩码 area 0 将其归于区域0 注意&#xff1a;1.进程ID编号可任意&#xff08;1-65535&#xff09;2.反掩码用4个255相减得到 如下图&#xff0c;根据给出要求配置OSPF单区…

【前端】使用json-server报错

当我们使用json-server模仿后端接口时需要运行json-server --watch index.json这个命令生成增删改查接口但是可能会报这个错误&#xff0c;如图 这时我们运行 npm i json-server -g命令即可&#xff0c;然后再重新运行json-server --watch index.json就行了

如何快速将钉钉员工信息同步到飞书

当企业内部在使用钉钉跟飞书时&#xff0c;那么当钉钉员工信息发生更改时&#xff0c;我们应该如何将信息快速同步到飞书上呢&#xff0c;接下来我们借助RestCloud AppLink平台进行演示。 第一步&#xff1a;获得钉钉以及飞书认证授权 钉钉授权 钉钉接入采用自建应用的方式&…

数字化档案管理系统解决方案

数字化档案管理系统解决方案是指将传统的纸质档案转化为数字化档案&#xff0c;采用计算机和网络技术实现对档案的存储、检索、管理、共享等操作的过程。 专久智能数字化档案管理解决方案及措施可以包括以下几个方面&#xff1a; 1. 采用数字化技术对档案进行数字化处理&#x…

拜耳阵列(Bayer Pattern)以及常见彩色滤波矩阵(CFA)

一、拜耳阵列的来源 图像传感器将光线转化成电流&#xff0c;光线越亮&#xff0c;电流的数值就越大&#xff1b;光线越暗&#xff0c;电流的数值就越小。图像传感器只能感受光的强弱&#xff0c;无法感受光的波长。由于光的颜色由波长决定&#xff0c;所以图像传播器无法记录…

北大腾讯打造多模态15边形战士!语言作“纽带”,拳打脚踢各模态,超越Imagebind

AI4Happiness 投稿 量子位 | 公众号 QbitAI 北大联合腾讯打造了一个多模态15边形战士&#xff01; 以语言为中心&#xff0c;“拳打脚踢”视频、音频、深度、红外理解等各模态。 具体来说&#xff0c;研究人员提出了一个叫做LanguageBind的多模态预训练框架。 用语言作为与其…

如何在 Linux 上部署 RabbitMQ

如何在 Linux 上部署 RabbitMQ 文章目录 如何在 Linux 上部署 RabbitMQ安装 Erlang从预构建的二进制包安装从源代码编译 Erlang RabbitMQ 的安装使用 RabbitMQ Assistant 连接 RabbitMQ Assistant 是一款优秀的RabbitMQ 可视化管理工具&#xff0c;提供丰富的管理功能。下载地址…

亚里士多德的思想统治西方世界,实体论证明人不是猪

苏格拉底、柏拉图、亚里士多德&#xff0c;并称古希腊三贤。 公元前384年&#xff0c;亚里士多德出生在马其顿的奴隶主家庭&#xff0c;父亲是马其顿国王腓力二世的宫廷御医。 公元前367年&#xff0c;亚里士多德迁居到雅典&#xff0c;进入柏拉图学园&#xff0c;跟随柏拉图…

systemverilog:interface中的modport用法

使用modport可以将interface中的信号分组并指定方向&#xff0c;方向是从modport连接的模块看过来的。简单示例如下&#xff1a; interface cnt_if (input bit clk);logic rstn;logic load_en;logic [3:0] load;logic [7:0] count;modport TEST (input clk, count,output rst…

知识解读:香港轻量云/云服务器/VPS性能差距解读

​  提起香港轻量云/云服务器/VPS 这三类&#xff0c;往往汇聚了中小企业和开发者等群体的讨论声音。当然&#xff0c;这跟它们本身产品定位有关&#xff0c;加上在初级配置这块价格上相差不大&#xff0c;也因此经常被拿来对比。 首先来简单了解一下最基础的区别&#xff1a…

C嘎嘎模板

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是模板&#xff0c;并且能熟练运用函数模…

力扣160:相交链表

力扣160&#xff1a;相交链表 题目描述&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式…

【MySQL】随手笔记(积跬步至千里)

一、常用函数 &#xff08;1&#xff09;uuid() select uuid(); //生成随机数 select replace(uuid(),-,); //将随机数的-去掉 select upper(replace(uuid(),-,)); ///改成大写应用举例&#xff1a; &#xff08;1&#xff09;非自增主键 非自增主键&#xff0c;insert语…

【LabVIEW学习】1.对labview的初步使用,控制数据流动,快捷键,参考手册打不开怎么办

一。初步使用labview 1.程序图标 2.打开之后继续点击新建VI 原因&#xff1a;最后的程序后缀就是 .vi 3.新建之后&#xff0c;会有三个界面&#xff08;没有不要紧&#xff0c;找找肯定有&#xff09; 4.程序操作方法 1.拖动控件到前面板 2.此时程序框图会出现对应的控件 拖动…

分享篇:最近在研究的AIGC内容

最近在研究AIGC自助生成报告的内容&#xff0c;分享一些查到的资料 前言&#xff08;一些使用心得&#xff09; 1、大模型会颠覆一些生产力&#xff0c;让强的人更强 归根到底&#xff0c;大模型是工具&#xff0c;和早些年的excel、python、ps没差&#xff0c;能不能用好工…

今年副业比主业赚得多...

我是从20年开始接触副业的&#xff0c;主要是在程序员外包平台上接单。从一开始的月入0到几百&#xff0c;到现在每个月稳定有小一万的收入。这个月接了一个比较大的项目&#xff0c;结款之后发现今年的副业已经比主业赚得多了&#xff0c;简直美滋滋~ 今年主业收入8w&#xff…

优维产品最佳实践第14期:让重要告警能有序跟进,最终根治

监控系统的首要任务是利用特定指标来反映系统内部的健康状态&#xff0c;当指标异常时&#xff0c;会触发告警。对于简单告警的处理&#xff0c;基于告警轨迹可清晰记录和观察告警的状态变化过程。 然而&#xff0c;对于一个复杂告警的处理&#xff0c;可能需要多角色多部门协…