《操作系统真象还原》第15章(上)

终于到最后一章了,行百里者半九十,本周搞定它!


15.1 fork的实现与原理

fork之后,由之前的一个进程变成了两个进程,也就是说内存中多了一个进程,进程拥有独立的地址空间,因此两个进程执行的是独立且相同的代码,也就是两套代码。

子进程是在fork函数返回之后才开始执行的,因此执行的是fork之后的代码,所以在fork之后,父子进程像是“分道扬镳”了。

fork利用老进程克隆出一个新进程并使新进程执行,新进程之所以能够执行,本质上它具备程序体,这其中包括代码和数据等资源。因此fork就是把某个进程的全部资源复制了一份,然后让处理器的cs:eip寄存器指向新进程的指令部分。

15.2 实现一个简单的shell

操作系统是为用户服务的,要想实现和用户的交互,操作系统得有办法感知用户的输入并给予反馈,也就是必须要为用户提供个交互接口。在Windows中,图形界面的资源管理器和命令行窗口都是交互接口,尽管这些交互接口名字及外观各异,但它们往往被统称为“外壳”程序。

shell的功能大致是获取用户的键入,然后分析输入的字符串,判断是内部命令,还是外部命令,然后执行不同的策略。


步骤:

1.fork系统调用

2.read系统调用

3.putchar、clear系统调用

4.实现一个简单的shell


1.fork系统调用

①thread/thread.h,在pcb结构体中增加一个成员,并增加一句函数声明:

int16_t parent_pid;         // 父进程pid

pid_t fork_pid(void);

②thread/thread.c,在init_thread()中将parent_pid初始化为-1,在初始化线程时先创建"init"线程:

复制代码
 1 pthread->parent_pid=-1;             // 父进程pid默认为-1表示没有父进程
 2 
 3 /* 初始化线程环境 */
 4 void thread_init(void){
 5     put_str("thread_init start\n");
 6     list_init(&thread_ready_list);
 7     list_init(&thread_all_list);
 8     lock_init(&pid_lock);
 9     process_execute(init,"init");   // 首先初始化init进程,其pid为1
10 
11     /* 将当前main函数创建为线程 */
12     make_main_thread();
13 
14     /* 创建idle线程 */
15     idle_thread=thread_start("idle",10,idle,NULL);
16 
17     put_str("thread_init done\n");
18 }
复制代码

③kernel/memory.c

复制代码
 1 /* 安装一页大小的vaddr,专门针对fork虚拟地址位图无须操作的情况 */
 2 void* get_a_page_without_opvaddrbitmap(enum pool_flags pf,uint32_t vaddr){
 3     struct pool* mem_pool=pf & PF_KERNEL?&kernel_pool:&user_pool;
 4     lock_acquire(&mem_pool->lock);
 5     void* page_phyaddr=palloc(mem_pool);
 6     if (page_phyaddr==NULL){
 7     lock_release(&mem_pool->lock);
 8     return NULL;
 9     }
10     page_table_add((void*)vaddr,page_phyaddr);
11     lock_release(&mem_pool->lock);
12     return (void*)vaddr;
13 }
复制代码

④kernel/memory.h,添加上述函数的声明:

void* get_a_page_without_opvaddrbitmap(enum pool_flags pf,uint32_t vaddr);

⑤userprog/fork.c

复制代码
  1 #include "fork.h"
  2 #include "process.h"
  3 #include "memory.h"
  4 #include "interrupt.h"
  5 #include "debug.h"
  6 #include "thread.h"
  7 #include "string.h"
  8 #include "file.h"
  9 
 10 extern void intr_exit(void);
 11 
 12 /* 将父进程的pcb、虚拟地址位图拷贝给子进程 */
 13 static int32_t copy_pcb_vaddrbitmap_stack0(struct task_struct* child_thread,struct task_struct* parent_thread){
 14     /* 1.复制pcb所在的整个页,里面包含进程pcb信息及0级栈,包含返回地址,再单独修改部分信息 */
 15     memcpy(child_thread,parent_thread,PG_SIZE);
 16     child_thread->pid=fork_pid();
 17     child_thread->elapsed_ticks=0;
 18     child_thread->status=TASK_READY;
 19     child_thread->ticks=child_thread->priority;   // 为新进程初始化时间片
 20     child_thread->parent_pid=parent_thread->pid;
 21     child_thread->general_tag.prev=child_thread->general_tag.next=NULL;
 22     child_thread->all_list_tag.prev=child_thread->all_list_tag.next=NULL;
 23     block_desc_init(child_thread->u_block_desc);
 24     /* 2.复制父进程的虚拟地址池的位图 */
 25     uint32_t bitmap_pg_cnt=DIV_ROUND_UP((0xc0000000-USER_VADDR_START)/PG_SIZE/8,PG_SIZE);
 26     void* vaddr_btmp=get_kernel_pages(bitmap_pg_cnt);
 27     if (vaddr_btmp==NULL) return -1;
 28     /* 此时child_thread->userprog_vaddr.vaddr_bitmap.bits还是指向父进程虚拟地址的位图地址
 29      * 下面将child_thread->userprog_vaddr.vaddr_bitmap.bits指向自己的位图vaddr_bimp*/
 30     memcpy(vaddr_btmp,child_thread->userprog_vaddr.vaddr_bitmap.bits,bitmap_pg_cnt*PG_SIZE);
 31     child_thread->userprog_vaddr.vaddr_bitmap.bits=vaddr_btmp;
 32     /* 调试用 */
 33     ASSERT(strlen(child_thread->name)<11);   // pcb.name的长度为16,为避免下面strcat越界
 34     strcat(child_thread->name,"_fork");
 35     return 0;
 36 }
 37 
 38 /* 复制子进程的进程体(代码和数据)及用户栈 */
 39 static void copy_body_stack3(struct task_struct* child_thread,struct task_struct* parent_thread,void* buf_page){
 40     uint8_t* vaddr_btmp=parent_thread->userprog_vaddr.vaddr_bitmap.bits;
 41     uint32_t btmp_bytes_len=parent_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len;
 42     uint32_t vaddr_start=parent_thread->userprog_vaddr.vaddr_start;
 43     uint32_t idx_byte=0;
 44     uint32_t idx_bit=0;
 45     uint32_t prog_vaddr=0;
 46 
 47     /* 在父进程的用户空间中查找已有数据的页 */
 48     while (idx_byte<btmp_bytes_len){
 49     if (vaddr_btmp[idx_byte]){
 50         idx_bit=0;
 51         while (idx_bit<8){
 52         if ((BITMAP_MASK<<idx_bit)&vaddr_btmp[idx_byte]){
 53             prog_vaddr=(idx_byte*8+idx_bit)*PG_SIZE+vaddr_start;
 54             /* 下面的操作是将父进程用户空间中的数据通过内核空间做中转,最终复制到子进程的用户空间 */
 55 
 56             /* 2.1 将父进程在用户空间中的数据复制到内核缓冲区buf_page,
 57              * 目的是下面切换到子进程的页表后,还能访问到父进程的数据 */
 58             memcpy(buf_page,(void*)prog_vaddr,PG_SIZE);
 59 
 60             /* 2.2 将页表切换到子进程,目的是避免下面申请内存的函数将pte及pde安装在父进程的页表中 */
 61             page_dir_activate(child_thread);
 62             /* 2.3 申请虚拟地址prog_vaddr */
 63             get_a_page_without_opvaddrbitmap(PF_USER,prog_vaddr);
 64 
 65             /* 2.4 从内核缓冲区中将父进程数据复制到子进程的用户空间 */
 66             memcpy((void*)prog_vaddr,buf_page,PG_SIZE);
 67 
 68             /* 2.5 恢复父进程页表 */
 69             page_dir_activate(parent_thread);
 70         }
 71         ++idx_bit;
 72         }
 73     }
 74     ++idx_byte;
 75     }
 76 }
 77 
 78 /* 为子进程构建thread_stack和修改返回值 */
 79 static int32_t build_child_stack(struct task_struct* child_thread){
 80     /* 1.使子进程pid返回值为0 */
 81     /* 获得子进程0级栈栈顶 */
 82     struct intr_stack* intr_0_stack=(struct intr_stack*)((uint32_t)child_thread+PG_SIZE-sizeof(struct intr_stack));
 83     /* 修改子进程的返回值0 */
 84     intr_0_stack->eax=0;
 85 
 86     /* 2.为switch_to构建struct thread_stack,将其构建在紧邻intr_stack之下的空间 */
 87     uint32_t* ret_addr_in_thread_stack=(uint32_t*)intr_0_stack-1;
 88 
 89     /* 以下三行非必须 */
 90     uint32_t* esi_ptr_in_thread_stack=(uint32_t*)intr_0_stack-2;
 91     uint32_t* edi_ptr_in_thread_stack=(uint32_t*)intr_0_stack-3;
 92     uint32_t* ebx_ptr_in_thread_stack=(uint32_t*)intr_0_stack-4;
 93     /**********************************************************/
 94 
 95     /* ebp在thread_stack中的地址便是当时esp(0级栈栈顶),
 96      * 即esp为(uint32_t*)intr_0_stack-5 */
 97     uint32_t* ebp_ptr_in_thread_stack=(uint32_t*)intr_0_stack-5;
 98 
 99     /* switch_to的返回地址更新为intr_exit,直接重中断返回 */
100     *ret_addr_in_thread_stack=(uint32_t)intr_exit;
101 
102     /* 下面这两行复制只是为了使构建的thread_stack更加清晰,其实也不需要,
103      * 因为在进入intr_exit后一系列的pop会把寄存器中的数据覆盖 */
104     *ebp_ptr_in_thread_stack=*ebx_ptr_in_thread_stack=\
105                  *edi_ptr_in_thread_stack=*esi_ptr_in_thread_stack=0;
106     /*********************************************************/
107 
108     /* 把构建的thread_stack的栈顶作为switch_to恢复数据时的栈顶 */
109     child_thread->self_kstack=ebp_ptr_in_thread_stack;
110     return 0;
111 }
112 
113 /* 更新inode打开数 */
114 static void update_inode_open_cnts(struct task_struct* thread){
115     int32_t local_fd=3,global_fd=0;
116     while (local_fd<MAX_FILES_OPEN_PER_PROC){
117     global_fd=thread->fd_table[local_fd];
118     ASSERT(global_fd<MAX_FILE_OPEN);
119     if (global_fd!=-1){
120         file_table[global_fd].fd_inode->i_open_cnts++;
121     }
122     ++local_fd;
123     }
124 }
125 
126 /* 子进程拷贝父进程本身所占用的资源 */
127 static int32_t copy_process(struct task_struct* child_thread,struct task_struct* parent_thread){
128     /* 内核缓冲区,作为父进程用户空间的数据复制到子进程用户空间的中转 */
129     void* buf_page=get_kernel_pages(1);
130     if (buf_page==NULL){
131     return -1;
132     }
133 
134     /* 1.子进程复制父进程的pcb、虚拟地址位图、内核 */
135     if (copy_pcb_vaddrbitmap_stack0(child_thread,parent_thread)==-1){
136     return -1;
137     }
138 
139     /* 2.为子进程创建页表,此页表仅包括内核空间 */
140     child_thread->pgdir=create_page_dir();
141     if (child_thread->pgdir==NULL){
142     return -1;
143     }
144 
145     /* 3.复制父进程进程体及用户栈给子进程 */
146     copy_body_stack3(child_thread,parent_thread,buf_page);
147 
148     /* 4.构建子进程thread_stack和修改返回值pid */
149     build_child_stack(child_thread);
150 
151     /* 5.更新文件inode的打开数 */
152     update_inode_open_cnts(child_thread);
153 
154     mfree_page(PF_KERNEL,buf_page,1);
155     return 0;
156 }
157 
158 /* fork子进程,内核线程不可直接调用 */
159 pid_t sys_fork(void){
160     struct task_struct* parent_thread=running_thread();
161     struct task_struct* child_thread=get_kernel_pages(1);
162     if (child_thread==NULL){
163     return -1;
164     }
165     ASSERT(INTR_OFF==intr_get_status() && parent_thread->pgdir!=NULL);
166 
167     if (copy_process(child_thread,parent_thread)==-1){
168     return -1;
169     }
170 
171     /* 添加到就绪队列和所有线程队列,子进程由调试器安排运行 */
172     ASSERT(!elem_find(&thread_ready_list,&child_thread->general_tag));
173     list_append(&thread_ready_list,&child_thread->general_tag);
174     ASSERT(!elem_find(&thread_all_list,&child_thread->all_list_tag));
175     list_append(&thread_all_list,&child_thread->all_list_tag);
176 
177     return child_thread->pid;   // 父进程返回子进程pid
178 }
View Code
复制代码

