6.S081「Xv6 and Unix utilities」
- insorker
- 2022/2/21
Hello
新学期,新气象,祝看到这篇文章的人一生不患腱鞘炎。
Introduction
This lab will familiarize you with xv6 and its system calls.
做了一遍所有题目,主要考察的还是你对系统调用的理解,虽然后面的东西还没学,但我目前猜测是先完善shell和gdb,做好调试工作,然后再深入操作系统。
因为和南大的PA是一起做的,所以取长补短还算有了一些心得【当然是取他人之长补我自己的短】。这里做一些推荐:
- ctags:很好用的vim插件
- CSAPP:学一遍还是很有帮助的
Problems
突然想起来的声明:因为只是练习题,所以不做异常判断了
1. sleep (easy)
简单题,主要就是让你熟悉一下做题大概的流程
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
static char sleep_error[] = "Sleep Error: No Input";
int
main(int argc, char *argv[])
{
if (argc == 1) {
write(1, sleep_error, strlen(sleep_error));
exit(1);
}
sleep(atoi(argv[1]));
exit(0);
}
2. pingpong (easy)
像乒乓球一样,想想一个来回的过程。
题目没说发送什么东西,所以重点在于你发送的顺序:
- parent send byte
- parent wait
- child read and print
- child send byte
- child exit
- parent read and print
然后代码就很简单了
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
static char pipe_error[] = "Pipe Error";
int
main(int argc, char *argv[])
{
int p[2], childpid;
char psend = 'f', ssend = 's';
char precv, srecv;
if (pipe(p) == -1) {
write(1, pipe_error, sizeof(pipe_error));
exit(-1);
}
childpid = fork();
if (childpid == 0) {
read(p[0], &srecv, sizeof(srecv));
printf("%d: received ping\n", getpid());
write(p[1], &ssend, sizeof(ssend));
}
else {
write(p[1], &psend, sizeof(&psend));
wait(0);
read(p[0], &precv, sizeof(precv));
printf("%d: received pong\n", getpid());
close(p[0]);
close(p[1]);
}
exit(0);
}
3. primes (moderate)/(hard)
这题确实算难题,但又其实很简单。我卡了一会的原因是我知道要关掉所有输出,但还是忘了关掉父线程的输出。
解释一下题目:
题目也说了,去这个页面csp的中间那张图的前后文理解一下题目,原页面的伪代码也写的很清楚,思路还是有的。
看完了后整体上第一感觉肯定是递归,但是写迭代也不是不能写(我就是),跳出条件就是遍历完34个数字。然后想的就是如何实现,因为具有传递性,所以对于某个中间部分,最少需要两个管道,一个从上一个部分读取,一个向下一个部分输出,因为时间上不能确定谁先谁后,所以必须要两个。
补充一个关于read系统调用的知识:
教程第16页中间有一段话
If no data is available, a read on a pipe waits for either data to be written or for all file descriptors referring to the write end to be closed; in the latter case, read will return 0, just as if the end of a data file had been reached.
意思就是read结束一个条件是EOF,另一个是所有输入关闭,所以这道题要我们手动close所有输入文件描述符
整理一下我们的思路,假设我们现在fork出来了一个子进程,他需要做什么:
- 父进程向他发送数字,从左边的管道读取,第一个数字是质数
- 本身需要读取所有数字,不能被质数整除的数字write到右边的管道
- 读取完成后,关闭读取,创建一个子进程,从操作1开始重复
- wait and exit
写成代码就是这样紫
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
int
main(int argc, char *argv[])
{
int pl[2], pr[2];
pipe(pr);
for (int i = 2; i <= 35; i ++ )
write(pr[1], &i, sizeof(i));
while (1) {
int num = -1, prime;
pl[0] = pr[0], close(pr[1]);
if (fork() == 0) {
pipe(pr);
read(pl[0], &prime, sizeof(prime));
printf("prime %d\n", prime);
/* printf("num"); */
while (read(pl[0], &num, sizeof(num))) {
/* printf(" %d", num); */
if (num % prime != 0)
write(pr[1], &num, sizeof(num));
}
/* printf("\n"); */
close(pl[0]);
if (num == -1) {
close(pr[0]);
exit(0);
}
}
else {
wait(0);
exit(0);
}
}
}
4. find (moderate)
这里需要注意的是find可能和我们平常用的方法不一样,这里的使用时find [path] [file]
按照这个思路编程就行了
然后参考ls.c,照着抄就很简单了(不是
另外要记得特判一下".“和”…"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/fs.h"
#define ALERT(x) printf("wrong => %d\n", x)
char*
fmtname(char *path)
{
char *p;
for (p = path + strlen(path); p >= path && *p != '/'; p -- )
;
p ++ ;
return p;
}
void
find(char *path, char *file)
{
char buf[512], *p;
int fd;
struct stat st;
struct dirent de;
if ((fd = open(path, 0)) < 0) {
ALERT(3); return;
}
if (fstat(fd, &st) < 0) {
ALERT(4); close(fd); return;
}
switch (st.type) {
case T_FILE:
if (strcmp(fmtname(path), file) == 0)
printf("%s\n", path);
break;
case T_DIR:
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);
if (strcmp(de.name, ".") == 0)
continue;
else if (strcmp(de.name, "..") == 0)
continue;
p[DIRSIZ] = 0;
find(buf, file);
}
break;
}
close(fd);
}
int
main(int argc, char *argv[])
{
if (argc < 3) {
ALERT(0);
exit(1);
}
for (int i = 2; i < argc; i ++ )
find(argv[1], argv[i]);
exit(0);
}
5. xargs (moderate)
这题也不难,内存分配好就没问题了,看了另外一个人的解说,这里粘贴过来
说实话,做实验之前我都不知道
xargs
是干嘛用的。所以得先了解一下xargs
的作用,参考阮一峰的博客其中,第二段的一句话非常关键;
xargs命令的作用,是将标准输入转为命令行参数 所以我们实验的整体思路,就是读入标准输入,转成多个命令行参数。
只要把握住这句话,这个实验代码就呼之欲出了。
其实这里我还有个疑问,malloc分配的内存是什么时候被释放的?
因为调用了exec,所以没办法手动释放,那么不就造成内存泄漏了吗?
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
#include "kernel/param.h"
int
main(int argc, char *argv[])
{
char *args[MAXARG], *p;
int args_idx = 0;
for (int i = 1; i < argc; i ++ ) {
args[args_idx] = (char *)malloc(sizeof(argv[i]));
memmove(args[args_idx], argv[i], sizeof(argv[i]));
args_idx ++ ;
}
args[args_idx] = (char *)malloc(512 * sizeof(char));
p = args[args_idx ++ ];
while (read(0, p, sizeof(char))) {
if (*p == ' ' || *p == '\n') {
*p = 0;
if (*p == '\n')
break;
args[args_idx] = (char *)malloc(512 * sizeof(char));
p = args[args_idx ++ ];
}
else p ++ ;
}
/* for (int i = 0; i < args_idx; i ++ ) */
/* printf("%s\n", args[i]); */
exec(args[0], args);
exit(0);
}
The End
开始?还是结束?