xv6 lab1: Xv6 and Unix utilities

185 阅读3分钟

本文参与「新人创作礼」活动,一起开启掘金创作之路。


xv6实验的第一个lab,具体就是实现一些基础的unix程序,通过利用xv6提供的一些系统调用来完成。

sleep

实现unix程序中的sleep,该程序可以让系统暂停,暂停的时间为用户指定的tick数。tick是xv6内核定义的一个时间概念,1个tick就是定时器芯片的两个中断之间的时间。编写的sleep.c应该放在user/目录下。

问题分解

  • 如何向程序中传递用户的参数

    通过C语言main函数的参数,argc和argv来实现,argc是参数的个数,argv是存储参数的字符指针数组,通常第一个即argv[0]为程序自己的名字。

  • 传递的参数一般是什么类型,是否需要转换

    传递的参数一般是字符串类型, 可能是需要转换成整数类型。

  • 如何实现程序暂停这一功能

    通过一个xv6提供的系统调用sleep来实现

    int sleep(int n) 暂停 n 个时钟节拍

在想清楚这些之后实现sleep就变得非常简单了,代码如下

#include <kernel/types.h>
#include <kernel/stat.h>
#include <user/user.h>

int main(int argc,char *argv[])
{
    if (argc <= 1)
    {
        fprintf(2,"Usage: sleep seconds...\n");
        exit(1);
    }
    if (argc <= 2)
    {
        int n = atoi(argv[1]);
        sleep(n);
        exit(0);
    }
    if (argc >= 3)
    {
        fprintf(2,"Usage: sleep <seconds>\n");
        exit(1);
    }
    exit(0);
}

piongpong

完成一个名为pingpong的unix用户级程序,它会创建两个进程,然后父进程和子进程之间通过管道来进行通信,父进程应发送一个字节给子方;子进程应打印":收到ping",其中是其进程ID,将该字节写在管道上给父进程,然后退出;父进程应从子进程读取该字节,打印":收到pong",然后退出。pingpong.c文件应存放在user/目录下。

问题分解

  • 管道是什么,怎样用它来完成父子进程之间的通信

    管道相当于内核空间内的一个小型buffer,它提供了两个文件描述符,一个用于读,一个用于写。使用系统调用int pipe(int p[]) 来创建一个管道,并把 read/write 文件描述符放在 p[0]和 p[1]中。这种方式显然可以用来实现进程之间的通信,即一个进程向管道写,另一个进程向管道里面读取。

  • 如何获取进程的pid

    通过系统调用int getpid() 返回当前进程的 PID来获取当前进程的pid。

  • 在多进程下,如何确保打印顺序不会紊乱

    在本次程序中,要求子进程先打印然后写入管道,所以我们可以让父进程等待子进程执行完毕后再继续执行打印操作,那么unix有没有提供这种功能呢?

    int wait(int *status) 等待一个子进程退出; 将退出状态存入*status; 返回子进程 PID。 wait可以帮助我们来实现这个功能。具体代码如下

#include <kernel/types.h>
#include <kernel/stat.h>
#include <user/user.h>

int main(int argc,char *argv[])
{
    int fd[2];
    char buf[1024];
    if (pipe(fd) == -1) // 生成 pipeline
    {
        fprintf(2,"pipe fail");
        exit(1);
    }
    if (fork() == 0 )
    {
        close(fd[0]);
        write(fd[1],"1",4);
        close(fd[1]);
        printf("%d: received ping\n",getpid());
    }
    else
    { 
        close(fd[0]);
        read(fd[0],buf,4);
        close(fd[1]);
        wait(0);
        printf("%d: received pong\n",getpid());
    }
    exit(0);
}

代码解释

  • int close(int fd),用来关闭一个文件描述符,这里用来及时释放掉不需要的文件描述符,因为fork之后,文件描述符表也会复制一份,所以两边对于文件描述符的操作相互独立,不会影响