⑥userprog/fork.h

复制代码
1 #ifndef __USERPROG_FORK_H
2 #define __USERPROG_FORK_H
3 #include "thread.h"
4 /* fork子进程,只能由用户进程通过系统调用fork调用,
5  * 内核线程不可直接调用,原因是会从0级栈中获取esp3等切换到用户进程栈 */
6 pid_t sys_fork(void);
7 #endif
View Code
复制代码

⑦lib/user/syscall.h,在枚举变量SYSCALL_NR中添加SYS_FORK,并添加fork()的函数声明:

复制代码
 1 #ifndef __LIB_USER_SYSCALL_H
 2 #define __LIB_USER_SYSCALL_H
 3 #include "stdint.h"
 4 enum SYSCALL_NR{
 5     SYS_GETPID,
 6     SYS_WRITE,
 7     SYS_MALLOC,
 8     SYS_FREE,
 9     SYS_FORK
10 };
11 uint32_t getpid(void);
12 uint32_t write(int32_t fd,const void* buf,uint32_t count);
13 void* malloc(uint32_t size);
14 void free(void* ptr);
15 int16_t fork(void);
16 #endif
View Code
复制代码

⑧lib/user/syscall.c:

/* 派生子进程,返回子进程pid */
pid_t fork(void){
    return _syscall0(SYS_FORK);
}

⑨userprog/syscall-init.c,添加fork.h头文件并注册fork系统调用:

#include "fork.h"

syscall_table[SYS_FORK]=sys_fork;

⑩kernel/main.c:

复制代码
 1 #include "print.h"
 2 #include "init.h"
 3 #include "thread.h"
 4 #include "interrupt.h"
 5 #include "console.h"
 6 #include "ioqueue.h"
 7 #include "process.h"
 8 #include "syscall.h"
 9 #include "syscall-init.h"
10 #include "stdio.h"
11 #include "memory.h"
12 #include "fs.h"
13 #include "string.h"
14 #include "file.h"
15 
16 void init(void);
17 
18 int main(void){
19     put_str("Welcome,\nI am kernel!\n");
20     init_all();
21 
22     while(1);
23     return 0;
24 }
25 
26 /* init进程 */
27 void init(void){
28     uint32_t ret_pid=fork();
29     if (ret_pid){
30     printf("I'm father, my pid is %d, child pid is %d.\n",getpid(),ret_pid);
31     }else {
32     printf("I'm child, my pid is %d, ret pid is %d.\n",getpid(),ret_pid);
33     }
34     while (1);
35 }
View Code
复制代码

最后还要修改makefile

来看看效果:


2.read系统调用+3.putchar、clear系统调用

①fs/fs.c,修改sys_read()并添加sys_putchar():

复制代码
 1 /* 从文件描述符fd指向的文件中读取count个字节到buf,若成功则返回读出的字节数,到文件尾则返回-1 */
 2 int32_t sys_read(int32_t fd,void* buf,uint32_t count){
 3     ASSERT(buf!=NULL);
 4     int32_t ret=-1;
 5     if (fd<0 || fd==stdout_no || fd==stderr_no){
 6     printk("sys_read: fd error\n");
 7     }else if (fd==stdin_no){
 8     char* buffer=buf;
 9     uint32_t bytes_read=0;
10     while (bytes_read<count){
11         *buffer=ioq_getchar(&kbd_buf);
12         ++bytes_read;
13         ++buffer;
14     }
15     ret=(bytes_read==0?-1:(int32_t)bytes_read);
16     }else {
17     uint32_t _fd=fd_local2global(fd);
18     ret=file_read(&file_table[_fd],buf,count);
19     }
20     return ret;
21 }
22 
23 /* 向屏幕输出一个字符 */
24 void sys_putchar(char char_asci){
25     console_put_char(char_asci);
26 }
复制代码

②fs/fs.h:

void sys_putchar(char char_asci);

以下和第一步相似:

③lib/user/syscall.c:

复制代码
 1 /* 从文件描述符fd中读取count个字节到buf */
 2 int32_t read(int32_t fd,void* buf,uint32_t count){
 3     return _syscall3(SYS_READ,fd,buf,count);
 4 }
 5 
 6 /* 输出一个字符 */
 7 void putchar(char char_asci){
 8     _syscall1(SYS_PUTCHAR,char_asci);
 9 }
10 
11 /* 清空屏幕 */
12 void clear(void){
13     _syscall0(SYS_CLEAR);
14 }
复制代码

④lib/user/syscall.h:

复制代码
 1 #ifndef __LIB_USER_SYSCALL_H
 2 #define __LIB_USER_SYSCALL_H
 3 #include "stdint.h"
 4 enum SYSCALL_NR{
 5     SYS_GETPID,
 6     SYS_WRITE,
 7     SYS_MALLOC,
 8     SYS_FREE,
 9     SYS_FORK,
10     SYS_READ,
11     SYS_PUTCHAR,
12     SYS_CLEAR
13 };
14 uint32_t getpid(void);
15 uint32_t write(int32_t fd,const void* buf,uint32_t count);
16 void* malloc(uint32_t size);
17 void free(void* ptr);
18 int16_t fork(void);
19 int32_t read(int32_t fd,void* buf,uint32_t count);
20 void putchar(char char_asci);
21 void clear(void);
22 #endif
View Code
复制代码

⑤userprog/syscall-init.c:

 syscall_table[SYS_READ]=sys_read;
 syscall_table[SYS_PUTCHAR]=sys_putchar;
 syscall_table[SYS_CLEAR]=cls_screen;

⑥lib/kernel/print.S,实现cls_screen:

复制代码
 1 global cls_screen
 2 cls_screen:
 3    pushad
 4    ;;;;;;;;;;
 5         ; 由于用户程序的cpl为3,显存段的dpl为0,故用于显存段的选择子gs在低于自己特权的环境中为0,
 6         ; 由于用户程序再次进入中断后,gs为0,故直接在put_str中每次都为gs赋值
 7    mov ax,SELECTOR_VIDEO
 8    mov gs,ax
 9    
