sleep
提示:
相关代码 user/echo.c, user/grep.c, user/rm.c。
参数错误提示。
atoi 字符串转整型数。
可以调用系统调用sleep。
kernel/sysproc.c 内核代码,看系统调用sleep的实现;user/user.h 对用户程序调用sleep的C定义;user/usys.S 从用户代码跳转到内核执行sleep。
main得调用exit()返回。
在Makefile中添加sleep,编译执行。(用文本编辑器打开Makefile进行修改)
sleep.c
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argc, char *argv[]){
if(argc != 2){
fprintf(2, "wrong parameters...\n");
exit(1);
}
sleep(atoi(argv[1]));
exit(0);
}
注:
1、exit(1)表示异常退出,在退出前可以给出一些提示信息,或在调试程序中察看出错原因。
2、exit(0)表示正常退出。
pingpong
要求:
编写一个程序,使用UNIX系统调用在两个进程之间通过一对管道“乒乓”一个字节,每个方向一个。父进程应该给子进程发送一个字节;子进程应该打印"<pid>: received ping",其中是它的进程ID,在父进程的管道上写字节,然后退出;父进程应该从子进程中读取字节,print“<pid>:recerved pong”。并退出。你的解决方案应该在user/pingpong.c文件中。
提示:
使用pipe创建管道。利接收一个长度为2的数组,数组下标0为读端、1为写端;
使用fork创建子进程,子进程和父进程一样,但之后可以让它们执行不同代码。返回值为0则是子进程,否则是父进程。
使用read从管道读取,使用write向管道写入。
在Makefile中将程序添加到UPROGS。
xv6上的用户程序只有一组有限的库函数可用,可以在user/user中看到该列表。资源(除了系统调用)在user/ulib.c,user/ printf.c,user/ umalloc.c。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(void){
int p1[2],p2[2];//0为读端,1为写端
char buf,msg='0';
//两个管道 int pipe(int*);
pipe(p1); //p1 父进程写,子进程读
pipe(p2); //p2 子进程写,父进程读
if(fork()==0){//是子进程
close(p1[1]);
close(p2[0]);//关闭用不到的父进程一侧的读写端
read(p1[0],&buf,1);//从p1[0]读到buffer
printf("%d: received ping\n",getpid());
write(p2[1],&msg,1);//将msg写入p2[1]
}else{
close(p1[0]);
close(p2[1]);
write(p1[1],&msg,1);
read(p2[0],&buf,1);
printf("%d: received pong\n",getpid());
}
exit(0);
}
顺序是父进程写,子进程读,子进程写,父进程读。
因此在子进程中先read再write,在父进程中先write再read。
primes
要求:
使用pipe和fork来设置管道。第一个进程将数字2到35输入到管道。对于每个质数,需要创建一个进程,该进程通过一个管道从它的左邻居读取数据,通过另一个管道向它的右邻居写入数据。由于xv6的文件描述符和进程数量有限,第一个进程可以在数字35时停止。
提示:
请小心关闭进程不需要的文件描述符,否则在第一个进程到达35之前,您的程序将耗尽xv6资源。
一旦第一个进程达到35,它应该等待直到整个管道终止,包括所有的子进程、孙进程。因此,主质数进程应该只在所有结果输出完成后退出,并且在所有其他质数进程退出后退出。
当管道的写端关闭时,read返回0。
直接将32位(4字节)整型写入管道是最简单的,而不是使用格式化的ASCIl I/O。
您应该仅在需要时在管道中创建进程。
在Makefile中将程序添加到UPROGS。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void func(int size, int* p1) {
int p2[2];
int counter = 0;
char buffer[4];
int nums[34];
int prime;
pipe(p2);//创建管道
read(p1[0], buffer, 4);
prime = *((int*)buffer);
printf("prime %d\n", prime);
if (size == 1) {
return;
}
size--;
for (int i = 0; i < size; i++) {
read(p1[0], buffer, 4);
int temp = *((int*)buffer);
if (temp % prime != 0) {
nums[counter] = temp;
counter++;
}
}
close(p1[0]);
for (int j = 0; j < counter; j++) {
write(p2[1], (char*)(nums + j), 4);
}
if (fork() == 0) {
func(counter, p2);
exit(0);
}
wait(0);
}
int main() {
int p[2];
pipe(p);//创建管道
for (int i = 0; i < 34; i++) {
int temp = i + 2;
write(p[1], (char*)(&temp), 4);
}
if (fork() == 0) {
func(34, p);//传入2-35
exit(0);
}
wait(0);
exit(0);
}
find
要求:
编写UNIX find程序的一个简单版本:查找具有特定名称的目录树中的所有文件。您的解决方案应该在user/ find.c文件中。
提示:
查看user/ls.c以了解如何读取目录。
使用递归查找到子目录。
不要递归到“.”和“..”
对文件系统的更改在qemu运行期间保持不变;要获得一个干净的文件系统,请运行make clean,然后make qemu。
你需要使用C字符串。看一下K&R (C的书),例如第5.5节。
注意==不像Python那样比较字符串。使用strcmp()来替代。
在Makefile中将程序添加到UPROGS。
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#include "kernel/fcntl.h"
char* fmtname(char* path) {
char* p;
for (p = path + strlen(path); p >= path && *p != '/'; p--);// 找到/后的第一个单词
p++;
return p;
}
void find(char* path, char* fileName) {
char buf[512], * p;
int fd;
struct dirent de;
struct stat st;
if ((fd = open(path, 0)) < 0) {
fprintf(2, "find: cannot open %s\n", path);
return;
}
if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
switch (st.type) {
case T_FILE:
if (strcmp(fmtname(path), fileName) == 0) {
printf("%s\n", path);
}
break;
case T_DIR:
if (strlen(path) + 1 + DIRSIZ + 1 > sizeof buf) {//DIRSIZ在kernel/fs.h中被设置为14
printf("find: path too long\n");
break;
}
strcpy(buf, path);
p = buf + strlen(buf);
*p++ = '/';
while (read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0 || strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
find(buf, fileName);
}
break;
}
close(fd);
}
int main(int argc, char* argv[])
{
if (argc != 3) {
printf("find: find <path> <fileName>\n");
exit(1);
}
else {
find(argv[1], argv[2]);
exit(0);
}
}
绝大部分代码都可以参考user/ls.c
xargs
要求:
编写一个UNIX xargs程序的简单版本:从标准输入中读取一些行,并为每一行运行一个命令,将该行作为命令的参数提供。 您的解决方案应该在user/xargs.c文件中。
提示:
使用fork和exec在每一行输入上调用命令。 在父进程中使用wait命令来等待子进程完成命令。
要读取单独的输入行,每次读取一个字符,直到出现换行符(’\n’)。
kernel/param.h声明了MAXARG,如果你需要声明一个argv数组,这可能会很有用。
在Makefile中将该程序添加到UPROGS中。
对文件系统的更改在qemu运行期间保持不变; 要获得一个干净的文件系统,请运行make clean,然后make qemu。
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/param.h"
int main(int argc, char* argv[]) {
int argcount = 0;
char block[32];//按块读取
char curarg[95];//参数
char* p = curarg;//下一个参数的起始位置
char* lineSplit[MAXARG];//MAXARG为32定义在kernel/param.h
for (int i = 1; i < argc; i++) {
lineSplit[argcount] = argv[i];//xarg 后的命令
argcount++;
}
int k, m = 0;
while ((k = read(0, block, sizeof(block))) > 0) {//读标准输入
for (int j = 0; j < k; j++) {
if (block[j] == '\n') {
curarg[m] = 0;//字符串补0
lineSplit[argcount] = p;
argcount++;
lineSplit[argcount] = 0;//参数组最后补0
if (fork() == 0) {//子进程
exec(argv[1], lineSplit);//执行命令 exec接收两个参数,第一个参数为命令cmd,第二个参数为一个数组,该数组的格式必须为{cmd, “arg1”, “arg2”, …, 0}
}
wait(0);
m = 0;
p = curarg;
argcount = argc - 1;
}
else if (block[j] == ' ') {
curarg[m] = 0;
m++;
lineSplit[argcount] = p;
argcount++;
p = &curarg[m];
}
else {
curarg[m] = block[j];
m++;
}
}
}
exit(0);
}
命令行中 | 表示管道,上一条命令的标准输出,作为下一条命令的标准输入。有 | 时,argc、argv都从 | 后开始算。
测试:
提交测试:./grade-lab-util