MisOS Lab01
Published:
MITOS—Lab01—Xv6 and Unix utilities
代码参考链接:https://github.com/ChenYuHengSJTU/MIT_OSLab
Boot xv6(easy)
Sleep(easy)
TODO
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
.
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.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc,char* argv[]){
if(argc<2){
printf("Sleep:Missing arguments [Error]\n");
exit(1);
}
sleep(atoi(argv[1]));
exit(0);
}
Do not forget
- add
$U/_sleep\
to makefile
How to check
$ ./grade-lab-util sleep
#或
$ make GRADEFLAGS=sleep grade
pingpong(easy)
TODO
- 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
.
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.
// 有点别扭,可能不符合好的 routine,关于dup,pipe更高级的用法,可以看我的SJTUOS_LAB1--shell
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc,char* argv[]){
// int ptoc_p[2],ctop_p[2];
// pipe(ptoc_p);
// pipe(ctop_p);
char byte[2];
int p[2];
pipe(p);
byte[1]='\0';
// do not forget to close both the pipes in parent and child process
if(fork()==0){
// read(ptoc_p[0],byte,1);
// printf("%d: received ping\n",getpid());
// write(ctop_p[1],byte,1);
read(p[0],byte,1);
printf("%d: received ping\n",getpid());
write(p[1],byte,1);
//
close(p[0]);
close(p[1]);
exit(0);
}
else{
// write(ptoc_p[1],"c",1);
// wait((int*)(0));
// read(ctop_p[0],byte,1);
write(p[1],"c",1);
close(p[1]);
wait((int*)(0));
read(p[0],byte,1);
close(p[0]);
printf("%d: received pong\n",getpid());
exit(0);
}
exit(0);
}
primes(moderate/hard)
- 关于素数晒,可以看课程网站上的介绍,也可以 bing 或者 google 自行搜索
TODO
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.
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.
- Add the program to UPROGS in Makefile.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
static int fd[2];
void primes(){
// printf("primes:\n");
int first_num = 0,num;
int buffer[35];
int sz=0;
close(fd[1]);
if(read(fd[0],&first_num,sizeof(int))){
printf("prime %d\n",first_num);
while(read(fd[0],&num,sizeof(int))){
// if(num == 0) break;
if(num % first_num != 0){
buffer[sz++] = num;
}
}
}
close(fd[0]);
// It is important to create new pipe,otherwise the process will keep
// writing and read what it has write.
pipe(fd);
write(fd[1],buffer,sz * sizeof(int));
if(fork() == 0){
primes();
}
else{
// important to close fd in parent process
close(fd[0]);
close(fd[1]);
wait((int*)0);
}
exit(0);
}
int main(int argc,char * argv[]){
pipe(fd);
int i;
for(i=2;i<=35;++i){
write(fd[1],&i,sizeof(int));
}
// i = 0;
// write(fd[1],&i,sizeof(int));
primes();
exit(0);
}
find (moderate)
写这篇博客的时候,因为课内课程需要(以及 NJUOS-2023-Lab1),为了实现一个 shell,自行学习了一下 linux 下遍历目录的方式(以后尽量出一片篇博客介绍,或者可以看 SJTUOS 课程大作业 shell 实现中对 cd 的特殊处理)
做这个实验的时候就是根据 hints,照猫画虎
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#define Bufsize 512
// the char[] path should be defined in main as a char[512] ended with '\0'
void find(char* path,char* target){
char buf[512],*p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path,0)) < 0){
fprintf(2,"find: cannot open %s [1]\n",path);
return;
}
if(fstat(fd, &st)<0){
fprintf(2,"find: cannot stat %s [1]\n", path);
close(fd);
return;
}
switch(st.type){
case T_FILE:
break;
case T_DIR:
if(strlen(path)+strlen(target)+1>Bufsize){
printf("find: path is too long\n");
break;
}
strcpy(buf,path);
p=buf+strlen(buf);
*p++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)){
if(de.inum == 0)
continue;
if(strcmp(de.name,"..")==0 || strcmp(de.name,".")==0)
continue;
if(strcmp(de.name,target)==0){
printf("%s/%s\n",path,target);
}
memmove(p,de.name,DIRSIZ);
if(stat(buf, &st) < 0){
printf("find: cannot stat %s [2]\n", buf);
continue;
}
find(buf,target);
}
break;
}
// close() is so important
// do not forget to close , otherwise some errors may occur
close(fd);
return;
}
int main(int argc,char* argv[]){
if(argc<2){
printf("Error:Too few arguments for find\n");
exit(1);
}
if(argc == 2)
find(".",argv[1]);
else find(argv[1],argv[2]);
exit(0);
}
xargs (moderate)
TODO
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.
The following example illustrates xarg’s behavior:
$ echo hello too | xargs echo bye
bye hello too
Note that the command here is “echo bye” and the additional arguments are “hello too”, making the command “echo bye hello too”, which outputs “bye hello too”.
Please note that xargs on UNIX makes an optimization where it will feed more than argument to the command at a time. We don’t expect you to make this optimization. To make xargs on UNIX behave the way we want it to for this lab, please run it with the -n option set to 1. For instance
$ echo "1\n2" | xargs -n 1 echo line
line 1
line 2
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.
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/param.h"
#include "kernel/fs.h"
#include "user/user.h"
#include <stddef.h>
#define MAXARGLEN 20
// static char exe_path[DIRSIZ+2]="./";
static char* Argv[MAXARG];
// static char buffer[MAXARG*MAXARGLEN]="";
static char tmp[MAXARGLEN];
static char* p;
static uint32 Argc,init_argc;
void Debug(){
char** argv = (char**)Argv;
for(int i=0;i<Argc;++i){
printf("%s\n",argv[i]);
}
printf("\n");
}
void Init(){
for(int i=0;i<MAXARG;++i){
Argv[i]=malloc(MAXARGLEN);
memset(Argv[i],0,MAXARGLEN);
}
}
void Clear(){
for(int i=init_argc;i<Argc;++i){
memset(Argv[i],0,MAXARGLEN);
}
Argc=init_argc;
}
void Exec(){
Debug();
if(fork()==0){
exec(Argv[0],Argv);
exit(0);
}
else{
wait((int*)0);
}
}
// Doubt life!!!
void Test(){
char* argv[4]={"grep","hello","./a/b",NULL};
if(fork()==0){
exec(argv[0],argv);
exit(0);
}
else wait((int*)0);
}
int main(int argc,char* argv[]){
// test();
if(argc < 2){
printf("Xargs: too few arguments\n");
exit(1);
}
Init();
init_argc = argc - 1;
Argc=init_argc;
// printf("%d\n",argc);
// for(int i=0;i<argc;++i) printf("%s\n",argv[i]);
for(int i=1;i<argc;++i){
strcpy(Argv[i-1],argv[i]);
}
memset(tmp,0,MAXARGLEN);
p = &tmp[0];
char ch;
while(read(0,&ch,1)){
// printf("%c",ch);
if(ch == ' ' || ch == '\n'){
*p++ = '\0';
// printf("tmp: %s\n",tmp);
strcpy(Argv[Argc],tmp);
memset(tmp,0,MAXARGLEN);
p = &tmp[0];
Argc++;
}
if(ch == '\n'){
// strcpy(exe_path+2,argv[1]);
// strcpy(Argv[0],argv[0]);
// printf("%s\n",exe_path);
// printf("%s\n",Argv[0]);
// Exec();
// Important!!!
char* argvs[32];
for(int i=0;i<Argc;++i) argvs[i]=Argv[i];
// must use NULL to mark the end of the argv
argvs[Argc]=NULL;
if(fork()==0){
exec(Argv[0],argvs);
exit(0);
}
else{
wait((int*)0);
}
Clear();
}
else{
*p++ = ch;
}
}
exit(0);
}
Submit
- 运行
make grade
来评分
Harvest and Reflections
一些需要注意的点以及踩坑的地方都在代码里有注释
特别需要注意,对于管道通信,在 fork() 后,parent process 一定要 close:
if(fork() == 0){
...
}
else{
close(fd[0]);
close(fd[1]);
wait(NULL);
}
- 以及传递参数时,最后一个参数,即 argv[argc] 一定要设置为 NULL,否则可能根本调用不了