10    mov ebx,0
11    mov ecx,80*25
12 .cls:
13    mov word [gs:ebx],0x0720   ; 0x0720是黑底白字的空格键
14    add ebx,2
15    loop .cls
16    mov ebx,0
17 
18 .set_cursor:
19    ;;;;;;;;;; 1.先设置高8位 ;;;;;;;;;;
20    mov dx,0x03d4
21    mov al,0x0e
22    out dx,al
23    mov dx,0x03d5
24    mov al,bh
25    out dx,al
26 
27    ;;;;;;;;;; 2.再设置低8位 ;;;;;;;;;;
28    mov dx,0x03d4
29    mov al,0x0f
30    out dx,al
31    mov dx,0x03d5
32    mov al,bl
33    out dx,al
34    popad
35    ret
复制代码

4.实现一个简单的shell

先弄个shell的雏形吧。

勘误:书中并没有写到重实现ASSERT为assert、PANIC为panic,所以我们就直接使用大写的两个函数吧,记得加上debug.h头文件。

①shell/shell.c

复制代码
 1 #include "shell.h"
 2 #include "stdint.h"
 3 #include "fs.h"
 4 #include "file.h"
 5 #include "syscall.h"
 6 #include "stdio.h"
 7 #include "global.h"
 8 #include "string.h"
 9 #include "debug.h"
10 
11 #define cmd_len 128    // 最大支持键入128个字符的命令行输入
12 #define MAX_ARG_NR 16    // 加上命令名外,最多支持15个参数
13 
14 /* 存储输入的命令 */
15 static char cmd_line[cmd_len]={0};
16 
17 /* 用来记录当前目录,是当前目录的缓存,每次执行cd命令时会更新此内容 */
18 char cwd_cache[64]={0};
19 
20 /* 输出提示符 */
21 void print_prompt(void){
22     printf("[zbb@LAPTOP %s]$ ",cwd_cache);
23 }
24 
25 /* 从键盘缓冲区中最多读入count个字节到buf */
26 static void readline(char* buf,int32_t count){
27     ASSERT(buf!=NULL && count>0);
28     char* pos=buf;
29     while (read(stdin_no,pos,1)!=-1 && (pos-buf)<count){
30     switch (*pos){
31         /* 找到回车符或换行符后认为键入的命令结束,直接返回 */
32         case '\n':
33         case '\r':
34         *pos=0;   // 添加cmd_line的终止字符0
35         putchar('\n');
36         return;
37     
38         case '\b':
39         if (buf[0]!='\b'){   // 没到达行首
40             --pos;
41             putchar('\b');
42         }
43         break;
44 
45         /* 非控制键则输出字符 */
46         default:
47         putchar(*pos);
48         ++pos;
49     }
50     }
51     printf("readline: can't find enter_key in the cmd_line, max num of char is 128\n");
52 }
53 
54 /* 简单的shell */
55 void my_shell(void){
56     cwd_cache[0]='/';
57     while (1){
58     print_prompt();
59     memset(cmd_line,0,cmd_len);
60     readline(cmd_line,cmd_len);
61     if (cmd_line[0]==0){   // 只键入回车
62         continue;
63     }
64     }
65     PANIC("my_shell: should not be here");
66 }
复制代码

②shell/shell.h:

1 #ifndef __KERNEL_SHELL_H
2 #define __KERNEL_SHELL_H
3 void print_prompt(void);
4 void my_shell(void);
5 #endif

③kernel/main.c:

复制代码
 1 #include "print.h"
 2 #include "init.h"
 3 #include "thread.h"
 4 #include "interrupt.h"
 5 #include "console.h"
 6 #include "ioqueue.h"
 7 #include "process.h"
 8 #include "syscall.h"
 9 #include "syscall-init.h"
10 #include "stdio.h"
11 #include "memory.h"
12 #include "fs.h"
13 #include "string.h"
14 #include "file.h"
15 #include "shell.h"
16 #include "debug.h"
17 
18 void init(void);
19 
20 int main(void){
21     put_str("Welcome,\nI am kernel!\n");
22     init_all();
23     cls_screen();
24     console_put_str("[zbb@LAPTOP //]$ ");
25     while(1);
26     return 0;
27 }
28 
29 /* init进程 */
30 void init(void){
31     uint32_t ret_pid=fork();
32     if (ret_pid){   // 父进程
33         printf("parent...\n");
34     while (1);
35     }else {   // 子进程
36         printf("son...\n");
37     my_shell();
38     }
39     PANIC("init: should not be here.");
40 }
View Code
复制代码

看,它真的只有一个空壳:(以下是没有清屏的,因为我想捋一下整个运行过程)

清屏后:


然后就要添加Ctrl+u和Ctrl+l快捷键了。

①shell/shell.c:

复制代码
  1 #include "shell.h"
  2 #include "stdint.h"
  3 #include "fs.h"
  4 #include "file.h"
  5 #include "syscall.h"
  6 #include "stdio.h"
  7 #include "global.h"
  8 #include "string.h"
  9 #include "debug.h"
 10 
 11 #define MAX_ARG_NR 16    // 加上命令名外,最多支持15个参数
 12 
 13 /* 存储输入的命令 */
 14 static char cmd_line[MAX_PATH_LEN]={0};
 15 char final_path[MAX_PATH_LEN]={0};   // 清除路径缓冲
 16 
 17 /* 用来记录当前目录,是当前目录的缓存,每次执行cd命令时会更新此内容 */
 18 char cwd_cache[64]={0};
 19 
 20 /* 输出提示符 */
 21 void print_prompt(void){
 22     printf("[zbb@LAPTOP %s]$ ",cwd_cache);
 23 }
 24 
 25 /* 从键盘缓冲区中最多读入count个字节到buf */
 26 static void readline(char* buf,int32_t count){
 27     ASSERT(buf!=NULL && count>0);
 28     char* pos=buf;
 29 
 30     while (read(stdin_no,pos,1)!=-1 && (pos-buf)<count){   // 直至回车符停止
 31     switch (*pos){
 32         /* 找到回车或换行符后认为键入命令结束 */
 33         case '\n':
 34         case '\r':
 35         *pos=0;   // 添加cmd_line的终止字符0
 36         putchar('\n');
 37         return;
 38 
 39         case '\b':
 40         if (cmd_line[0]!='\b'){   // 到行首
 41             --pos;
 42             putchar('\b');
 43         }
 44         break;
 45         
 46         /* Ctrl+l 清屏 */
 47         case 'l'-'a':
 48         /* 1.先将当前字符'l'-'a'置为0 */
 49         *pos=0;
 50         /* 2.再将屏幕清空 */
 51         clear();
 52         /* 3.打印提示符 */
 53         print_prompt();
 54         /* 4.将之前键入的内容再次打印 */
 55         printf("%s",buf);
 56         break;
 57 
 58         /* Ctrl+u 清掉输入 */
 59         case 'u'-'a':
 60         while (buf!=pos){
 61             putchar('\b');
 62             *(pos--)=0;
 63         }
 64         break;
 65         
 66         /* 非控制则输出字符 */
 67         default:
 68         putchar(*pos);
 69         ++pos;
 70     }
 71     }
 72     printf("readline: can't find enter_key in the cmd_line, max num of char is 128\n");
 73 }
 74 
 75 /* 分析字符串cmd_str中以token为分隔符的单词,将各单词的指针存入argv数组 */
 76 static int32_t cmd_parse(char* cmd_str,char** argv,char token){
 77     ASSERT(cmd_str!=NULL);
 78     int32_t arg_idx=0;
 79     while (arg_idx<MAX_ARG_NR){
 80     argv[arg_idx]=NULL;
 81     ++arg_idx;
 82     }
 83     char* next=cmd_str;
 84     int32_t argc=0;
 85     /* 外层循环处理整个命令行 */
 86     while (*next){
 87     /* 去除命令字或参数间空格 */
 88     while (*next==token){
 89         ++next;
 90     }
 91     /* 处理最后一个参数后接空格的情况 */
 92     if (*next==0){
 93         break;
 94     }
 95     argv[argc]=next;
 96 
 97     /* 内层循环处理命令行中的每个命令字及参数 */
 98     while (*next && *next!=token){   // 在字符串结束前找单词分隔符
 99         ++next;
100     }
101 
102     /* 如果未结束(是token字符),使token变成0 */
103     if (*next){
104         *next++=0;
105     }
106 
107     /* argv数组越界则返回-1 */
108     if (argc>MAX_ARG_NR){
109         return -1;
110     }
111     ++argc;
112     }
113     return argc;
114 }
115 
116 char* argv[MAX_ARG_NR];   // argv必须为全局变量
117 int32_t argc=-1;
118 
119 /* 简单的shell */
120 void my_shell(void){
121     cwd_cache[0]='/';
122     while (1){
123     print_prompt();
124     memset(final_path,0,MAX_PATH_LEN);
125     memset(cmd_line,0,MAX_PATH_LEN);
126     readline(cmd_line,MAX_PATH_LEN);
127     if (cmd_line[0]==0){   // 只键入回车
128         continue;
129     }
130     argc=-1;
131     argc=cmd_parse(cmd_line,argv,' ');
132     if (argc==-1){
133         printf("num of arguments exceed %d\n",MAX_ARG_NR);
134         continue;
135     }
136 
137     int32_t arg_idx=0;
138     while (arg_idx<argc){
139         printf("%s ",argv[arg_idx]);
140         ++arg_idx;
141     }
142     printf("\n");
143     }
144     PANIC("my_shell: should not be here");
145 }
View Code
复制代码

②shell/shell.h:

复制代码
1 #ifndef __KERNEL_SHELL_H
2 #define __KERNEL_SHELL_H
3 #include "fs.h"
4 void print_prompt(void);
5 void my_shell(void);
6 extern char final_path[MAX_PATH_LEN];
7 #endif
View Code
复制代码

