lab地址
pdos.csail.mit.edu/6.S081/2022…
This lab will familiarize you with xv6 and its system calls.
sleep (easy)
题目大意:实现一个sleep程序,指定时间睡眠
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int main(int argsc, char *argv[]) {
if (argsc < 2) {
fprintf(2, "Usage: sleep second\n");
exit(0);
}
int s = atoi(argv[1]);
sleep(s);
exit(0);
}
pingpong (easy)
题目大意:根据管道写一个ping-pong程序,父进程发送ping,子进程打印received ping,并且返回pong,然后父进程打印received pong
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void main(int argsc, char *argv[]) {
int parent_fd[2], child_fd[2];
pipe(parent_fd);
pipe(child_fd);
char buf[512];
if (fork() == 0) {
int pid = getpid();
read(child_fd[0], buf, 4);
printf("%d: received %s\n", pid, buf);
write(parent_fd[1], "pong", 4);
close(parent_fd[0]);
close(parent_fd[1]);
close(child_fd[0]);
close(child_fd[1]);
} else {
int pid = getpid();
write(child_fd[1], "ping", 4);
read(parent_fd[0], buf, 4);
printf("%d: received %s\n", pid, buf);
close(parent_fd[0]);
close(parent_fd[1]);
close(child_fd[0]);
close(child_fd[1]);
}
}
primes (moderate)/(hard)
题目大意: 需要打印2-35之间的质数,打印的方式如下
- 第一个进程A在管道中填充2-35的数,之后他的子进程拿到2-35的数
- A的子进程B拿出父进程的数,打印2,然后剔除掉2的倍数,再把剩余的数写入管道,即3,5,7,9...35
- B的子进程C拿出父进程的数,打印3,然后剔除掉3的倍数,再把剩余的数写入管道,即5,7,11...35
- 一直持续到所有数都被打印
代码如下:
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void main(int argsc, char *argv[]) {
int p[2];
pipe(p);
// 首先把2-35填充进管道
for (int i = 2; i <= 35; i++) {
write(p[1], &i, sizeof(int));
}
close(p[1]);
f(p[0]);
}
void f(int read_fd) {
int p[2];
pipe(p);
int prime;
if (!read(read_fd, &prime, sizeof(int))) {
exit(0);
}
printf("prime %d\n", prime);
if (fork() == 0) {
close(p[0]);
int cur;
while (read(read_fd, &cur, sizeof(int))) {
if (cur % prime != 0) {
write(p[1], &cur, sizeof(int));
}
}
exit(0);
} else {
close(p[1]);
wait(0);
f(p[0]);
}
}
find (moderate)
实现find方法,比如示例find . b
表示找到当前目录下面所有名字为b的文件
提示让我们读ls.c
的源码并且使用递归,别说,还真挺有道理,如果我能用ls
找到当前目录下所有符合名字文件,用递归找到子目录的下符合名字的文件
那么问题来了,ls.c
的源码晦涩难懂
核心源码如下
void
ls(char *path)
{
char buf[512], *p;
int fd;
struct dirent de;
struct stat st;
if((fd = open(path, 0)) < 0){
fprintf(2, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_DEVICE:
case T_FILE:
printf("%s %d %d %l\n", fmtname(path), st.type, st.ino, st.size);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: 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)
continue;
memmove(p, de.name, DIRSIZ);
p[DIRSIZ] = 0;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
printf("%s %d %d %d\n", fmtname(buf), st.type, st.ino, st.size);
}
break;
}
close(fd);
}
到26行之前应该都能看懂,26行是重头戏
如果字符串路径长度 + DIRSIZ(我理解就是文件名字,因为后面还得加个名字) > 缓冲区长度,报错
将path拷贝到buf中,然后将p指向buf的末尾并且加上/
循环把fd读到de中,如果没有数据了就跳出循环
如果de.inum==0,表示该文件没用,跳过
对读取到的文件,把名字追加到p的后面,再加个结束符
针对现在的p就是最新遍历到的文件,如果把读取到stat中失败就报错,成功就打印文件相关信息
大概就是这样,不知道为什么c语言处理字符串这么复杂,怪我大一没学好,OK,接下来在ls的模板上改一改就行了
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void find(char *path, char *target);
void printTarget(char *path, char *target);
void main(int argsc, char *argv[]) {
if(argsc < 2){
find(".", argv[2]);
exit(0);
}
find(argv[1], argv[2]);
exit(0);
}
// 寻找path下面叫做target的文件
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, "ls: cannot open %s\n", path);
return;
}
if(fstat(fd, &st) < 0){
fprintf(2, "ls: cannot stat %s\n", path);
close(fd);
return;
}
switch(st.type){
case T_DEVICE:
case T_FILE:
printTarget(path, target);
break;
case T_DIR:
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("ls: 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;
if(stat(buf, &st) < 0){
printf("ls: cannot stat %s\n", buf);
continue;
}
// printf("%s %d %d %d\n", buf, st.type, st.ino, st.size);
find(buf, target);
}
break;
}
close(fd);
}
void printTarget(char *path, char *target) {
char *p;
// Find first character after last slash.
for(p=path+strlen(path); p >= path && *p != '/'; p--) ;
p++;
if(strcmp(p, target) == 0) {
printf(path);
printf("\n");
}
}
xargs (moderate)
题目是什么意思呢,举个例子
echo hello too | xargs echo bye
等价于echo bye hello too
也就是说
左边的输出,会当做右边的输入
比如find . b | xargs grep hello
假设find . b
的输出为
./a/b
./c/b
./b
那么我们等价于下面三行
grep hello ./a/b
grep hello ./c/b
grep hello ./b
接下来用代码实现,大致逻辑如下,用文件描述符0接收输入,遇到\n执行exec命令
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
void main(int argsc, char *argv[]) {
int max = 1024;
// 定义一个参数数组,先把xargs的参数存下来
char *param[max];
char *p;
int paramCur = 0;
for (int i = 1; i < argsc; i++) {
p = argv[i];
param[paramCur++] = p;
}
// 开始存标准输入的参数
char buf[max];
char temp[max];
int r = 0;
int l = 0;
int i = 0;
// 读取标准输入到buf中
while (read(0, &buf[i], 1) > 0) {
if (buf[i] == '\n') {
temp[r] = '\0';
if (fork() > 0) {
wait(0);
} else {
param[paramCur] = &temp[l];
// 子进程执行命令,注意这个param[0]得是cmd
exec(argv[1], param);
exit(0);
}
l = r + 1;
} else {
temp[r] = buf[i];
}
r++;
}
exit(0);
}