OS_shell实现文档

BUAA_OS_Shell实现文档

本人代码完成顺序也是按照如下顺序进行

实现不带 .b 后缀指令

这可以说是最简单的部分,只需要在spawn函数中添加特判即可,如果没找到文件,现在在后缀添加.b再次尝试是否可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int len = strlen(prog);
if ((fd = open(prog, O_RDONLY)) < 0) {
if (fd == -E_NOT_FOUND && (len < 2 || prog[len - 1] != 'b' || prog[len - 2] != '.')) {
char prog_bin[MAXPATHLEN];
strcpy(prog_bin, prog);
prog_bin[len] = '.';
prog_bin[len + 1] = 'b';
prog_bin[len + 2] = '\0';
if((fd = open(prog_bin , O_RDONLY)) < 0){
return fd;
}
} else {
return fd;
}
}

实现一行多指令

使用 ; 将多条指令隔开从而从左至右依顺序执行每条指令的功能.

fork出一个子进程,让子进程继续执行,父进程等待子进程执行结束后继续执行右侧命令,不断递归达到目的。

1
2
3
4
5
6
7
8
9
case ';':
r = fork();
if (r) {
wait(r);
return parsecmd(argv, rightpipe);
} else {
return argc;
}
break;

实现引号支持

类似于OO第一单元,需要将引号内部的内容看作是一个token,针对_gettokken()中的代码,只需要做如下添加并更新p1,p2的位置即可。

1
2
3
4
5
6
7
8
9
10
11
12
if (*s == '\"') {
*s = 0;
s++;
*p1 = s;
while(*s != 0 && *s != '\"') {
s++;
}
*s = 0;
s++;
*p2 = s;
return 'w';
}

实现注释功能

最简单的部分!!!

读到#意味着后面可以不读了

1
2
3
if (*s == 0 || *s == '#') {
return 0;
}

实现更多指令

首先类似于lab5-exam,在文件系统中增加create服务。并将fsipc_create()和create添加到user/lib/lib.h中。并在/user/include/fsreq.h中的enum中增加FSREQ_CREATE

1
2
3
4
5
6
7
8
9
10
11

void serve_create(u_int envid, struct Fsreq_create *rq); // /fs/serv.c

int fsipc_create(const char* path, int f_type); // /user/lib/fsipc.c

int create(const char* path, int f_type); // /user/lib/file.c

struct Fsreq_create {
char req_path[MAXPATHLEN];
int f_type;
}; // /user/include/fsreq.h

由于mkdir中需要判断对应path下的文件类型

1
2
3
int file_get_type(struct File *f); // /ls/serv.h

#define O_GETTYPE 0x0888 // /user/include/lib.h

file_get_type()我的写法如下

1
2
3
4
5
6
7
8
int file_get_type(struct File *f) {
if (f->f_type == FTYPE_REG) {
return -2237;
} else if (f->f_type = FTYPE_DIR){
return -1147;
}
return 0;
}

并且修改serve_open()使得在open时可以通过返回值判断文件类型增加如下

1
2
3
4
5
6
if (rq->req_omode & O_GETTYPE) {
if ((r = (file_get_type(f))) < 0) {
ipc_send(envid, r, 0, 0);
return;
}
}

完成上述工作,我耗费了大量时间思考,接下来的工作就比较简单了

touch.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <lib.h>

int main(int argc, char **argv) {
int r;
if (!((r = open(argv[1], O_RDONLY)) < 0)) {
return -1;
}
if ((r = create(argv[1], FTYPE_REG)) < 0) {
fprintf(1, "touch: cannot touch '%s': No such file or directory\n", argv[1]);
return -1;
}
return 0;
}

mkdir.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
25
26
27
28
29
30
31
32
33
#include <lib.h>