③device/keyboard,添加处理Ctrl+'u'和Ctrl+'l'的逻辑:

复制代码
  1 /* 键盘中断处理程序 */
  2 static void intr_keyboard_handler(void){
  3     
  4     /* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */
  5     bool ctrl_down_last=ctrl_status;
  6     bool shift_down_last=shift_status;
  7     bool caps_lock_last=caps_lock_status;
  8 
  9     bool break_code;
 10     uint16_t scancode=inb(KBD_BUF_PORT);
 11 
 12     /* 若扫描码scancode是e0开头的,表示此键的按下将产生多个扫描码,
 13      * 所以马上结束此次中断处理函数,等待下一个扫描码进来 */
 14     if (scancode==0xe0){
 15     ext_scancode=true;   // 打开e0标记
 16     return;
 17     }
 18 
 19     /* 如果上次是以0xe0开头的,将扫描码合并 */
 20     if (ext_scancode){
 21     scancode=((0xe000) | scancode);
 22     ext_scancode=false;   // 关闭e0标记
 23     }
 24 
 25     break_code=((scancode & 0x0080)!=0);   // 获取break_code
 26 
 27     if(break_code){   // 若是断码(按键弹起时产生的扫描码)
 28 
 29         uint16_t make_code=(scancode &= 0xff7f); //多字节扫描码暂不处理
 30 
 31     /* 若是以下三个键弹起了,将状态设为false */
 32         if (make_code==ctrl_l_make || make_code==ctrl_r_make){
 33         ctrl_status=false;
 34     }
 35         else if (make_code==shift_l_make || make_code==shift_r_make){
 36         shift_status=false;
 37     }
 38         else if (make_code==alt_l_make || make_code==alt_r_make){
 39         alt_status=false;
 40     }
 41 
 42         return;   // 直接返回结束此次中断处理程序
 43 
 44     }
 45     /* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */
 46     else if ((scancode>0x00 && scancode<0x3b) || (scancode==alt_r_make) || (scancode==ctrl_r_make)){
 47         bool shift=false;   // 判断是否与shift结合,用来在一维数组中索引对应的字符,先默认设置成false
 48         if ((scancode<0x0e) || (scancode==0x29) || (scancode==0x1a) || \
 49            (scancode==0x1b) || (scancode==0x2b) || (scancode==0x27) || \
 50            (scancode==0x28) || (scancode==0x33) || (scancode==0x34) || \
 51            (scancode==0x35)){
 52             if (shift_down_last){   // 如果同时按下了shift键
 53         shift=true;
 54         }
 55         }
 56         else{   // 默认为字母键
 57             if (shift_down_last && caps_lock_last){   // 同时按下shift和capslock
 58         shift=false;
 59         }
 60             else if (shift_down_last || caps_lock_last){   // 按下shift或capslock
 61         shift=true;
 62         }
 63             else{
 64         shift=false;
 65         }
 66         }
 67     
 68         uint8_t index=(scancode &= 0x00ff);
 69         char cur_char=keymap[index][shift];   // 在数组中找到对应的字符
 70     
 71     /* 只处理ASCII码不为0的键 */
 72         if (cur_char){
 73         if ((ctrl_down_last && cur_char=='l') || (ctrl_down_last && cur_char=='u')){
 74         cur_char-='a';
 75         }
 76 
 77             if (!ioq_full(&kbd_buf)){
 78             ioq_putchar(&kbd_buf,cur_char);
 79         }
 80         return;
 81     }
 82 
 83     /* 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键 */
 84     if (scancode==ctrl_l_make || scancode==ctrl_r_make){
 85         ctrl_status=true;
 86     }
 87     else if (scancode==shift_l_make || scancode==shift_r_make){
 88             shift_status=true;
 89     }
 90     else if (scancode==alt_l_make || scancode==alt_r_make){
 91         alt_status=true;
 92     }
 93     else if (scancode==caps_lock_make){
 94         caps_lock_status=!caps_lock_status;
 95     }
 96     else{
 97         put_str("unknown key\n");
 98     }
 99     }
100     return;
101 }
View Code
复制代码

④kernel/main.c:

复制代码
 1 #include "print.h"
 2 #include "init.h"
 3 #include "thread.h"
 4 #include "interrupt.h"
 5 #include "console.h"
 6 #include "ioqueue.h"
 7 #include "process.h"
 8 #include "syscall.h"
 9 #include "syscall-init.h"
10 #include "stdio.h"
11 #include "memory.h"
12 #include "fs.h"
13 #include "string.h"
14 #include "file.h"
15 #include "shell.h"
16 #include "debug.h"
17 
18 void init(void);
19 
20 int main(void){
21     put_str("Welcome,\nI am kernel!\n");
22     init_all();
23     intr_enable();
24     cls_screen();
25     console_put_str("[zbb@LAPTOP //]$ ");
26     while(1);
27     return 0;
28 }
29 
30 /* init进程 */
31 void init(void){
32     uint32_t ret_pid=fork();
33     if (ret_pid){   // 父进程
34         printf("parent...\n");
35     while (1);
36     }else {   // 子进程
37         printf("son...\n");
38     my_shell();
39     }
40     PANIC("init: should not be here.");
41 }
View Code
复制代码

运行结果如图:

键入Ctrl+l成功清屏:


为fs中的"sys_"添加系统调用。

①lib/user/syscall.h,赋系统调用号、函数声明:

复制代码
 1 #ifndef __LIB_USER_SYSCALL_H
 2 #define __LIB_USER_SYSCALL_H
 3 #include "stdint.h"
 4 #include "fs.h"
 5 
 6 enum SYSCALL_NR{
 7     SYS_GETPID,
 8     SYS_WRITE,
 9     SYS_MALLOC,
10     SYS_FREE,
11     SYS_FORK,
12     SYS_READ,
13     SYS_PUTCHAR,
14     SYS_CLEAR,
15     SYS_GETCWD,
16     SYS_OPEN,
17     SYS_CLOSE,
18     SYS_LSEEK,
19     SYS_UNLINK,
20     SYS_MKDIR,
21     SYS_OPENDIR,
22     SYS_CLOSEDIR,
23     SYS_CHDIR,
24     SYS_RMDIR,
25     SYS_READDIR,
26     SYS_REWINDDIR,
27     SYS_STAT,
28     SYS_PS
29 };
30 uint32_t getpid(void);
31 uint32_t write(int32_t fd,const void* buf,uint32_t count);
32 void* malloc(uint32_t size);
33 void free(void* ptr);
34 int16_t fork(void);
35 int32_t read(int32_t fd,void* buf,uint32_t count);
36 void putchar(char char_asci);
37 void clear(void);
38 char* getcwd(char* buf,uint32_t size);
39 int32_t open(char* pathname,uint8_t flag);
40 int32_t close(int32_t fd);
41 int32_t lseek(int32_t fd,int32_t offset,uint8_t whence);
42 int32_t unlink(const char* pathname);
43 int32_t mkdir(const char* pathname);
44 struct dir* opendir(const char* name);
45 int32_t closedir(struct dir* dir);
46 int32_t rmdir(const char* pathname);
47 struct dir_entry* readdir(struct dir* dir);
48 void rewinddir(struct dir* dir);
49 int32_t stat(const char* path,struct stat* buf);
50 int32_t chdir(const char* path);
51 void ps(void);
52 #endif
View Code
复制代码

②lib/user/syscall.c,新增系统调用实现:

复制代码
  1 #include "syscall.h"
  2 #include "thread.h"
  3 
  4 /* 无参数的系统调用 */
  5 #define _syscall0(NUMBER) ({     \
  6         int retval;        \
  7        asm volatile (        \
  8         "int $0x80"         \
  9         : "=a"(retval)         \
 10         : "a"(NUMBER)         \
 11         : "memory");         \
 12         retval;         \
 13 })
 14 
 15 /* 一个参数的系统调用 */
 16 #define _syscall1(NUMBER,ARG1)     ({     \
 17         int retval;        \
 18        asm volatile (        \
 19         "int $0x80"         \
 20         : "=a"(retval)         \
 21         : "a"(NUMBER),"b"(ARG1)    \
 22         : "memory");         \
 23         retval;         \
 24 })
 25 
 26 #define _syscall2(NUMBER,ARG1,ARG2)    ({     \
 27         int retval;        \
 28        asm volatile (        \
 29         "int $0x80"         \
 30         : "=a"(retval)         \
 31         : "a"(NUMBER),"b"(ARG1),"c"(ARG2) \
 32         : "memory");         \
 33         retval;         \
 34 })
 35 
 36 #define _syscall3(NUMBER,ARG1,ARG2,ARG3)({     \
 37         int retval;        \
 38        asm volatile (        \
 39         "int $0x80"         \
 40         : "=a"(retval)         \
 41         : "a"(NUMBER),"b"(ARG1),"c"(ARG2),"d"(ARG3)     \
 42         : "memory");         \
 43         retval;         \
 44 })
 45 
 46 /* 获取任务pid */
 47 uint32_t getpid(void){
 48     return _syscall0(SYS_GETPID);
 49 }
 50 
 51 /* 把buf中count个字符写入文件描述符fd */
 52 uint32_t write(int32_t fd,const void* buf,uint32_t count){
 53     return _syscall3(SYS_WRITE,fd,buf,count);
 54 }
 55 
 56 /* 申请size字节大小的内存,并返回结果 */
 57 void* malloc(uint32_t size){
 58     return (void*)_syscall1(SYS_MALLOC,size);
 59 }
 60 
 61 /* 释放ptr指向的内存 */
 62 void free(void* ptr){
 63     _syscall1(SYS_FREE,ptr);
 64 }
 65 
 66 /* 派生子进程,返回子进程pid */
 67 pid_t fork(void){
 68     return _syscall0(SYS_FORK);
 69 }
 70 
 71 /* 从文件描述符fd中读取count个字节到buf */
 72 int32_t read(int32_t fd,void* buf,uint32_t count){
 73     return _syscall3(SYS_READ,fd,buf,count);
 74 }
 75 
 76 /* 输出一个字符 */
 77 void putchar(char char_asci){
 78     _syscall1(SYS_PUTCHAR,char_asci);
 79 }
 80 
 81 /* 清空屏幕 */
 82 void clear(void){
 83     _syscall0(SYS_CLEAR);
 84 }
 85 
 86 /* 获取当前工作目录 */
 87 char* getcwd(char* buf,uint32_t size){
 88     return (char*)_syscall2(SYS_GETCWD,buf,size);
 89 }
 90 
 91 /* 以flag方式打开文件pathname */
 92 int32_t open(char* pathname,uint8_t flag){
 93     return _syscall2(SYS_OPEN,pathname,flag);
 94 }
 95 
 96 /* 关闭文件fd */
 97 int32_t close(int32_t fd){
 98     return _syscall1(SYS_CLOSE,fd);
 99 }
