lab1:Xv6 and Unix utilities
官方网址6.S081 / Fall 2021 (mit.edu)
sleep
- Implement the UNIX program
sleep
for xv6; yoursleep
should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the fileuser/sleep.c
.
Some hints:
- Before you start coding, read Chapter 1 of the xv6 book.
- Look at some of the other programs in
user/
(e.g.,user/echo.c
,user/grep.c
, anduser/rm.c
) to see how you can obtain the command-line arguments passed to a program. - If the user forgets to pass an argument, sleep should print an error message.
- The command-line argument is passed as a string; you can convert it to an integer using
atoi
(see user/ulib.c). - Use the system call
sleep
. - See
kernel/sysproc.c
for the xv6 kernel code that implements thesleep
system call (look forsys_sleep
),user/user.h
for the C definition ofsleep
callable from a user program, anduser/usys.S
for the assembler code that jumps from user code into the kernel forsleep
. - Make sure
main
callsexit()
in order to exit your program. - Add your
sleep
program toUPROGS
in Makefile; once you’ve done that,make qemu
will compile your program and you’ll be able to run it from the xv6 shell. - Look at Kernighan and Ritchie’s book The C programming language (second edition) (K&R) to learn about C.
solution
- 熟悉系统调用,与相关参数。热身题
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[]) //argc表示shell参数个数(shell参数以字符串为单位传送),argv接收字符串
{
if(2 != argc){
fprintf(2, "Usage: sleep time...\n");
exit(1);
}
int n = atoi(argv[1]);
int a = sleep(n);
if(a < 0){
fprintf(2, "error\n");
}
exit(0);
}
pingpong
- Write a program that uses UNIX system calls to ‘‘ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file
user/pingpong.c
Some hints
- Use
pipe
to create a pipe. - Use
fork
to create a child. - Use
read
to read from the pipe, andwrite
to write to the pipe. - Use
getpid
to find the process ID of the calling process. - Add the program to
UPROGS
in Makefile. - User programs on xv6 have a limited set of library functions available to them. You can see the list in
user/user.h
; the source (other than for system calls) is inuser/ulib.c
,user/printf.c
, anduser/umalloc.c
.
fork
fork
系统调用在两个进程中都会返回,在原始的进程中,fork
系统调用会返回大于0的整数,这个是新创建进程的ID。而在新创建的进程中,fork系统调用会返回0。所以即使两个进程的内存是完全一样的,我们还是可以通过fork的返回值区分旧进程和新进程。
I/O和文件描述符(file descriptor)
- 文件描述符是一个小整数,代表了一个由内核管理的读写对象。一个进程可以通过打开文件、目录或者设备的方式,或者创建一个管道,又或者通过复制一个存在的文件描述符来获得一个文件描述符。文件描述符将文件、管道、设备等进行抽象,使得不同的他们看着都像字节流,我们称输入和输出为I/O。
- 在xv6中,0和1分别为标准输入和输出。2为标准输出错误信息
- 系统调用
read(fd,buf,n)
将从文件fd
(文件描述符)中读取最多n个字节。并将他们拷贝到buf
中,并返回读取的字节数。 write(fd, buf, n)
写buf
中的 n 个字节到fd
并且返回实际写出的字节数。如果返回值小于 n 那么只可能是发生了错误。就像read
一样,write
也从当前文件的偏移处开始写,在写的过程中增加这个偏移- 系统调用
close
会释放一个文件描述符,使得它未来可以被open
,pipe
,dup
等调用重用。一个新分配的文件描述符永远都是当前进程的最小的未被使用的文件描述符。
文件描述符和fork的交叉使用使得I/O重定向变得非常容易
-
char *argv[2]; argv[0] = "cat"; ### argv[1] = 0; if(fork() == 0) { close(0); //使子进程中文件描述符表中的0指向"input.txt" open("input.txt", O_RDONLY); //exec会替换调用它的进程的内存但是会保留它的文件描述符表。这种行为使得 shell 可以这样实现重定向 exec("cat", argv); }
子进程关闭文件描述符0后,我们可以保证
open
会使用0作为新打开的文件input.txt
的文件描述符(因为0是open
执行时的最小可用文件描述符)
exec
- exec系统调用会保留当前的文件描述符表单。所以任何在exec系统调用之前的文件描述符,例如0,1,2等。它们在新的程序中表示相同的东西。
- 通常来说exec系统调用不会返回,因为exec会完全替换当前进程的内存,相当于当前进程不复存在了,所以exec系统调用已经没有地方能返回了。。exec系统调用只会当出错时才会返回,因为某些错误会阻止操作系统为你运行文件中的指令,例如程序文件根本不存在,因为exec系统调用不能找到文件,exec会返回-1来表示:出错了,我找不到文件。所以通常来说exec系统调用不会返回,它只会在kernel不能运行相应的文件时返回。
- 你可以传入命令行参数,exec允许你传入一个命令行参数的数组,这里就是一个C语言中的指针数组
- Shell会执行fork,之后fork出的子进程再调用exec系统调用,这是一个非常常见的Unix程序调用风格。对于那些想要运行程序,但是还希望能拿回控制权的场景,可以先执行fork系统调用,然后在子进程中调用exec。
pipe
-
pipe是一小块内核缓冲,以一对文件描述符(输入和输出)的形式暴露给程序使用。
int p[2]; char *argv[2]; argv[0] = "wc"; argv[1] = 0; pipe(p); if