int main(int argc, char **argv) {
int r;
// printf("%s man! What can I say !!!", argv[1]);
if (strcmp(argv[1], "-p") == 0) {
if (!((r = open(argv[2], O_RDONLY)) < 0)) {
return -1;
}
if ((r = create(argv[2], FTYPE_DIR)) < 0) {
char *path = argv[2];
char p[MAXPATHLEN];
int len = strlen(path);
for (int i = 0; i < len; i++) {
if (path[i] == '/') {
create(p, FTYPE_DIR);
}
p[i] = path[i];
}
create(p, FTYPE_DIR);
}
} else {
if (!((r = open(argv[1], O_RDONLY)) < 0)) {
fprintf(1, "mkdir: cannot create directory '%s': File exists\n", argv[1]);
return -1;
}
if ((r = create(argv[1], FTYPE_DIR)) < 0) {
fprintf(1, "mkdir: cannot create directory '$s': No such file or directory\n", argv[1]);
return -1;
}
}
return 0;
}

rm.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
25
26
27
28
29
30
31
32
33
34
35
36
#include <lib.h>

int main(int argc, char **argv) {
struct Fd *fd;
struct Filefd *ffd;
int r;
if (strcmp(argv[1], "-r") == 0) {
if ((r = open(argv[2], O_RDONLY)) < 0) {
fprintf(1, "rm: cannot remove '%s': No such file or directory\n", argv[2]);
return -1;
}
if ((r = remove(argv[2])) < 0) {
return -1;
}
} else if (strcmp(argv[1], "-rf") == 0) {
if ((r = open(argv[2], O_RDONLY)) < 0) {
return -1;
}
if ((r = remove(argv[2])) < 0) {
return -1;
}
} else {
if ((r = open(argv[1], O_RDONLY)) < 0) {
fprintf(1, "rm: cannot remove '%s': No such file or directory\n", argv[1]);
return -1;
}
r = open(argv[1], O_GETTYPE);
if (r == -1147) {
fprintf(1, "rm: cannot remove '%s': Is a directory\n", argv[1]);
return -1;
} else {
remove(argv[1]);
}
}
return 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
case '>':
if (gettoken(0, &t) != 'w') {
debugf("syntax error: > not followed by word\n");
exit(1);
}
if (flag == 1) {
fd=open(t, O_WRONLY | O_CREAT);
if (fd < 0) {
debugf("failed to open '%s'\n",t);
exit(1);
}
struct Stat st;
stat(t, &st);
seek(fd, st.st_size);
} else {
fd=open(t, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
debugf("failed to open '%s'\n",t);
exit(1);
}
}
dup(fd,1);
close(fd);
//user_panic("> redirection not implemented");

break;

坑点:open时不只有只写操作!!!

实现反引号

我的总体处理类似于;处理,不过需要在gettoken时将一对`变成一个,但是这样并不适用于整个所有,个人认为更加有效的处理是将反引号内部运行的结果作为参数传给echo,而不是直接给echo传空值。

1
2
3
4
5
6
7
8
9
case '`':
r = fork();
if (r) {
wait(r);
return argc;
} else {
return parsecmd(argv, rightpipe);
}
break;
1
2
3
4
5
6
7
8
if (*s == '`') {
char *p = s;
p++;
while (*p != '`') {
p++;
}
*p = ' ';
}

实现指令条件执行

好难!!!

根据提示修改exit函数,并通过ipc将flag信号传递给父进程。

1
2
3
4
5
6
7
8
9
10
11
void exit(int flag) {
// After fs is ready (lab5), all our open files should be closed before dying.
#if !defined(LAB) || LAB >= 5
close_all();
#endif

syscall_ipc_try_send(envs[ENVX(env->env_parent_id)].env_parent_id, flag, 0, 0);
syscall_env_destroy(0);
user_panic("unreachable code");
}

接下来就是根据返回值进行不同操作,具体如下

返回值 当前符号 目标
0 \ \ 寻找下一个&&后命令
1 \ \ 指向下一命令判断返回值
0 && 指向下一命令判断返回值
1 && 寻找下一个\ \ 后命令

从env中获取到exit的返回值,在读取到&&和||时进行上述操作即可

1
2
3
4
if ((r = (syscall_ipc_recv(0)) < 0)) {
return r;
}
int value = env -> env_ipc_value;

坑点:父进程不用wait子进程!!!!!!!!!!!!!!

前后台任务

好难!!!!!!!!!,耗时最长的部分,主要理解一直有误,导致一直卡着

开始一直在用户空间储存结构体发现不对,才将其放在内核态。着实理解不足

env.h中增加结构体

1
2
3
4
5
6
7
8
#define MAXJOBS 1000
struct job{
int job_id;
int status;
u_int env_id;
int killed;
char cmd[100];
};

并为用户态增加如下系统调用达到访问jobs目的

1
2
3
4
5
int syscall_fg_job(int fgId);
int syscall_kill_job(int killId);
int syscall_print_job();
int syscall_add_job(u_int envid, char * cmd);
int syscall_done_job(u_int envid);

剩下部分就和往年类似,根据hangup判断是否需要等待

1
2
3
4
5
6
7
8
9
10
r = fork();
if (r) {
hangup = 1;
syscall_add_job(r, cmd);
return parsecmd(argv, rightpipe);
} else {
hangup = 0;
return argc;
}
break;

接下来,kill、fg等各参数根据argv中的参数进行各类操作即可,这里我的syscall_fg_job(fgId)起到一个返回对应fg的envid的作用,然后让进程等待该进程则可。

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
void runcmd(char *s) {
save_history(s);
strcpy(cmd, s);
gettoken(s, 0);

char *argv[MAXARGS];
int rightpipe = 0;
int argc = parsecmd(argv, &rightpipe);
if (argc == 0) {
return;
}
argv[argc] = 0;
if (strcmp(argv[0], "fg") == 0) {
int fgId = 0;
char *str = argv[1];
while (*str >= '0' && *str <= '9') {
fgId = fgId * 10 + (*str - '0');
str++;
}
// fprintf(1, "%d", fgId);
int envid = syscall_fg_job(fgId);
// fprintf(1, "%x" , envid);
wait(envid);
exit(0);
return;
} else if (strcmp(argv[0], "kill") == 0) {
int killId = 0;
char *str = argv[1];
while (*str >= '0' && *str <= '9') {
killId = killId * 10 + (*str - '0');
str++;
}
int envid = syscall_kill_job(killId);
exit(0);
return;
} else if (strcmp(argv[0], "jobs") == 0) {
syscall_print_job();
exit(0);
return;
} else if (strcmp(argv[0], "history") == 0) {
print_history();
exit(0);
return;
}
int child = spawn(argv[0], argv);
close_all();
if (child >= 0) {
if (hangup == 0) {
wait(child);
}
} else {
debugf("spawn %s: %d\n", argv[0], child);
}
if (rightpipe) {
wait(rightpipe);
}
exit(0);
}

注意最后在exit时要通过系统调用将进程标记为结束。

1
2
3
4
5
6
7
8
9
10
11
void exit(int flag) {
// After fs is ready (lab5), all our open files should be closed before dying.
#if !defined(LAB) || LAB >= 5
close_all();
#endif

syscall_ipc_try_send(envs[ENVX(env->env_parent_id)].env_parent_id, flag, 0, 0);
syscall_done_job(env->env_id);
syscall_env_destroy(0);
user_panic("unreachable code");
}

系统调用内部主要就是各种结构体的赋值,内部参数的输出,较为简单。

做完之后回看这部分,其实没有想的那么难,把思路理清楚,虽然工作量很大,但做起来很快。

历史指令

难点主要在读取上下键极其后续处理上

编码
27 ‘[‘ ‘A’
27 ‘[‘ ‘B’

需要完成以下函数

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
void save_history(char *cmd);
void print_history();

//
void save_history(char *cmd) {
int fd;
if ((fd = open(".mosh_history", O_CREAT | O_WRONLY )) < 0){
return;
}
struct Stat st;
stat(".mosh_history", &st);
seek(fd, st.st_size);
write(fd, cmd, strlen(cmd));
write(fd, "\n", 1);
close(fd);
return;
}

void print_history() {
int r;
if ((r = open(".mosh_history", O_RDONLY)) < 0){
return;
}
char buf;
while (read(r, &buf, 1)) {
fprintf(1, "%c", buf);
}
close(r);
}

save_history有点类似于重定向,需要处理一下偏移量的问题。

print_history记得在runcmd()的第一句执行save_history(s)保存以下即可


OS_shell实现文档
https://fantasylee21.github.io/2024/06/13/OS-shell实现文档/
作者
Fantasylee
发布于
2024年6月13日
更新于
2024年6月21日
许可协议