100 
101 /* 设置文件偏移量 */
102 int32_t lseek(int32_t fd,int32_t offset,uint8_t whence){
103     return _syscall3(SYS_LSEEK,fd,offset,whence);
104 }
105 
106 /* 删除文件pathname */
107 int32_t unlink(const char* pathname){
108     return _syscall1(SYS_UNLINK,pathname);
109 }
110 
111 /* 创建目录pathname */
112 int32_t mkdir(const char* pathname){
113     return _syscall1(SYS_MKDIR,pathname);
114 }
115 
116 /* 打开目录name */
117 struct dir* opendir(const char* name){
118     return (struct dir*)_syscall1(SYS_OPENDIR,name);
119 }
120 
121 /* 关闭目录dir */
122 int32_t closedir(struct dir* dir){
123     return _syscall1(SYS_CLOSEDIR,dir);
124 }
125 
126 /* 删除目录pathname */
127 int32_t rmdir(const char* pathname){
128     return _syscall1(SYS_RMDIR,pathname);
129 }
130 
131 /* 读取目录dir */
132 struct dir_entry* readdir(struct dir* dir){
133     return (struct dir_entry*)_syscall1(SYS_READDIR,dir);
134 }
135 
136 /* 重置目录指针 */
137 void rewinddir(struct dir* dir){
138     _syscall1(SYS_REWINDDIR,dir);
139 }
140 
141 /* 获取path属性到buf中 */
142 int32_t stat(const char* path,struct stat* buf){
143     return _syscall2(SYS_STAT,path,buf);
144 }
145 
146 /* 改变工作目录path */
147 int32_t chdir(const char* path){
148    return _syscall1(SYS_CHDIR,path);
149 }
150 
151 /* 显示任务列表 */
152 void ps(void){
153     _syscall0(SYS_PS);
154 }
View Code
复制代码

③userprog/syscall-init.c,注册系统调用:

复制代码
 1 #include "syscall-init.h"
 2 #include "syscall.h"
 3 #include "stdint.h"
 4 #include "print.h"
 5 #include "thread.h"
 6 #include "console.h"
 7 #include "string.h"
 8 #include "memory.h"
 9 #include "fs.h"
10 #include "fork.h"
11 
12 #define syscall_nr 32
13 typedef void* syscall;
14 syscall syscall_table[syscall_nr];
15 
16 /* 返回当前任务的pid */
17 uint32_t sys_getpid(void){
18     return running_thread()->pid;
19 }
20 
21 /* 初始化系统调用 */
22 void syscall_init(void){
23     put_str("syscall_init start\n");
24     syscall_table[SYS_GETPID]=sys_getpid;
25     syscall_table[SYS_WRITE]=sys_write;
26     syscall_table[SYS_MALLOC]=sys_malloc;
27     syscall_table[SYS_FREE]=sys_free;
28     syscall_table[SYS_FORK]=sys_fork;
29     syscall_table[SYS_READ]=sys_read;
30     syscall_table[SYS_PUTCHAR]=sys_putchar;
31     syscall_table[SYS_CLEAR]=cls_screen;
32     syscall_table[SYS_GETCWD]=sys_getcwd;
33     syscall_table[SYS_OPEN]=sys_open;
34     syscall_table[SYS_CLOSE]=sys_close;
35     syscall_table[SYS_LSEEK]=sys_lseek;
36     syscall_table[SYS_UNLINK]=sys_unlink;
37     syscall_table[SYS_MKDIR]=sys_mkdir;
38     syscall_table[SYS_OPENDIR]=sys_opendir;
39     syscall_table[SYS_CLOSEDIR]=sys_closedir;
40     syscall_table[SYS_CHDIR]=sys_chdir;
41     syscall_table[SYS_RMDIR]=sys_rmdir;
42     syscall_table[SYS_READDIR]=sys_readdir;
43     syscall_table[SYS_REWINDDIR]=sys_rewinddir;
44     syscall_table[SYS_STAT]=sys_stat;
45     syscall_table[SYS_PS]=sys_ps;
46     put_str("syscall_init done\n");
47 }
View Code
复制代码

④thread/thread.c,实现打印进程信息的sys_ps()的有关函数:

复制代码
 1 #include "stdio.h"
 2 #include "file.h"
 3 #include "fs.h"
 4 
 5 /* 以填充空格的方式输出buf */
 6 static void pad_print(char* buf,int32_t buf_len,void* ptr,char format){
 7     memset(buf,0,buf_len);
 8     uint8_t out_pad_0idx=0;
 9     switch (format){
10     case 's':
11         out_pad_0idx=sprintf(buf,"%s",ptr);
12         break;
13     case 'd':
14         out_pad_0idx=sprintf(buf,"%d",*((int16_t*)ptr));
15     case 'x':
16         out_pad_0idx=sprintf(buf,"%x",*((int16_t*)ptr));
17     }
18     while (out_pad_0idx<buf_len){   // 空格填充
19     buf[out_pad_0idx]=' ';
20     ++out_pad_0idx;
21     }
22     sys_write(stdout_no,buf,buf_len-1);
23 }
24 
25 /* 用于在list_traversal函数中的回调函数,用于针对线程队列的处理 */
26 static bool elem2thread_info(struct list_elem* pelem,int arg){
27     struct task_struct* pthread=elem2entry(struct task_struct,all_list_tag,pelem);
28     char out_pad[16]={0};
29 
30     pad_print(out_pad,16,&pthread->pid,'d');      // 打印pid
31 
32     if (pthread->parent_pid==-1){           // 打印parent_pid
33     pad_print(out_pad,16,"NULL",'s');
34     }else {
35     pad_print(out_pad,16,&pthread->parent_pid,'d');
36     }
37 
38     switch (pthread->status){            // 打印状态
39     case 0:
40         pad_print(out_pad,16,"RUNNING",'s');
41         break;
42     case 1:
43         pad_print(out_pad,16,"READY",'s');
44         break;
45     case 2:
46         pad_print(out_pad,16,"BLOCKED",'s');
47         break;
48     case 3:
49         pad_print(out_pad,16,"WAITING",'s');
50         break;
51     case 4:
52         pad_print(out_pad,16,"HANGING",'s');
53         break;
54     case 5:
55         pad_print(out_pad,16,"DIED",'s');
56     }
57     pad_print(out_pad,16,&pthread->elapsed_ticks,'x');    // 打印运行时间片
58 
59     memset(out_pad,0,16);
60     ASSERT(strlen(pthread->name)<17);
61     memcpy(out_pad,pthread->name,strlen(pthread->name));
62     strcat(out_pad,"\n");
63     sys_write(stdout_no,out_pad,strlen(out_pad));    // 打印进程名
64     return false;   // 返回false是为了迎合主调函数list_traversal,只有一直返回false才会一直遍历thread_all_list
65 }
66 
67 /* 打印任务列表 */
68 void sys_ps(void){
69     char* ps_title="PID             PPID            STAT            TICKS           COMMAND\n";
70     sys_write(stdout_no,ps_title,strlen(ps_title));
71     list_traversal(&thread_all_list,elem2thread_info,0);
72 }
复制代码

⑤thread/thread.h,添加一句函数声明:

void sys_ps(void);

路径解析转换,也就是将我们输入的相对路径(含“..”和“.”)在用户态程序中转换为绝对路径(不含“..”和“.”),再将绝对路径提交给内核态下的系统调用中断。

①新创建的shell/buildin_cmd.c

复制代码
 1 #include "buildin_cmd.h"
 2 #include "syscall.h"
 3 #include "stdio.h"
 4 #include "string.h"
 5 #include "fs.h"
 6 #include "global.h"
 7 #include "dir.h"
 8 #include "shell.h"
 9 #include "debug.h"
