如何退出xv6(qemu):先一起按下ctrl+a,然后再点击一下x即可

Sleep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "kernel/types.h"
#include "user/user.h"

int
main (int argc ,char *argv[]){
if (argc != 2){
fprintf(2,"sleep:lack of arguement should input 2 you;but argc = %d",argc);
}

int num = (int)atoi(argv[1]);

sleep(num);

exit(0);
}

Pingpong

总体上没用什么难点,特别的就是需要创建两个管道,用于两个进程读写,避免自我阻塞问题

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
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main (int argc,char *argv[]){
if (argc > 1){
fprintf(2,"pingpong: too many arg");
}
int p1[2];
int p2[2];

if (pipe(p1)<0){
fprintf(2,"pipe");
}
if (pipe(p2)<0){
fprintf(2,"pipe");
}
if(fork() == 0){
close(p1[1]);
read(p1[0],"",1);
close(p1[0]);

printf("%d: received ping\n",getpid());

close(p2[0]);
write(p2[1],"",1);
close(p2[1]);

exit(0);
}else{
close(p1[0]);
write(p1[1],"",1);
close(p1[1]);

close(p2[1]);
read(p2[0],"",1);
close(p2[0]);

printf("%d: received pong\n",getpid());

exit(0);
}
}

Primes:素数筛(sieve)

使用pipe编写一个并发版本的素数过滤,可以参考pipeline

我们需要创建多个进程,分别过滤掉2、3、5、7、…的倍数。在本次实验中我们主要是采用pipe与fork两个系统调用来进行实现pipeline的效果。

伪代码:

1
2
3
4
5
6
p = 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 neighbor

如下图所示,当pipe1接收到数据后进行除2,并将没有过滤掉的数据(3,5,7,9..)继续发往下一个进程,以此类推。

如果你单纯的项通过测试,可以直接printf(bushi),作为一个有专研精神的程序员,当前是要继续挑战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main(int argc,char* argv[]){
if (argc > 1){
fprintf(2,"prime: too many arg");
}
int i,j;
for(i = 2 ; i <= 35 ; ++i){
for(j = 2;j < i/2+1 ; ++j){
if(i%j == 0){
break;
}
}
if(j == i/2+1){
printf("prime %d\n",i);
}
}
exit(0);
}
  1. 首先需要考虑创建多少个筛子,也就是进程。[2,35]之间有多少个素数也就是我们要创建的进程数—(11个)。

  2. 创建管道数,做完pingpong程序后我们知道了,当两进程需要来回传递时我们要创建2个管道避免阻塞,为了使子进程能够和自己的子进程传递数据那么就需要一个新的管道。

  3. 特别的,没有过滤掉的第一个数肯定是素数,例如2倍数的筛子(将2的倍数筛掉)->3倍数的筛子(将3的倍数筛掉),我们收到的第一个数就是三我们也就是可以直接打印。

NOTE: 当发送端的写入管道关闭时,接收端read返回值为0。在管道篇讲解到当缓冲区没有数据时会读取管道会出现阻塞,因此当write将数字发送完后,接收方读取完buffer数据,发送端就会关闭输出管道,接收方read返回值就为0

  1. 递归的方式实现,当递归调用最后一个primes函数时,此时读取p管道发现其关闭则结束递归调用,调用exit(0);
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
void primes(int *p){//for parent

close(p[1]);//关闭该进程的写

int prime,tmp;
if(read(p[0],&prime,sizeof(int)) == 0){//当没有输入时
//让最后一个进程退出
exit(0);
}

printf("prime %d\n",prime);

int p1[2];//for next child process
if(pipe(p1)<0){
fprintf(2,"pipe: err");
exit(0);
}

if(fork() == 0 ){
primes(p1);
}else{//parent
close(p1[0]);
while(read(p[0],&tmp,sizeof(int)) != 0){//read from parent
if((tmp%prime) != 0){
write(p1[1],&tmp,sizeof(int));//write to child
}
}

close(p[0]);
close(p1[1]);

wait(0);//发送端等待接收端退出
}

exit(0);
}

