6.1810: Operating System Engineering <LEC 1>

课程链接:6.1810 / Fall 2023

一、本节任务 

6.1810: Operating System Engineering <LEC 1>_第1张图片

实验环境:

6.1810: Operating System Engineering <LEC 1>_第2张图片

二、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 
 * to sleep for some tick
 */

int main(int argc, char *argv[])
{
        if (argc != 2)
        {
                fprintf(2, "usage: sleep \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质数算法,大致流程如下图,每个进程打印第一个输入的质数,然后使用第一个质数筛选一遍剩下的数(即不可被第一个质数整除),这样到达最后一个进程即可打印全部质数:

6.1810: Operating System Engineering <LEC 1>_第3张图片

代码:

#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  \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  ...");
                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 均通过: 

6.1810: Operating System Engineering <LEC 1>_第4张图片

你可能感兴趣的:(学习,linux,c语言)