MIT 6.s081操作系统 lab1utilities 0基础详细解析通俗易懂

lab1utilities

1.sleep(easy)

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

1.Before you start coding, read Chapter 1 of the xv6 book(https://pdos.csail.mit.edu/6.S081/2021/xv6/book-riscv-rev2.pdf).
2.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.
3.If the user forgets to pass an argument, sleep should print an error message.
4.The command-line argument is passed as a string; you can convert it to an integer using atoi (see user/ulib.c).
5.Use the system call sleep.
6.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.
7.Make sure main calls exit() in order to exit your program.
8.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.
9.Look at Kernighan and Ritchie’s book The C programming language (second edition) (K&R) to learn about C.

Run the program from the xv6 shell:

  $ make qemu
  ...
  init: starting sh
  $ sleep 10
  (nothing happens for a little while)
  $

Your solution is correct if your program pauses when run as shown above. Run make grade to see if you indeed pass the sleep tests.

Note that make grade runs all tests, including the ones for the assignments below. If you want to run the grade tests for one assignment, type:

 $ ./grade-lab-util sleep

This will run the grade tests that match “sleep”. Or, you can type:

   $ make GRADEFLAGS=sleep grade

which does the same.

solve

在xv6-labs-2020/user中创建sleep.c,下同

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"                           //参考ls引入头文件 
int main(int argc,char* argv[]){                 // argc代表传入参数个数,argv[]代表传入的参数,argv[0]通常为命令名 
 if(argc!=2){								 //若参数个数不为2,报错。调用格式应为 命令名 参数,例: sleep 10	 
     fprintf(2, "Usage: rm files...\n");
     exit(1);								//除0外都为错误结束 
 }
 sleep(atoi(argv[1]));						//atoi将字符串转为数字,sleep系统调用 
 exit(0);									//成功结束 
}

除此之外,需要在xv6-labs-2020/Makefile中添加,下同

UPROGS=\
 $U/_cat\
 $U/_echo\
 $U/_forktest\
 $U/_grep\
 $U/_init\
 $U/_kill\
 $U/_ln\
 $U/_ls\
 $U/_mkdir\
 $U/_rm\
 $U/_sh\
 $U/_stressfs\
 $U/_usertests\
 $U/_grind\
 $U/_wc\
 $U/_zombie\
 $U/_sleep           //此为自己新增,$U/_<命令名>

2.pingpong (easy)

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.
Run the program from the xv6 shell and it should produce the following output:

$ make qemu
...
init: starting sh
$ pingpong
4: received ping
3: received pong
$

Your solution is correct if your program exchanges a byte between two processes and produces output as shown above.

solve

需要用到的系统调用:
int write(int fd, char *buf, int n) Write n bytes from buf to file descriptor fd; returns n.:
int read(int fd, char *buf, int n) Read n bytes into buf; returns number read; or 0 if end of file.
int pipe(int p[]) Create a pipe, put read/write file descriptors in p[0] and p[1].:
int wait(int *status) Wait for a child to exit; exit status in *status; returns child PID.:
wait等到子进程返回后再继续执行以下内容。fork后父子进程并发运行(单核处理器)/并行(多核处理器)。
并发:多个进程在宏观上看似同时运行,在微观上通过同一cpu的调度算法轮流执行,执行时占据系统全部资源
并行:宏观微观上都同时运行,由不同核心执行
int fork() Create a process, return child’s PID:
创建子进程,子进程会复制一份父进程在内存中存储的所有信息,为了提高效率,子进程只是读取、使用父进程的内存信息时,只为引用,修改时才真正复制一份。父子进程都将继续运行fork后的内容。fork调用一次会返回两次,fork在父进程中返回子进程的PID,在子进程中返回0,返回负数创建失败。
getpid():
获得当前进程的PID值

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int main(int argc, char *argv[]){
    int pfd[2], cfd[2];								 		//定义管道标识符 
    pipe(pfd);
    pipe(cfd);												
																 
    char buf[4];											//缓存区 
    if (fork() == 0) {										
        read(pfd[0], buf, 4);								
        printf("%d: received %s\n", getpid(), buf);
        write(cfd[1], "pong", 4);
    }
    else {
        write(pfd[1], "ping", 4);
        wait(0);
        read(cfd[0], buf, 4);
        printf("%d: received %s\n", getpid(), buf);
    }
    exit(0);
}

3.primes (moderate)/(hard)

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(https://swtch.com/~rsc/thread/) 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.
Add the program to UPROGS in Makefile.

Your solution is correct if it implements a pipe-based sieve and produces the following output:

$ make qemu
...
init: starting sh
$ primes
prime 2
prime 3
prime 5
prime 7
prime 11
prime 13
prime 17
prime 19
prime 23
prime 29
prime 31
$

solve

素数筛,伪代码:

p = get a number from left neighbor
print p
loop:
    n = get a number from left neighbor
    if (p does not divide n)
        send n to right neighbor

在这里插入图片描述

int close(int fd) Release open file fd.

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#define MAXNUM 36


void prime(int* LPD){//父进程创建的子进程构成素数筛,向右展开。父进程的右管道与子进程相连,在子进程中称为左管道
    
    int p;
    close(LPD[1]);					//左管道不需要写,右管道不需要读 
    read(LPD[0],&p,sizeof(p));
    if(p==MAXNUM)					//达到所需判断的最大素数,结束 
        exit(0);
    printf("prime %d\n",p);			//此数在该筛中留下,打印输出,数p为该筛的素数 

    int RPD[2];
    pipe(RPD);

    int pid=fork();
    if(pid>0){
        close(RPD[0]); 
		int n;
		while(read(LPD[0], &n, sizeof(n)) && n != MAXNUM) { 
			if(n % p != 0) { 						//数n在该筛中能被数p整除即不为素数,被筛去 
				write(RPD[1], &n, sizeof(n)); 		//不能整除,传入下一个筛判断 
			}
		}
		write(RPD[1], &n, sizeof(n)); 				// 达到所需判断的最大数,作为结束标识 
		wait(0); 
    }
    else if(pid==0){
        close(LPD[0]);								//左管道不需要写,右管道不需要读 
        close(RPD[1]);
        prime(RPD);
       
    }
     exit(0);
}


int main(int argc,char* argv[]){
    int RPD[2];
    int pid;
    pipe(RPD);
    pid =fork();
    if(pid>0){
        close(RPD[0]); 							//第一个素数筛,不需要读左邻居 
        int i;
        for(i=2;i<MAXNUM;i++) 					//第一个素数2开始 
            write(RPD[1],&i,sizeof(i));			//将数i写入右管道 
        write(RPD[1], &i, sizeof(i));			//达到所需判断的最大数,作为结束标识 
        wait(0);
    }
    else if (pid==0)
    {
        close(RPD[1]);							//第一个素数筛的子进程的左管道仅需用来接受父进程传递的值,不需要写。 
        prime(RPD);
    }
    exit(0);
}

5.xargs (moderate)

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
$

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, find, and grep combine well:

  $ find . b | xargs grep hello

will run “grep hello” on each file named b in the directories below “.”.
To test your solution for xargs, run the shell script xargstest.sh. Your solution is correct if it produces the following output:

  $ make qemu
  ...
  init: starting sh
  $ sh < xargstest.sh
  $ $ $ $ $ $ hello
  hello
  hello
  $ $   

You may have to go back and fix bugs in your find program. The output has many $ because the xv6 shell doesn’t realize it is processing commands from a file instead of from the console, and prints a $ for each command in the file.

solve

#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/param.h"

#define MAXSIZE 64

int main(int argc,char* argv[]){
 int x_argc=0;
 char* x_argv[MAXARG];
 char buf[MAXSIZE];
 int pid;
 int k;
 char* p;

 if(argc<2){
     fprintf(2,"usage: xargs your_command\n");
     exit(1);
 }

 if(argc>MAXARG){
     fprintf(2,"fail: too many arguments\n");
     exit(1);

 }

 if(read(0,buf,MAXSIZE)<=0){                         //管道左边的结果作为标准输入传给管道右边。读取管道左边结果的结果到buf。
     fprintf(2,"fail: piple output nothing\n");
     exit(1);
 }

 for (int i=1;i<argc;i++){                           //管道右边需要执行 xargs <命令1> <参数> <管道左边的结果1>
     x_argv[x_argc++]=argv[i];                                             //<命令1> <参数> <管道左边的结果2>……                                             
 }                                                   //argv[0]=xargs,argv[1]=命令1,后面使用exec执行命令需要去除argv[0],因此构建新的传入参数                      

 k=strlen(buf);
 p=buf;
  for(int i=0;i<k;i++){
     if(buf[i]=='\n'){                               //管道左侧的输出已经存入buf,内容中存在“\n”或以“\n”作为结尾时,获取偏移量i
     pid=fork();
     if(pid>0){
         wait(0);                                    //等待子进程结束
         p=&buf[i+1];                                //更新p,p为记录下一条参数的开始偏移量
     }
     else if (pid==0){
         buf[i]=0;                                   //将buf中i处偏移量的字节"\n"置0,代表该参数结束符。
         x_argv[x_argc++]=p;                         //新的参数放在x_argv中,p为该段参数的开始
         x_argv[x_argc]=0;                           //参数结束标识
         exec(x_argv[0],x_argv);                     //执行命令,此处的x_argv[0]=在创建进程前管道右边需要执行的命令
         exit(0);
     }
     else{
         fprintf(2,"error:creat fork fail");
         exit(1);
     }
  }
  }
  exit(0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值