$ git clone git://g.csail.mit.edu/xv6-labs-2021 Cloning into 'xv6-labs-2021'... ... $ cd xv6-labs-2021 $ git checkout util Branch 'util'set up to track remote branch 'util' from 'origin'. Switched to a new branch 'util'
Implement the UNIX program sleep for xv6; your sleep 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 file user/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, and user/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 the sleep system call (look for sys_sleep), user/user.h for the C definition of sleep callable from a user program, and user/usys.S for the assembler code that jumps from user code into the kernel for sleep.
Make sure main calls exit() in order to exit your program.
Add your sleep program to UPROGS 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.
int main(int argc, char *argv[]) { if(argc < 2) { fprintf(2, "Please enter a number!\n"); exit(1); } int time = atoi(argv[1]); sleep(time); exit(0); }
测试:
1 2 3 4 5
$ sudo ./grade-lab-util sleep make: “kernel/kernel”已是最新。 == Test sleep, no arguments == sleep, no arguments: OK (1.5s) == Test sleep, returns == sleep, returns: OK (0.9s) == Test sleep, makes syscall == sleep, makes syscall: OK (0.9s)
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, and write 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 in user/ulib.c, user/printf.c, and user/umalloc.c.
Write a concurrent version of prime sieve using pipes. This idea is due to Doug McIlroy, inventor of Unix pipes. The picture halfway down this page and the surrounding text explain how to do it. Your solution should be in the file user/primes.c.
Your goal is to use pipe and fork to set up the pipeline. The first process feeds the numbers 2 through 35 into the pipeline. For each prime number, you will arrange to create one process that reads from its left neighbor over a pipe and writes to its right neighbor over another pipe. Since xv6 has limited number of file descriptors and processes, the first process can stop at 35.
Some hints:
Be careful to close file descriptors that a process doesn’t need, because otherwise your program will run xv6 out of resources before the first process reaches 35.
Once the first process reaches 35, it should wait until the entire pipeline terminates, including all children, grandchildren, &c. Thus the main primes process should only exit after all the output has been printed, and after all the other primes processes have exited.
Hint: read returns zero when the write-side of a pipe is closed.
It’s simplest to directly write 32-bit (4-byte) ints to the pipes, rather than using formatted ASCII I/O.
You should create the processes in the pipeline only as they are needed.
int main(int argc, char *argv[]) { int input[2]; pipe(input); int start = 2, end = 35;
if(fork() != 0) { close(input[END_READ]);
for(int i = start; i <= end; i ++ ) { if(write(input[END_WRITE], &i, sizeof(i)) != sizeof(i)) { fprintf(2, "failed to write %d into the pipe in the parent", i); exit(1); } }
Write a simple version of the UNIX find program: find all the files in a directory tree with a specific name. Your solution should be in the file user/find.c.
Some hints:
Look at user/ls.c to see how to read directories.
Use recursion to allow find to descend into sub-directories.
Don’t recurse into “.” and “…”.
Changes to the file system persist across runs of qemu; to get a clean file system run make clean and then make qemu.
You’ll need to use C strings. Have a look at K&R (the C book), for example Section 5.5.
Note that == does not compare strings like in Python. Use strcmp() instead.
structstat { int dev; // File system's disk device //文件系统设备号 uint ino; // Inode number //Inode 值 short type; // Type of file //文件类型 short nlink; // Number of links to file //文件被链接数 uint64 size; // Size of file in bytes //文件大小 };
dirent结构体:
inum是说这个文件占了几个inode,name是这个文件的名字。
1 2 3 4 5 6 7
// Directory is a file containing a sequence of dirent structures. #define DIRSIZ 14
$ sudo ./grade-lab-util find make: “kernel/kernel”已是最新。 == Test find, in current directory == find, in current directory: OK (1.3s) == Test find, recursive == find, recursive: OK (1.1s)
Write a simple version of the UNIX xargs program: read lines from the standard input and run a command for each line, supplying the line as arguments to the command. Your solution should be in the file user/xargs.c.
Some hints:
Use fork and exec to invoke the command on each line of input. Use wait in the parent to wait for the child to complete the command.
To read individual lines of input, read a character at a time until a newline (‘\n’) appears.
kernel/param.h declares MAXARG, which may be useful if you need to declare an argv array.
Add the program to UPROGS in Makefile.
Changes to the file system persist across runs of qemu; to get a clean file system run make clean and then make qemu.
xargs效果示例:
1 2 3
$ echo hello too | xargs echobye bye hello too $
1 2 3 4
$ echo"1\n2" | xargs -n 1 echo line line 1 line 2 $
int readline(char *new_argv[MAXARG], int cur) { char buf[1024]; int n = 0; while(read(0, buf + n, 1)) { if(n == 1023) { fprintf(2, "the argument is too long...\n"); exit(1); }
$ vim time.txt # 写上自己花在这个 Lab 多少个小时的时间 $ make grade == Test sleep, no arguments == $ make qemu-gdb sleep, no arguments: OK (2.5s) == Test sleep, returns == $ make qemu-gdb sleep, returns: OK (0.4s) == Test sleep, makes syscall == $ make qemu-gdb sleep, makes syscall: OK (1.1s) == Test pingpong == $ make qemu-gdb pingpong: OK (0.9s) == Test primes == $ make qemu-gdb primes: OK (1.1s) == Test find, in current directory == $ make qemu-gdb find, in current directory: OK (1.0s) == Test find, recursive == $ make qemu-gdb find, recursive: OK (1.1s) == Test xargs == $ make qemu-gdb xargs: OK (1.1s) == Test time == time: OK Score: 100/100