\(\mathcal{Author:gpf}\)
OS仓库链接:solor-wind/BUAA_OS
(github.com)
最好的指导书:YannaのBlog -
听铃,聆听,浮生清欢~ (yanna-zy.github.io)
指导书
使用vscode连接远程服务器
实现不带 .b
后缀指令
在 user/lib/spawn.c
中修改 spawn
函数,当直接执行失败时,再原参数后面加上".b"重新执行。
1 2 3 4 5 6 7 8 9 10 11 12
| int fd; if ((fd = open(prog, O_RDONLY)) < 0) { int len=strlen(prog); char myprog[len+2]; strcpy(myprog,prog); myprog[len]='.'; myprog[len+1]='b'; myprog[len+2]='\0'; if ((fd = open(myprog, O_RDONLY)) < 0) { return fd; } }
|
实现指令条件执行
在进程控制块中增加 env_return_value
,存储返回值,并编写相应系统调用来修改返回值。
1 2 3 4 5 6 7
| int sys_env_set_return_value(u_int envid,int value,int isreceived){ struct Env* e; try(envid2env(envid, &e, 0)); e->env_return_value=value; e->env_isreceived=isreceived; return 0; }
|
在 user/lib/libos.c
中,将main函数返回值存储并送至父进程(spawn)的父进程(fork)处
1 2
| int r=main(argc, argv); r=syscall_env_set_return_value(envs[ENVX(env->env_parent_id)].env_parent_id,r,1);
|
最后修改sh.c中的解析过程(以 &&
为例),当前面指令执行为非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
| ch=gettoken(0, &t); if (ch == '&') { if(dupflag){ dup(0,1); }else{ dup(1,0); } r=fork(); *rightpipe=r; if(r==0) { return argc; } else { wait(r); int value=env->env_return_value; if(!value) return parsecmd(argv, rightpipe,post); else { close_all(); exit(); } } }
|
同时,对于管道指令,判断之后先存储argv[0]=t,并通过post表示递归调用argc时的初始值为1
实现更多指令
总体上,新建相应 .c
文件,而后在 user
目录下的 include.mk
中加上相应的 .b
文件即可,如下所示
1 2 3 4
| USERAPPS := num.b \ touch.b \ mkdir.b \ rm.b \
|
touch
直接调用 open
函数即可,对返回值进行判断
1 2 3 4
| f=open(argv[1],O_CREAT); if(f<0){ printf("touch: cannot touch '%s': No such file or directory\n", argv[1]); }
|
mkdir
类似touch,调用 open
函数,但需要在
fs/serv.c
中修改 serve_open
函数,当请求模式为
O_MKDIR
时,将文件的类型标记为目录。
1 2 3 4 5 6 7 8
| if ((rq->req_omode & O_MKDIR) && (r = file_create(rq->req_path, &f)) < 0 && r != -E_FILE_EXISTS) { ipc_send(envid, r, 0, 0); return; } if(rq->req_omode & O_MKDIR){ f->f_type=FTYPE_DIR; }
|
之后在mkdir中调用即可
rm
同样类似touch,但需要新增请求模式。
首先在 user/include/lib.h
中新增
#define O_TYPE 0x1000
,表示对文件类型的查询。
其次在 user/lib/file.c
的 open
函数中新增对模式的判断,如果是查询文件类型,则在ipc调用之后立刻返回。
1 2 3 4
| r=fsipc_open(path,mode,fd); if(mode&O_TYPE){ return r; }
|
最后,也要在 fs/serv.c
中修改 serve_open
函数,当请求模式为 O_TYPE
时,找到文件后直接返回文件类型。
1 2 3 4 5 6 7 8 9
| if ((rq->req_omode & O_TYPE) && (r = file_open(rq->req_path, &f)) < 0 && r != -E_FILE_EXISTS) { ipc_send(envid, r, 0, 0); return; } if(rq->req_omode & O_TYPE){ ipc_send(envid, f->f_type, 0, 0); return; }
|
实现注释
修改 user/sh.c
中的 main
函数,当
buf
中首次出现 #
时,相应位置赋值为为
'\0'
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| if (r == 0) { for(int i=0;buf[i];i++) { if(buf[i]=='#') { buf[i]='\0'; break; } } runcmd(buf); exit(); } else { wait(r); }
|
实现历史指令
历史指令的存储
分为两部分,一部分为全局变量,另一部分保存在
.mosh_history
中,两部分的内容一样,但全局变量更容易存取。其中histcmd相当于循环链表,hist表示下一条指令应当写入到哪里
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
| #define HISTFILESIZE 20 int hist; char histcmd[HISTFILESIZE][1024];
void wirte_history(){ int tmpflag=0; for(int i=0;buf[i];i++){ if(buf[i]!='\t'||buf[i]!='\r'||buf[i]!='\n'){ tmpflag=1; } } if(!tmpflag){ return; }
int f=open(".mosh_history",O_RDONLY); if(f<0){ f=open(".mosh_history",O_CREAT); } close(f);
if(hist<HISTFILESIZE&&histcmd[hist][0]=='\0'){ f=open(".mosh_history",O_RWR|O_WRONLY); strcpy(histcmd[hist],buf); int len=strlen(buf); histcmd[hist][len]='\n'; histcmd[hist][len+1]='\0'; write(f,histcmd[hist],strlen(histcmd[hist])); close(f); hist++; if(hist==HISTFILESIZE){ hist=0; } } else{ strcpy(histcmd[hist],buf); int len=strlen(buf); histcmd[hist][len]='\n'; histcmd[hist][len+1]='\0';
f=open(".mosh_history",O_WRONLY|O_TRUNC); hist++; if(hist==HISTFILESIZE){ hist=0; } int flag=hist; write(f,histcmd[hist],strlen(histcmd[hist])); close(f); f=open(".mosh_history",O_RWR|O_WRONLY); while(1){ hist++; if(hist==HISTFILESIZE){ hist=0; } if(hist==flag){ break; } write(f,histcmd[hist],strlen(histcmd[hist])); } } lasthist=-1; lastcmd[0]='\0'; }
|
实现up和down
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 71 72 73 74 75 76 77 78 79
| if(i>=2&&buf[i-2]==27&&buf[i-1]==91&&buf[i]==65){ i-=2; printf("%c%c%c",27,91,66); for(int j=0;j<i;j++){ printf("\b \b"); } if(lasthist==-1){ strcpy(lastcmd,buf); lastcmd[i]='\0'; } if(lasthist==-1){ if(hist==0){ if(histcmd[HISTFILESIZE-1][0]!='\0'){ lasthist=HISTFILESIZE-1; }else{ lasthist=0; } }else{ lasthist=hist-1; } }else{ if(lasthist==0&&histcmd[HISTFILESIZE-1][0]=='\0'){ ; }else{ if(lasthist==hist){ ; }else{ lasthist--; if(lasthist<0){ lasthist=HISTFILESIZE-1; } } } } strcpy(buf,histcmd[lasthist]); if(buf[strlen(buf)-1]=='\n'){ buf[strlen(buf)-1]='\0'; } printf("%s", buf); i=strlen(buf)-1; continue; } else if(i>=2&&buf[i-2]==27&&buf[i-1]==91&&buf[i]==66){ i-=2; for(int j=0;j<i;j++){ printf("\b \b"); } if(lasthist==-1){ strcpy(lastcmd,buf); lastcmd[i]='\0'; } if(lasthist==-1){ lasthist==-2; }else{ if((hist==0&&lasthist==HISTFILESIZE-1)||(hist&&lasthist==hist-1)){ lasthist=-2; }else{ lasthist++; if(lasthist>=HISTFILESIZE){ lasthist=0; } if(histcmd[lasthist][0]=='\0'){ lasthist=-2; } } } if(lasthist<0){ strcpy(buf,lastcmd); }else{ strcpy(buf,histcmd[lasthist]); } if(buf[strlen(buf)-1]=='\n'){ buf[strlen(buf)-1]='\0'; } printf("%s", buf); i=strlen(buf)-1; continue; }
|
实现history
在 runcmd
函数中进行判断即可
1 2 3 4 5 6 7
| if(strcmp(argv[0],"history")==0){ int f=open(".mosh_history",O_RDONLY); char tmp[10240]; readn(f,tmp,10240); printf("%s\n",tmp); exit(); }
|
实现一行多指令
修改 user/sh.c
中的 parsecmd
函数即可
1 2 3 4 5 6 7 8 9 10 11 12 13
| case ';': r=fork(); *rightpipe=r; if(r==0) { return argc; } else { wait(r); return parsecmd(argv, rightpipe); } break;
|
实现反引号
在 sh.c
的 _gettoken
函数中,首先创建管道,然后fork,让子进程执行反引号中的内容,并在父进程中通过管道读取结果。
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
| else if(*s=='`'){ char cmd[1024]; char cmdans[1024]; char after[1024]; for(int i=1;s[i];i++){ if(s[i]=='`'){ cmd[i-1]='\0'; strcpy(after,s+i+1); break; }else{ cmd[i-1]=s[i]; } } int p[2]; pipe(p); int r=fork(); if(r==0) { dup(p[1],1); close(p[0]); close(p[1]); runcmd(cmd); exit(); } else { close(p[1]); int len,pos=0; char tmp[1024]; while((len=read(p[0],tmp,1024))>0){ tmp[len]='\0'; strcpy(cmdans+pos,tmp); pos+=len; } close(p[0]); } strcpy(s,cmdans); strcpy(s+strlen(cmdans),after); s+=strlen(cmdans)-1; }
|
实现重定向
首先在 user/include/lib.h
中新增
#define O_RWR 0x2000
,表示重定向写入。
其次在 fs/serv.c
的 serv_open
函数中新增对模式的判断,如果是重定向写入,则修改offset。
1 2 3
| if(rq->req_omode & O_RWR){ ff->f_fd.fd_offset =ff->f_file.f_size; }
|
最后在 sh.c
的 parsecmd
中修改对
>
的处理
1 2 3 4 5 6 7 8 9 10
| if(ch=='>'){ ch=gettoken(0, &t); if (ch != 'w') { debugf("syntax error: > not followed by word\n"); exit(); } fd=open(t,O_RWR|O_WRONLY|O_CREAT); }else{ fd=open(t,O_WRONLY|O_TRUNC|O_CREAT); }
|
实现引号支持
在 sh.c
的 _gettoken
函数中的第一个取空白代码段后面添加关于字符串的处理即可,即遇到引号则读到下一个引号为止,并返回
w
的token
1 2 3 4 5 6 7 8 9 10 11
| if(*s=='\"'){ *s++=0; *p1 = s; while (*s && *s!='\"') { s++; } *s++=0; *p2 = s; return 'w'; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| if(*s=='\"'){ char tmp[1024]; strcpy(tmp,s+1); int i=0,j=0,flag=0; for(;tmp[j];j++){ if(tmp[j]=='\"'&&!flag){ flag=j; continue; }else{ s[i++]=tmp[j]; } } s[i]='\0'; s+=flag-1; }
|
实现前后台任务管理
env.h
文件中,新增宏定义、结构体定义、系统调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #define MAXJOB 1024 #define Running 0 #define Done 1
struct Job { int job_id; int status; int env_id; char cmd[1025]; };
int add_job(int envid,char cmd[]); int set_job_status(int job_id,int status); int get_job(int envid); int get_job_envid(int job_id); int get_jobs(struct Job usrjobs[]); int print_jobs();
|
env.h
文件中,新增任务结构体、与系统调用实现
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
| struct Job jobs[MAXJOB+1]; int job_cnt=0;
int add_job(int envid,char cmd[]){ if(job_cnt>=MAXJOB){ return -1; } job_cnt++; jobs[job_cnt].job_id=job_cnt; jobs[job_cnt].env_id=envid; jobs[job_cnt].status=Running; strcpy(jobs[job_cnt].cmd,cmd); return 0; }
int set_job_status(int job_id,int status){ if(job_id>0&&job_id<=job_cnt){ jobs[job_id].status=status; return 0; }else{ return -1; } }
int get_job(int envid){ for(int i=1;i<=job_cnt;i++){ if(jobs[i].env_id==envid){ return i; } } return -1; }
int get_job_envid(int job_id){ if(job_id>0&&job_id<=job_cnt){ return jobs[job_id].env_id; }else{ return -1; } }
int get_jobs(struct Job usrjobs[]){ memcpy(usrjobs,jobs,sizeof(jobs));
return job_cnt; }
int print_jobs(){ for(int i=1;i<=job_cnt;i++){ if(jobs[i].status==Running) printk("[%d] %-10s 0x%08x %s\n", jobs[i].job_id, "Running", jobs[i].env_id, jobs[i].cmd); else printk("[%d] %-10s 0x%08x %s\n", jobs[i].job_id, "Done", jobs[i].env_id, jobs[i].cmd); } return 0; }
|
syscall.h syscall_all.c syscall_lib.c lib.h
文件中,新增系统调用相关
kill的实现:
1 2 3 4 5 6 7 8 9
| int sys_mykill(u_int envid) { struct Env *e; try(envid2env(envid, &e, 0));
printk("[%08x] destroying %08x\n", curenv->env_id, e->env_id); env_destroy(e); return 0; }
|
add_job与标记完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| if(child>0&&*post){ char tmp[1024]; int len=0; for(int i=0;i<argc;i++){ strcpy(tmp+len,argv[i]); len+=strlen(argv[i]); tmp[len++]=' '; tmp[len]='\0'; } syscall_add_job(child,tmp); }
int job_id=syscall_get_job(env->env_id); if(job_id>0){ syscall_set_job_status(job_id,Done); }
|
jobs
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
| else if(strcmp(argv[0],"jobs")==0){ syscall_print_jobs(); close_all(); exit(); }}else if(strcmp(argv[0],"fg")==0){ int job_id=0; for(int i=0;argv[1][i];i++){ if(argv[1][i]>='0'&&argv[1][i]<='9'){ job_id*=10; job_id+=argv[1][i]-'0'; } } int envid=syscall_get_job_envid(job_id); if(envid!=-1){ if(syscall_get_job_status(job_id)!=Running){ printf("fg: (0x%08x) not running\n", envid); }else{ wait(envid); } }else{ printf("fg: job (%d) do not exist\n", job_id); } close_all(); exit(); }else if(strcmp(argv[0],"kill")==0){ int job_id=0; for(int i=0;argv[1][i];i++){ if(argv[1][i]>='0'&&argv[1][i]<='9'){ job_id*=10; job_id+=argv[1][i]-'0'; } } int envid=syscall_get_job_envid(job_id); if(envid!=-1){ if(syscall_get_job_status(job_id)!=Running){ printf("fg: (0x%08x) not running\n", envid); }else{ syscall_set_job_status(job_id,Done); syscall_mykill(envid); } }else{ printf("fg: job (%d) do not exist\n", job_id); } close_all(); exit(); }
|
其他
syscall_all.c
1 2 3 4 5 6 7
| int sys_cgetc(void) { int ch; while ((ch = scancharc()) == 0) { break; } return ch; }
|
runcmd用int* post,表示是否为后台指令