Preparation
In WSL2, I have to move ubuntu-18.04 from C disk to D disk, by using tool LxRunOffline
, everything seems easier.
1 | # 查看所有的WSL系统 |
Install dependencies:
1 | $ sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu |
problems
The version of QEMU on “buster” is too old, so you’d have to get that separately.
qemu-system-misc fix
At this moment in time, it seems that the package qemu-system-misc
has received an update that breaks its compatibility with our kernel. If you run make qemu
and the script appears to hang after
1 | qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 |
you’ll need to uninstall that package and install an older version:
1 | $ sudo apt-get remove qemu-system-misc |
IF YOU ARE WORKING ON UBUNTU 20.04
, THERE WILL NOT BE SO MANY ERRORS, THROW AWAY YOUR UBUNTU 18.04
AND UBUNTU 22.04
!!!
lab 1
Xv6 and Unix utilities
system call provided by xv6
file descriptor
On a Unix-like operating system, the first three file descriptors, by default, are STDIN, STDOUT, and STDERR.
Name File descriptor Description Abbreviation Standard input 0 The default data stream for input, for example in a command pipeline. In the terminal, this defaults to keyboard input from the user. stdin Standard output 1 The default data stream for output, for example when a command prints text. In the terminal, this defaults to the user’s screen. stdout Standard error 2 The default data stream for output that relates to an error occurring. In the terminal, this defaults to the user’s screen. stderr
sleep
create a file :
user/sleep.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int
main(int argc, char *argv[])
{
if(argc < 2){
//If the user forgets to pass an argument, sleep should print an error message.
fprintf(2, "usage: sleep [number]\n");
exit(1);
}
if (argc == 2)
{
/* code */
int interval = atoi(argv[1]);
printf("(nothing happens for a little while)\n");
sleep(interval);
} else {
fprintf(2, "Too many arguments!\n");
exit(1);
}
exit(0);
}Add line
$U/_sleep\
behind theUPROGS
in MakefileCompile
1
make qemu
Testing for grade
1
2
3
4
5$ ./grade-lab-util sleep
make: 'kernel/kernel' is up to date.
== Test sleep, no arguments == sleep, no arguments: OK (0.8s)
== Test sleep, returns == sleep, returns: OK (0.8s)
== Test sleep, makes syscall == sleep, makes syscall: OK (1.1s)Pay attention!
No Error
1
2
3Errors !
1
2
3Errors are as follows : “user/user.h” are lack of the definitions of
uint
because you include “user/user.h” before the “kernel/types.h” where uint is defined as unsigned int.1
2
3
4
5
6./user/user.h:42:36: error: unknown type name ‘uint’; did you mean ‘int’?
42 | void *memcpy(void *, const void *, uint);
| ^~~~
| int
cc1: all warnings being treated as errors
make: *** [<builtin>: user/sleep.o] Error 1
pingpong
Same as above
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36int main(int argc, char const *argv[])
{
int p1[2]; int p2[2]; // create a pipe
pipe(p1); pipe(p2); // create a pipe and return its fds in p, dp[0]=>read; dp[1]=>write
if (fork()==0) {
// child process
close(0); // close stdin and bind it to pipe
dup(p1[0]); //子进程将管道的读端口拷贝在描述符0上,关闭 p1中的描述符
// 从小到大找没开的文件描述符, 此时是0(前面stdin关了)
close(p1[0]);
close(p1[1]);
char buffer_ch[2];
read(0, buffer_ch, 1);// read(fd, buf, n) fd -> buf (n bytes upmost)
printf("%d: received ping\n", getpid());
write(p2[1],buffer_ch, 1);// send back
close(p2[0]);
close(p2[1]);
exit(0); // Indispensable, u can't miss this line!
} else {
// father process
write(p1[1],"ccc",1); // write(fd, buf) buf -> fd (n bytes upmost)
close(p1[0]);
close(p1[1]);
close(0);
dup(p2[0]);
close(p2[0]);
close(p2[0]);
char buffer_fa[2];
read(0, buffer_fa, 1);
printf("%d: received pong\n",getpid());
exit(0); // u can't miss this line!
}
return 0;
}For simplifying:
No need to borrow fd from the stdin(0), stdout(1), stderr(0)
You can take up 0 or 2, but not 1, because you need to print out the right result.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int p1[2]; // create a pipe
int p2[2];
pipe(p1); // create a pipe and return its fds in p, p[0]=>read pipe; p[1]=>write pipe
pipe(p2);
if (fork()==0) {
char buffer_ch[2];
read(p1[0], buffer_ch, 1);// read(fd, buf, n) fd -> buf (n bytes upmost)
printf("%d: received ping\n", getpid());
write(p2[1],buffer_ch, 1);// send back
exit(0); // Indispensable, u can't miss this line!
} else {
write(p1[1],"ccc",1); // write(fd, buf) buf -> fd (n bytes upmost)
// 向管道写 c
char buffer_fa[2];
read(p2[0], buffer_fa, 1);
printf("%d: received pong\n",getpid());
exit(0); // u can't miss this line!
}For the other perspective:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30if (fork()==0) {
// child process
close(2); // close stdin and bind it to pipe
dup(p1[0]); // 子进程将管道的读端口拷贝在描述符0上,关闭 p1中的描述符
close(p1[0]);
close(p1[1]);
char buffer_ch[2];
read(2, buffer_ch, 1);// read(fd, buf, n) fd -> buf (n bytes upmost)
printf("%d: received ping\n", getpid());
write(p2[1],buffer_ch, 1);// send back
close(p2[0]);
close(p2[1]);
exit(0); // Indispensable, u can't miss this line!
} else {
// father process
write(p1[1],"ccc",1); // write(fd, buf) buf -> fd (n bytes upmost)
close(p1[0]);
close(p1[1]);
close(2); <-- 关stderr也行
dup(p2[0]);
close(p2[0]);
close(p2[0]);
char buffer_fa[2];
read(2, buffer_fa, 1);
printf("%d: received pong\n",getpid());
exit(0); // u can't miss this line!
}Testing and grade
1
2
3$ ./grade-lab-util pingpong
make: 'kernel/kernel' is up to date.
== Test pingpong == pingpong: OK (1.6s)
primes
Schematic
pseudocode
1
2
3
4
5
6p = 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 neighborAC code
- 管道用来过滤第一个质数的倍数(筛法)
- 父进程需要调用
wait(0)
等待子进程结束,否则会导致执行流混乱 - fd不够用,所以对于子进程,每次写完过滤好的数据之后,需要关闭和父进程共享的pipe的读端口,也要关闭和子进程共享的pipe的写端口
- 由于read会阻塞,因此每次多写入一个
-1
表示所有数据已经传递完成跳出循环,子进程同时把pipe的读端口给关闭了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63int p[35][2];
int index = 0; // children fd number
int main(int argc, char const *argv[])
{
pipe(p[index]); // create pipe for child
printf("prime 2\n");
int i;
for (i=3; i<=35; i++) {
if(i%2 != 0){
write(p[index][1], &i, 4);
}
}
i = -1;
write(p[index][1], &i, 4); // 终止信号
close(p[index][1]);
if (fork()==0) {
child_process_handler(index);
exit(0);
} else {
wait(0);//等待子进程结束
exit(0);
}
return 0;
}
void child_process_handler(int index) {
int number;
int flw_num; // following numbers
read(p[index][0], &number, 4);
// read returns zero when the write-side of a pipe is closed.
// 4 bytes -> integer
if (number == -1) {
//已经全部结束
return;
}
printf("prime %d\n", number);
pipe(p[index+1]);//不要放到判断 number==-1上面
while(read(p[index][0], &flw_num, 4)!=-1) {
// not closed by that pipe, but it will block.
if (flw_num == -1) {
break;
}
if (flw_num%number != 0) {
write(p[index+1][1], &flw_num, 4);
}
}
flw_num = -1;
write(p[index+1][1], &flw_num, 4);// 终止信号
close(p[index][0]);//关闭上一个读的
close(p[index+1][1]);//关闭下一个写的
if (fork()==0) {
child_process_handler(index+1);
exit(0);
} else {
wait(0);//等待子进程结束
exit(0);
}
return;
}
find
fmtname
1
fmtname(path) return single-filename
fstat :
fstat
可以获取一个文件描述符指向的文件的信息, 同一个文件(称为inode
)可能有多个名字,称为连接 (links
)1
2
3
4
5
6
7
8
9
10
11
12
13
// Directory
// File
// Device
struct stat {
short type; // Type of file
int dev; // File system’s disk device
uint ino; // Inode number
short nlink; // Number of links to file
uint size; // Size of file in bytes
};dir struct
1
2
3
4
5
struct dirent {
ushort inum;
char name[DIRSIZ];
};memmove
1
C 库函数 void *memmove(void *str1, const void *str2, size_t n) 从 str2 复制 n 个字符到 str1,但是在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法
find
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71void find(char *path, char *fname) {
struct dirent de;
struct stat st;
int fd;
char buf[512], *new_path; // new path pointer
if ((fd = open(path, 0))<0) {
fprintf(2, "find: path '%s' doesn't exist.\n", path);
return;
}
if (fstat(fd, &st) < 0) {
fprintf(2, "find: cannot stat %s\n", path);
close(fd);
return;
}
char *pname;
char *pp;
switch (st.type)
{
case T_FILE:
pname = fmtname(path);
pp = pname;
//printf("pname %s?\n", pp);
while(1) {
if (*pp == 32) break;
if (*pp == 0) break;
//printf("%d ", *pp);
pp++;
}
*pp = 0;
//printf("pname %s? fname %s?\n", path, fname);
if (strcmp(pname, fname) == 0) {
printf("%s\n", path);
}
break;
case T_DIR:
// 递归遍历文件夹
if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
printf("find: path too long\n");
break;
}
strcpy(buf, path);
new_path = buf + strlen(buf);
*new_path++ = '/';
while(read(fd, &de, sizeof(de)) == sizeof(de)) {
if (de.inum == 0) { // 文件夹下的没有文件
continue;
}
memmove(new_path, de.name, DIRSIZ);
new_path[DIRSIZ] = 0;
if (stat(buf, &st) < 0) {
printf("find: cannot stat %s\n", buf);
continue;
}
char * fn = fmtname(buf);
//清除DIRSIZ的空格
char * p = fn;
while(*p != ' ' && *p != '\0') {
p++;
}
*p = '\0';
if (strcmp(fn, ".") == 0 || strcmp(fn, "..") == 0) {
continue;
} else {
find(buf, fname);
}
}
break;
}
close(fd);
return;
}
xargs
hints:
- Use
fork
andexec
to invoke the command on each line of input. Usewait
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.
1 | int main(int argc, char *argv[]) { |
Addition
concurrency
- CSP : communicating sequential processes
reference
- lecture schedule : https://pdos.csail.mit.edu/6.828/2020/schedule.html
- lecture translated : https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/
- lab1 utilities page : https://pdos.csail.mit.edu/6.S081/2020/labs/util.html