10 
11 /* 将路径old_abs_path中的..和.转换为实际路径后存入new_abs_path */
12 static void wash_path(char* old_abs_path,char* new_abs_path){
13     ASSERT(old_abs_path[0]=='/');
14     char name[MAX_FILE_NAME_LEN]={0};
15     char* sub_path=old_abs_path;
16     sub_path=path_parse(sub_path,name);
17     if (name[0]==0){   // 若只键入了"/",直接将"/"存入new_abs_path后返回
18     new_abs_path[0]='/';
19     new_abs_path[1]=0;
20     return;
21     }
22     new_abs_path[0]=0;   // 避免传给new_abs_path的缓冲区不干净
23     strcat(new_abs_path,"/");
24     while (name[0]){
25     /* 如果是上一级目录".." */
26     if (!strcmp("..",name)){
27         char* slash_ptr=strrchr(new_abs_path,'/');
28         /* 如果未到new_abs_path中的顶层目录,就将最右边的'/'换成0,
29          * 这样便去除了new_abs_path中最后一层路径,相当于到了上一级目录 */
30         if (slash_ptr!=new_abs_path){
31         *slash_ptr=0;
32         }else {
33         /* 若new_abs_path中只有一个'/',即表示已经到了顶层目录,
34          * 就将下一个字符置为结束符0. */
35         *(slash_ptr+1)=0;
36         }
37     }else if (strcmp(".",name)){
38         if (strcmp(new_abs_path,"/")){
39         strcat(new_abs_path,"/");
40         }
41         strcat(new_abs_path,name);
42     }  // 若name为当前目录".",无需处理new_abs_path
43 
44     /* 继续遍历下一层路径 */
45     memset(name,0,MAX_FILE_NAME_LEN);
46     if (sub_path){
47         sub_path=path_parse(sub_path,name);
48     }
49     }
50 }
51 
52 /* 将path处理成不含..和.的绝对路径,存储在final_path */
53 void make_clear_abs_path(char* path,char* final_path){
54     char abs_path[MAX_PATH_LEN]={0};
55     /* 先判断输入的是否为绝对路径 */
56     if (path[0]!='/'){   // 若输入的不是绝对路径,就拼接成绝对路径
57     memset(abs_path,0,MAX_PATH_LEN);
58     if (getcwd(abs_path,MAX_PATH_LEN)!=NULL){
59         if (!((abs_path[0]=='/') && (abs_path[1]==0))){
60         strcat(abs_path,"/");
61         }
62     }
63     }
64     strcat(abs_path,path);
65     wash_path(abs_path,final_path);
66 }
View Code
复制代码

②shell/buildin_cmd.h:

复制代码
#ifndef __SHELL_BUILDIN_CMD_H
#define __SHELL_BUILDIN_CMD_H
#include "stdint.h"
void make_clear_abs_path(char* path,char* wash_buf);
#endif
View Code
复制代码

③shell/shell.c,修改my_shell():

复制代码
 1 /* 简单的shell */
 2 void my_shell(void){
 3     cwd_cache[0]='/';
 4     cwd_cache[1]=0;
 5     while (1){
 6     print_prompt();
 7     memset(final_path,0,MAX_PATH_LEN);
 8     memset(cmd_line,0,MAX_PATH_LEN);
 9     readline(cmd_line,MAX_PATH_LEN);
10     if (cmd_line[0]==0){   // 只键入回车
11         continue;
12     }
13     argc=-1;
14     argc=cmd_parse(cmd_line,argv,' ');
15     if (argc==-1){
16         printf("num of arguments exceed %d\n",MAX_ARG_NR);
17         continue;
18     }
19     
20     char buf[MAX_PATH_LEN]={0};
21     int32_t arg_idx=0;
22     while (arg_idx<argc){
23         make_clear_abs_path(argv[arg_idx],buf);
24         printf("%s -> %s\n",argv[arg_idx],buf);
25         ++arg_idx;
26     }
27     printf("\n");
28     }
29     PANIC("my_shell: should not be here");
30 }
View Code
复制代码

④shell/shell.h,添加头文件:

#include "buildin_cmd.h"

⑤还要修改fs/fs.c和fs/fs.h,将path_parse()修改为非静态函数:

