本文参与「新人创作礼」活动,一起开启掘金创作之路。
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之后,文件描述符表也会复制一份,所以两边对于文件描述符的操作相互独立,不会影响