6.S081「Xv6 and Unix utilities」

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)

像乒乓球一样,想想一个来回的过程。

题目没说发送什么东西,所以重点在于你发送的顺序:

  1. parent send byte
  2. parent wait
  3. child read and print
  4. child send byte
  5. child exit
  6. 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出来了一个子进程,他需要做什么:

  1. 父进程向他发送数字,从左边的管道读取,第一个数字是质数
  2. 本身需要读取所有数字,不能被质数整除的数字write到右边的管道
  3. 读取完成后,关闭读取,创建一个子进程,从操作1开始重复
  4. 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

开始?还是结束?

下一站: Lab pgtbl: Page tables

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值