char* path_parse(char* pathname,char* name_store){...

以及makefile

测试结果如图所示:


还要实现ls、cd、mkdir、ps、rm等命令

①shell/buildin_cmd.c,添加一系列内建函数:

复制代码
  1 #include "buildin_cmd.h"
  2 #include "syscall.h"
  3 #include "stdio.h"
  4 #include "string.h"
  5 #include "fs.h"
  6 #include "global.h"
  7 #include "dir.h"
  8 #include "shell.h"
  9 #include "debug.h"
 10 
 11 /* 将路径old_abs_path中的..和.转换为实际路径后存入new_abs_path */
 12 static void wash_path(char* old_abs_path,char* new_abs_path){
 13     ASSERT(old_abs_path[0]=='/');
 14     char name[MAX_FILE_NAME_LEN]={0};
 15     char* sub_path=old_abs_path;
 16     sub_path=path_parse(sub_path,name);
 17     if (name[0]==0){   // 若只键入了"/",直接将"/"存入new_abs_path后返回
 18     new_abs_path[0]='/';
 19     new_abs_path[1]=0;
 20     return;
 21     }
 22     new_abs_path[0]=0;   // 避免传给new_abs_path的缓冲区不干净
 23     strcat(new_abs_path,"/");
 24     while (name[0]){
 25     /* 如果是上一级目录".." */
 26     if (!strcmp("..",name)){
 27         char* slash_ptr=strrchr(new_abs_path,'/');
 28         /* 如果未到new_abs_path中的顶层目录,就将最右边的'/'换成0,
 29          * 这样便去除了new_abs_path中最后一层路径,相当于到了上一级目录 */
 30         if (slash_ptr!=new_abs_path){
 31         *slash_ptr=0;
 32         }else {
 33         /* 若new_abs_path中只有一个'/',即表示已经到了顶层目录,
 34          * 就将下一个字符置为结束符0. */
 35         *(slash_ptr+1)=0;
 36         }
 37     }else if (strcmp(".",name)){
 38         if (strcmp(new_abs_path,"/")){
 39         strcat(new_abs_path,"/");
 40         }
 41         strcat(new_abs_path,name);
 42     }  // 若name为当前目录".",无需处理new_abs_path
 43 
 44     /* 继续遍历下一层路径 */
 45     memset(name,0,MAX_FILE_NAME_LEN);
 46     if (sub_path){
 47         sub_path=path_parse(sub_path,name);
 48     }
 49     }
 50 }
 51 
 52 /* 将path处理成不含..和.的绝对路径,存储在final_path */
 53 void make_clear_abs_path(char* path,char* final_path){
 54     char abs_path[MAX_PATH_LEN]={0};
 55     /* 先判断输入的是否为绝对路径 */
 56     if (path[0]!='/'){   // 若输入的不是绝对路径,就拼接成绝对路径
 57     memset(abs_path,0,MAX_PATH_LEN);
 58     if (getcwd(abs_path,MAX_PATH_LEN)!=NULL){
 59         if (!((abs_path[0]=='/') && (abs_path[1]==0))){
 60         strcat(abs_path,"/");
 61         }
 62     }
 63     }
 64     strcat(abs_path,path);
 65     wash_path(abs_path,final_path);
 66 }
 67 
 68 /* pwd命令的内建函数 */
 69 void buildin_pwd(uint32_t argc,char** argv){
 70     if (argc!=1){
 71     printf("pwd: no argument support!\n");
 72     return;
 73     }else {
 74     if (NULL!=getcwd(final_path,MAX_PATH_LEN)){
 75         printf("%s\n",final_path);
 76     }else {
 77         printf("pwd: get current work directory failed.\n");
 78     }
 79     }
 80 }
 81 
 82 /* cd命令的内建函数 */
 83 char* buildin_cd(uint32_t argc,char** argv){
 84     if (argc>2){
 85     printf("cd: only support 1 argument!\n");
 86     return NULL;
 87     }
 88 
 89     /* 若是只键入cd而无参数,直接返回到根目录 */
 90     if (argc==1){
 91     final_path[0]='/';
 92     final_path[1]=0;
 93     }else {
 94     make_clear_abs_path(argv[1],final_path);
 95     }
 96 
 97     if (chdir(final_path)==-1){
 98     printf("cd: no such directory %s.\n",final_path);
 99     return NULL;
100     }
101     return final_path;
102 }
103 
104 /* ls命令的内建函数 */
105 void buildin_ls(uint32_t argc,char** argv){
106     char* pathname=NULL;
107     struct stat file_stat;
108     memset(&file_stat,0,sizeof(struct stat));
109     bool long_info=false;
110     uint32_t arg_path_nr=0;
111     uint32_t arg_idx=1;
112     while (arg_idx<argc){
113     if (argv[arg_idx][0]=='-'){
114         if (!strcmp("-l",argv[arg_idx])){   // 如果是参数 -l
115         long_info=true;
116         }else if (!strcmp("-h",argv[arg_idx])){   // 如果是参数 -h
117         printf("usage: -l list all infomation about the file.\n-h for help\nlist all files in the current directory if no option\n");
118         return;
119         }else {   // 只支持-l -h
120         printf("ls: invalid option %s\nTry 'ls -h' for more infomation.\n",argv[arg_idx]);
121         return;
122         }
123     }else {   // ls的路径参数
124         if (arg_path_nr==0){
125         pathname=argv[arg_idx];
126         arg_path_nr=1;
127         }else {
128         printf("ls: only support one path\n");
129         return;
130         }
131     }
132     ++arg_idx;
133     }
134 
135     if (pathname==NULL){   // 若只输入了ls或ls -l没有输入操作路径,则默认以当前路径作为操作路径
136     if (NULL!=getcwd(final_path,MAX_PATH_LEN)){
137         pathname=final_path;
138     }else {
139         printf("ls: getcwd for default path failed.\n");
140         return;
141     }
142     }else {
143     make_clear_abs_path(pathname,final_path);
144     pathname=final_path;
145     }
146 
147     if (stat(pathname,&file_stat)==-1){
148     printf("ls: cannot access %s: No such file or directory\n",pathname);
149     return;
150     }
151     if (file_stat.st_filetype==FT_DIRECTORY){
152     struct dir* dir=opendir(pathname);
153     struct dir_entry* dir_e=NULL;
154     char sub_pathname[MAX_PATH_LEN]={0};
155     uint32_t pathname_len=strlen(pathname);
156     uint32_t last_char_idx=pathname_len-1;
157     memcpy(sub_pathname,pathname,pathname_len);
158     if (sub_pathname[last_char_idx]!='/'){
159         sub_pathname[pathname_len]='/';
160         ++pathname_len;
161     }
162     rewinddir(dir);
163     if (long_info){
164         char ftype;
165         printf("total: %d\n",file_stat.st_size);
166         while ((dir_e==readdir(dir))){
167         ftype='d';
168         if (dir_e->f_type==FT_REGULAR){
169             ftype='-';
170         }
171         sub_pathname[pathname_len]=0;
172         strcat(sub_pathname,dir_e->filename);
173         memset(&file_stat,0,sizeof(struct stat));
174         if (stat(sub_pathname,&file_stat)==-1){
175             printf("ls: cannot access %s: No such file or directory\n",dir_e->filename);
176             return;
177         }
178         printf("%c  %d  %d  %s\n",ftype,dir_e->i_no,file_stat.st_size,dir_e->filename);
179         }
180     }else {
181         while ((dir_e==readdir(dir))){
182         printf("%s ",dir_e->filename);
183         }
184         printf("\n");
185     }
186     closedir(dir);
187     }else {
188     if (long_info){
189         printf("-  %d  %d  %s\n",file_stat.st_ino,file_stat.st_size,pathname);
190     }else {
191         printf("%s\n",pathname);
192     }
193     }
194 }
195 
196 /* ps命令内建函数 */
197 void buildin_ps(uint32_t argc,char** argv){
198     if (argc!=1){
199     printf("ps: no argument support!\n");
200     return;
201     }
202     ps();
203 }
204 
205 /* clear命令内建函数 */
206 void buildin_clear(uint32_t argc,char** argv){
207     if (argc!=1){
208     printf("clear: no argument support!\n");
209     return;
210     }
211     clear();
212 }
213 
214 /* mkdir命令内建函数 */
215 int32_t buildin_mkdir(uint32_t argc,char** argv){
216     int32_t ret=-1;
217     if (argc!=2){
218     printf("mkdir: only support 1 argument!\n");
219     }else {
220     make_clear_abs_path(argv[1],final_path);
221     /* 若创建的不是根目录 */
222     if (strcmp("/",final_path)){
223         if (mkdir(final_path)==0){
224         ret=0;
225         }else {
226         printf("mkdir: create directory %s failed.\n",argv[1]);
227         }
228     }
229     }
230     return ret;
231 }
232 
233 /* rmdir命令的内建函数 */
234 int32_t buildin_rmdir(uint32_t argc,char** argv){
235     int32_t ret=-1;
236     if (argc!=2){
237     printf("rmdir: only suppory 1 argument!\n");
238     }else {
239     make_clear_abs_path(argv[1],final_path);
240     /* 若删除的不是根目录 */
241     if (strcmp("/",final_path)){
242         if (rmdir(final_path)==0){
243         ret=0;
244         }else {
245         printf("rmdir: remove %s failed.\n",argv[1]);
246         }
247     }
248     }
249     return ret;
250 }
251 
252 /* rm命令内建函数 */
253 int32_t buildin_rm(uint32_t argc,char** argv){
254     int32_t ret=-1;
255     if (argc!=2){
256     printf("rm: only support 1 argument!\n");
257     }else {
258     make_clear_abs_path(argv[1],final_path);
259     /* 若删除的不是根目录 */
260     if (strcmp("/",final_path)){
261         if (unlink(final_path)==0){
262         ret=0;
263         }else {
264         printf("rm: delete %s failed.\n",argv[1]);
265         }
266     }
267     }
268     return ret;
269 }
View Code
复制代码

②shell/buildin_cmd.h:

复制代码
 1 #ifndef __SHELL_BUILDIN_CMD_H
 2 #define __SHELL_BUILDIN_CMD_H
 3 #include "stdint.h"
 4 void make_clear_abs_path(char* path,char* wash_buf);
 5 void buildin_ls(uint32_t argc,char** argv);
 6 char* buildin_cd(uint32_t argc,char** argv);
 7 int32_t buildin_mkdir(uint32_t argc,char** argv);
 8 int32_t buildin_rmdir(uint32_t argc,char** argv);
 9 int32_t buildin_rm(uint32_t argc,char** argv);
10 void buildin_pwd(uint32_t argc,char** argv);
11 void buildin_ps(uint32_t argc,char** argv);
12 void buildin_clear(uint32_t argc,char** argv);
13 #endif
View Code
复制代码

③shell/shell.c,修改my_shell():

复制代码
 1 /* 简单的shell */
 2 void my_shell(void){
 3     cwd_cache[0]='/';
 4     while (1){
 5     print_prompt();
 6     memset(final_path,0,MAX_PATH_LEN);
 7     memset(cmd_line,0,MAX_PATH_LEN);
 8     readline(cmd_line,MAX_PATH_LEN);
 9     if (cmd_line[0]==0){   // 只键入回车
10         continue;
11     }
12     argc=-1;
13     argc=cmd_parse(cmd_line,argv,' ');
14     if (argc==-1){
15         printf("num of arguments exceed %d\n",MAX_ARG_NR);
16         continue;
17     }
18     if (!strcmp("ls",argv[0])){
19         buildin_ls(argc,argv);
20     }else if (!strcmp("cd",argv[0])){
21         if (buildin_cd(argc,argv)!=NULL){
22         memset(cwd_cache,0,MAX_PATH_LEN);
23         strcpy(cwd_cache,final_path);
24         }
25     }else if (!strcmp("pwd",argv[0])){
26         buildin_pwd(argc,argv);
27     }else if (!strcmp("ps",argv[0])){
28         buildin_ps(argc,argv);
29         }else if (!strcmp("clear",argv[0])){
30             buildin_clear(argc,argv);
31         }else if (!strcmp("mkdir",argv[0])){
32             buildin_mkdir(argc,argv);
33         }else if (!strcmp("rmdir",argv[0])){
34             buildin_rmdir(argc,argv);
35         }else if (!strcmp("rm",argv[0])){
36             buildin_rm(argc,argv);
37     }else {
38         printf("external command\n");
39     }
40     }
41     PANIC("my_shell: should not be here");
42 }
View Code
复制代码

④makefile:

复制代码
  1 BUILD_DIR = ./build
  2 ENTRY_POINT = 0xc0001500
  3 AS = nasm
  4 CC = gcc
  5 LD = ld
  6 LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/ -I thread/ -I userprog/ -I fs/ -I shell/
  7 ASFLAGS = -f elf
  8 CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
  9 LDFLAGS =  -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
 10 OBJS =     $(BUILD_DIR)/main.o     $(BUILD_DIR)/init.o     $(BUILD_DIR)/interrupt.o \
 11           $(BUILD_DIR)/timer.o     $(BUILD_DIR)/kernel.o     $(BUILD_DIR)/print.o \
 12     $(BUILD_DIR)/switch.o     $(BUILD_DIR)/debug.o     $(BUILD_DIR)/string.o \
 13     $(BUILD_DIR)/memory.o     $(BUILD_DIR)/bitmap.o     $(BUILD_DIR)/thread.o \
 14     $(BUILD_DIR)/list.o     $(BUILD_DIR)/sync.o     $(BUILD_DIR)/console.o \
 15     $(BUILD_DIR)/keyboard.o $(BUILD_DIR)/ioqueue.o     $(BUILD_DIR)/tss.o \
 16     $(BUILD_DIR)/process.o     $(BUILD_DIR)/syscall.o     $(BUILD_DIR)/syscall-init.o \
 17     $(BUILD_DIR)/stdio.o     $(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o \
 18     $(BUILD_DIR)/fs.o     $(BUILD_DIR)/inode.o    $(BUILD_DIR)/file.o \
 19     $(BUILD_DIR)/dir.o    $(BUILD_DIR)/fork.o    $(BUILD_DIR)/shell.o \
 20     $(BUILD_DIR)/buildin_cmd.o
 21 ##############     c代码编译     ###############
 22 $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \
 23         lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \
 24         thread/thread.h kernel/interrupt.h device/console.h \
 25         device/keyboard.h device/ioqueue.h userprog/process.h \
 26     lib/user/syscall.h userprog/syscall-init.h lib/stdio.h \
 27     lib/kernel/stdio-kernel.o fs/fs.o lib/string.h fs/file.h \
 28     shell/shell.o kernel/debug.h
 29     $(CC) $(CFLAGS) $< -o $@
 30 
 31 $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \
 32         lib/stdint.h kernel/interrupt.h device/timer.h kernel/memory.h \
 33         thread/thread.h device/console.h device/keyboard.h userprog/tss.h \
 34     userprog/syscall-init.h device/ide.h fs/fs.h
 35     $(CC) $(CFLAGS) $< -o $@
 36 
 37 $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \
 38         lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h
 39     $(CC) $(CFLAGS) $< -o $@
 40 
 41 $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/io.h \
 42     lib/kernel/print.h kernel/interrupt.h thread/thread.h kernel/debug.h 
 43     $(CC) $(CFLAGS) $< -o $@
 44 
 45 $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \
 46         lib/kernel/print.h lib/stdint.h kernel/interrupt.h
 47     $(CC) $(CFLAGS) $< -o $@
 48     
 49 $(BUILD_DIR)/string.o: lib/string.c lib/string.h \
 50     kernel/debug.h kernel/global.h
 51     $(CC) $(CFLAGS) $< -o $@
 52     
 53 $(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \
 54     lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h \
 55     thread/sync.h thread/thread.h lib/kernel/list.h kernel/interrupt.h
 56     $(CC) $(CFLAGS) $< -o $@
 57     
 58 $(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h kernel/global.h \
 59     lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h
 60     $(CC) $(CFLAGS) $< -o $@
 61     
 62 $(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \
 63     lib/stdint.h lib/string.h kernel/global.h kernel/memory.h \
 64     kernel/debug.h kernel/interrupt.h lib/kernel/print.h userprog/process.h \
 65     lib/stdio.h fs/file.h fs/fs.h
 66     $(CC) $(CFLAGS) $< -o $@
 67     
 68 $(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h \
 69     kernel/interrupt.h lib/stdint.h kernel/debug.h
 70     $(CC) $(CFLAGS) $< -o $@
 71     
 72 $(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h \
 73     lib/stdint.h thread/thread.h kernel/debug.h kernel/interrupt.h
 74     $(CC) $(CFLAGS) $< -o $@
 75     
 76 $(BUILD_DIR)/console.o: device/console.c device/console.h \
 77     lib/kernel/print.h thread/sync.h
 78     $(CC) $(CFLAGS) $< -o $@
 79     
 80 $(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h \
 81     lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \
 82     kernel/global.h lib/stdint.h device/ioqueue.h
 83     $(CC) $(CFLAGS) $< -o $@
 84     
 85 $(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h \
 86     kernel/interrupt.h kernel/global.h kernel/debug.h
 87     $(CC) $(CFLAGS) $< -o $@
 88     
 89 $(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h \
 90     kernel/global.h thread/thread.h lib/kernel/print.h
 91     $(CC) $(CFLAGS) $< -o $@
 92     
 93 $(BUILD_DIR)/process.o: userprog/process.c userprog/process.h \
 94     lib/string.h kernel/global.h kernel/memory.h lib/kernel/print.h \
 95     thread/thread.h kernel/interrupt.h kernel/debug.h device/console.h
 96     $(CC) $(CFLAGS) $< -o $@
 97 
 98 $(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h \
 99     lib/stdint.h thread/thread.h
100     $(CC) $(CFLAGS) $< -o $@
101 
102 $(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \
103     lib/user/syscall.h lib/stdint.h lib/kernel/print.h device/console.h \
104     thread/thread.h lib/string.h kernel/memory.h fs/fs.h userprog/fork.h
105     $(CC) $(CFLAGS) $< -o $@
106 
107 $(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h lib/string.h \
108     lib/user/syscall.h
109     $(CC) $(CFLAGS) $< -o $@
110 
111 $(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h \
112     lib/stdio.h device/console.h
113     $(CC) $(CFLAGS) $< -o $@
114 
115 $(BUILD_DIR)/ide.o: device/ide.c device/ide.h lib/stdint.h thread/sync.h \
116         lib/kernel/list.h kernel/global.h thread/thread.h lib/kernel/bitmap.h \
117          kernel/memory.h lib/kernel/io.h lib/stdio.h lib/stdint.h \
118            lib/kernel/stdio-kernel.h kernel/interrupt.h kernel/debug.h \
119            device/console.h device/timer.h lib/string.h
120     $(CC) $(CFLAGS) $< -o $@
121 
122 $(BUILD_DIR)/fs.o: fs/fs.c fs/fs.h fs/super_block.h fs/inode.h fs/dir.h \
123     lib/stdint.h lib/kernel/stdio-kernel.h lib/kernel/list.h lib/string.h \
124     device/ide.h kernel/global.h kernel/debug.h kernel/memory.h \
125     fs/file.h
126     $(CC) $(CFLAGS) $< -o $@
127 
128 $(BUILD_DIR)/inode.o: fs/inode.c fs/inode.h lib/stdint.h lib/kernel/list.h \
129         kernel/global.h fs/fs.h device/ide.h thread/sync.h thread/thread.h \
130          lib/kernel/bitmap.h kernel/memory.h fs/file.h kernel/debug.h \
131           kernel/interrupt.h lib/kernel/stdio-kernel.h
132     $(CC) $(CFLAGS) $< -o $@
133 
134 $(BUILD_DIR)/file.o: fs/file.c fs/file.h lib/stdint.h device/ide.h thread/sync.h \
135         lib/kernel/list.h kernel/global.h thread/thread.h lib/kernel/bitmap.h \
136          kernel/memory.h fs/fs.h fs/inode.h fs/dir.h lib/kernel/stdio-kernel.h \
137           kernel/debug.h kernel/interrupt.h
138     $(CC) $(CFLAGS) $< -o $@
139 
140 $(BUILD_DIR)/dir.o: fs/dir.c fs/dir.h lib/stdint.h fs/inode.h lib/kernel/list.h \
141         kernel/global.h device/ide.h thread/sync.h thread/thread.h \
142          lib/kernel/bitmap.h kernel/memory.h fs/fs.h fs/file.h \
143           lib/kernel/stdio-kernel.h kernel/debug.h kernel/interrupt.h
144     $(CC) $(CFLAGS) $< -o $@
145 
146 $(BUILD_DIR)/fork.o: userprog/fork.c userprog/fork.h thread/thread.h \
147     userprog/process.h kernel/memory.h kernel/interrupt.h kernel/debug.h \
148     lib/string.h fs/file.h
149     $(CC) $(CFLAGS) $< -o $@
150 
151 $(BUILD_DIR)/shell.o: shell/shell.c shell/shell.h lib/stdint.h fs/fs.h \
152     lib/user/syscall.h lib/stdio.h lib/stdint.h kernel/global.h \
153     kernel/debug.h fs/fs.h shell/buildin_cmd.h
154     $(CC) $(CFLAGS) $< -o $@
155 
156 $(BUILD_DIR)/buildin_cmd.o: shell/buildin_cmd.c shell/buildin_cmd.h lib/stdint.h \
157     lib/user/syscall.h lib/stdio.h lib/string.h kernel/global.h \
158     kernel/debug.h fs/fs.h fs/dir.h shell/shell.h
159     $(CC) $(CFLAGS) $< -o $@
160     
161 ##############    汇编代码编译    ###############
162 $(BUILD_DIR)/kernel.o: kernel/kernel.S
163     $(AS) $(ASFLAGS) $< -o $@
164 
165 $(BUILD_DIR)/print.o: lib/kernel/print.S
166     $(AS) $(ASFLAGS) $< -o $@
167 
168 $(BUILD_DIR)/switch.o: thread/switch.S
169     $(AS) $(ASFLAGS) $< -o $@
170 
171 ##############    链接所有目标文件    #############
172 $(BUILD_DIR)/kernel.bin: $(OBJS)
173     $(LD) $(LDFLAGS) $^ -o $@
174 
175 .PHONY : mk_dir hd clean all
176 
177 mk_dir:
178     if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
179 
180 hd:
181     dd if=$(BUILD_DIR)/kernel.bin \
182            of=/home/zbb/bochs/hd60M.img \
183            bs=512 count=200 seek=9 conv=notrunc
184 
185 clean:
186     cd $(BUILD_DIR) && rm -f  ./*
187 
188 build: $(BUILD_DIR)/kernel.bin
189 
190 all: mk_dir build hd
View Code
复制代码

得到结果:

well,尝试了一下,确实搞不懂到底哪儿出了问题导致每次在根目录下创建dir,都会赋no=15,哎算了,感觉也无伤大雅,希望后面不会有坑。


本章我们来看看fork(),准确说是sys_fork()的实现过程:

1.得到当前线程的PCB,并在内核空间开辟一页内存,存放fork出的子进程的PCB

2.执行copy_process()——子进程拷贝父进程的资源

因为内核空间是共享的,所以在内核空间开辟一页,作为数据从父进程用户空间复制到到子进程用户空间的中转。

接下来有五步:

(1)子进程复制父进程的PCB、虚拟地址位图、内核——copy_pcb_vaddrbitmap_stack0()——PCB的信息当然都存在于PCB中,只要一句memcpy()就搞定了,不过要把pid、status、parent_pid等部分单独修改一下;虚拟地址位图则需要在内核空间开辟一页将父进程的复制过来。

(2)为子进程创建页目录表——create_page_dir()

(3)复制父进程进程体及用户栈给子进程——copy_body_stack3()——这时就要用到之前的中转页了:数据将从父进程用户空间 -> 内核中转页 -> 子进程用户空间

(4)为子进程构建thread stack和修改返回值——build_child_stack()——子进程返回值位于intr stack的eax中,故eax=0;构建thread stack时ret_addr赋值为intr_exit,这样子进程稍后就可以假装从中断返回,获得上下文,像父进程一样从fork()的下一行开始执行。

(5)更新文件inode的打开数——update_inode_open_cnts()

3.将进程添加到就绪队列和全局队列中。

4.将子进程的pid return给父进程。

释放此前的中转页。


参考博客:

posted @   Hell0er  阅读(76)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示