int
main(int argc,char* argv[]){
if (argc > 1){
fprintf(2,"primes: too many args" );
}

int p[2];
if(pipe(p)<0){
fprintf(2,"pipe: err");
exit(0);
}

if(fork() == 0){
primes(p);
}else{
close(p[0]);

int i;
//write value to 2 times process
for(i = 2 ; i <= 35 ; ++i){
if(write(p[1],&i,sizeof(int)) != sizeof(int)){
fprintf(2,"primes:err write");
exit(1);
}
}

close(p[1]);
wait(0);
}

exit(0);
}

find

通过编写find.c文件实现在unix下的find命令实现

  1. find 目录 -> 打印目录下的所有文件
  2. find 文件 -> 打印当前目录下的文件路径
  1. 首先我们需要观看ls.c文件查看如何读入目录文件的内容;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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);
    }

    当文件类型为T_DIR时,拷贝path字符串。通过dirent结构体进行读取当前目录下的内容(可能含有数据文件、设备、目录),de.name就是文件名(含有当前文件路径./)。

  2. 通过fmtname()函数对文件的路径名进行格式化。如我们输入/a/b/c路径字符串,我们返回的字符串将为c。在后续的修改中需要将memset的初始化字符修改为0。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    char*
    fmtname(char *path)
    {
    static char buf[DIRSIZ+1];
    char *p;

    // Find first character after last slash.
    for(p=path+strlen(path); p >= path && *p != '/'; p--)
    ;
    p++;

    // Return blank-padded name.
    if(strlen(p) >= DIRSIZ)
    return p;
    memmove(buf, p, strlen(p));
    memset(buf+strlen(p), ' ', DIRSIZ-strlen(p));
    return buf;
    }
  3. 修改ls中代码如下所示

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
switch(st.type){
case T_DEVICE:
case T_FILE:
printf("%s\n", path);//递归输入的参数是数据文件路径则终止递归
close(fd);
return;

case T_DIR:
...
strcpy(buf, path);
printf("%s\n",buf);//打印目录名,不需要fmt
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;
char* tmpname = fmtname(buf);
if(strcmp(tmpname,".") == 0||
strcmp(tmpname,"..") == 0 ){
continue;
}
find(buf);//子目录或是数据文件都开启递归
}
close(fd);
return;
}

NOTE: 字符串比较不能使用==比较符号,需要使用字符串函数stmcpy。而且需要修改ls.c中的fmtname函数,将memset(buf+strlen(p), ' ', DIRSIZ-strlen(p))中的空格修改为0'\0'

xarg

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
#include "kernel/types.h"
#include "user/user.h"
#include "kernel/param.h"

void copy(char** dsn,char* src){
*dsn = (char*)malloc(strlen(src)+1);
strcpy(*dsn,src);
}

int
main(int argc,char* argv[]){
if(argc < 2 ){
fprintf(2,"xargs:err");
exit(1);
}

int i;
char* vcmd[MAXARG];

for(i=1;i<argc&&i<MAXARG;i++)
copy(&vcmd[i-1],argv[i]);

i--;
char buf[100];
char *p;
while(read(0,buf,sizeof(buf))>0){
if((p = strchr(buf,'\n')))
*(p--) = 0;
if(strlen(buf)==0){
continue;
}
copy(&vcmd[i],buf);
i++;
memset(buf,0,strlen(buf));
}

char* execarg[3];
copy(&execarg[0],vcmd[0]);
copy(&execarg[1],vcmd[1]);

int j;
for(j=2;j<i;j++){
if(fork()==0){
copy(&execarg[2],vcmd[j]);
// printf("%s\n",execarg[2]);
exec(execarg[0],execarg);
printf("exec file wrong\n");
exit(1);
}else{
wait(0);
}
}

exit(0);
}