├── .vscode └── settings.json ├── README.md ├── boot ├── include │ └── boot.inc ├── loader.S └── mbr.S ├── build └── kernel.map ├── device ├── console.c ├── console.h ├── ide.c ├── ide.h ├── ioqueue.c ├── ioqueue.h ├── keyboard.c ├── keyboard.h ├── timer.c └── timer.h ├── fs ├── dir.c ├── dir.h ├── file.c ├── file.h ├── fs.c ├── fs.h ├── inode.c ├── inode.h └── super_block.h ├── kernel ├── debug.c ├── debug.h ├── global.h ├── init.c ├── init.h ├── interrupt.c ├── interrupt.h ├── kernel.S ├── main.c ├── memory.c └── memory.h ├── lib ├── kernel │ ├── bitmap.c │ ├── bitmap.h │ ├── io.h │ ├── list.c │ ├── list.h │ ├── print.S │ ├── print.h │ ├── stdio-kernel.c │ └── stdio-kernel.h ├── stdint.h ├── stdio.c ├── stdio.h ├── string.c ├── string.h └── user │ ├── assert.c │ ├── assert.h │ ├── syscall.c │ └── syscall.h ├── makefile ├── shell ├── buildin_cmd.c ├── buildin_cmd.h ├── pipe.c ├── pipe.h ├── shell.c └── shell.h ├── thread ├── switch.S ├── sync.c ├── sync.h ├── thread.c └── thread.h └── userprog ├── exec.c ├── exec.h ├── fork.c ├── fork.h ├── process.c ├── process.h ├── syscall-init.c ├── syscall-init.h ├── tss.c ├── tss.h ├── wait_exit.c └── wait_exit.h /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "print.h": "c", 4 | "debug.h": "c" 5 | } 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kernel 2 | 该项目参照操作系统真像还原,[作者源代码链接](https://github.com/elephantos/elephant) 3 | 4 | 笔记记录在[我的博客](https://melodywei.github.io/categories/kernel/) 5 | 6 | [知乎专栏](https://zhuanlan.zhihu.com/c_177480196) 7 | 8 | -------------------------------------------------------------------------------- /boot/include/boot.inc: -------------------------------------------------------------------------------- 1 | LOADER_BASE_ADDR equ 0x900 2 | LOADER_STACK_TOP equ LOADER_BASE_ADDR 3 | LOADER_START_SECTOR equ 0x2 4 | 5 | KERNEL_BIN_BASE_ADDR equ 0x70000 6 | KERNEL_IMAGE_BASE_ADDR equ 0x1500 7 | KERNEL_ENTRY_POINT equ 0xc0001500 8 | KERNEL_START_SECTOR equ 0x9 9 | 10 | PAGE_DIR_TABLE_POS equ 0x100000 11 | 12 | 13 | ;---------------------------- 14 | ;gdt描述符属性 15 | 16 | DESC_G_4K equ 1_00000000000000000000000b 17 | DESC_D_32 equ 1_0000000000000000000000b 18 | DESC_L equ 0_000000000000000000000b ; 64位代码标记,此处标记为0便可。 19 | DESC_AVL equ 0_00000000000000000000b ; cpu不用此位,暂置为0 20 | DESC_LIMIT_CODE2 equ 1111_0000000000000000b 21 | DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 22 | DESC_LIMIT_VIDEO2 equ 0000_000000000000000b 23 | DESC_P equ 1_000000000000000b 24 | DESC_DPL_0 equ 00_0000000000000b 25 | DESC_DPL_1 equ 01_0000000000000b 26 | DESC_DPL_2 equ 10_0000000000000b 27 | DESC_DPL_3 equ 11_0000000000000b 28 | DESC_S_CODE equ 1_000000000000b 29 | DESC_S_DATA equ DESC_S_CODE 30 | DESC_S_sys equ 0_000000000000b 31 | DESC_TYPE_CODE equ 1000_00000000b ;x=1,c=0,r=0,a=0 代码段是可执行的,非依从>的,不可读的,已访问位a清0. 32 | DESC_TYPE_DATA equ 0010_00000000b ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上>扩展的,可写的,已访问位a清0. 33 | 34 | ;DESC_CODE_HIGH4 = 1100 1111 1001 1000 0000 0000 代码段的高4字节 35 | DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + DESC_DPL_0 + DESC_S_CODE + DESC_TYPE_CODE + 0x00 36 | 37 | ;DESC_DATA_HIGH4 = 1100 1111 1001 0010 0000 0000 38 | DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x00 39 | 40 | ;DESC_VIDEO_HIGH4= 1100 0000 1001 0010 0000 1011 41 | DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b 42 | 43 | ;-------------- 选择子属性 --------------- 44 | RPL0 equ 00b 45 | RPL1 equ 01b 46 | RPL2 equ 10b 47 | RPL3 equ 11b 48 | TI_GDT equ 000b 49 | TI_LDT equ 100b 50 | 51 | ;---------------- 页表相关属性 -------------- 52 | PG_P equ 1b 53 | PG_RW_R equ 00b 54 | PG_RW_W equ 10b 55 | PG_US_U equ 100b 56 | 57 | 58 | ;------------- program type 定义 -------------- 59 | PT_NULL equ 0 60 | 61 | 62 | -------------------------------------------------------------------------------- /boot/loader.S: -------------------------------------------------------------------------------- 1 | %include "boot.inc" 2 | section loader vstart=LOADER_BASE_ADDR 3 | 4 | 5 | ; 构建全局描述符表,并填充段描述符,段描述符的大小为8字节,在这里将其分为低4字节与高4字节来定义 6 | ; dd=define double-word,为4字节 7 | ;-------------------------------------------------------- 8 | 9 | ; gdt的起始地址为GDT_BASE的地址,且gdt的第0个描述符不可用,所以将其直接定义为0 10 | GDT_BASE: dd 0x00000000 11 | dd 0x00000000 12 | 13 | ; 代码段 14 | CODE_DESC: dd 0x0000ffff 15 | dd DESC_CODE_HIGH4 16 | 17 | ; 数据段和栈段 18 | DATA_STACK_DESC: dd 0x0000ffff 19 | dd DESC_DATA_HIGH4 20 | 21 | ; 显存段描述符 22 | VIDEO_DESC: dd 0x80000007 23 | dd DESC_VIDEO_HIGH4 24 | 25 | GDT_SIZE equ $-GDT_BASE 26 | GDT_LIMIT equ GDT_SIZE - 1 27 | times 60 dq 0 ;dq表示8字节的数据,在这里预留出60个8字节的空间。以便将来扩充 28 | 29 | SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0 30 | SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上 31 | SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上 32 | 33 | ;total_mem_bytes 存放获取到的总内存,该变量的地址为 0xb00 = 0x900 + 60 * 8 + 8 * 4; 34 | total_mem_bytes dd 0 35 | 36 | gdt_ptr dw GDT_LIMIT ;gdt的前2字节是段界限,后4字节是段基址 37 | dd GDT_BASE 38 | 39 | ards_buf times 244 db 0 40 | ards_nr dw 0 41 | 42 | loader_start: 43 | ;int 0x15 eax=0xe820 edx=0x534d4150 44 | ;-------------------------------------- 45 | xor ebx, ebx ;将ebx清0 46 | mov edx, 0x534d4150 47 | mov di, ards_buf ;ards结构缓冲区 48 | .e820_mem_get_loop: 49 | mov eax, 0xe820 50 | mov ecx, 20 ;ards地址范围描述符结构大小位20字节 51 | int 0x15 52 | jc .e820_failed_so_try_e801 53 | 54 | add di, cx ;使di增加20字节指向缓冲区中新的ards结构位置 55 | inc word [ards_nr] ;记录ards数量 56 | cmp ebx, 0 ;如果ebx为0且cf位不为1,说明adrs全部返回 57 | jnz .e820_mem_get_loop 58 | 59 | ;在所有ards结构中找出(base_addr_low + length_low)的最大值,即为内存的容量 60 | mov cx, [ards_nr] 61 | mov ebx, ards_buf 62 | xor edx, edx 63 | .find_max_mem_area: 64 | mov eax, [ebx] ;base_addr_low 65 | add eax, [ebx + 8] ;length_low 66 | add ebx, 20 67 | cmp edx, eax 68 | jge .next_ards 69 | mov edx, eax 70 | .next_ards: 71 | loop .find_max_mem_area 72 | jmp .mem_get_ok 73 | 74 | .e820_failed_so_try_e801: 75 | mov ax, 0xe801 76 | int 0x15 77 | jc .e801_failed_so_tyy88 78 | 79 | ;先算出低15MB的内存 80 | mov cx, 0x400 ;将获取到的低15M内存乘1024转化成byte 81 | mul cx 82 | shl edx, 16 83 | and eax, 0xffff ;只取低16位,防止乘法溢出 84 | or edx, eax 85 | add eax, 0x100000 86 | mov esi, edx 87 | 88 | ;再将16MB以上的空间转化成byte为单位 89 | xor eax, eax 90 | mov ax, bx 91 | mov ecx, 0x10000 ;32位下默认被乘数是eax,将获取到的内存乘以64KB转换成byte 92 | mul ecx 93 | add esi, eax 94 | mov edx, esi 95 | jmp .mem_get_ok 96 | 97 | .e801_failed_so_tyy88: 98 | mov ah, 0x88 99 | int 0x15 100 | jc .error_hlt 101 | and eax, 0xffff 102 | mov cx, 0x400 103 | mul cx 104 | shl edx, 16 105 | or edx, eax 106 | add edx, 0x100000 107 | 108 | 109 | .mem_get_ok: 110 | mov [total_mem_bytes], edx 111 | 112 | ;--------------------------- 113 | ;准备进入保护模式 114 | ;1. 打开A20 115 | ;2. 加载gdt 116 | ;3. 将cr0的PE位置1 117 | ;--------------------------- 118 | 119 | 120 | ;-------打开A20-------- 121 | in al, 0x92 122 | or al, 0000_0010b 123 | out 0x92, al 124 | 125 | ;-------加载gdt------- 126 | lgdt [gdt_ptr] 127 | 128 | ;------cr0第0位置1----- 129 | mov eax, cr0 130 | or eax, 0x00000001 131 | mov cr0, eax 132 | 133 | jmp SELECTOR_CODE:p_mode_start 134 | 135 | 136 | .error_hlt: 137 | hlt 138 | 139 | [bits 32] 140 | p_mode_start: 141 | mov ax, SELECTOR_DATA 142 | mov ds, ax 143 | mov es, ax 144 | mov ss, ax 145 | mov esp, LOADER_STACK_TOP 146 | mov ax, SELECTOR_VIDEO 147 | mov gs, ax 148 | 149 | ; ------------------------- 加载kernel ---------------------- 150 | mov eax, KERNEL_START_SECTOR ; kernel.bin所在的扇区号 151 | mov ebx, KERNEL_BIN_BASE_ADDR ; 从磁盘读出后,写入到ebx指定的地址 152 | mov ecx, 200 ; 读入的扇区数 153 | 154 | call rd_disk_m_32 155 | 156 | 157 | call setup_page 158 | 159 | ;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载 160 | sgdt [gdt_ptr] 161 | 162 | ;将gdt描述符中视频段描述符中的段基址+0xc0000000 163 | mov ebx, [gdt_ptr + 2] 164 | 165 | ;显存段是第3个段描述符,每个描述符是8字节,故0x18。;段描述符的高4字节的最高位是段基址的31~24位 166 | or dword [ebx + 0x18 + 4], 0xc0000000 167 | 168 | ;将gdt的基址加上0xc0000000使其成为内核所在的高地址 169 | add dword [gdt_ptr + 2], 0xc0000000 170 | 171 | ; 将栈指针同样映射到内核地址 172 | add esp, 0xc0000000 173 | 174 | ; 把页目录地址赋给cr3 175 | mov eax, PAGE_DIR_TABLE_POS 176 | mov cr3, eax 177 | 178 | ; 打开cr0的pg位(第31位) 179 | mov eax, cr0 180 | or eax, 0x80000000 181 | mov cr0, eax 182 | 183 | ;在开启分页后,用gdt新的地址重新加载 184 | lgdt [gdt_ptr] ; 重新加载 185 | 186 | jmp SELECTOR_CODE:enter_kernel ;强制刷新流水线,更新gdt 187 | enter_kernel: 188 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 189 | call kernel_init 190 | mov esp, 0xc009f000 191 | jmp KERNEL_ENTRY_POINT ; 用地址0x1500访问测试,结果ok 192 | 193 | 194 | 195 | ;----------------- 将kernel.bin中的segment拷贝到编译的地址 ----------- 196 | kernel_init: 197 | xor eax, eax 198 | xor ebx, ebx ;ebx记录程序头表地址 199 | xor ecx, ecx ;cx记录程序头表中的program header数量 200 | xor edx, edx ;dx 记录program header尺寸,即e_phentsize 201 | 202 | mov dx, [KERNEL_BIN_BASE_ADDR + 42] ; 偏移文件42字节处的属性是e_phentsize,表示program header大小 203 | mov ebx, [KERNEL_BIN_BASE_ADDR + 28] ; 偏移文件开始部分28字节的地方是e_phoff,表示第1 个program header在文件中的偏移量 204 | ; 其实该值是0x34,不过还是谨慎一点,这里来读取实际值 205 | add ebx, KERNEL_BIN_BASE_ADDR 206 | mov cx, [KERNEL_BIN_BASE_ADDR + 44] ; 偏移文件开始部分44字节的地方是e_phnum,表示有几个program header 207 | .each_segment: 208 | cmp byte [ebx + 0], PT_NULL ; 若p_type等于 PT_NULL,说明此program header未使用。 209 | je .PTNULL 210 | 211 | 212 | ;为函数memcpy压入参数,参数是从右往左依然压入.函数原型类似于 memcpy(dst,src,size) 213 | push dword [ebx + 16] ; program header中偏移16字节的地方是p_filesz,>压入函数memcpy的第三个参数:size 214 | mov eax, [ebx + 4] ; 距程序头偏移量为4字节的位置是p_offset 215 | add eax, KERNEL_BIN_BASE_ADDR ; 加上kernel.bin被加载到的物理地址,eax为该段的物理地址 216 | push eax ; 压入函数memcpy的第二个参数:源地址 217 | push dword [ebx + 8] ; 压入函数memcpy的第一个参数:目的地址,偏移程序头8字节的位置是p_vaddr,这就是目的地址 218 | call mem_cpy ; 调用mem_cpy完成段复制 219 | add esp,12 ; 清理栈中压入的三个参数 220 | .PTNULL: 221 | add ebx, edx ; edx为program header大小,即e_phentsize,在此ebx指>向下一个program header 222 | loop .each_segment 223 | ret 224 | 225 | 226 | 227 | ;---------- 逐字节拷贝 mem_cpy(dst,src,size) ------------ 228 | ;输入:栈中三个参数(dst,src,size) 229 | ;输出:无 230 | ;--------------------------------------------------------- 231 | mem_cpy: 232 | cld 233 | push ebp 234 | mov ebp, esp 235 | push ecx ; rep指令用到了ecx,但ecx对于外层段的循环还有用,故先入栈备份 236 | mov edi, [ebp + 8] ; dst 237 | mov esi, [ebp + 12] ; src 238 | mov ecx, [ebp + 16] ; size 239 | rep movsb ; 逐字节拷贝 240 | 241 | ;恢复环境 242 | pop ecx 243 | pop ebp 244 | ret 245 | 246 | 247 | ;----------创建页目录及页表---------- 248 | setup_page: 249 | mov ecx, 4096 250 | mov esi, 0 251 | .clear_page_dir: 252 | mov byte [PAGE_DIR_TABLE_POS + esi], 0 253 | inc esi 254 | loop .clear_page_dir 255 | 256 | .create_pde: 257 | mov eax, PAGE_DIR_TABLE_POS 258 | add eax, 0x1000 ; 此时eax为第一个页表的位置及属性 259 | mov ebx, eax ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。 260 | 261 | ; 下面将页目录项0和0xc00都存为第一个页表的地址, 262 | ; 一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表, 263 | ; 这是为将地址映射为内核地址做准备 264 | or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问. 265 | mov [PAGE_DIR_TABLE_POS + 0x0], eax ; 第1个目录项,在页目录表中的第1个目录项写入第一个>页表的位置(0x101000)及属性(7) 266 | mov [PAGE_DIR_TABLE_POS + 0xc00], eax ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间, 267 | ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程. 268 | sub eax, 0x1000 269 | mov [PAGE_DIR_TABLE_POS + 4092], eax ; 使最后一个目录项指向页目录表自己的地址 270 | 271 | ;下面创建页表项(PTE) 272 | mov ecx, 256 ; 1M低端内存 / 每页大小4k = 256 273 | mov esi, 0 274 | mov edx, PG_US_U | PG_RW_W | PG_P ; 属性为7,US=1,RW=1,P=1 275 | .create_pte: 276 | mov [ebx+esi*4],edx ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 277 | add edx,4096 278 | inc esi 279 | loop .create_pte 280 | ;创建内核其它页表的PDE 281 | mov eax, PAGE_DIR_TABLE_POS 282 | add eax, 0x2000 ; 此时eax为第二个页表的位置 283 | or eax, PG_US_U | PG_RW_W | PG_P ; 页目录项的属性US,RW和P位都为1 284 | mov ebx, PAGE_DIR_TABLE_POS 285 | mov ecx, 254 ; 范围为第769~1022的所有目录项数量 286 | mov esi, 769 287 | .create_kernel_pde: 288 | mov [ebx+esi*4], eax 289 | inc esi 290 | add eax, 0x1000 291 | loop .create_kernel_pde 292 | ret 293 | 294 | 295 | 296 | ;------------------------------------------------------------------------------- 297 | ;功能:读取硬盘n个扇区 298 | rd_disk_m_32: 299 | ;------------------------------------------------------------------------------- 300 | ; eax=LBA扇区号 301 | ; ebx=将数据写入的内存地址 302 | ; ecx=读入的扇区数 303 | mov esi,eax ; 备份eax 304 | mov di,cx ; 备份扇区数到di 305 | ;读写硬盘: 306 | ;第1步:设置要读取的扇区数 307 | mov dx,0x1f2 308 | mov al,cl 309 | out dx,al ;读取的扇区数 310 | 311 | mov eax,esi ;恢复ax 312 | ;第2步:将LBA地址存入0x1f3 ~ 0x1f6 313 | 314 | ;LBA地址7~0位写入端口0x1f3 315 | mov dx,0x1f3 316 | out dx,al 317 | 318 | ;LBA地址15~8位写入端口0x1f4 319 | mov cl,8 320 | shr eax,cl 321 | mov dx,0x1f4 322 | out dx, al 323 | 324 | ;LBA地址23~16位写入端口0x1f5 325 | shr eax,cl 326 | mov dx,0x1f5 327 | out dx,al 328 | 329 | shr eax,cl 330 | and al,0x0f ;lba第24~27位 331 | or al,0xe0 ; 设置7~4位为1110,表示lba模式 332 | 333 | mov dx,0x1f6 334 | out dx,al 335 | 336 | ;第3步:向0x1f7端口写入读命令,0x20 337 | mov dx,0x1f7 338 | mov al,0x20 339 | out dx,al 340 | 341 | ;;;;;;; 至此,硬盘控制器便从指定的lba地址(eax)处,读出连续的cx个扇区,下面检查硬盘>状态,不忙就能把这cx个扇区的数据读出来 342 | 343 | ;第4步:检测硬盘状态 344 | .not_ready: ;测试0x1f7端口(status寄存器)的的BSY位 345 | ;同一端口,写时表示写入命令字,读时表示读入硬盘状态 346 | nop 347 | in al,dx 348 | and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘>忙 349 | cmp al,0x08 350 | jnz .not_ready ;若未准备好,继续等。 351 | ;第5步:从0x1f0端口读数据 352 | mov ax, di ;以下从硬盘端口读数据用insw指令更快捷,不过尽可能多的演示>命令使用, 353 | ;在此先用这种方法,在后面内容会用到insw和outsw等 354 | 355 | mov dx, 256 ;di为要读取的扇区数,一个扇区有512字节,每次读入一个字,共需di*512/2次,所以di*256 356 | mul dx 357 | mov cx, ax 358 | mov dx, 0x1f0 359 | .go_on_read: 360 | in ax,dx 361 | mov [ebx], ax 362 | add ebx, 2 363 | 364 | loop .go_on_read 365 | ret 366 | -------------------------------------------------------------------------------- /boot/mbr.S: -------------------------------------------------------------------------------- 1 | ; 主引导程序 2 | ;--------------------- 3 | 4 | %include "boot.inc" 5 | 6 | SECTION MBR vstart=0x7c00 ;程序开始的地址 7 | mov ax, cs ;使用cs初始化其他的寄存器 8 | mov ds, ax ;因为是通过jmp 0:0x7c00到的MBR开始地址 9 | mov es, ax ;所以此时的cs为0,也就是用0初始化其他寄存器 10 | mov ss, ax ;此类的寄存器不同通过立即数赋值,采用ax中转 11 | mov fs, ax 12 | mov sp, 0x7c00 ;初始化栈指针,sp也就是32位下的esp 13 | mov ax, 0xb800 14 | mov gs, ax 15 | 16 | ;清屏利用0x10中断的0x6号功能 17 | ;------------------------ 18 | mov ax, 0x600 19 | mov bx, 0x700 20 | mov cx, 0 21 | mov dx, 0x184f 22 | 23 | int 0x10 24 | 25 | mov byte [gs:0x00],'1' 26 | mov byte [gs:0x01],0xA4 27 | 28 | mov byte [gs:0x02],' ' 29 | mov byte [gs:0x03],0xA4 30 | 31 | mov byte [gs:0x04],'M' 32 | mov byte [gs:0x05],0xA4 33 | 34 | mov byte [gs:0x06],'B' 35 | mov byte [gs:0x07],0xA4 36 | 37 | mov byte [gs:0x08],'R' 38 | mov byte [gs:0x09],0xA4 39 | 40 | mov eax, LOADER_START_SECTOR ;起始扇区的lba地址 41 | mov bx, LOADER_BASE_ADDR ;写入的地址 42 | mov cx, 4 ;读入的扇区数 43 | call rd_disk_m_16 44 | 45 | jmp LOADER_BASE_ADDR + 0x300 46 | 47 | ;读取n个扇区 48 | ;--------------------- 49 | rd_disk_m_16: ;eax=扇区号,cx=读入的扇区数,bx=将数据写入的内存地址 50 | mov esi, eax ;备份eax和cx 51 | mov di, cx 52 | 53 | ;设置要读取的扇区数 54 | mov dx, 0x1f2 55 | mov al, cl 56 | out dx, al 57 | mov eax, esi 58 | 59 | ;将lba地址存入0x1f3-0x1f6 60 | 61 | ;lba地址0-7位写入端口0x1f3 62 | mov dx, 0x1f3 63 | out dx, al 64 | 65 | ;lba地址8-15位写入端口0x1f4 66 | mov cl, 8 67 | shr eax, cl 68 | mov dx, 0x1f4 69 | out dx, al 70 | 71 | ;lba地址16-23位写入端口0x1f5 72 | shr eax, cl 73 | mov dx, 0x1f5 74 | out dx, al 75 | 76 | 77 | shr eax, cl 78 | and al, 0x0f 79 | or al, 0xe0 80 | mov dx, 0x1f6 81 | out dx, al 82 | 83 | ;向0x1f7端口写入读命令 84 | mov dx, 0x1f7 85 | mov al, 0x20 86 | out dx, al 87 | .not_ready: 88 | nop 89 | in al, dx 90 | and al, 0x88 ;第4位为1表示硬盘控制器已经准备号数据传输,第7位为1表示硬盘忙 91 | cmp al, 0x08 92 | jnz .not_ready 93 | 94 | ;从0x1f0端口读数据 95 | mov ax, di 96 | mov dx, 256 97 | mul dx 98 | mov cx, ax 99 | mov dx, 0x1f0 100 | .go_on_read: 101 | in ax, dx 102 | mov [bx], ax 103 | add bx, 2 104 | loop .go_on_read 105 | ret 106 | 107 | 108 | times 510-($-$$) db 0 109 | db 0x55, 0xaa -------------------------------------------------------------------------------- /device/console.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "../lib/kernel/print.h" 3 | #include "../lib/stdint.h" 4 | #include "../thread/sync.h" 5 | #include "../thread/thread.h" 6 | static struct lock console_lock; // 控制台锁 7 | 8 | /* 初始化终端 */ 9 | void console_init() 10 | { 11 | lock_init(&console_lock); 12 | } 13 | 14 | /* 获取终端 */ 15 | void console_acquire() 16 | { 17 | lock_acquire(&console_lock); 18 | } 19 | 20 | /* 释放终端 */ 21 | void console_release() 22 | { 23 | lock_release(&console_lock); 24 | } 25 | 26 | /* 终端中输出字符串 */ 27 | void console_put_str(char *str) 28 | { 29 | console_acquire(); 30 | put_str(str); 31 | console_release(); 32 | } 33 | 34 | /* 终端中输出字符 */ 35 | void console_put_char(uint8_t char_asci) 36 | { 37 | console_acquire(); 38 | put_char(char_asci); 39 | console_release(); 40 | } 41 | 42 | /* 终端中输出16进制整数 */ 43 | void console_put_int(uint32_t num) 44 | { 45 | console_acquire(); 46 | put_int(num); 47 | console_release(); 48 | } 49 | -------------------------------------------------------------------------------- /device/console.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEVICE_CONSOLE_H_ 2 | #define _DEVICE_CONSOLE_H_ 3 | #include "../lib/stdint.h" 4 | 5 | void console_init(void); 6 | void console_acquire(void); 7 | void console_release(void); 8 | void console_put_str(char* str); 9 | void console_put_char(uint8_t char_asci); 10 | void console_put_int(uint32_t num); 11 | 12 | #endif //!_DEVICE_CONSOLE_H_ 13 | -------------------------------------------------------------------------------- /device/ide.c: -------------------------------------------------------------------------------- 1 | #include "ide.h" 2 | #include "../thread/sync.h" 3 | #include "../lib/kernel/io.h" 4 | #include "../lib/stdio.h" 5 | #include "../lib/kernel/stdio-kernel.h" 6 | #include "../kernel/interrupt.h" 7 | #include "../kernel/memory.h" 8 | #include "../kernel/debug.h" 9 | #include "console.h" 10 | #include "timer.h" 11 | #include "../lib/string.h" 12 | #include "../lib/kernel/list.h" 13 | 14 | /* 定义硬盘各寄存器的端口号 */ 15 | #define reg_data(channel) (channel->port_base + 0) 16 | #define reg_error(channel) (channel->port_base + 1) 17 | #define reg_sect_cnt(channel) (channel->port_base + 2) 18 | #define reg_lba_l(channel) (channel->port_base + 3) 19 | #define reg_lba_m(channel) (channel->port_base + 4) 20 | #define reg_lba_h(channel) (channel->port_base + 5) 21 | #define reg_dev(channel) (channel->port_base + 6) 22 | #define reg_status(channel) (channel->port_base + 7) 23 | #define reg_cmd(channel) (reg_status(channel)) 24 | #define reg_alt_status(channel) (channel->port_base + 0x206) 25 | #define reg_ctl(channel) reg_alt_status(channel) 26 | 27 | /* reg_status寄存器的一些关键位 */ 28 | #define BIT_STAT_BSY 0x80 // 硬盘忙 29 | #define BIT_STAT_DRDY 0x40 // 驱动器准备好 30 | #define BIT_STAT_DRQ 0x8 // 数据传输准备好了 31 | 32 | /* device寄存器的一些关键位 */ 33 | #define BIT_DEV_MBS 0xa0 // 第7位和第5位固定为1 34 | #define BIT_DEV_LBA 0x40 35 | #define BIT_DEV_DEV 0x10 36 | 37 | /* 一些硬盘操作的指令 */ 38 | #define CMD_IDENTIFY 0xec // identify指令 39 | #define CMD_READ_SECTOR 0x20 // 读扇区指令 40 | #define CMD_WRITE_SECTOR 0x30 // 写扇区指令 41 | 42 | /* 定义可读写的最大扇区数,调试用的 */ 43 | #define max_lba ((80 * 1024 * 1024 / 512) - 1) // 只支持80MB硬盘 44 | 45 | uint8_t channel_cnt; // 按硬盘数计算的通道数 46 | struct ide_channel channels[2]; // 有两个ide通道 47 | 48 | /* 用于记录总扩展分区的起始lba,初始为0,partition_scan时以此为标记 */ 49 | int32_t ext_lba_base = 0; 50 | 51 | uint8_t p_no = 0, l_no = 0; // 用来记录硬盘主分区和逻辑分区的下标 52 | 53 | struct list partition_list; // 分区队列 54 | 55 | /* 构建一个16字节大小的结构体,用来存分区表项 */ 56 | struct partition_table_entry 57 | { 58 | uint8_t bootable; // 是否可引导 59 | uint8_t start_head; // 起始磁头号 60 | uint8_t start_sec; // 起始扇区号 61 | uint8_t start_chs; // 起始柱面号 62 | uint8_t fs_type; // 分区类型 63 | uint8_t end_head; // 结束磁头号 64 | uint8_t end_sec; // 结束扇区号 65 | uint8_t end_chs; // 结束柱面号 66 | /* 更需要关注的是下面这两项 */ 67 | uint32_t start_lba; // 本分区起始扇区的lba地址 68 | uint32_t sec_cnt; // 本分区的扇区数目 69 | } __attribute__((packed)); // 保证此结构是16字节大小 70 | 71 | /* 引导扇区,mbr或ebr所在的扇区 */ 72 | struct boot_sector 73 | { 74 | uint8_t other[446]; // 引导代码 75 | struct partition_table_entry partition_table[4]; // 分区表中有4项,共64字节 76 | uint16_t signature; // 启动扇区的结束标志是0x55,0xaa, 77 | } __attribute__((packed)); 78 | 79 | /* 选择读写的硬盘 */ 80 | static void select_disk(struct disk *hd) 81 | { 82 | uint8_t reg_device = BIT_DEV_MBS | BIT_DEV_LBA; 83 | if (hd->dev_no == 1) 84 | { // 若是从盘就置DEV位为1 85 | reg_device |= BIT_DEV_DEV; 86 | } 87 | outb(reg_dev(hd->my_channel), reg_device); 88 | } 89 | 90 | /* 向硬盘控制器写入起始扇区地址及要读写的扇区数 */ 91 | static void select_sector(struct disk *hd, uint32_t lba, uint8_t sec_cnt) 92 | { 93 | ASSERT(lba <= max_lba); 94 | struct ide_channel *channel = hd->my_channel; 95 | 96 | /* 写入要读写的扇区数*/ 97 | outb(reg_sect_cnt(channel), sec_cnt); // 如果sec_cnt为0,则表示写入256个扇区 98 | 99 | /* 写入lba地址(即扇区号) */ 100 | outb(reg_lba_l(channel), lba); // lba地址的低8位,不用单独取出低8位.outb函数中的汇编指令outb %b0, %w1会只用al。 101 | outb(reg_lba_m(channel), lba >> 8); // lba地址的8~15位 102 | outb(reg_lba_h(channel), lba >> 16); // lba地址的16~23位 103 | 104 | /* 因为lba地址的24~27位要存储在device寄存器的0~3位, 105 | * 无法单独写入这4位,所以在此处把device寄存器再重新写入一次*/ 106 | outb(reg_dev(channel), BIT_DEV_MBS | BIT_DEV_LBA | (hd->dev_no == 1 ? BIT_DEV_DEV : 0) | lba >> 24); 107 | } 108 | 109 | /* 向通道channel发命令cmd */ 110 | static void cmd_out(struct ide_channel *channel, uint8_t cmd) 111 | { 112 | /* 只要向硬盘发出了命令便将此标记置为true,硬盘中断处理程序需要根据它来判断 */ 113 | channel->expecting_intr = true; 114 | outb(reg_cmd(channel), cmd); 115 | } 116 | 117 | /* 硬盘读入sec_cnt个扇区的数据到buf */ 118 | static void read_from_sector(struct disk *hd, void *buf, uint8_t sec_cnt) 119 | { 120 | uint32_t size_in_byte; 121 | if (sec_cnt == 0) 122 | { 123 | /* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */ 124 | size_in_byte = 256 * 512; 125 | } 126 | else 127 | { 128 | size_in_byte = sec_cnt * 512; 129 | } 130 | insw(reg_data(hd->my_channel), buf, size_in_byte / 2); 131 | } 132 | 133 | /* 将buf中sec_cnt扇区的数据写入硬盘 */ 134 | static void write2sector(struct disk *hd, void *buf, uint8_t sec_cnt) 135 | { 136 | uint32_t size_in_byte; 137 | if (sec_cnt == 0) 138 | { 139 | /* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */ 140 | size_in_byte = 256 * 512; 141 | } 142 | else 143 | { 144 | size_in_byte = sec_cnt * 512; 145 | } 146 | outsw(reg_data(hd->my_channel), buf, size_in_byte / 2); 147 | } 148 | 149 | /* 等待30秒 */ 150 | static bool busy_wait(struct disk *hd) 151 | { 152 | struct ide_channel *channel = hd->my_channel; 153 | uint16_t time_limit = 30 * 1000; // 可以等待30000毫秒 154 | while (time_limit -= 10 >= 0) 155 | { 156 | if (!(inb(reg_status(channel)) & BIT_STAT_BSY)) 157 | { 158 | return (inb(reg_status(channel)) & BIT_STAT_DRQ); 159 | } 160 | else 161 | { 162 | mtime_sleep(10); // 睡眠10毫秒 163 | } 164 | } 165 | return false; 166 | } 167 | 168 | /* 从硬盘读取sec_cnt个扇区到buf */ 169 | void ide_read(struct disk *hd, uint32_t lba, void *buf, uint32_t sec_cnt) 170 | { // 此处的sec_cnt为32位大小 171 | ASSERT(lba <= max_lba); 172 | ASSERT(sec_cnt > 0); 173 | lock_acquire(&hd->my_channel->lock); 174 | 175 | /* 1 先选择操作的硬盘 */ 176 | select_disk(hd); 177 | 178 | uint32_t secs_op; // 每次操作的扇区数 179 | uint32_t secs_done = 0; // 已完成的扇区数 180 | while (secs_done < sec_cnt) 181 | { 182 | if ((secs_done + 256) <= sec_cnt) 183 | { 184 | secs_op = 256; 185 | } 186 | else 187 | { 188 | secs_op = sec_cnt - secs_done; 189 | } 190 | 191 | /* 2 写入待读入的扇区数和起始扇区号 */ 192 | select_sector(hd, lba + secs_done, secs_op); 193 | 194 | /* 3 执行的命令写入reg_cmd寄存器 */ 195 | cmd_out(hd->my_channel, CMD_READ_SECTOR); // 准备开始读数据 196 | 197 | /********************* 阻塞自己的时机 *********************** 198 | 在硬盘已经开始工作(开始在内部读数据或写数据)后才能阻塞自己,现在硬盘已经开始忙了, 199 | 将自己阻塞,等待硬盘完成读操作后通过中断处理程序唤醒自己*/ 200 | sema_down(&hd->my_channel->disk_done); 201 | /*************************************************************/ 202 | 203 | /* 4 检测硬盘状态是否可读 */ 204 | /* 醒来后开始执行下面代码*/ 205 | if (!busy_wait(hd)) 206 | { // 若失败 207 | char error[64]; 208 | sprintf(error, "%s read sector %d failed!!!!!!\n", hd->name, lba); 209 | PANIC(error); 210 | } 211 | 212 | /* 5 把数据从硬盘的缓冲区中读出 */ 213 | read_from_sector(hd, (void *)((uint32_t)buf + secs_done * 512), secs_op); 214 | secs_done += secs_op; 215 | } 216 | lock_release(&hd->my_channel->lock); 217 | } 218 | 219 | /* 将buf中sec_cnt扇区数据写入硬盘 */ 220 | void ide_write(struct disk *hd, uint32_t lba, void *buf, uint32_t sec_cnt) 221 | { 222 | ASSERT(lba <= max_lba); 223 | ASSERT(sec_cnt > 0); 224 | lock_acquire(&hd->my_channel->lock); 225 | 226 | /* 1 先选择操作的硬盘 */ 227 | select_disk(hd); 228 | 229 | uint32_t secs_op; // 每次操作的扇区数 230 | uint32_t secs_done = 0; // 已完成的扇区数 231 | while (secs_done < sec_cnt) 232 | { 233 | if ((secs_done + 256) <= sec_cnt) 234 | { 235 | secs_op = 256; 236 | } 237 | else 238 | { 239 | secs_op = sec_cnt - secs_done; 240 | } 241 | 242 | /* 2 写入待写入的扇区数和起始扇区号 */ 243 | select_sector(hd, lba + secs_done, secs_op); // 先将待读的块号lba地址和待读入的扇区数写入lba寄存器 244 | 245 | /* 3 执行的命令写入reg_cmd寄存器 */ 246 | cmd_out(hd->my_channel, CMD_WRITE_SECTOR); // 准备开始写数据 247 | 248 | /* 4 检测硬盘状态是否可读 */ 249 | if (!busy_wait(hd)) 250 | { // 若失败 251 | char error[64]; 252 | sprintf(error, "%s write sector %d failed!!!!!!\n", hd->name, lba); 253 | PANIC(error); 254 | } 255 | 256 | /* 5 将数据写入硬盘 */ 257 | write2sector(hd, (void *)((uint32_t)buf + secs_done * 512), secs_op); 258 | 259 | /* 在硬盘响应期间阻塞自己 */ 260 | sema_down(&hd->my_channel->disk_done); 261 | secs_done += secs_op; 262 | } 263 | /* 醒来后开始释放锁*/ 264 | lock_release(&hd->my_channel->lock); 265 | } 266 | 267 | /* 将dst中len个相邻字节交换位置后存入buf */ 268 | static void swap_pairs_bytes(const char *dst, char *buf, uint32_t len) 269 | { 270 | uint8_t idx; 271 | for (idx = 0; idx < len; idx += 2) 272 | { 273 | /* buf中存储dst中两相邻元素交换位置后的字符串*/ 274 | buf[idx + 1] = *dst++; 275 | buf[idx] = *dst++; 276 | } 277 | buf[idx] = '\0'; 278 | } 279 | 280 | /* 获得硬盘参数信息 */ 281 | static void identify_disk(struct disk *hd) 282 | { 283 | char id_info[512]; 284 | select_disk(hd); 285 | cmd_out(hd->my_channel, CMD_IDENTIFY); 286 | /* 向硬盘发送指令后便通过信号量阻塞自己, 287 | * 待硬盘处理完成后,通过中断处理程序将自己唤醒 */ 288 | sema_down(&hd->my_channel->disk_done); 289 | 290 | /* 醒来后开始执行下面代码*/ 291 | if (!busy_wait(hd)) 292 | { // 若失败 293 | char error[64]; 294 | sprintf(error, "%s identify failed!!!!!!\n", hd->name); 295 | PANIC(error); 296 | } 297 | read_from_sector(hd, id_info, 1); 298 | 299 | char buf[64]; 300 | uint8_t sn_start = 10 * 2, sn_len = 20, md_start = 27 * 2, md_len = 40; 301 | swap_pairs_bytes(&id_info[sn_start], buf, sn_len); 302 | printk(" disk %s info:\n SN: %s\n", hd->name, buf); 303 | memset(buf, 0, sizeof(buf)); 304 | swap_pairs_bytes(&id_info[md_start], buf, md_len); 305 | printk(" MODULE: %s\n", buf); 306 | uint32_t sectors = *(uint32_t *)&id_info[60 * 2]; 307 | printk(" SECTORS: %d\n", sectors); 308 | printk(" CAPACITY: %dMB\n", sectors * 512 / 1024 / 1024); 309 | } 310 | 311 | /* 扫描硬盘hd中地址为ext_lba的扇区中的所有分区 */ 312 | static void partition_scan(struct disk *hd, uint32_t ext_lba) 313 | { 314 | struct boot_sector *bs = sys_malloc(sizeof(struct boot_sector)); 315 | ide_read(hd, ext_lba, bs, 1); 316 | uint8_t part_idx = 0; 317 | struct partition_table_entry *p = bs->partition_table; 318 | 319 | /* 遍历分区表4个分区表项 */ 320 | while (part_idx++ < 4) 321 | { 322 | if (p->fs_type == 0x5) 323 | { // 若为扩展分区 324 | if (ext_lba_base != 0) 325 | { 326 | /* 子扩展分区的start_lba是相对于主引导扇区中的总扩展分区地址 */ 327 | partition_scan(hd, p->start_lba + ext_lba_base); 328 | } 329 | else 330 | { // ext_lba_base为0表示是第一次读取引导块,也就是主引导记录所在的扇区 331 | /* 记录下扩展分区的起始lba地址,后面所有的扩展分区地址都相对于此 */ 332 | ext_lba_base = p->start_lba; 333 | partition_scan(hd, p->start_lba); 334 | } 335 | } 336 | else if (p->fs_type != 0) 337 | { // 若是有效的分区类型 338 | if (ext_lba == 0) 339 | { // 此时全是主分区 340 | hd->prim_parts[p_no].start_lba = ext_lba + p->start_lba; 341 | hd->prim_parts[p_no].sec_cnt = p->sec_cnt; 342 | hd->prim_parts[p_no].my_disk = hd; 343 | list_append(&partition_list, &hd->prim_parts[p_no].part_tag); 344 | sprintf(hd->prim_parts[p_no].name, "%s%d", hd->name, p_no + 1); 345 | p_no++; 346 | ASSERT(p_no < 4); // 0,1,2,3 347 | } 348 | else 349 | { 350 | hd->logic_parts[l_no].start_lba = ext_lba + p->start_lba; 351 | hd->logic_parts[l_no].sec_cnt = p->sec_cnt; 352 | hd->logic_parts[l_no].my_disk = hd; 353 | list_append(&partition_list, &hd->logic_parts[l_no].part_tag); 354 | sprintf(hd->logic_parts[l_no].name, "%s%d", hd->name, l_no + 5); // 逻辑分区数字是从5开始,主分区是1~4. 355 | l_no++; 356 | if (l_no >= 8) // 只支持8个逻辑分区,避免数组越界 357 | return; 358 | } 359 | } 360 | p++; 361 | } 362 | sys_free(bs); 363 | } 364 | 365 | /* 打印分区信息 */ 366 | static bool partition_info(struct list_elem *pelem, int arg UNUSED) 367 | { 368 | struct partition *part = elem2entry(struct partition, part_tag, pelem); 369 | printk(" %s start_lba:0x%x, sec_cnt:0x%x\n", part->name, part->start_lba, part->sec_cnt); 370 | 371 | /* 在此处return false与函数本身功能无关, 372 | * 只是为了让主调函数list_traversal继续向下遍历元素 */ 373 | return false; 374 | } 375 | 376 | /* 硬盘中断处理程序 */ 377 | void intr_hd_handler(uint8_t irq_no) 378 | { 379 | ASSERT(irq_no == 0x2e || irq_no == 0x2f); 380 | uint8_t ch_no = irq_no - 0x2e; 381 | struct ide_channel *channel = &channels[ch_no]; 382 | ASSERT(channel->irq_no == irq_no); 383 | /* 不必担心此中断是否对应的是这一次的expecting_intr, 384 | * 每次读写硬盘时会申请锁,从而保证了同步一致性 */ 385 | if (channel->expecting_intr) 386 | { 387 | channel->expecting_intr = false; 388 | sema_up(&channel->disk_done); 389 | 390 | /* 读取状态寄存器使硬盘控制器认为此次的中断已被处理, 391 | * 从而硬盘可以继续执行新的读写 */ 392 | inb(reg_status(channel)); 393 | } 394 | } 395 | 396 | /* 硬盘数据结构初始化 */ 397 | void ide_init() 398 | { 399 | printk("ide_init start\n"); 400 | uint8_t hd_cnt = *((uint8_t *)(0x475)); // 获取硬盘的数量 401 | ASSERT(hd_cnt > 0); 402 | list_init(&partition_list); 403 | channel_cnt = DIV_ROUND_UP(hd_cnt, 2); // 一个ide通道上有两个硬盘,根据硬盘数量反推有几个ide通道 404 | struct ide_channel *channel; 405 | uint8_t channel_no = 0, dev_no = 0; 406 | 407 | /* 处理每个通道上的硬盘 */ 408 | while (channel_no < channel_cnt) 409 | { 410 | channel = &channels[channel_no]; 411 | sprintf(channel->name, "ide%d", channel_no); 412 | 413 | /* 为每个ide通道初始化端口基址及中断向量 */ 414 | switch (channel_no) 415 | { 416 | case 0: 417 | channel->port_base = 0x1f0; // ide0通道的起始端口号是0x1f0 418 | channel->irq_no = 0x20 + 14; // 从片8259a上倒数第二的中断引脚,温盘,也就是ide0通道的的中断向量号 419 | break; 420 | case 1: 421 | channel->port_base = 0x170; // ide1通道的起始端口号是0x170 422 | channel->irq_no = 0x20 + 15; // 从8259A上的最后一个中断引脚,我们用来响应ide1通道上的硬盘中断 423 | break; 424 | } 425 | 426 | channel->expecting_intr = false; // 未向硬盘写入指令时不期待硬盘的中断 427 | lock_init(&channel->lock); 428 | 429 | /* 初始化为0,目的是向硬盘控制器请求数据后,硬盘驱动sema_down此信号量会阻塞线程, 430 | 直到硬盘完成后通过发中断,由中断处理程序将此信号量sema_up,唤醒线程. */ 431 | sema_init(&channel->disk_done, 0); 432 | 433 | register_handler(channel->irq_no, intr_hd_handler); 434 | 435 | /* 分别获取两个硬盘的参数及分区信息 */ 436 | while (dev_no < 2) 437 | { 438 | struct disk *hd = &channel->devices[dev_no]; 439 | hd->my_channel = channel; 440 | hd->dev_no = dev_no; 441 | sprintf(hd->name, "sd%c", 'a' + channel_no * 2 + dev_no); 442 | identify_disk(hd); // 获取硬盘参数 443 | if (dev_no != 0) 444 | { // 内核本身的裸硬盘(hd60M.img)不处理 445 | partition_scan(hd, 0); // 扫描该硬盘上的分区 446 | } 447 | p_no = 0, l_no = 0; 448 | dev_no++; 449 | } 450 | dev_no = 0; // 将硬盘驱动器号置0,为下一个channel的两个硬盘初始化。 451 | channel_no++; // 下一个channel 452 | } 453 | 454 | printk("\n all partition info\n"); 455 | /* 打印所有分区信息 */ 456 | list_traversal(&partition_list, partition_info, (int)NULL); 457 | printk("ide_init done\n"); 458 | } 459 | -------------------------------------------------------------------------------- /device/ide.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEVICE_IDE_H 2 | #define __DEVICE_IDE_H 3 | #include "../lib/stdint.h" 4 | #include "../thread/sync.h" 5 | #include "../lib/kernel/list.h" 6 | #include "../lib/kernel/bitmap.h" 7 | 8 | /* 分区结构 */ 9 | struct partition 10 | { 11 | uint32_t start_lba; // 起始扇区 12 | uint32_t sec_cnt; // 扇区数 13 | struct disk *my_disk; // 分区所属的硬盘 14 | struct list_elem part_tag; // 用于队列中的标记 15 | char name[8]; // 分区名称 16 | struct super_block *sb; // 本分区的超级块 17 | struct bitmap block_bitmap; // 块位图 18 | struct bitmap inode_bitmap; // i结点位图 19 | struct list open_inodes; // 本分区打开的i结点队列 20 | }; 21 | 22 | /* 硬盘结构 */ 23 | struct disk 24 | { 25 | char name[8]; // 本硬盘的名称,如sda等 26 | struct ide_channel *my_channel; // 此块硬盘归属于哪个ide通道 27 | uint8_t dev_no; // 本硬盘是主0还是从1 28 | struct partition prim_parts[4]; // 主分区顶多是4个 29 | struct partition logic_parts[8]; // 逻辑分区数量无限,但总得有个支持的上限,那就支持8个 30 | }; 31 | 32 | /* ata通道结构 */ 33 | struct ide_channel 34 | { 35 | char name[8]; // 本ata通道名称, 如ata0,也被叫做ide0. 可以参考bochs配置文件中关于硬盘的配置。 36 | uint16_t port_base; // 本通道的起始端口号 37 | uint8_t irq_no; // 本通道所用的中断号 38 | struct lock lock; 39 | bool expecting_intr; // 向硬盘发完命令后等待来自硬盘的中断 40 | struct semaphore disk_done; // 硬盘处理完成.线程用这个信号量来阻塞自己,由硬盘完成后产生的中断将线程唤醒 41 | struct disk devices[2]; // 一个通道上连接两个硬盘,一主一从 42 | }; 43 | 44 | void intr_hd_handler(uint8_t irq_no); 45 | void ide_init(void); 46 | extern uint8_t channel_cnt; 47 | extern struct ide_channel channels[]; 48 | extern struct list partition_list; 49 | void ide_read(struct disk *hd, uint32_t lba, void *buf, uint32_t sec_cnt); 50 | void ide_write(struct disk *hd, uint32_t lba, void *buf, uint32_t sec_cnt); 51 | #endif 52 | -------------------------------------------------------------------------------- /device/ioqueue.c: -------------------------------------------------------------------------------- 1 | #include "ioqueue.h" 2 | #include "../kernel/interrupt.h" 3 | #include "../kernel/global.h" 4 | #include "../kernel/debug.h" 5 | 6 | /* 初始化io队列ioq */ 7 | void ioqueue_init(struct ioqueue *ioq) 8 | { 9 | lock_init(&ioq->lock); // 初始化io队列的锁 10 | ioq->producer = ioq->consumer = NULL; // 生产者和消费者置空 11 | ioq->head = ioq->tail = 0; // 队列的首尾指针指向缓冲区数组第0个位置 12 | } 13 | 14 | /* 返回pos在缓冲区中的下一个位置值 */ 15 | static int32_t next_pos(int32_t pos) 16 | { 17 | return (pos + 1) % bufsize; 18 | } 19 | 20 | /* 判断队列是否已满 */ 21 | bool ioq_full(struct ioqueue *ioq) 22 | { 23 | ASSERT(intr_get_status() == INTR_OFF); 24 | return next_pos(ioq->head) == ioq->tail; 25 | } 26 | 27 | /* 判断队列是否已空 */ 28 | bool ioq_empty(struct ioqueue *ioq) 29 | { 30 | ASSERT(intr_get_status() == INTR_OFF); 31 | return ioq->head == ioq->tail; 32 | } 33 | 34 | /* 使当前生产者或消费者在此缓冲区上等待 */ 35 | static void ioq_wait(task_struct **waiter) 36 | { 37 | ASSERT(*waiter == NULL && waiter != NULL); 38 | *waiter = running_thread(); 39 | thread_block(TASK_BLOCKED); 40 | } 41 | 42 | /* 唤醒waiter */ 43 | static void wakeup(task_struct **waiter) 44 | { 45 | ASSERT(*waiter != NULL); 46 | thread_unblock(*waiter); 47 | *waiter = NULL; 48 | } 49 | 50 | /* 消费者从ioq队列中获取一个字符 */ 51 | char ioq_getchar(struct ioqueue *ioq) 52 | { 53 | ASSERT(intr_get_status() == INTR_OFF); 54 | 55 | /* 若缓冲区(队列)为空,把消费者ioq->consumer记为当前线程自己, 56 | * 目的是将来生产者往缓冲区里装商品后,生产者知道唤醒哪个消费者, 57 | * 也就是唤醒当前线程自己*/ 58 | while (ioq_empty(ioq)) 59 | { 60 | lock_acquire(&ioq->lock); 61 | ioq_wait(&ioq->consumer); 62 | lock_release(&ioq->lock); 63 | } 64 | 65 | char byte = ioq->buf[ioq->tail]; // 从缓冲区中取出 66 | ioq->tail = next_pos(ioq->tail); // 把读游标移到下一位置 67 | 68 | if (ioq->producer != NULL) 69 | { 70 | wakeup(&ioq->producer); // 唤醒生产者 71 | } 72 | 73 | return byte; 74 | } 75 | 76 | /* 生产者往ioq队列中写入一个字符byte */ 77 | void ioq_putchar(struct ioqueue *ioq, char byte) 78 | { 79 | ASSERT(intr_get_status() == INTR_OFF); 80 | 81 | /* 若缓冲区(队列)已经满了,把生产者ioq->producer记为自己, 82 | * 为的是当缓冲区里的东西被消费者取完后让消费者知道唤醒哪个生产者, 83 | * 也就是唤醒当前线程自己*/ 84 | while (ioq_full(ioq)) 85 | { 86 | lock_acquire(&ioq->lock); 87 | ioq_wait(&ioq->producer); 88 | lock_release(&ioq->lock); 89 | } 90 | ioq->buf[ioq->head] = byte; // 把字节放入缓冲区中 91 | ioq->head = next_pos(ioq->head); // 把写游标移到下一位置 92 | 93 | if (ioq->consumer != NULL) 94 | { 95 | wakeup(&ioq->consumer); // 唤醒消费者 96 | } 97 | } 98 | 99 | uint32_t ioq_length(struct ioqueue* ioq) 100 | { 101 | uint32_t len = 0; 102 | if (ioq->head >= ioq->tail) 103 | { 104 | len = ioq->head - ioq->tail; 105 | } 106 | else 107 | { 108 | len = bufsize - (ioq->tail - ioq->head); 109 | } 110 | return len; 111 | } 112 | -------------------------------------------------------------------------------- /device/ioqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEVICE_IOQUEUE_H_ 2 | #define _DEVICE_IOQUEUE_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../thread/thread.h" 6 | #include "../thread/sync.h" 7 | 8 | #define bufsize 64 9 | 10 | /* 环形队列 */ 11 | struct ioqueue 12 | { 13 | // 生产者消费者问题 14 | struct lock lock; 15 | /* 生产者,缓冲区不满时就继续往里面放数据,否则就睡眠,此项记录哪个生产者在此缓冲区上睡眠。*/ 16 | task_struct *producer; 17 | 18 | /* 消费者,缓冲区不空时就继续从往里面拿数据, 否则就睡眠,此项记录哪个消费者在此缓冲区上睡眠。*/ 19 | task_struct *consumer; 20 | char buf[bufsize]; // 缓冲区大小 21 | int32_t head; // 队首,数据往队首处写入 22 | int32_t tail; // 队尾,数据从队尾处读出 23 | }; 24 | 25 | void ioqueue_init(struct ioqueue *ioq); 26 | bool ioq_full(struct ioqueue *ioq); 27 | char ioq_getchar(struct ioqueue *ioq); 28 | void ioq_putchar(struct ioqueue *ioq, char byte); 29 | uint32_t ioq_length(struct ioqueue* ioq); 30 | bool ioq_empty(struct ioqueue *ioq); 31 | #endif 32 | -------------------------------------------------------------------------------- /device/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "keyboard.h" 2 | #include "../lib/kernel/print.h" 3 | #include "../kernel/interrupt.h" 4 | #include "../lib/kernel/io.h" 5 | #include "../kernel/global.h" 6 | #include "ioqueue.h" 7 | 8 | #define KBD_BUF_PORT 0x60 // 键盘buffer寄存器端口号为0x60 9 | 10 | /* 用转义字符定义部分控制字符 */ 11 | #define esc '\033' // 八进制表示字符,也可以用十六进制'\x1b' 12 | #define backspace '\b' 13 | #define tab '\t' 14 | #define enter '\r' 15 | #define delete '\177' // 八进制表示字符,十六进制为'\x7f' 16 | 17 | /* 以上不可见字符一律定义为0 */ 18 | #define char_invisible 0 19 | #define ctrl_l_char char_invisible 20 | #define ctrl_r_char char_invisible 21 | #define shift_l_char char_invisible 22 | #define shift_r_char char_invisible 23 | #define alt_l_char char_invisible 24 | #define alt_r_char char_invisible 25 | #define caps_lock_char char_invisible 26 | 27 | /* 定义控制字符的通码和断码 */ 28 | #define shift_l_make 0x2a 29 | #define shift_r_make 0x36 30 | #define alt_l_make 0x38 31 | #define alt_r_make 0xe038 32 | #define alt_r_break 0xe0b8 33 | #define ctrl_l_make 0x1d 34 | #define ctrl_r_make 0xe01d 35 | #define ctrl_r_break 0xe09d 36 | #define caps_lock_make 0x3a 37 | 38 | struct ioqueue kbd_buf; 39 | 40 | /* 定义以下变量记录相应键是否按下的状态, 41 | * ext_scancode用于记录makecode是否以0xe0开头 */ 42 | static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode; 43 | 44 | /* 以通码make_code为索引的二维数组 */ 45 | static char keymap[][2] = 46 | { 47 | /* 扫描码 未与shift组合 与shift组合*/ 48 | /* ---------------------------------- */ 49 | /* 0x00 */ {0, 0}, 50 | /* 0x01 */ {esc, esc}, 51 | /* 0x02 */ {'1', '!'}, 52 | /* 0x03 */ {'2', '@'}, 53 | /* 0x04 */ {'3', '#'}, 54 | /* 0x05 */ {'4', '$'}, 55 | /* 0x06 */ {'5', '%'}, 56 | /* 0x07 */ {'6', '^'}, 57 | /* 0x08 */ {'7', '&'}, 58 | /* 0x09 */ {'8', '*'}, 59 | /* 0x0A */ {'9', '('}, 60 | /* 0x0B */ {'0', ')'}, 61 | /* 0x0C */ {'-', '_'}, 62 | /* 0x0D */ {'=', '+'}, 63 | /* 0x0E */ {backspace, backspace}, 64 | /* 0x0F */ {tab, tab}, 65 | /* 0x10 */ {'q', 'Q'}, 66 | /* 0x11 */ {'w', 'W'}, 67 | /* 0x12 */ {'e', 'E'}, 68 | /* 0x13 */ {'r', 'R'}, 69 | /* 0x14 */ {'t', 'T'}, 70 | /* 0x15 */ {'y', 'Y'}, 71 | /* 0x16 */ {'u', 'U'}, 72 | /* 0x17 */ {'i', 'I'}, 73 | /* 0x18 */ {'o', 'O'}, 74 | /* 0x19 */ {'p', 'P'}, 75 | /* 0x1A */ {'[', '{'}, 76 | /* 0x1B */ {']', '}'}, 77 | /* 0x1C */ {enter, enter}, 78 | /* 0x1D */ {ctrl_l_char, ctrl_l_char}, 79 | /* 0x1E */ {'a', 'A'}, 80 | /* 0x1F */ {'s', 'S'}, 81 | /* 0x20 */ {'d', 'D'}, 82 | /* 0x21 */ {'f', 'F'}, 83 | /* 0x22 */ {'g', 'G'}, 84 | /* 0x23 */ {'h', 'H'}, 85 | /* 0x24 */ {'j', 'J'}, 86 | /* 0x25 */ {'k', 'K'}, 87 | /* 0x26 */ {'l', 'L'}, 88 | /* 0x27 */ {';', ':'}, 89 | /* 0x28 */ {'\'', '"'}, 90 | /* 0x29 */ {'`', '~'}, 91 | /* 0x2A */ {shift_l_char, shift_l_char}, 92 | /* 0x2B */ {'\\', '|'}, 93 | /* 0x2C */ {'z', 'Z'}, 94 | /* 0x2D */ {'x', 'X'}, 95 | /* 0x2E */ {'c', 'C'}, 96 | /* 0x2F */ {'v', 'V'}, 97 | /* 0x30 */ {'b', 'B'}, 98 | /* 0x31 */ {'n', 'N'}, 99 | /* 0x32 */ {'m', 'M'}, 100 | /* 0x33 */ {',', '<'}, 101 | /* 0x34 */ {'.', '>'}, 102 | /* 0x35 */ {'/', '?'}, 103 | /* 0x36 */ {shift_r_char, shift_r_char}, 104 | /* 0x37 */ {'*', '*'}, 105 | /* 0x38 */ {alt_l_char, alt_l_char}, 106 | /* 0x39 */ {' ', ' '}, 107 | /* 0x3A */ {caps_lock_char, caps_lock_char} 108 | /*其它按键暂不处理*/ 109 | }; 110 | 111 | /* 键盘中断处理程序 */ 112 | static void intr_keyboard_handler(void) 113 | { 114 | 115 | /* 这次中断发生前的上一次中断,以下任意三个键是否有按下 */ 116 | bool ctrl_down_last = ctrl_status; 117 | bool shift_down_last = shift_status; 118 | bool caps_lock_last = caps_lock_status; 119 | 120 | bool break_code; 121 | uint16_t scancode = inb(KBD_BUF_PORT); 122 | 123 | /* 若扫描码是e0开头的,表示此键的按下将产生多个扫描码, 124 | * 所以马上结束此次中断处理函数,等待下一个扫描码进来*/ 125 | if (scancode == 0xe0) 126 | { 127 | ext_scancode = true; // 打开e0标记 128 | return; 129 | } 130 | 131 | /* 如果上次是以0xe0开头,将扫描码合并 */ 132 | if (ext_scancode) 133 | { 134 | scancode = ((0xe000) | scancode); 135 | ext_scancode = false; // 关闭e0标记 136 | } 137 | 138 | break_code = ((scancode & 0x0080) != 0); // 获取break_code 139 | 140 | if (break_code) 141 | { 142 | // 若是断码break_code(按键弹起时产生的扫描码) 143 | 144 | /* 由于ctrl_r 和alt_r的make_code和break_code都是两字节,所以可用下面的方法取make_code,多字节的扫描码暂不处理 */ 145 | uint16_t make_code = (scancode &= 0xff7f); // 得到其make_code(按键按下时产生的扫描码) 146 | 147 | /* 若是任意以下三个键弹起了,将状态置为false */ 148 | if (make_code == ctrl_l_make || make_code == ctrl_r_make) 149 | { 150 | ctrl_status = false; 151 | } 152 | else if (make_code == shift_l_make || make_code == shift_r_make) 153 | { 154 | shift_status = false; 155 | } 156 | else if (make_code == alt_l_make || make_code == alt_r_make) 157 | { 158 | alt_status = false; 159 | } /* 由于caps_lock不是弹起后关闭,所以需要单独处理 */ 160 | 161 | return; // 直接返回结束此次中断处理程序 162 | } 163 | /* 若为通码,只处理数组中定义的键以及alt_right和ctrl键,全是make_code */ 164 | else if ((scancode > 0x00 && scancode < 0x3b) || 165 | (scancode == alt_r_make) || 166 | (scancode == ctrl_r_make)) 167 | { 168 | bool shift = false; // 判断是否与shift组合,用来在一维数组中索引对应的字符 169 | if ((scancode < 0x0e) || (scancode == 0x29) || 170 | (scancode == 0x1a) || (scancode == 0x1b) || 171 | (scancode == 0x2b) || (scancode == 0x27) || 172 | (scancode == 0x28) || (scancode == 0x33) || 173 | (scancode == 0x34) || (scancode == 0x35)) 174 | { 175 | /****** 代表两个字母的键 ******** 176 | 0x0e 数字'0'~'9',字符'-',字符'=' 177 | 0x29 字符'`' 178 | 0x1a 字符'[' 179 | 0x1b 字符']' 180 | 0x2b 字符'\\' 181 | 0x27 字符';' 182 | 0x28 字符'\'' 183 | 0x33 字符',' 184 | 0x34 字符'.' 185 | 0x35 字符'/' 186 | *******************************/ 187 | if (shift_down_last) 188 | { // 如果同时按下了shift键 189 | shift = true; 190 | } 191 | } 192 | else 193 | { // 默认为字母键 194 | if (shift_down_last && caps_lock_last) 195 | { // 如果shift和capslock同时按下 196 | shift = false; 197 | } 198 | else if (shift_down_last || caps_lock_last) 199 | { // 如果shift和capslock任意被按下 200 | shift = true; 201 | } 202 | else 203 | { 204 | shift = false; 205 | } 206 | } 207 | 208 | uint8_t index = (scancode &= 0x00ff); // 将扫描码的高字节置0,主要是针对高字节是e0的扫描码. 209 | char cur_char = keymap[index][shift]; // 在数组中找到对应的字符 210 | 211 | /* 只处理ascii码不为0的键 */ 212 | if (cur_char) 213 | { 214 | if(!ioq_full(&kbd_buf)) 215 | { 216 | put_char(cur_char); 217 | ioq_putchar(&kbd_buf, cur_char); 218 | } 219 | 220 | return; 221 | } 222 | 223 | /* 记录本次是否按下了下面几类控制键之一,供下次键入时判断组合键 */ 224 | if (scancode == ctrl_l_make || scancode == ctrl_r_make) 225 | { 226 | ctrl_status = true; 227 | } 228 | else if (scancode == shift_l_make || scancode == shift_r_make) 229 | { 230 | shift_status = true; 231 | } 232 | else if (scancode == alt_l_make || scancode == alt_r_make) 233 | { 234 | alt_status = true; 235 | } 236 | else if (scancode == caps_lock_make) 237 | { 238 | /* 不管之前是否有按下caps_lock键,当再次按下时则状态取反, 239 | * 即:已经开启时,再按下同样的键是关闭。关闭时按下表示开启。*/ 240 | caps_lock_status = !caps_lock_status; 241 | } 242 | } 243 | else 244 | { 245 | put_str("unknown key\n"); 246 | } 247 | } 248 | 249 | /* 键盘初始化 */ 250 | void keyboard_init() 251 | { 252 | put_str("keyboard init start\n"); 253 | ioqueue_init(&kbd_buf); 254 | register_handler(0x21, intr_keyboard_handler); 255 | put_str("keyboard init done\n"); 256 | } 257 | -------------------------------------------------------------------------------- /device/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEVICE_KEYBOARD_H_ 2 | #define _DEVICE_KEYBOARD_H_ 3 | 4 | void keyboard_init(); 5 | 6 | extern struct ioqueue kbd_buf; 7 | 8 | #endif // !_DEVICE_KEYBOARD_H_ -------------------------------------------------------------------------------- /device/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "../lib/kernel/io.h" 3 | #include "../lib/kernel/print.h" 4 | #include "../thread/thread.h" 5 | #include "../kernel/debug.h" 6 | #include "../kernel/interrupt.h" 7 | 8 | 9 | #define IRQ0_FREQUENCY 100 10 | #define INPUT_FREQUENCY 1193180 11 | #define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY 12 | #define CONTRER0_PORT 0x40 13 | #define COUNTER0_NO 0 14 | #define COUNTER_MODE 2 15 | #define READ_WRITE_LATCH 3 16 | #define PIT_CONTROL_PORT 0x43 17 | 18 | #define mil_seconds_per_intr (1000 / IRQ0_FREQUENCY) 19 | 20 | uint32_t ticks; 21 | 22 | static void intr_timer_handler() 23 | { 24 | task_struct *cur_thread = running_thread(); 25 | 26 | ASSERT(cur_thread->stack_magic == 0x19971234); 27 | 28 | cur_thread->elapsed_ticks++; 29 | 30 | ticks++; 31 | 32 | if(cur_thread->ticks == 0) 33 | { 34 | schedule(); 35 | } 36 | else 37 | { 38 | cur_thread->ticks--; 39 | } 40 | } 41 | 42 | static void frequency_set(uint8_t counter_port, uint8_t counter_no, uint8_t rwl, uint8_t counter_mode, uint16_t counter_value) 43 | { 44 | /* 往控制字寄存器端口0x43中写入控制字 */ 45 | outb(PIT_CONTROL_PORT, (uint8_t)(counter_no << 6 | rwl << 4 | counter_mode << 1)); 46 | /* 先写入counter_value的低8位 */ 47 | outb(counter_port, (uint8_t)counter_value); 48 | /* 再写入counter_value的高8位 */ 49 | outb(counter_port, (uint8_t)counter_value >> 8); 50 | } 51 | 52 | static void ticks_to_sleep(uint32_t sleep_ticks) 53 | { 54 | uint32_t start_tick = ticks; 55 | 56 | while (ticks - start_tick < sleep_ticks) 57 | { 58 | thread_yield(); 59 | } 60 | } 61 | 62 | void mtime_sleep(uint32_t m_seconds) 63 | { 64 | uint32_t sleep_ticks = DIV_ROUND_UP(m_seconds, mil_seconds_per_intr); 65 | ASSERT(sleep_ticks > 0); 66 | ticks_to_sleep(sleep_ticks); 67 | } 68 | 69 | /* 初始化PIT8253 */ 70 | void timer_init() 71 | { 72 | put_str("timer_init start\n"); 73 | /* 设置8253的定时周期,也就是发中断的周期 */ 74 | frequency_set(CONTRER0_PORT, COUNTER0_NO, READ_WRITE_LATCH, COUNTER_MODE, COUNTER0_VALUE); 75 | register_handler(0x20, intr_timer_handler); 76 | put_str("timer_init done\n"); 77 | } 78 | -------------------------------------------------------------------------------- /device/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEVICE_TIMER_H_ 2 | #define _DEVICE_TIMER_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | void timer_init(); 7 | void mtime_sleep(uint32_t m_seconds); 8 | 9 | #endif //!_DEVICE_TIMER_H_ -------------------------------------------------------------------------------- /fs/dir.c: -------------------------------------------------------------------------------- 1 | #include "dir.h" 2 | #include "../device/ide.h" 3 | #include "super_block.h" 4 | #include "../lib/kernel/stdio-kernel.h" 5 | #include "../lib/string.h" 6 | #include "../kernel/global.h" 7 | #include "../kernel/debug.h" 8 | #include "file.h" 9 | #include "fs.h" 10 | #include "inode.h" 11 | #include "../kernel/memory.h" 12 | 13 | struct dir root_dir; // 根目录 14 | 15 | /* 打开根目录 */ 16 | void open_root_dir(struct partition *part) 17 | { 18 | root_dir.inode = inode_open(part, part->sb->root_inode_no); 19 | root_dir.dir_pos = 0; 20 | } 21 | 22 | /* 在分区part上打开i结点为inode_no的目录并返回目录指针 */ 23 | struct dir *dir_open(struct partition *part, uint32_t inode_no) 24 | { 25 | struct dir *pdir = (struct dir *)sys_malloc(sizeof(struct dir)); 26 | pdir->inode = inode_open(part, inode_no); 27 | pdir->dir_pos = 0; 28 | return pdir; 29 | } 30 | 31 | /* 在part分区内的pdir目录内寻找名为name的文件或目录, 32 | * 找到后返回true并将其目录项存入dir_e,否则返回false */ 33 | bool search_dir_entry(struct partition *part, struct dir *pdir, 34 | const char *name, struct dir_entry *dir_e) 35 | { 36 | uint32_t block_cnt = 140; // 12个直接块+128个一级间接块=140块 37 | 38 | /* 12个直接块大小+128个间接块,共560字节 */ 39 | uint32_t *all_blocks = (uint32_t *)sys_malloc(48 + 512); 40 | if (all_blocks == NULL) 41 | { 42 | printk("search_dir_entry: sys_malloc for all_blocks failed"); 43 | return false; 44 | } 45 | 46 | uint32_t block_idx = 0; 47 | while (block_idx < 12) 48 | { 49 | all_blocks[block_idx] = pdir->inode->i_sectors[block_idx]; 50 | block_idx++; 51 | } 52 | block_idx = 0; 53 | 54 | if (pdir->inode->i_sectors[12] != 0) 55 | { // 若含有一级间接块表 56 | ide_read(part->my_disk, pdir->inode->i_sectors[12], all_blocks + 12, 1); 57 | } 58 | /* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */ 59 | 60 | /* 写目录项的时候已保证目录项不跨扇区, 61 | * 这样读目录项时容易处理, 只申请容纳1个扇区的内存 */ 62 | uint8_t *buf = (uint8_t *)sys_malloc(SECTOR_SIZE); 63 | struct dir_entry *p_de = (struct dir_entry *)buf; // p_de为指向目录项的指针,值为buf起始地址 64 | uint32_t dir_entry_size = part->sb->dir_entry_size; 65 | uint32_t dir_entry_cnt = SECTOR_SIZE / dir_entry_size; // 1扇区内可容纳的目录项个数 66 | 67 | /* 开始在所有块中查找目录项 */ 68 | while (block_idx < block_cnt) 69 | { 70 | /* 块地址为0时表示该块中无数据,继续在其它块中找 */ 71 | if (all_blocks[block_idx] == 0) 72 | { 73 | block_idx++; 74 | continue; 75 | } 76 | ide_read(part->my_disk, all_blocks[block_idx], buf, 1); 77 | 78 | uint32_t dir_entry_idx = 0; 79 | /* 遍历扇区中所有目录项 */ 80 | while (dir_entry_idx < dir_entry_cnt) 81 | { 82 | /* 若找到了,就直接复制整个目录项 */ 83 | if (!strcmp(p_de->filename, name)) 84 | { 85 | memcpy(dir_e, p_de, dir_entry_size); 86 | sys_free(buf); 87 | sys_free(all_blocks); 88 | return true; 89 | } 90 | dir_entry_idx++; 91 | p_de++; 92 | } 93 | block_idx++; 94 | p_de = (struct dir_entry *)buf; // 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向为buf 95 | memset(buf, 0, SECTOR_SIZE); // 将buf清0,下次再用 96 | } 97 | sys_free(buf); 98 | sys_free(all_blocks); 99 | return false; 100 | } 101 | 102 | /* 关闭目录 */ 103 | void dir_close(struct dir *dir) 104 | { 105 | /*************  根目录不能关闭 *************** 106 | *1 根目录自打开后就不应该关闭,否则还需要再次open_root_dir(); 107 | *2 root_dir所在的内存是低端1M之内,并非在堆中,free会出问题 */ 108 | if (dir == &root_dir) 109 | { 110 | /* 不做任何处理直接返回*/ 111 | return; 112 | } 113 | inode_close(dir->inode); 114 | sys_free(dir); 115 | } 116 | 117 | /* 在内存中初始化目录项p_de */ 118 | void create_dir_entry(char *filename, uint32_t inode_no, uint8_t file_type, struct dir_entry *p_de) 119 | { 120 | ASSERT(strlen(filename) <= MAX_FILE_NAME_LEN); 121 | 122 | /* 初始化目录项 */ 123 | memcpy(p_de->filename, filename, strlen(filename)); 124 | p_de->i_no = inode_no; 125 | p_de->f_type = file_type; 126 | } 127 | 128 | /* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */ 129 | bool sync_dir_entry(struct dir *parent_dir, struct dir_entry *p_de, void *io_buf) 130 | { 131 | struct inode *dir_inode = parent_dir->inode; 132 | uint32_t dir_size = dir_inode->i_size; 133 | uint32_t dir_entry_size = cur_part->sb->dir_entry_size; 134 | 135 | ASSERT(dir_size % dir_entry_size == 0); // dir_size应该是dir_entry_size的整数倍 136 | 137 | uint32_t dir_entrys_per_sec = (512 / dir_entry_size); // 每扇区最大的目录项数目 138 | int32_t block_lba = -1; 139 | 140 | /* 将该目录的所有扇区地址(12个直接块+ 128个间接块)存入all_blocks */ 141 | uint8_t block_idx = 0; 142 | uint32_t all_blocks[140] = {0}; // all_blocks保存目录所有的块 143 | 144 | /* 将12个直接块存入all_blocks */ 145 | while (block_idx < 12) 146 | { 147 | all_blocks[block_idx] = dir_inode->i_sectors[block_idx]; 148 | block_idx++; 149 | } 150 | 151 | struct dir_entry *dir_e = (struct dir_entry *)io_buf; // dir_e用来在io_buf中遍历目录项 152 | int32_t block_bitmap_idx = -1; 153 | 154 | /* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空闲位, 155 | * 在不超过文件大小的情况下申请新扇区来存储新目录项 */ 156 | block_idx = 0; 157 | while (block_idx < 140) 158 | { // 文件(包括目录)最大支持12个直接块+128个间接块=140个块 159 | block_bitmap_idx = -1; 160 | if (all_blocks[block_idx] == 0) 161 | { // 在三种情况下分配块 162 | block_lba = block_bitmap_alloc(cur_part); 163 | if (block_lba == -1) 164 | { 165 | printk("alloc block bitmap for sync_dir_entry failed\n"); 166 | return false; 167 | } 168 | 169 | block_bitmap_idx = block_lba - cur_part->sb->data_start_lba; 170 | ASSERT(block_bitmap_idx != -1); 171 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 172 | 173 | block_bitmap_idx = -1; 174 | if (block_idx < 12) 175 | { // 若是直接块 176 | dir_inode->i_sectors[block_idx] = all_blocks[block_idx] = block_lba; 177 | } 178 | else if (block_idx == 12) 179 | { // 若是尚未分配一级间接块表(block_idx等于12表示第0个间接块地址为0) 180 | dir_inode->i_sectors[12] = block_lba; // 将上面分配的块做为一级间接块表地址 181 | block_lba = -1; 182 | block_lba = block_bitmap_alloc(cur_part); // 再分配一个块做为第0个间接块 183 | if (block_lba == -1) 184 | { 185 | block_bitmap_idx = dir_inode->i_sectors[12] - cur_part->sb->data_start_lba; 186 | bitmap_set(&cur_part->block_bitmap, block_bitmap_idx, 0); 187 | dir_inode->i_sectors[12] = 0; 188 | printk("alloc block bitmap for sync_dir_entry failed\n"); 189 | return false; 190 | } 191 | 192 | /* 每分配一个块就同步一次block_bitmap */ 193 | block_bitmap_idx = block_lba - cur_part->sb->data_start_lba; 194 | ASSERT(block_bitmap_idx != -1); 195 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 196 | 197 | all_blocks[12] = block_lba; 198 | /* 把新分配的第0个间接块地址写入一级间接块表 */ 199 | ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); 200 | } 201 | else 202 | { // 若是间接块未分配 203 | all_blocks[block_idx] = block_lba; 204 | /* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */ 205 | ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); 206 | } 207 | /* 再将新目录项p_de写入新分配的间接块 */ 208 | memset(io_buf, 0, 512); 209 | memcpy(io_buf, p_de, dir_entry_size); 210 | ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1); 211 | dir_inode->i_size += dir_entry_size; 212 | return true; 213 | } 214 | 215 | /* 若第block_idx块已存在,将其读进内存,然后在该块中查找空目录项 */ 216 | ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1); 217 | /* 在扇区内查找空目录项 */ 218 | uint8_t dir_entry_idx = 0; 219 | while (dir_entry_idx < dir_entrys_per_sec) 220 | { 221 | if ((dir_e + dir_entry_idx)->f_type == FT_UNKNOWN) 222 | { // FT_UNKNOWN为0,无论是初始化或是删除文件后,都会将f_type置为FT_UNKNOWN. 223 | memcpy(dir_e + dir_entry_idx, p_de, dir_entry_size); 224 | ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1); 225 | 226 | dir_inode->i_size += dir_entry_size; 227 | return true; 228 | } 229 | dir_entry_idx++; 230 | } 231 | block_idx++; 232 | } 233 | printk("directory is full!\n"); 234 | return false; 235 | } 236 | 237 | /* 把分区part目录pdir中编号为inode_no的目录项删除 */ 238 | bool delete_dir_entry(struct partition *part, struct dir *pdir, uint32_t inode_no, void *io_buf) 239 | { 240 | struct inode *dir_inode = pdir->inode; 241 | uint32_t block_idx = 0, all_blocks[140] = {0}; 242 | /* 收集目录全部块地址 */ 243 | while (block_idx < 12) 244 | { 245 | all_blocks[block_idx] = dir_inode->i_sectors[block_idx]; 246 | block_idx++; 247 | } 248 | if (dir_inode->i_sectors[12]) 249 | { 250 | ide_read(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); 251 | } 252 | 253 | /* 目录项在存储时保证不会跨扇区 */ 254 | uint32_t dir_entry_size = part->sb->dir_entry_size; 255 | uint32_t dir_entrys_per_sec = (SECTOR_SIZE / dir_entry_size); // 每扇区最大的目录项数目 256 | struct dir_entry *dir_e = (struct dir_entry *)io_buf; 257 | struct dir_entry *dir_entry_found = NULL; 258 | uint8_t dir_entry_idx, dir_entry_cnt; 259 | bool is_dir_first_block = false; // 目录的第1个块 260 | 261 | /* 遍历所有块,寻找目录项 */ 262 | block_idx = 0; 263 | while (block_idx < 140) 264 | { 265 | is_dir_first_block = false; 266 | if (all_blocks[block_idx] == 0) 267 | { 268 | block_idx++; 269 | continue; 270 | } 271 | dir_entry_idx = dir_entry_cnt = 0; 272 | memset(io_buf, 0, SECTOR_SIZE); 273 | /* 读取扇区,获得目录项 */ 274 | ide_read(part->my_disk, all_blocks[block_idx], io_buf, 1); 275 | 276 | /* 遍历所有的目录项,统计该扇区的目录项数量及是否有待删除的目录项 */ 277 | while (dir_entry_idx < dir_entrys_per_sec) 278 | { 279 | if ((dir_e + dir_entry_idx)->f_type != FT_UNKNOWN) 280 | { 281 | if (!strcmp((dir_e + dir_entry_idx)->filename, ".")) 282 | { 283 | is_dir_first_block = true; 284 | } 285 | else if (strcmp((dir_e + dir_entry_idx)->filename, ".") && 286 | strcmp((dir_e + dir_entry_idx)->filename, "..")) 287 | { 288 | dir_entry_cnt++; // 统计此扇区内的目录项个数,用来判断删除目录项后是否回收该扇区 289 | if ((dir_e + dir_entry_idx)->i_no == inode_no) 290 | { // 如果找到此i结点,就将其记录在dir_entry_found 291 | ASSERT(dir_entry_found == NULL); // 确保目录中只有一个编号为inode_no的inode,找到一次后dir_entry_found就不再是NULL 292 | dir_entry_found = dir_e + dir_entry_idx; 293 | /* 找到后也继续遍历,统计总共的目录项数 */ 294 | } 295 | } 296 | } 297 | dir_entry_idx++; 298 | } 299 | if (dir_entry_found == NULL) 300 | { 301 | block_idx++; 302 | continue; 303 | } 304 | 305 | /* 在此扇区中找到目录项后,清除该目录项并判断是否回收扇区,随后退出循环直接返回 */ 306 | ASSERT(dir_entry_cnt >= 1); 307 | /* 除目录第1个扇区外,若该扇区上只有该目录项自己,则将整个扇区回收 */ 308 | if (dir_entry_cnt == 1 && !is_dir_first_block) 309 | { 310 | /* a 在块位图中回收该块 */ 311 | uint32_t block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba; 312 | bitmap_set(&part->block_bitmap, block_bitmap_idx, 0); 313 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 314 | 315 | /* b 将块地址从数组i_sectors或索引表中去掉 */ 316 | if (block_idx < 12) 317 | { 318 | dir_inode->i_sectors[block_idx] = 0; 319 | } 320 | else 321 | { // 在一级间接索引表中擦除该间接块地址 322 | /*先判断一级间接索引表中间接块的数量,如果仅有这1个间接块,连同间接索引表所在的块一同回收 */ 323 | uint32_t indirect_blocks = 0; 324 | uint32_t indirect_block_idx = 12; 325 | while (indirect_block_idx < 140) 326 | { 327 | if (all_blocks[indirect_block_idx] != 0) 328 | { 329 | indirect_blocks++; 330 | } 331 | } 332 | ASSERT(indirect_blocks >= 1); // 包括当前间接块 333 | 334 | if (indirect_blocks > 1) 335 | { // 间接索引表中还包括其它间接块,仅在索引表中擦除当前这个间接块地址 336 | all_blocks[block_idx] = 0; 337 | ide_write(part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1); 338 | } 339 | else 340 | { // 间接索引表中就当前这1个间接块,直接把间接索引表所在的块回收,然后擦除间接索引表块地址 341 | /* 回收间接索引表所在的块 */ 342 | block_bitmap_idx = dir_inode->i_sectors[12] - part->sb->data_start_lba; 343 | bitmap_set(&part->block_bitmap, block_bitmap_idx, 0); 344 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 345 | 346 | /* 将间接索引表地址清0 */ 347 | dir_inode->i_sectors[12] = 0; 348 | } 349 | } 350 | } 351 | else 352 | { // 仅将该目录项清空 353 | memset(dir_entry_found, 0, dir_entry_size); 354 | ide_write(part->my_disk, all_blocks[block_idx], io_buf, 1); 355 | } 356 | 357 | /* 更新i结点信息并同步到硬盘 */ 358 | ASSERT(dir_inode->i_size >= dir_entry_size); 359 | dir_inode->i_size -= dir_entry_size; 360 | memset(io_buf, 0, SECTOR_SIZE * 2); 361 | inode_sync(part, dir_inode, io_buf); 362 | 363 | return true; 364 | } 365 | /* 所有块中未找到则返回false,若出现这种情况应该是serarch_file出错了 */ 366 | return false; 367 | } 368 | -------------------------------------------------------------------------------- /fs/dir.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_DIR_H_ 2 | #define _FS_DIR_H_ 3 | 4 | #include "inode.h" 5 | #include "fs.h" 6 | #include "../lib/stdint.h" 7 | #include "../device/ide.h" 8 | #include "../kernel/global.h" 9 | 10 | #define MAX_FILE_NAME_LEN 16 11 | 12 | struct dir 13 | { 14 | struct inode *inode; 15 | uint32_t dir_pos; 16 | uint8_t dir_buf[512]; 17 | }; 18 | 19 | 20 | struct dir_entry 21 | { 22 | char filename[MAX_FILE_NAME_LEN]; 23 | uint32_t i_no; 24 | enum file_types f_type; 25 | }; 26 | 27 | extern struct dir root_dir; // 根目录 28 | 29 | void open_root_dir(struct partition* part); 30 | struct dir* dir_open(struct partition* part, uint32_t inode_no); 31 | void dir_close(struct dir* dir); 32 | bool search_dir_entry(struct partition* part, struct dir* pdir, const char* name, struct dir_entry* dir_e); 33 | void create_dir_entry(char* filename, uint32_t inode_no, uint8_t file_type, struct dir_entry* p_de); 34 | bool sync_dir_entry(struct dir* parent_dir, struct dir_entry* p_de, void* io_buf); 35 | bool delete_dir_entry(struct partition *part, struct dir *pdir, uint32_t inode_no, void *io_buf); 36 | 37 | #endif // !_FS_DIR_H_ -------------------------------------------------------------------------------- /fs/file.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_FILE_H_ 2 | #define _FS_FILE_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../device/ide.h" 6 | #include "dir.h" 7 | #include "../kernel/global.h" 8 | 9 | /* 文件结构 */ 10 | struct file 11 | { 12 | uint32_t fd_pos; // 记录当前文件操作的偏移地址,以0为起始,最大为文件大小-1 13 | uint32_t fd_flag; 14 | struct inode *fd_inode; 15 | }; 16 | 17 | /* 标准输入输出描述符 */ 18 | enum std_fd 19 | { 20 | stdin_no, // 0 标准输入 21 | stdout_no, // 1 标准输出 22 | stderr_no // 2 标准错误 23 | }; 24 | 25 | /* 位图类型 */ 26 | enum bitmap_type 27 | { 28 | INODE_BITMAP, // inode位图 29 | BLOCK_BITMAP // 块位图 30 | }; 31 | 32 | #define MAX_FILE_OPEN 32 // 系统可打开的最大文件数 33 | 34 | extern struct file file_table[MAX_FILE_OPEN]; 35 | int32_t inode_bitmap_alloc(struct partition *part); 36 | int32_t block_bitmap_alloc(struct partition *part); 37 | int32_t file_create(struct dir *parent_dir, char *filename, uint8_t flag); 38 | void bitmap_sync(struct partition *part, uint32_t bit_idx, uint8_t btmp); 39 | int32_t get_free_slot_in_global(); 40 | int32_t pcb_fd_install(int32_t globa_fd_idx); 41 | int32_t file_open(uint32_t inode_no, uint8_t flag); 42 | int32_t file_close(struct file *file); 43 | int32_t file_write(struct file *file, const void *buf, uint32_t count); 44 | int32_t file_read(struct file *file, void *buf, uint32_t count); 45 | #endif // !_FS_FILE_H_ 46 | -------------------------------------------------------------------------------- /fs/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_FS_H_ 2 | #define _FS_FS_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../device/ide.h" 6 | 7 | #define MAX_FILES_PER_PART 4096 // 每个分区所支持最大创建的文件数 8 | #define BITS_PER_SECTOR 4096 // 每扇区的位数 9 | #define SECTOR_SIZE 512 // 扇区字节大小 10 | #define BLOCK_SIZE SECTOR_SIZE // 块字节大小 11 | 12 | #define MAX_PATH_LEN 512 // 路径最大长度 13 | 14 | 15 | /* 文件类型 */ 16 | enum file_types 17 | { 18 | FT_UNKNOWN, // 不支持的文件类型 19 | FT_REGULAR, // 普通文件 20 | FT_DIRECTORY // 目录 21 | }; 22 | 23 | /* 打开文件的选项 */ 24 | enum oflags 25 | { 26 | O_RDONLY, // 只读 27 | O_WRONLY, // 只写 28 | O_RDWR, // 读写 29 | O_CREAT = 4 // 创建 30 | }; 31 | 32 | // 文件读写位置偏移量 33 | enum whence 34 | { 35 | SEEK_SET = 1, 36 | SEEK_CUR, 37 | SEEK_END 38 | }; 39 | 40 | /* 用来记录查找文件过程中已找到的上级路径,也就是查找文件过程中"走过的地方" */ 41 | struct path_search_record 42 | { 43 | char searched_path[MAX_PATH_LEN]; // 查找过程中的父路径 44 | struct dir *parent_dir; // 文件或目录所在的直接父目录 45 | enum file_types file_type; // 找到的是普通文件还是目录,找不到将为未知类型(FT_UNKNOWN) 46 | }; 47 | 48 | extern struct partition* cur_part; 49 | 50 | void filesys_init(); 51 | int32_t path_depth_cnt(char* pathname); 52 | int32_t sys_open(const char* pathname, uint8_t flags); 53 | int32_t sys_close(int32_t fd); 54 | int32_t sys_write(int32_t fd, const void *buf, uint32_t count); 55 | int32_t sys_read(int32_t fd, void *buf, uint32_t count); 56 | int32_t sys_lseek(int32_t fd, int32_t offset, uint8_t whence); 57 | int32_t sys_unlink(const char *pathname); 58 | #endif //!_FS_FS_H_ -------------------------------------------------------------------------------- /fs/inode.c: -------------------------------------------------------------------------------- 1 | #include "inode.h" 2 | #include "super_block.h" 3 | #include "file.h" 4 | #include "fs.h" 5 | #include "../kernel/global.h" 6 | #include "../lib/stdint.h" 7 | #include "../device/ide.h" 8 | #include "../kernel/debug.h" 9 | #include "../lib/string.h" 10 | #include "../kernel/interrupt.h" 11 | 12 | struct inode_position 13 | { 14 | bool two_sec; // inode是否跨扇区 15 | uint32_t sec_lba; // inode所在的扇区号 16 | uint32_t off_size; // 在扇区内的偏移 17 | }; 18 | 19 | // 获取inode所在的扇区和扇区内的偏移量 20 | static void inode_locate(struct partition *part, uint32_t inode_no, struct inode_position *inode_pos) 21 | { 22 | ASSERT(inode_no < 4096); 23 | uint32_t inode_table_lba = part->sb->inode_table_lba; 24 | 25 | uint32_t inode_size = sizeof(struct inode); 26 | uint32_t off_size = inode_no * inode_size; 27 | 28 | uint32_t off_sec = off_size / 512; 29 | uint32_t off_size_in_sec = off_size % 512; 30 | 31 | /* 判断此i结点是否跨越2个扇区 */ 32 | uint32_t left_in_sec = 512 - off_size_in_sec; 33 | 34 | if (left_in_sec < inode_size) 35 | { 36 | // 若扇区内剩下的空间不足以容纳一个inode,必然是I结点跨越了2个扇区 37 | inode_pos->two_sec = true; 38 | } 39 | else 40 | { 41 | // 否则,所查找的inode未跨扇区 42 | inode_pos->two_sec = false; 43 | } 44 | inode_pos->sec_lba = inode_table_lba + off_sec; 45 | inode_pos->off_size = off_size_in_sec; 46 | } 47 | 48 | /* 将inode写入到分区part */ 49 | void inode_sync(struct partition *part, struct inode *inode, void *io_buf) 50 | { 51 | // io_buf是用于硬盘io的缓冲区 52 | uint8_t inode_no = inode->i_no; 53 | struct inode_position inode_pos; 54 | inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos 55 | ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt)); 56 | 57 | /* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的, 58 | * 它们只在内存中记录链表位置和被多少进程共享 */ 59 | struct inode pure_inode; 60 | memcpy(&pure_inode, inode, sizeof(struct inode)); 61 | 62 | /* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */ 63 | pure_inode.i_open_cnts = 0; 64 | pure_inode.write_deny = false; // 置为false,以保证在硬盘中读出时为可写 65 | pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL; 66 | 67 | char *inode_buf = (char *)io_buf; 68 | if (inode_pos.two_sec) 69 | { // 若是跨了两个扇区,就要读出两个扇区再写入两个扇区 70 | /* 读写硬盘是以扇区为单位,若写入的数据小于一扇区,要将原硬盘上的内容先读出来再和新数据拼成一扇区后再写入 */ 71 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2); // inode在format中写入硬盘时是连续写入的,所以读入2块扇区 72 | 73 | /* 开始将待写入的inode拼入到这2个扇区中的相应位置 */ 74 | memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode)); 75 | 76 | /* 将拼接好的数据再写入磁盘 */ 77 | ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2); 78 | } 79 | else 80 | { // 若只是一个扇区 81 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1); 82 | memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode)); 83 | ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1); 84 | } 85 | } 86 | 87 | /* 根据i结点号返回相应的i结点 */ 88 | struct inode *inode_open(struct partition *part, uint32_t inode_no) 89 | { 90 | /* 先在已打开inode链表中找inode,此链表是为提速创建的缓冲区 */ 91 | struct list_elem *elem = part->open_inodes.head.next; 92 | struct inode *inode_found; 93 | while (elem != &part->open_inodes.tail) 94 | { 95 | inode_found = elem2entry(struct inode, inode_tag, elem); 96 | if (inode_found->i_no == inode_no) 97 | { 98 | inode_found->i_open_cnts++; 99 | return inode_found; 100 | } 101 | elem = elem->next; 102 | } 103 | 104 | /*由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表 */ 105 | struct inode_position inode_pos; 106 | 107 | /* inode位置信息会存入inode_pos, 包括inode所在扇区地址和扇区内的字节偏移量 */ 108 | inode_locate(part, inode_no, &inode_pos); 109 | 110 | /* 为使通过sys_malloc创建的新inode被所有任务共享, 111 | * 需要将inode置于内核空间,故需要临时 112 | * 将cur_pbc->pgdir置为NULL */ 113 | task_struct *cur = running_thread(); 114 | uint32_t *cur_pagedir_bak = cur->pgdir; 115 | cur->pgdir = NULL; 116 | /* 以上三行代码完成后下面分配的内存将位于内核区 */ 117 | inode_found = (struct inode *)sys_malloc(sizeof(struct inode)); 118 | /* 恢复pgdir */ 119 | cur->pgdir = cur_pagedir_bak; 120 | 121 | char *inode_buf; 122 | if (inode_pos.two_sec) 123 | { // 考虑跨扇区的情况 124 | inode_buf = (char *)sys_malloc(1024); 125 | 126 | /* i结点表是被partition_format函数连续写入扇区的, 127 | * 所以下面可以连续读出来 */ 128 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2); 129 | } 130 | else 131 | { // 否则,所查找的inode未跨扇区,一个扇区大小的缓冲区足够 132 | inode_buf = (char *)sys_malloc(512); 133 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1); 134 | } 135 | memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode)); 136 | 137 | /* 因为一会很可能要用到此inode,故将其插入到队首便于提前检索到 */ 138 | list_push(&part->open_inodes, &inode_found->inode_tag); 139 | inode_found->i_open_cnts = 1; 140 | 141 | sys_free(inode_buf); 142 | return inode_found; 143 | } 144 | 145 | /* 关闭inode或减少inode的打开数 */ 146 | void inode_close(struct inode *inode) 147 | { 148 | /* 若没有进程再打开此文件,将此inode去掉并释放空间 */ 149 | enum intr_status old_status = intr_disable(); 150 | if (--inode->i_open_cnts == 0) 151 | { 152 | list_remove(&inode->inode_tag); // 将I结点从part->open_inodes中去掉 153 | 154 | /* inode_open时为实现inode被所有进程共享, 155 | * 已经在sys_malloc为inode分配了内核空间, 156 | * 释放inode时也要确保释放的是内核内存池 */ 157 | task_struct *cur = running_thread(); 158 | uint32_t *cur_pagedir_bak = cur->pgdir; 159 | cur->pgdir = NULL; 160 | sys_free(inode); 161 | cur->pgdir = cur_pagedir_bak; 162 | } 163 | intr_set_status(old_status); 164 | } 165 | 166 | /* 将硬盘分区part上的inode清空 */ 167 | void inode_delete(struct partition *part, uint32_t inode_no, void *io_buf) 168 | { 169 | ASSERT(inode_no < 4096); 170 | struct inode_position inode_pos; 171 | inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos 172 | ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt)); 173 | 174 | char *inode_buf = (char *)io_buf; 175 | if (inode_pos.two_sec) 176 | { // inode跨扇区,读入2个扇区 177 | /* 将原硬盘上的内容先读出来 */ 178 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2); 179 | /* 将inode_buf清0 */ 180 | memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode)); 181 | /* 用清0的内存数据覆盖磁盘 */ 182 | ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2); 183 | } 184 | else 185 | { // 未跨扇区,只读入1个扇区就好 186 | /* 将原硬盘上的内容先读出来 */ 187 | ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1); 188 | /* 将inode_buf清0 */ 189 | memset((inode_buf + inode_pos.off_size), 0, sizeof(struct inode)); 190 | /* 用清0的内存数据覆盖磁盘 */ 191 | ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1); 192 | } 193 | } 194 | 195 | /* 回收inode的数据块和inode本身 */ 196 | void inode_release(struct partition *part, uint32_t inode_no) 197 | { 198 | struct inode *inode_to_del = inode_open(part, inode_no); 199 | ASSERT(inode_to_del->i_no == inode_no); 200 | 201 | /* 1 回收inode占用的所有块 */ 202 | uint8_t block_idx = 0, block_cnt = 12; 203 | uint32_t block_bitmap_idx; 204 | uint32_t all_blocks[140] = {0}; //12个直接块+128个间接块 205 | 206 | /* a 先将前12个直接块存入all_blocks */ 207 | while (block_idx < 12) 208 | { 209 | all_blocks[block_idx] = inode_to_del->i_sectors[block_idx]; 210 | block_idx++; 211 | } 212 | 213 | /* b 如果一级间接块表存在,将其128个间接块读到all_blocks[12~], 并释放一级间接块表所占的扇区 */ 214 | if (inode_to_del->i_sectors[12] != 0) 215 | { 216 | ide_read(part->my_disk, inode_to_del->i_sectors[12], all_blocks + 12, 1); 217 | block_cnt = 140; 218 | /* 回收一级间接块表占用的扇区 */ 219 | block_bitmap_idx = inode_to_del->i_sectors[12] - part->sb->data_start_lba; 220 | ASSERT(block_bitmap_idx > 0); 221 | bitmap_set(&part->block_bitmap, block_bitmap_idx, 0); 222 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 223 | } 224 | 225 | /* c inode所有的块地址已经收集到all_blocks中,下面逐个回收 */ 226 | block_idx = 0; 227 | while (block_idx < block_cnt) 228 | { 229 | if (all_blocks[block_idx] != 0) 230 | { 231 | block_bitmap_idx = 0; 232 | block_bitmap_idx = all_blocks[block_idx] - part->sb->data_start_lba; 233 | ASSERT(block_bitmap_idx > 0); 234 | bitmap_set(&part->block_bitmap, block_bitmap_idx, 0); 235 | bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP); 236 | } 237 | block_idx++; 238 | } 239 | 240 | /*2 回收该inode所占用的inode */ 241 | bitmap_set(&part->inode_bitmap, inode_no, 0); 242 | bitmap_sync(cur_part, inode_no, INODE_BITMAP); 243 | 244 | /****** 以下inode_delete是调试用的 ****** 245 | * 此函数会在inode_table中将此inode清0, 246 | * 但实际上是不需要的,inode分配是由inode位图控制的, 247 | * 硬盘上的数据不需要清0,可以直接覆盖*/ 248 | void *io_buf = sys_malloc(1024); 249 | inode_delete(part, inode_no, io_buf); 250 | sys_free(io_buf); 251 | /***********************************************/ 252 | 253 | inode_close(inode_to_del); 254 | } 255 | 256 | /* 初始化new_inode */ 257 | void inode_init(uint32_t inode_no, struct inode *new_inode) 258 | { 259 | new_inode->i_no = inode_no; 260 | new_inode->i_size = 0; 261 | new_inode->i_open_cnts = 0; 262 | new_inode->write_deny = false; 263 | 264 | /* 初始化块索引数组i_sector */ 265 | uint8_t sec_idx = 0; 266 | while (sec_idx < 13) 267 | { 268 | /* i_sectors[12]为一级间接块地址 */ 269 | new_inode->i_sectors[sec_idx] = 0; 270 | sec_idx++; 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /fs/inode.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_INODE_H_ 2 | #define _FS_INODE_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../lib/kernel/list.h" 6 | #include "../device/ide.h" 7 | 8 | struct inode 9 | { 10 | uint32_t i_no; // inode编号 11 | uint32_t i_size; // 此inode为文件时,表示文件的大小。为目录时,表示该目录下所有目录项大小之和 12 | uint32_t i_open_cnts; // 文件被打开的次数 13 | bool write_deny; // 写文件的标识 14 | 15 | uint32_t i_sectors[13]; // 一个文件只支持13个块,12个直接块,1个间接块 16 | struct list_elem inode_tag; 17 | }; 18 | 19 | struct inode *inode_open(struct partition *part, uint32_t inode_no); 20 | void inode_sync(struct partition *part, struct inode *inode, void *io_buf); 21 | void inode_init(uint32_t inode_no, struct inode *new_inode); 22 | void inode_close(struct inode *inode); 23 | void inode_release(struct partition *part, uint32_t inode_no); 24 | void inode_delete(struct partition *part, uint32_t inode_no, void *io_buf); 25 | 26 | #endif // !_FS_INODE_H_ -------------------------------------------------------------------------------- /fs/super_block.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_SUPER_BLOCK_H_ 2 | #define _FS_SUPER_BLOCK_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | /* 超级块 */ 7 | struct super_block 8 | { 9 | uint32_t magic; // 用来标识文件系统类型,支持多文件系统的操作系统通过此标志来识别文件系统类型 10 | uint32_t sec_cnt; // 本分区总共的扇区数 11 | uint32_t inode_cnt; // 本分区中inode数量 12 | uint32_t part_lba_base; // 本分区的起始lba地址 13 | 14 | uint32_t block_bitmap_lba; // 块位图本身起始扇区地址 15 | uint32_t block_bitmap_sects; // 扇区位图本身占用的扇区数量 16 | 17 | uint32_t inode_bitmap_lba; // i结点位图起始扇区lba地址 18 | uint32_t inode_bitmap_sects; // i结点位图占用的扇区数量 19 | 20 | uint32_t inode_table_lba; // i结点表起始扇区lba地址 21 | uint32_t inode_table_sects; // i结点表占用的扇区数量 22 | 23 | uint32_t data_start_lba; // 数据区开始的第一个扇区号 24 | uint32_t root_inode_no; // 根目录所在的I结点号 25 | uint32_t dir_entry_size; // 目录项大小 26 | 27 | uint8_t pad[460]; // 加上460字节,凑够512字节1扇区大小 28 | } __attribute__((packed)); 29 | #endif 30 | -------------------------------------------------------------------------------- /kernel/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "../lib/kernel/print.h" 3 | #include "interrupt.h" 4 | 5 | 6 | void panic_spin(char *filename, int line, const char *func, const char *condition) 7 | { 8 | intr_disable(); 9 | 10 | put_str("\n\n\n !!!! error !!!!\n"); 11 | put_str("filename: ");put_str(filename);put_str("\n"); 12 | put_str("line: ");put_int(line);put_str("\n"); 13 | put_str("func: ");put_str(func);put_str("\n"); 14 | put_str("condition: ");put_str(condition);put_str("\n"); 15 | while(1); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /kernel/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_DEBUG_H_ 2 | #define _KERNEL_DEBUG_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | 7 | void panic_spin(char *filname, int line, const char *func, const char *condition); 8 | 9 | #define PANIC(...) panic_spin(__FILE__, __LINE__, __func__, __VA_ARGS__) 10 | 11 | #ifdef NDEBUG 12 | #define ASSERT(CONDITION) ((void)0) 13 | #else 14 | #define ASSERT(CONDITION) if(CONDITION){} else{PANIC(#CONDITION);} 15 | #endif //NDEBUG 16 | 17 | 18 | #endif //!_KERNEL_DEBUG_H_ -------------------------------------------------------------------------------- /kernel/global.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_GLOBAL_H_ 2 | #define _KERNEL_GLOBAL_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | // ---------------- GDT描述符属性 ---------------- 7 | 8 | #define DESC_G_4K 1 9 | #define DESC_D_32 1 10 | #define DESC_L 0 // 64位代码标记,此处标记为0便可。 11 | #define DESC_AVL 0 // cpu不用此位,暂置为0 12 | #define DESC_P 1 13 | #define DESC_DPL_0 0 14 | #define DESC_DPL_1 1 15 | #define DESC_DPL_2 2 16 | #define DESC_DPL_3 3 17 | /* 18 | 代码段和数据段属于存储段,tss和各种门描述符属于系统段 19 | s为1时表示存储段,为0时表示系统段. 20 | */ 21 | #define DESC_S_CODE 1 22 | #define DESC_S_DATA DESC_S_CODE 23 | #define DESC_S_SYS 0 24 | #define DESC_TYPE_CODE 8 // x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0. 25 | #define DESC_TYPE_DATA 2 // x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0. 26 | #define DESC_TYPE_TSS 9 // B位为0,不忙 27 | 28 | 29 | #define RPL0 0 30 | #define RPL1 1 31 | #define RPL2 2 32 | #define RPL3 3 33 | 34 | #define TI_GDT 0 35 | #define TI_LDT 1 36 | 37 | #define SELECTOR_K_CODE ((1 << 3) + (TI_GDT << 2) + RPL0) 38 | #define SELECTOR_K_DATA ((2 << 3) + (TI_GDT << 2) + RPL0) 39 | #define SELECTOR_K_STACK SELECTOR_K_DATA 40 | #define SELECTOR_K_GS ((3 << 3) + (TI_GDT << 2) + RPL0) 41 | /* 第3个段描述符是显存,第4个是tss */ 42 | #define SELECTOR_U_CODE ((5 << 3) + (TI_GDT << 2) + RPL3) 43 | #define SELECTOR_U_DATA ((6 << 3) + (TI_GDT << 2) + RPL3) 44 | #define SELECTOR_U_STACK SELECTOR_U_DATA 45 | 46 | #define GDT_ATTR_HIGH ((DESC_G_4K << 7) + (DESC_D_32 << 6) + (DESC_L << 5) + (DESC_AVL << 4)) 47 | #define GDT_CODE_ATTR_LOW_DPL3 ((DESC_P << 7) + (DESC_DPL_3 << 5) + (DESC_S_CODE << 4) + DESC_TYPE_CODE) 48 | #define GDT_DATA_ATTR_LOW_DPL3 ((DESC_P << 7) + (DESC_DPL_3 << 5) + (DESC_S_DATA << 4) + DESC_TYPE_DATA) 49 | 50 | //--------------- TSS描述符属性 ------------ 51 | #define TSS_DESC_D 0 52 | 53 | #define TSS_ATTR_HIGH ((DESC_G_4K << 7) + (TSS_DESC_D << 6) + (DESC_L << 5) + (DESC_AVL << 4) + 0x0) 54 | #define TSS_ATTR_LOW ((DESC_P << 7) + (DESC_DPL_0 << 5) + (DESC_S_SYS << 4) + DESC_TYPE_TSS) 55 | #define SELECTOR_TSS ((4 << 3) + (TI_GDT << 2 ) + RPL0) 56 | 57 | struct gdt_desc 58 | { 59 | uint16_t limit_low_word; 60 | uint16_t base_low_word; 61 | uint8_t base_mid_byte; 62 | uint8_t attr_low_byte; 63 | uint8_t limit_high_attr_high; 64 | uint8_t base_high_byte; 65 | }; 66 | 67 | 68 | //-------------- IDT描述符属性 ------------ 69 | #define IDT_DESC_P 1 70 | #define IDT_DESC_DPL0 0 71 | #define IDT_DESC_DPL3 3 72 | #define IDT_DESC_32_TYPE 0xE // 32位的门 73 | #define IDT_DESC_16_TYPE 0x6 // 16位的门,不用,定义它只为和32位门区分 74 | #define IDT_DESC_ATTR_DPL0 ((IDT_DESC_P << 7) + (IDT_DESC_DPL0 << 5) + IDT_DESC_32_TYPE) 75 | #define IDT_DESC_ATTR_DPL3 ((IDT_DESC_P << 7) + (IDT_DESC_DPL3 << 5) + IDT_DESC_32_TYPE) 76 | 77 | 78 | 79 | #define EFLAGS_MBS (1 << 1) // 此项必须要设置 80 | #define EFLAGS_IF_1 (1 << 9) // if为1,开中断 81 | #define EFLAGS_IF_0 0 // if为0,关中断 82 | #define EFLAGS_IOPL_3 (3 << 12) // IOPL3,用于测试用户程序在非系统调用下进行IO 83 | #define EFLAGS_IOPL_0 (0 << 12) // IOPL0 84 | 85 | #define DIV_ROUND_UP(X, STEP) ((X + STEP - 1) / (STEP)) 86 | 87 | 88 | #define NULL ((void*)0) 89 | #define bool int 90 | #define true 1 91 | #define false 0 92 | #define PG_SIZE 4096 93 | 94 | #define UNUSED __attribute__ ((unused)) 95 | 96 | #endif //!_KERNEL_GLOBAL_H_ -------------------------------------------------------------------------------- /kernel/init.c: -------------------------------------------------------------------------------- 1 | #include "../lib/stdint.h" 2 | #include "../lib/kernel/print.h" 3 | #include "../lib/kernel/io.h" 4 | #include "../device/timer.h" 5 | #include "memory.h" 6 | #include "../thread/thread.h" 7 | #include "../device/console.h" 8 | #include "../device/keyboard.h" 9 | #include "../userprog/tss.h" 10 | #include "../userprog/syscall-init.h" 11 | #include "../device/ide.h" 12 | #include "../fs/file.h" 13 | 14 | void init_all() 15 | { 16 | put_str("init_all\n"); 17 | idt_init(); 18 | timer_init(); 19 | mem_init(); 20 | thread_init(); 21 | console_init(); 22 | keyboard_init(); 23 | tss_init(); 24 | syscall_init(); 25 | intr_enable(); // 后面的ide_init需要打开中断 26 | ide_init(); // 初始化硬盘 27 | filesys_init(); 28 | } 29 | -------------------------------------------------------------------------------- /kernel/init.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_INIT_H_ 2 | #define _KERNEL_INIT_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | void init_all(); 7 | 8 | #endif //!_KERNEL_INIT_H_ -------------------------------------------------------------------------------- /kernel/interrupt.c: -------------------------------------------------------------------------------- 1 | #include "interrupt.h" 2 | #include "../lib/stdint.h" 3 | #include "global.h" 4 | #include "../lib/kernel/io.h" 5 | #include "../lib/kernel/print.h" 6 | 7 | #define PIC_M_CTRL 0x20 // 这里用的可编程中断控制器是8259A,主片的控制端口是0x20 8 | #define PIC_M_DATA 0x21 // 主片的数据端口是0x21 9 | #define PIC_S_CTRL 0xa0 // 从片的控制端口是0xa0 10 | #define PIC_S_DATA 0xa1 // 从片的数据端口是0xa1 11 | 12 | #define IDT_DESC_CNT 0x81 // 目前总共支持的中断数 13 | 14 | #define EFLAGS_IF 0x00000200 // eflags寄存器中的if位为1 15 | #define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0" : "=g"(EFLAG_VAR)) 16 | 17 | extern uint32_t syscall_handler(); 18 | 19 | /*中断门描述符结构体*/ 20 | struct gate_desc 21 | { 22 | uint16_t func_offset_low_word; 23 | uint16_t selector; 24 | uint8_t dcount; //此项为双字计数字段,是门描述符中的第4字节。此项固定值,不用考虑 25 | uint8_t attribute; 26 | uint16_t func_offset_high_word; 27 | }; 28 | 29 | // 静态函数声明,非必须 30 | static void make_idt_desc(struct gate_desc *p_gdesc, uint8_t attr, intr_handler function); 31 | static struct gate_desc idt[IDT_DESC_CNT]; // idt是中断描述符表,本质上就是个中断门描述符数组 32 | 33 | char *intr_name[IDT_DESC_CNT]; // 用于保存异常的名字 34 | 35 | /******** 定义中断处理程序数组 ******** 36 | * 在kernel.S中定义的intrXXentry只是中断处理程序的入口, 37 | * 最终调用的是ide_table中的处理程序*/ 38 | intr_handler idt_table[IDT_DESC_CNT]; 39 | /********************************************/ 40 | extern intr_handler intr_entry_table[IDT_DESC_CNT]; // 声明引用定义在kernel.S中的中断处理函数入口数组 41 | 42 | /* 初始化可编程中断控制器8259A */ 43 | static void pic_init(void) 44 | { 45 | 46 | /* 初始化主片 */ 47 | outb(PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. 48 | outb(PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27. 49 | outb(PIC_M_DATA, 0x04); // ICW3: IR2接从片. 50 | outb(PIC_M_DATA, 0x01); // ICW4: 8086模式, 正常EOI 51 | 52 | /* 初始化从片 */ 53 | outb(PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联8259, 需要ICW4. 54 | outb(PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F. 55 | outb(PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的IR2引脚 56 | outb(PIC_S_DATA, 0x01); // ICW4: 8086模式, 正常EOI 57 | 58 | 59 | outb(PIC_M_DATA, 0xf8); 60 | outb(PIC_S_DATA, 0xbf); 61 | 62 | put_str(" pic_init done\n"); 63 | } 64 | 65 | /* 创建中断门描述符 */ 66 | static void make_idt_desc(struct gate_desc *p_gdesc, uint8_t attr, intr_handler function) 67 | { 68 | p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000FFFF; 69 | p_gdesc->selector = SELECTOR_K_CODE; 70 | p_gdesc->dcount = 0; 71 | p_gdesc->attribute = attr; 72 | p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16; 73 | } 74 | 75 | /*初始化中断描述符表*/ 76 | static void idt_desc_init(void) 77 | { 78 | int lastindex = IDT_DESC_CNT - 1; 79 | for (int i = 0; i < IDT_DESC_CNT - 1; i++) 80 | { 81 | make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]); 82 | } 83 | 84 | make_idt_desc(&idt[lastindex], IDT_DESC_ATTR_DPL3, syscall_handler); 85 | 86 | put_str(" idt_desc_init done\n"); 87 | } 88 | 89 | /* 通用的中断处理函数,一般用在异常出现时的处理 */ 90 | static void general_intr_handler(uint8_t vec_nr) 91 | { 92 | if (vec_nr == 0x27 || vec_nr == 0x2f) 93 | { // 0x2f是从片8259A上的最后一个irq引脚,保留 94 | return; //IRQ7和IRQ15会产生伪中断(spurious interrupt),无须处理。 95 | } 96 | /* 将光标置为0,从屏幕左上角清出一片打印异常信息的区域,方便阅读 */ 97 | set_cursor(0); 98 | int cursor_pos = 0; 99 | while (cursor_pos < 320) 100 | { 101 | put_char(' '); 102 | cursor_pos++; 103 | } 104 | 105 | set_cursor(0); // 重置光标为屏幕左上角 106 | put_str("!!!!!!! excetion message begin !!!!!!!!\n"); 107 | set_cursor(88); // 从第2行第8个字符开始打印 108 | put_str(intr_name[vec_nr]); 109 | if (vec_nr == 14) 110 | { 111 | // 若为Pagefault,将缺失的地址打印出来并悬停 112 | int page_fault_vaddr = 0; 113 | asm("movl %%cr2, %0" : "=r"(page_fault_vaddr)); // cr2是存放造成page_fault的地址 114 | put_str("\npage fault addr is "); 115 | put_int(page_fault_vaddr); 116 | } 117 | put_str("\n!!!!!!! excetion message end !!!!!!!!\n"); 118 | // 能进入中断处理程序就表示已经处在关中断情况下, 119 | // 不会出现调度进程的情况。故下面的死循环不会再被中断。 120 | while (1) 121 | ; 122 | } 123 | 124 | /* 完成一般中断处理函数注册及异常名称注册 */ 125 | static void exception_init(void) 126 | { 127 | // 完成一般中断处理函数注册及异常名称注册 128 | for (int i = 0; i < IDT_DESC_CNT; i++) 129 | { 130 | 131 | /* idt_table数组中的函数是在进入中断后根据中断向量号调用的, 132 | * 见kernel/kernel.S的call [idt_table + %1*4] */ 133 | idt_table[i] = general_intr_handler; // 默认为general_intr_handler。 134 | // 以后会由register_handler来注册具体处理函数。 135 | intr_name[i] = "unknown"; // 先统一赋值为unknown 136 | } 137 | intr_name[0] = "#DE Divide Error"; 138 | intr_name[1] = "#DB Debug Exception"; 139 | intr_name[2] = "NMI Interrupt"; 140 | intr_name[3] = "#BP Breakpoint Exception"; 141 | intr_name[4] = "#OF Overflow Exception"; 142 | intr_name[5] = "#BR BOUND Range Exceeded Exception"; 143 | intr_name[6] = "#UD Invalid Opcode Exception"; 144 | intr_name[7] = "#NM Device Not Available Exception"; 145 | intr_name[8] = "#DF Double Fault Exception"; 146 | intr_name[9] = "Coprocessor Segment Overrun"; 147 | intr_name[10] = "#TS Invalid TSS Exception"; 148 | intr_name[11] = "#NP Segment Not Present"; 149 | intr_name[12] = "#SS Stack Fault Exception"; 150 | intr_name[13] = "#GP General Protection Exception"; 151 | intr_name[14] = "#PF Page-Fault Exception"; 152 | // intr_name[15] 第15项是intel保留项,未使用 153 | intr_name[16] = "#MF x87 FPU Floating-Point Error"; 154 | intr_name[17] = "#AC Alignment Check Exception"; 155 | intr_name[18] = "#MC Machine-Check Exception"; 156 | intr_name[19] = "#XF SIMD Floating-Point Exception"; 157 | } 158 | 159 | /* 开中断并返回开中断前的状态*/ 160 | enum intr_status intr_enable() 161 | { 162 | enum intr_status old_status; 163 | if (INTR_ON == intr_get_status()) 164 | { 165 | old_status = INTR_ON; 166 | return old_status; 167 | } 168 | else 169 | { 170 | old_status = INTR_OFF; 171 | asm volatile("sti"); // 开中断,sti指令将IF位置1 172 | return old_status; 173 | } 174 | } 175 | 176 | /* 关中断,并且返回关中断前的状态 */ 177 | enum intr_status intr_disable() 178 | { 179 | enum intr_status old_status; 180 | if (INTR_ON == intr_get_status()) 181 | { 182 | old_status = INTR_ON; 183 | asm volatile("cli" 184 | : 185 | : 186 | : "memory"); // 关中断,cli指令将IF位置0 187 | return old_status; 188 | } 189 | else 190 | { 191 | old_status = INTR_OFF; 192 | return old_status; 193 | } 194 | } 195 | 196 | /* 将中断状态设置为status */ 197 | enum intr_status intr_set_status(enum intr_status status) 198 | { 199 | return status & INTR_ON ? intr_enable() : intr_disable(); 200 | } 201 | 202 | /* 获取当前中断状态 */ 203 | enum intr_status intr_get_status() 204 | { 205 | uint32_t eflags = 0; 206 | GET_EFLAGS(eflags); 207 | return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF; 208 | } 209 | 210 | /* 在中断处理程序数组第vector_no个元素中注册安装中断处理程序function */ 211 | void register_handler(uint8_t vector_no, intr_handler function) 212 | { 213 | /* idt_table数组中的函数是在进入中断后根据中断向量号调用的, 214 | * 见kernel/kernel.S的call [idt_table + %1*4] */ 215 | idt_table[vector_no] = function; 216 | } 217 | 218 | /*完成有关中断的所有初始化工作*/ 219 | void idt_init() 220 | { 221 | put_str("idt_init start\n"); 222 | idt_desc_init(); // 初始化中断描述符表 223 | exception_init(); // 异常名初始化并注册通常的中断处理函数 224 | pic_init(); // 初始化8259A 225 | 226 | /* 加载idt */ 227 | uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)(uint32_t)idt << 16)); 228 | asm volatile("lidt %0" 229 | : 230 | : "m"(idt_operand)); 231 | put_str("idt_init done\n"); 232 | } 233 | -------------------------------------------------------------------------------- /kernel/interrupt.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_INTERRUPT_H_ 2 | #define _KERNEL_INTERRUPT_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | typedef void * intr_handler; 7 | 8 | enum intr_status 9 | { 10 | INTR_OFF, 11 | INTR_ON 12 | }; 13 | 14 | void idt_init(); 15 | 16 | // 通过EFLAGS中的if位获取当前中断状态 17 | enum intr_status intr_get_status(); 18 | 19 | // 开中断,返回中断前的状态 20 | enum intr_status intr_enable(); 21 | 22 | // 关中断,返回中断前的状态 23 | enum intr_status intr_disable(); 24 | 25 | // 设置中断状态 26 | enum intr_status intr_set_status(enum intr_status status); 27 | 28 | void register_handler(uint8_t vec_no, intr_handler function); 29 | 30 | #endif //!_KERNEL_INTERRUPT_H_ -------------------------------------------------------------------------------- /kernel/kernel.S: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | %define ERROR_CODE nop 3 | %define ZERO push 0 ;当没有错误码时,为了保证栈结构的统一,手动push4字节 4 | 5 | extern put_str 6 | extern idt_table 7 | 8 | section .data 9 | intr_str db "interrupt occur!", 0xa, 0 10 | global intr_entry_table 11 | 12 | intr_entry_table: 13 | 14 | %macro VECTOR 2 15 | section .text 16 | intr%1entry: ;每个中断处理程序都要圧入中断向量号 17 | %2 ;如果有错误码,会圧在eip后面 18 | push ds 19 | push es 20 | push fs 21 | push gs 22 | pushad 23 | 24 | ;如果时从片上进入中断,除了往从片上发送EOI外,还要往主片上发送 25 | mov al, 0x20 ;中断结束命令EOI 26 | out 0xa0, al ;向从片发送 27 | out 0x20, al ;向主片发送 28 | 29 | push %1 ;参数1为中断向量号 30 | call [idt_table + %1 * 4] ; 调用中断处理程序 31 | jmp intr_exit; 32 | 33 | section .data 34 | dd intr%1entry 35 | 36 | %endmacro 37 | 38 | section .text 39 | global intr_exit 40 | intr_exit: 41 | add esp, 4 ;跳过中断号 42 | popad 43 | pop gs 44 | pop fs 45 | pop es 46 | pop ds 47 | add esp, 4 ;跳过error_code 48 | iretd 49 | 50 | 51 | ;对宏进行调用 52 | 53 | VECTOR 0x00,ZERO 54 | VECTOR 0x01,ZERO 55 | VECTOR 0x02,ZERO 56 | VECTOR 0x03,ZERO 57 | VECTOR 0x04,ZERO 58 | VECTOR 0x05,ZERO 59 | VECTOR 0x06,ZERO 60 | VECTOR 0x07,ZERO 61 | VECTOR 0x08,ERROR_CODE 62 | VECTOR 0x09,ZERO 63 | VECTOR 0x0a,ERROR_CODE 64 | VECTOR 0x0b,ERROR_CODE 65 | VECTOR 0x0c,ZERO 66 | VECTOR 0x0d,ERROR_CODE 67 | VECTOR 0x0e,ERROR_CODE 68 | VECTOR 0x0f,ZERO 69 | VECTOR 0x10,ZERO 70 | VECTOR 0x11,ERROR_CODE 71 | VECTOR 0x12,ZERO 72 | VECTOR 0x13,ZERO 73 | VECTOR 0x14,ZERO 74 | VECTOR 0x15,ZERO 75 | VECTOR 0x16,ZERO 76 | VECTOR 0x17,ZERO 77 | VECTOR 0x18,ERROR_CODE 78 | VECTOR 0x19,ZERO 79 | VECTOR 0x1a,ERROR_CODE 80 | VECTOR 0x1b,ERROR_CODE 81 | VECTOR 0x1c,ZERO 82 | VECTOR 0x1d,ERROR_CODE 83 | VECTOR 0x1e,ERROR_CODE 84 | VECTOR 0x1f,ZERO 85 | VECTOR 0x20,ZERO 86 | VECTOR 0x21,ZERO ;键盘中断对应的入口 87 | VECTOR 0x22,ZERO ;级联用的 88 | VECTOR 0x23,ZERO ;串口2对应的入口 89 | VECTOR 0x24,ZERO ;串口1对应的入口 90 | VECTOR 0x25,ZERO ;并口2对应的入口 91 | VECTOR 0x26,ZERO ;软盘对应的入口 92 | VECTOR 0x27,ZERO ;并口1对应的入口 93 | VECTOR 0x28,ZERO ;实时时钟对应的入口 94 | VECTOR 0x29,ZERO ;重定向 95 | VECTOR 0x2a,ZERO ;保留 96 | VECTOR 0x2b,ZERO ;保留 97 | VECTOR 0x2c,ZERO ;ps/2鼠标 98 | VECTOR 0x2d,ZERO ;fpu浮点单元异常 99 | VECTOR 0x2e,ZERO ;硬盘 100 | VECTOR 0x2f,ZERO ;保留 101 | 102 | 103 | [bits 32] 104 | extern syscall_table 105 | section .text 106 | global syscall_handler 107 | syscall_handler: 108 | push 0 109 | push ds 110 | push es 111 | push fs 112 | push gs 113 | pushad 114 | 115 | push 0x80 116 | 117 | push edx 118 | push ecx 119 | push ebx 120 | 121 | call [syscall_table + 4 * eax] 122 | add esp, 12 123 | mov [esp + 4 * 8], eax 124 | jmp intr_exit -------------------------------------------------------------------------------- /kernel/main.c: -------------------------------------------------------------------------------- 1 | #include "../lib/kernel/print.h" 2 | #include "init.h" 3 | #include "../thread/thread.h" 4 | #include "interrupt.h" 5 | #include "../device/console.h" 6 | #include "../userprog/process.h" 7 | #include "../userprog/syscall-init.h" 8 | #include "../lib/user/syscall.h" 9 | #include "memory.h" 10 | #include "../lib/stdio.h" 11 | 12 | void k_thread_a(void *); 13 | void k_thread_b(void *); 14 | void u_prog_a(void); 15 | void u_prog_b(void); 16 | int test_var_a = 0, test_var_b = 0; 17 | int prog_a_pid = 0, prog_b_pid = 0; 18 | 19 | void k_thread_a(void *); 20 | void k_thread_b(void *); 21 | void u_prog_a(void); 22 | void u_prog_b(void); 23 | 24 | int main(void) 25 | { 26 | put_str("I am kernel\n"); 27 | init_all(); 28 | while (1); 29 | //process_execute(u_prog_a, "u_prog_a"); 30 | //process_execute(u_prog_b, "u_prog_b"); 31 | //thread_start("k_thread_a", 31, k_thread_a, "I am thread_a"); 32 | //thread_start("k_thread_b", 31, k_thread_b, "I am thread_b"); 33 | while (1); 34 | return 0; 35 | } 36 | 37 | /* 在线程中运行的函数 */ 38 | void k_thread_a(void *arg) 39 | { 40 | void *addr1 = sys_malloc(256); 41 | void *addr2 = sys_malloc(255); 42 | void *addr3 = sys_malloc(254); 43 | console_put_str(" thread_a malloc addr:0x"); 44 | console_put_int((int)addr1); 45 | console_put_char(','); 46 | console_put_int((int)addr2); 47 | console_put_char(','); 48 | console_put_int((int)addr3); 49 | console_put_char('\n'); 50 | int cpu_delay = 100000; 51 | while (cpu_delay-- > 0); 52 | //sys_free(addr1); 53 | //sys_free(addr2); 54 | //sys_free(addr3); 55 | while (1); 56 | } 57 | 58 | /* 在线程中运行的函数 */ 59 | void k_thread_b(void *arg) 60 | { 61 | void *addr1 = sys_malloc(256); 62 | void *addr2 = sys_malloc(255); 63 | void *addr3 = sys_malloc(254); 64 | console_put_str(" thread_b malloc addr:0x"); 65 | console_put_int((int)addr1); 66 | console_put_char(','); 67 | console_put_int((int)addr2); 68 | console_put_char(','); 69 | console_put_int((int)addr3); 70 | console_put_char('\n'); 71 | 72 | int cpu_delay = 100000; 73 | while (cpu_delay-- > 0); 74 | sys_free(addr1); 75 | sys_free(addr2); 76 | sys_free(addr3); 77 | while (1); 78 | } 79 | 80 | /* 测试用户进程 */ 81 | void u_prog_a(void) 82 | { 83 | void *addr1 = malloc(256); 84 | void *addr2 = malloc(255); 85 | void *addr3 = malloc(254); 86 | printf(" prog_a malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3); 87 | int cpu_delay = 100000; 88 | while (cpu_delay-- > 0); 89 | free(addr1); 90 | free(addr2); 91 | free(addr3); 92 | while (1); 93 | } 94 | 95 | /* 测试用户进程 */ 96 | void u_prog_b(void) 97 | { 98 | void *addr1 = malloc(256); 99 | void *addr2 = malloc(255); 100 | void *addr3 = malloc(254); 101 | printf(" prog_b malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3); 102 | 103 | int cpu_delay = 100000; 104 | while (cpu_delay-- > 0); 105 | free(addr1); 106 | free(addr2); 107 | free(addr3); 108 | while (1); 109 | } 110 | -------------------------------------------------------------------------------- /kernel/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include "../lib/kernel/print.h" 3 | #include "../lib/kernel/bitmap.h" 4 | #include "global.h" 5 | #include "../lib/string.h" 6 | #include "debug.h" 7 | #include "../thread/sync.h" 8 | #include "../thread/thread.h" 9 | #include "global.h" 10 | #include "../kernel/interrupt.h" 11 | 12 | 13 | // 一个页框(4KB)的位图,所能表示的内存大小为 4096 * 32 / 1024 = 128MB 14 | // 0xc0009f00为内核主线程的栈顶,0xc0009e00位内核主线程的pcb 15 | // 位图地址安排在0xc0009a00,也就是说0xc0009e00 - 0xc0009a00 = 0x4000,可以存放4个页框的位图,共512MB 16 | 17 | 18 | #define MEM_BITMAP_BASE 0xc0009a00 19 | #define K_HEAP_START 0xc0100000 // 内核所用堆的起始地址,因为loader通过页表映射在了低端1MB的空间中,也就是0xc0000000~0xc000ffff映射到0x0~0xffff,所以这里需要跨过这部分空间 20 | 21 | #define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) 22 | #define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) 23 | 24 | struct pool 25 | { 26 | struct bitmap pool_bitmap; // 内存池的位图结构 27 | uint32_t phy_addr_start; 28 | uint32_t pool_size; 29 | struct lock lock; 30 | }; 31 | 32 | struct arena 33 | { 34 | struct mem_block_desc *desc; 35 | uint32_t cnt; 36 | bool large; 37 | }; 38 | 39 | 40 | struct mem_block_desc k_block_descs[DESC_CNT]; 41 | 42 | struct pool kernel_pool, user_pool; // 生成内核内存池和用户内存池 43 | struct virtual_addr kernel_vaddr; // 此结构用来给内核分配虚拟地址 44 | 45 | // 在虚拟内存池中申请pg_cnt个虚拟页 46 | static void *vaddr_get(enum pool_flags pf, uint32_t pg_cnt) 47 | { 48 | int vaddr_start = 0; 49 | int bit_idx_start = -1; 50 | uint32_t cnt = 0; 51 | 52 | if(pf == PF_KERNEL) 53 | { 54 | bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt); 55 | if(bit_idx_start == -1) 56 | { 57 | return NULL; 58 | } 59 | 60 | while (cnt < pg_cnt) 61 | { 62 | bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1); 63 | } 64 | 65 | vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE; 66 | } 67 | else 68 | { 69 | // 用户内存池 70 | task_struct *cur = running_thread(); 71 | bit_idx_start = bitmap_scan(&cur->userprog_vaddr.vaddr_bitmap, pg_cnt); 72 | if(bit_idx_start == -1) 73 | return NULL; 74 | 75 | while (cnt < pg_cnt) 76 | { 77 | bitmap_set(&cur->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1); 78 | } 79 | 80 | vaddr_start = cur->userprog_vaddr.vaddr_start + bit_idx_start * PG_SIZE; 81 | 82 | ASSERT((uint32_t)vaddr_start < (0xc0000000 - PG_SIZE)); 83 | } 84 | 85 | return (void *)vaddr_start; 86 | } 87 | 88 | // 得到虚拟地址对应的pte指针 89 | uint32_t *pte_ptr(uint32_t vaddr) 90 | { 91 | uint32_t *pte = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4); 92 | 93 | return pte; 94 | } 95 | 96 | // 得到虚拟地址对应的pde指针 97 | uint32_t *pde_ptr(uint32_t vaddr) 98 | { 99 | uint32_t *pde = (uint32_t*)(0xfffff000 + PDE_IDX(vaddr) * 4); 100 | 101 | return pde; 102 | } 103 | 104 | // 在m_pool指向的物理内存池中分配一个物理页 105 | static void *palloc(struct pool *m_pool) 106 | { 107 | int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1); 108 | if(bit_idx == -1) 109 | { 110 | return NULL; 111 | } 112 | 113 | bitmap_set(&m_pool->pool_bitmap, bit_idx, 1); 114 | uint32_t page_phyaddr = bit_idx * PG_SIZE + m_pool->phy_addr_start; 115 | 116 | return (void*)page_phyaddr; 117 | } 118 | 119 | // 在页表中添加虚拟地址到物理地址的映射关系 120 | static void page_table_add(void *_vaddr, void *_page_phyaddr) 121 | { 122 | uint32_t vaddr = (uint32_t)_vaddr; 123 | uint32_t page_phyaddr = (uint32_t)_page_phyaddr; 124 | 125 | uint32_t *pde = pde_ptr((uint32_t)vaddr); 126 | uint32_t *pte = pte_ptr((uint32_t)vaddr); 127 | 128 | // 在页目录内判断目录项的P位,若为1,表示该表已存在 129 | if(*pde & 0x01) 130 | { 131 | // 创建页表的时候,pte不应该存在 132 | ASSERT(!(*pte & 0x01)); 133 | 134 | if(!(*pte & 0x01)) 135 | { 136 | *pte = page_phyaddr | PG_US_U | PG_RW_W | PG_P_1; 137 | } 138 | } 139 | else 140 | {// 页目录项不存在,此时先创建页目录项 141 | uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool); 142 | 143 | *pde = pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1; 144 | memset((void*)((int)pte & 0xfffff000), 0, PG_SIZE); 145 | 146 | ASSERT(!(*pte & 0x01)); 147 | *pte = page_phyaddr | PG_US_U | PG_RW_W | PG_P_1; 148 | } 149 | } 150 | 151 | // 分配pg_cnt 个页空间 152 | void *malloc_page(enum pool_flags pf, uint32_t pg_cnt) 153 | { 154 | ASSERT(pg_cnt > 0 && pg_cnt < 3840); 155 | 156 | void *vaddr_start = vaddr_get(pf, pg_cnt); 157 | if(vaddr_start == NULL) 158 | { 159 | return NULL; 160 | } 161 | 162 | uint32_t vaddr = (uint32_t)vaddr_start; 163 | uint32_t cnt = pg_cnt; 164 | 165 | struct pool *mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool; 166 | 167 | while (cnt-- > 0) 168 | { 169 | void *page_phyaddr = palloc(mem_pool); 170 | if(page_phyaddr == NULL) 171 | {// 此处分配失败需要释放已申请的虚拟页和物理页 172 | return NULL; 173 | } 174 | page_table_add((void*)vaddr, page_phyaddr); 175 | vaddr += PG_SIZE; 176 | } 177 | return vaddr_start; 178 | } 179 | 180 | // 在内核物理内存池中申请pg_cnt页内存 181 | void *get_kernel_pages(uint32_t pg_cnt) 182 | { 183 | void *vaddr = malloc_page(PF_KERNEL, pg_cnt); 184 | 185 | if(vaddr != NULL) 186 | { 187 | memset(vaddr,0, pg_cnt * PG_SIZE); 188 | } 189 | return vaddr; 190 | } 191 | 192 | void *get_user_pages(uint32_t pg_cnt) 193 | { 194 | lock_acquire(&user_pool.lock); 195 | void *vaddr = malloc_page(PF_USER, pg_cnt); 196 | memset(vaddr, 0, pg_cnt * PG_SIZE); 197 | lock_release(&user_pool.lock); 198 | return vaddr; 199 | } 200 | 201 | /* 将地址vaddr与pf池中的物理地址关联,仅支持一页空间分配 */ 202 | void *get_a_page(enum pool_flags pf, uint32_t vaddr) 203 | { 204 | struct pool *mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool; 205 | 206 | lock_acquire(&mem_pool->lock); 207 | 208 | /* 先将虚拟地址对应的位图置1 */ 209 | task_struct *cur = running_thread(); 210 | int32_t bit_idx = -1; 211 | 212 | /* 若当前是用户进程申请用户内存,就修改用户进程自己的虚拟地址位图 */ 213 | if (cur->pgdir != NULL && pf == PF_USER) 214 | { 215 | bit_idx = (vaddr - cur->userprog_vaddr.vaddr_start) / PG_SIZE; 216 | ASSERT(bit_idx > 0); 217 | bitmap_set(&cur->userprog_vaddr.vaddr_bitmap, bit_idx, 1); 218 | } 219 | else if (cur->pgdir == NULL && pf == PF_KERNEL) 220 | { 221 | /* 如果是内核线程申请内核内存,就修改kernel_vaddr. */ 222 | bit_idx = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE; 223 | ASSERT(bit_idx > 0); 224 | bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx, 1); 225 | } 226 | else 227 | { 228 | PANIC("get_a_page:not allow kernel alloc userspace or user alloc kernelspace by get_a_page"); 229 | } 230 | 231 | void *page_phyaddr = palloc(mem_pool); 232 | if (page_phyaddr == NULL) 233 | { 234 | return NULL; 235 | } 236 | page_table_add((void *)vaddr, page_phyaddr); 237 | lock_release(&mem_pool->lock); 238 | return (void *)vaddr; 239 | } 240 | 241 | /* 安装1页大小的vaddr,专门针对fork时虚拟地址位图无须操作的情况 */ 242 | void *get_a_page_without_opvaddrbitmap(enum pool_flags pf, uint32_t vaddr) 243 | { 244 | struct pool *mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool; 245 | lock_acquire(&mem_pool->lock); 246 | void *page_phyaddr = palloc(mem_pool); 247 | if (page_phyaddr == NULL) 248 | { 249 | lock_release(&mem_pool->lock); 250 | return NULL; 251 | } 252 | page_table_add((void *)vaddr, page_phyaddr); 253 | lock_release(&mem_pool->lock); 254 | return (void *)vaddr; 255 | } 256 | 257 | /* 得到虚拟地址映射到的物理地址 */ 258 | uint32_t addr_v2p(uint32_t vaddr) 259 | { 260 | uint32_t *pte = pte_ptr(vaddr); 261 | return (*pte & 0xfffff000) + (vaddr & 0x00000fff); 262 | } 263 | 264 | 265 | void block_desc_init(struct mem_block_desc *desc_array) 266 | { 267 | uint16_t desc_idx, block_size = 16; 268 | 269 | for (desc_idx = 0; desc_idx < DESC_CNT; ++desc_idx) 270 | { 271 | desc_array[desc_idx].block_size = block_size; 272 | 273 | desc_array[desc_idx].blocks_per_arena = (PG_SIZE - sizeof(struct arena)) / block_size; 274 | list_init(&desc_array[desc_idx].free_list); 275 | block_size *= 2; 276 | } 277 | } 278 | 279 | static struct mem_block* arena2block(struct arena *a, uint32_t idx) 280 | { 281 | return (struct mem_block*)((uint32_t)a + sizeof(struct arena) + idx * a->desc->block_size); 282 | } 283 | 284 | static struct arena *block2arena(struct mem_block *b) 285 | { 286 | return (struct arena*)((uint32_t)b & 0xfffff000); 287 | } 288 | 289 | void *sys_malloc(uint32_t size) 290 | { 291 | enum pool_flags pf; 292 | struct pool *mem_pool; 293 | uint32_t pool_size; 294 | struct mem_block_desc *descs; 295 | task_struct *cur_thread = running_thread(); 296 | 297 | if(cur_thread->pgdir == NULL) 298 | { 299 | pf = PF_KERNEL; 300 | pool_size = kernel_pool.pool_size; 301 | mem_pool = &kernel_pool; 302 | descs = k_block_descs; 303 | } 304 | else 305 | { 306 | pf = PF_USER; 307 | pool_size = user_pool.pool_size; 308 | mem_pool = &user_pool; 309 | descs = cur_thread->u_block_desc; 310 | } 311 | 312 | if(!(size > 0 && size < pool_size)) 313 | return NULL; 314 | 315 | struct arena *a; 316 | struct mem_block *b; 317 | lock_acquire(&mem_pool->lock); 318 | 319 | if(size > 1024) 320 | { 321 | uint32_t page_cnt = DIV_ROUND_UP(size + sizeof(struct arena), PG_SIZE); 322 | 323 | a = malloc_page(pf, page_cnt); 324 | if (a != NULL) 325 | { 326 | memset(a, 0, page_cnt * PG_SIZE); 327 | 328 | a->desc = NULL; 329 | a->cnt = page_cnt; 330 | a->large = true; 331 | lock_release(&mem_pool->lock); 332 | return (void *)(a + 1); 333 | } 334 | else 335 | { 336 | lock_release(&mem_pool->lock); 337 | return NULL; 338 | } 339 | } 340 | else 341 | { 342 | int desc_idx = 0; 343 | for (; desc_idx < DESC_CNT; ++desc_idx) 344 | { 345 | if(size <= descs[desc_idx].block_size) 346 | break; 347 | } 348 | 349 | if (list_empty(&descs[desc_idx].free_list)) 350 | { 351 | a = malloc_page(pf, 1); 352 | if(a == NULL) 353 | { 354 | lock_release(&mem_pool->lock); 355 | return NULL; 356 | } 357 | 358 | memset(a, 0, PG_SIZE); 359 | 360 | a->desc = &descs[desc_idx]; 361 | a->cnt = descs[desc_idx].blocks_per_arena; 362 | a->large = false; 363 | 364 | enum intr_status old_status = intr_disable(); 365 | 366 | uint32_t block_idx = 0; 367 | 368 | for (; block_idx < descs[desc_idx].blocks_per_arena; ++block_idx) 369 | { 370 | b = arena2block(a, block_idx); 371 | ASSERT(!elem_find(&a->desc->free_list, &b->free_elem)); 372 | list_append(&a->desc->free_list, &b->free_elem); 373 | 374 | } 375 | intr_set_status(old_status); 376 | } 377 | 378 | b = elem2entry(struct mem_block, free_elem, list_pop(&(descs[desc_idx].free_list))); 379 | 380 | memset(b, 0, descs[desc_idx].block_size); 381 | a = block2arena(b); 382 | a->cnt--; 383 | lock_release(&mem_pool->lock); 384 | return (void *)b; 385 | } 386 | } 387 | 388 | void pfree(uint32_t pg_phy_addr) 389 | { 390 | struct pool *mem_pool; 391 | uint32_t bit_idx = 0; 392 | 393 | if(pg_phy_addr >= user_pool.phy_addr_start) 394 | { 395 | mem_pool = &user_pool; 396 | bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE; 397 | } 398 | else 399 | { 400 | mem_pool = &kernel_pool; 401 | bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE; 402 | } 403 | 404 | bitmap_set(&mem_pool->pool_bitmap, bit_idx , 0); 405 | } 406 | 407 | static void page_table_pte_remove(uint32_t vaddr) 408 | { 409 | uint32_t *pte = pte_ptr(vaddr); 410 | *pte &= ~PG_P_1; 411 | asm volatile("invlpg %0" :: "m" (vaddr):"memory"); 412 | } 413 | 414 | static void vaddr_remove(enum pool_flags pf, void *_vaddr, uint32_t pg_cnt) 415 | { 416 | uint32_t bit_idx_start = 0; 417 | uint32_t vaddr = (uint32_t)_vaddr; 418 | uint32_t cnt = 0; 419 | 420 | if (pf == PF_KERNEL) 421 | { 422 | bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE; 423 | while (cnt < pg_cnt) 424 | { 425 | bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0); 426 | } 427 | } 428 | else 429 | { 430 | task_struct *cur_thread = running_thread(); 431 | bit_idx_start = (vaddr - cur_thread->userprog_vaddr.vaddr_start) / PG_SIZE; 432 | while (cnt < pg_cnt) 433 | { 434 | bitmap_set(&cur_thread->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0); 435 | } 436 | } 437 | } 438 | 439 | void mfree_page(enum pool_flags pf, void *_vaddr, uint32_t pg_cnt) 440 | { 441 | uint32_t pg_phy_addr; 442 | uint32_t vaddr = (int32_t)_vaddr, page_cnt = 0; 443 | ASSERT(pg_cnt >= 1 && vaddr % PG_SIZE == 0); 444 | pg_phy_addr = addr_v2p(vaddr); // 获取虚拟地址vaddr对应的物理地址 445 | 446 | /* 确保待释放的物理内存在低端1M+1k大小的页目录+1k大小的页表地址范围外 */ 447 | ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= 0x102000); 448 | 449 | /* 判断pg_phy_addr属于用户物理内存池还是内核物理内存池 */ 450 | if (pg_phy_addr >= user_pool.phy_addr_start) 451 | { 452 | // 位于user_pool内存池 453 | vaddr -= PG_SIZE; 454 | while (page_cnt < pg_cnt) 455 | { 456 | vaddr += PG_SIZE; 457 | pg_phy_addr = addr_v2p(vaddr); 458 | 459 | /* 确保物理地址属于用户物理内存池 */ 460 | ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= user_pool.phy_addr_start); 461 | 462 | /* 先将对应的物理页框归还到内存池 */ 463 | pfree(pg_phy_addr); 464 | 465 | /* 再从页表中清除此虚拟地址所在的页表项pte */ 466 | page_table_pte_remove(vaddr); 467 | 468 | page_cnt++; 469 | } 470 | /* 清空虚拟地址的位图中的相应位 */ 471 | vaddr_remove(pf, _vaddr, pg_cnt); 472 | } 473 | else 474 | { 475 | // 位于kernel_pool内存池 476 | vaddr -= PG_SIZE; 477 | while (page_cnt < pg_cnt) 478 | { 479 | vaddr += PG_SIZE; 480 | pg_phy_addr = addr_v2p(vaddr); 481 | /* 确保待释放的物理内存只属于内核物理内存池 */ 482 | ASSERT((pg_phy_addr % PG_SIZE) == 0 && 483 | pg_phy_addr >= kernel_pool.phy_addr_start && 484 | pg_phy_addr < user_pool.phy_addr_start); 485 | /* 先将对应的物理页框归还到内存池 */ 486 | pfree(pg_phy_addr); 487 | 488 | /* 再从页表中清除此虚拟地址所在的页表项pte */ 489 | page_table_pte_remove(vaddr); 490 | 491 | page_cnt++; 492 | } 493 | /* 清空虚拟地址的位图中的相应位 */ 494 | vaddr_remove(pf, _vaddr, pg_cnt); 495 | } 496 | } 497 | 498 | /* 回收内存ptr */ 499 | void sys_free(void* ptr) 500 | { 501 | ASSERT(ptr != NULL); 502 | if (ptr != NULL) 503 | { 504 | enum pool_flags PF; 505 | struct pool* mem_pool; 506 | 507 | /* 判断是线程还是进程 */ 508 | if (running_thread()->pgdir == NULL) 509 | { 510 | ASSERT((uint32_t)ptr >= K_HEAP_START); 511 | PF = PF_KERNEL; 512 | mem_pool = &kernel_pool; 513 | } 514 | else 515 | { 516 | PF = PF_USER; 517 | mem_pool = &user_pool; 518 | } 519 | 520 | lock_acquire(&mem_pool->lock); 521 | struct mem_block* b = ptr; 522 | struct arena* a = block2arena(b); // 把mem_block转换成arena,获取元信息 523 | ASSERT(a->large == 0 || a->large == 1); 524 | if (a->desc == NULL && a->large == true) 525 | { // 大于1024的内存 526 | mfree_page(PF, a, a->cnt); 527 | } 528 | else 529 | { 530 | // 小于等于1024的内存块 531 | /* 先将内存块回收到free_list */ 532 | list_append(&a->desc->free_list, &b->free_elem); 533 | 534 | /* 再判断此arena中的内存块是否都是空闲,如果是就释放arena */ 535 | if (++a->cnt == a->desc->blocks_per_arena) 536 | { 537 | uint32_t block_idx; 538 | for (block_idx = 0; block_idx < a->desc->blocks_per_arena; block_idx++ ) 539 | { 540 | struct mem_block* b = arena2block(a, block_idx); 541 | ASSERT(elem_find(&a->desc->free_list, &b->free_elem)); 542 | list_remove(&b->free_elem); 543 | } 544 | mfree_page(PF, a, 1); 545 | } 546 | } 547 | lock_release(&mem_pool->lock); 548 | } 549 | } 550 | 551 | void free_a_phy_addr(uint32_t pg_phy_addr) 552 | { 553 | struct pool *mem_pool; 554 | uint32_t bit_idx = 0; 555 | 556 | if (pg_phy_addr >= user_pool.phy_addr_start) 557 | { 558 | mem_pool = &user_pool; 559 | bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE; 560 | } 561 | else 562 | { 563 | mem_pool = &kernel_pool; 564 | bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE; 565 | } 566 | 567 | bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0); 568 | } 569 | 570 | // 初始化内存池 571 | static void mem_pool_init(uint32_t all_mem) 572 | { 573 | put_str(" mem_pool_init start\n"); 574 | 575 | uint32_t page_table_size = PG_SIZE * 256; 576 | 577 | uint32_t used_mem = page_table_size + 0x100000; 578 | 579 | uint32_t free_mem = all_mem - used_mem; 580 | uint16_t all_free_pages = free_mem / PG_SIZE; 581 | 582 | uint16_t kernel_free_pages = all_free_pages / 2; 583 | uint16_t user_free_pages = all_free_pages - kernel_free_pages; 584 | 585 | // 内核的位图大小,在位图中,1bit表示1页 586 | uint32_t kbm_length = kernel_free_pages / 8; 587 | uint32_t ubm_length = user_free_pages / 8; 588 | 589 | // 内核内存池的起始地址 590 | uint32_t kp_start = used_mem; 591 | 592 | // 用户内存池的起始地址 593 | uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE; 594 | 595 | kernel_pool.phy_addr_start = kp_start; 596 | user_pool.phy_addr_start = up_start; 597 | 598 | kernel_pool.pool_size = kernel_free_pages * PG_SIZE; 599 | user_pool.pool_size = user_free_pages * PG_SIZE; 600 | 601 | kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length; 602 | user_pool.pool_bitmap.btmp_bytes_len = ubm_length; 603 | 604 | kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE; 605 | user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length); 606 | 607 | // 输出内存信息 608 | put_str(" kernel_pool_bitmap_start:"); 609 | put_int((int)kernel_pool.pool_bitmap.bits); 610 | put_str(" kernel_pool_phy_addr_start:"); 611 | put_int(kernel_pool.phy_addr_start); 612 | put_str("\n"); 613 | put_str(" user_pool_bitmap_start:"); 614 | put_int((int)user_pool.pool_bitmap.bits); 615 | put_str(" user_pool_phy_addr_start:"); 616 | put_int(user_pool.phy_addr_start); 617 | put_str("\n"); 618 | 619 | // 将位图置0 620 | bitmap_init(&kernel_pool.pool_bitmap); 621 | bitmap_init(&user_pool.pool_bitmap); 622 | 623 | lock_init(&kernel_pool.lock); 624 | lock_init(&user_pool.lock); 625 | 626 | kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length; 627 | kernel_vaddr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length); 628 | 629 | kernel_vaddr.vaddr_start = K_HEAP_START; 630 | bitmap_init(&kernel_vaddr.vaddr_bitmap); 631 | put_str(" mem_pool_init done\n"); 632 | } 633 | 634 | void mem_init() 635 | { 636 | put_str("mem_init start\n"); 637 | 638 | // 物理内存的大小放在地址0xb00处 639 | uint32_t mem_bytes_total = *((uint32_t*)0xc0000b00); 640 | 641 | mem_pool_init(mem_bytes_total); 642 | block_desc_init(k_block_descs); 643 | put_str("mem_init done\n"); 644 | } 645 | 646 | 647 | 648 | -------------------------------------------------------------------------------- /kernel/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_MEMORY_H_ 2 | #define _KERNEL_MEMORY_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../lib/kernel/bitmap.h" 6 | #include "../lib/kernel/list.h" 7 | 8 | 9 | // 页表或页目录存在位 10 | #define PG_P_1 1 11 | #define PG_P_0 0 12 | 13 | // R/W属性位 14 | #define PG_RW_R 0 15 | #define PG_RW_W 2 16 | 17 | // 用户级/系统级 属性位 18 | #define PG_US_S 0 19 | #define PG_US_U 4 20 | 21 | // 内存块描述符个数 22 | #define DESC_CNT 7 23 | 24 | 25 | enum pool_flags 26 | { 27 | PF_KERNEL=1, 28 | PF_USER 29 | }; 30 | 31 | struct mem_block 32 | { 33 | struct list_elem free_elem; 34 | }; 35 | 36 | // 内存块描述符 37 | struct mem_block_desc 38 | { 39 | uint32_t block_size; // 内存块规格 40 | uint32_t blocks_per_arena; //本arena中可容纳mem_block的数量 41 | struct list free_list; // 目前可用的mem_block链表 42 | }; 43 | 44 | struct virtual_addr 45 | { 46 | struct bitmap vaddr_bitmap; 47 | uint32_t vaddr_start; 48 | }; 49 | 50 | extern struct pool kernel_pool, user_pool; 51 | 52 | 53 | void mem_init(void); 54 | void* get_kernel_pages(uint32_t pg_cnt); 55 | void* malloc_page(enum pool_flags pf, uint32_t pg_cnt); 56 | void malloc_init(void); 57 | uint32_t* pte_ptr(uint32_t vaddr); 58 | uint32_t* pde_ptr(uint32_t vaddr); 59 | uint32_t addr_v2p(uint32_t vaddr); 60 | void* get_a_page(enum pool_flags pf, uint32_t vaddr); 61 | void* get_user_pages(uint32_t pg_cnt); 62 | void block_desc_init(struct mem_block_desc *desc_array); 63 | void *sys_malloc(uint32_t size); 64 | void pfree(uint32_t pg_phy_addr); 65 | void sys_free(void* ptr); 66 | void *get_a_page_without_opvaddrbitmap(enum pool_flags pf, uint32_t vaddr); 67 | void free_a_phy_addr(uint32_t pg_phy_addr); 68 | #endif // !_KERNEL_MEMORY_H_ -------------------------------------------------------------------------------- /lib/kernel/bitmap.c: -------------------------------------------------------------------------------- 1 | #include "bitmap.h" 2 | #include "../stdint.h" 3 | #include "../string.h" 4 | #include "../../kernel/interrupt.h" 5 | #include "print.h" 6 | #include "../../kernel/debug.h" 7 | 8 | // 初始化位图,将位图中的数据清0 9 | void bitmap_init(struct bitmap *btmp) 10 | { 11 | memset(btmp->bits, 0, btmp->btmp_bytes_len); 12 | } 13 | 14 | // 判断位图中的第bit_idx位是否为1, 如果是,返回true 15 | bool bitmap_scan_test(struct bitmap *btmp, uint32_t bit_idx) 16 | { 17 | uint32_t byte_idx = bit_idx / 8; // 位图中bit_idx所在的byte索引 18 | uint32_t bit_odd = bit_idx % 8; // 该bit在 1byte中的偏移量 19 | 20 | return btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd); 21 | } 22 | 23 | // 在位图中申请连续的cnt位个空间,成功返回在位图中的起始下标 24 | int bitmap_scan(struct bitmap *btmp, uint32_t cnt) 25 | { 26 | uint32_t idx_byte = 0; 27 | 28 | while((0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) 29 | {// 判断该字节是否全部为1, 且位图大小必须 小于 要申请的空间 30 | // 代表该字节已全部占用,继续向下字节查找 31 | ++idx_byte; 32 | } 33 | 34 | ASSERT(idx_byte < btmp->btmp_bytes_len); 35 | 36 | // 内存池中找不到可用空间 37 | if (idx_byte == btmp->btmp_bytes_len) 38 | { 39 | return -1; 40 | } 41 | 42 | // 在idx_byte中有空闲位后,对该byte进行逐bit比对,直到有连续的bits=cnt 43 | int idx_bit = 0; 44 | while ((uint8_t)(BITMAP_MASK << idx_bit) & btmp->bits[idx_byte]) 45 | {// 找到idx_byte中为0的bit所在的位置 46 | ++idx_bit; 47 | } 48 | 49 | int bit_idx_start = idx_byte * 8 + idx_bit; // 空闲位在位图中的bit偏移量 50 | 51 | if(cnt == 1) 52 | { 53 | return bit_idx_start; 54 | } 55 | 56 | // 位图中还剩余的bits数 57 | uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start); 58 | 59 | uint32_t next_bit = bit_idx_start + 1; 60 | uint32_t count = 1; // 记录找到的空闲位数量 61 | 62 | bit_idx_start = -1; 63 | // 在剩余的空间中继续查找,直到有连续的bits=cnt 64 | while (bit_left-- > 0) 65 | { 66 | if(!bitmap_scan_test(btmp, next_bit)) 67 | { 68 | ++count; 69 | } 70 | else 71 | { 72 | count = 0; 73 | } 74 | 75 | if(count == cnt) 76 | { 77 | bit_idx_start = next_bit - cnt + 1; 78 | } 79 | 80 | ++next_bit; 81 | } 82 | return bit_idx_start; 83 | } 84 | 85 | // 将位图中的bit_idx位 设置为value 86 | void bitmap_set(struct bitmap *btmp, uint32_t bit_idx, int8_t value) 87 | { 88 | ASSERT(value == 0 || value == 1); 89 | 90 | uint32_t byte_idx = bit_idx / 8; 91 | uint32_t bit_odd = bit_idx % 8; 92 | 93 | if(value) 94 | { 95 | btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd); 96 | } 97 | else 98 | { 99 | btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd); 100 | } 101 | } -------------------------------------------------------------------------------- /lib/kernel/bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_KERNEL_BITMAP_H_ 2 | #define _LIB_KERNEL_BITMAP_H_ 3 | 4 | #include "../stdint.h" 5 | #include "../../kernel/global.h" 6 | 7 | #define BITMAP_MASK 1 // 通过按位与的方式判断位图中的相应位是否位1 8 | 9 | // 10 | struct bitmap 11 | { 12 | uint32_t btmp_bytes_len; 13 | uint8_t *bits; 14 | }; 15 | 16 | // 初始化位图,将位图中的数据清0 17 | void bitmap_init(struct bitmap *bitmp); 18 | 19 | // 判断位图中的第bit_idx位是否为1, 如果是,返回true 20 | bool bitmap_scan_test(struct bitmap *btmp, uint32_t bit_idx); 21 | 22 | // 在位图中申请连续的cnt位个空间,成功 返回在位图中的起始下标(单位bit) 23 | int bitmap_scan(struct bitmap *btmp, uint32_t cnt); 24 | 25 | // 将位图中的bit_idx位 设置为value 26 | void bitmap_set(struct bitmap *btmp, uint32_t bit_idx, int8_t value); 27 | 28 | 29 | #endif // !_LIB_KERNEL_BITMAP_H_ -------------------------------------------------------------------------------- /lib/kernel/io.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_KERNEL_IO_H_ 2 | #define _LIB_KERNEL_IO_H_ 3 | 4 | #include "../stdint.h" 5 | 6 | // 向端口port写入一字节数据 7 | static inline void outb(uint16_t port, uint8_t data) 8 | { 9 | // 对端口指定N表示0~255, d表示用dx存储端口号 10 | // %b0表示对应al, %w1表示对应dx 11 | asm volatile("out %b0, %w1"::"a"(data),"Nd"(port)); 12 | } 13 | 14 | // 将addr处起始的word_cnt个字节写入端口port 15 | static inline void outsw(uint16_t port, const void *addr, uint32_t word_cnt) 16 | { 17 | // outsw是把ds:esi处的16位的内容写入port端口 18 | asm volatile("cld; rep outsw":"+S"(addr), "+c"(word_cnt):"d"(port)); 19 | } 20 | 21 | // 将从端口port读入的一个字节返回 22 | static inline uint8_t inb(uint16_t port) 23 | { 24 | uint8_t data; 25 | asm volatile("inb %w1, %b0":"=a"(data):"Nd"(port)); 26 | return data; 27 | } 28 | 29 | // 将从端口port读入的word_cnt个字节写入addr 30 | static inline void insw(uint16_t port, void *addr, uint32_t word_cnt) 31 | { 32 | asm volatile("cld; rep insw":"+D"(addr), "+c"(word_cnt):"d"(port):"memory"); 33 | } 34 | 35 | #endif //!_LIB_KERNEL_IO_H_ -------------------------------------------------------------------------------- /lib/kernel/list.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | #include "../../kernel/interrupt.h" 3 | 4 | // 初始化双向链表list 5 | void list_init (struct list* list) 6 | { 7 | list->head.prev = NULL; 8 | list->head.next = &list->tail; 9 | list->tail.prev = &list->head; 10 | list->tail.next = NULL; 11 | } 12 | 13 | // 把链表元素elem插入在元素before之前 14 | void list_insert_before(struct list_elem* before, struct list_elem* elem) 15 | { 16 | enum intr_status old_status = intr_disable(); 17 | 18 | // 将before前驱元素的后继元素更新为elem, 暂时使before脱离链表 19 | before->prev->next = elem; 20 | 21 | // 更新elem自己的前驱结点为before的前驱, 22 | // 更新elem自己的后继结点为before, 于是before又回到链表 23 | elem->prev = before->prev; 24 | elem->next = before; 25 | 26 | // 更新before的前驱结点为elem 27 | before->prev = elem; 28 | 29 | intr_set_status(old_status); 30 | } 31 | 32 | // 添加元素到列表队首,类似栈push操作 33 | void list_push(struct list* plist, struct list_elem* elem) 34 | { 35 | list_insert_before(plist->head.next, elem); 36 | } 37 | 38 | // 追加元素到链表队尾,类似队列的先进先出操作 39 | void list_append(struct list* plist, struct list_elem* elem) 40 | { 41 | list_insert_before(&plist->tail, elem); 42 | } 43 | 44 | // 使元素pelem脱离链表 45 | void list_remove(struct list_elem* pelem) 46 | { 47 | enum intr_status old_status = intr_disable(); 48 | 49 | pelem->prev->next = pelem->next; 50 | pelem->next->prev = pelem->prev; 51 | 52 | intr_set_status(old_status); 53 | } 54 | 55 | // 将链表第一个元素弹出并返回,类似栈的pop操作 56 | struct list_elem* list_pop(struct list* plist) 57 | { 58 | struct list_elem* elem = plist->head.next; 59 | list_remove(elem); 60 | return elem; 61 | } 62 | 63 | // 从链表中查找元素obj_elem,成功时返回true,失败时返回false 64 | bool elem_find(struct list* plist, struct list_elem* obj_elem) 65 | { 66 | struct list_elem* elem = plist->head.next; 67 | while (elem != &plist->tail) 68 | { 69 | if (elem == obj_elem) 70 | { 71 | return true; 72 | } 73 | elem = elem->next; 74 | } 75 | return false; 76 | } 77 | 78 | /* 把列表plist中的每个元素elem和arg传给回调函数func, 79 | * arg给func用来判断elem是否符合条件. 80 | * 本函数的功能是遍历列表内所有元素,逐个判断是否有符合条件的元素。 81 | * 找到符合条件的元素返回元素指针,否则返回NULL. */ 82 | struct list_elem* list_traversal(struct list* plist, function func, int arg) 83 | { 84 | struct list_elem* elem = plist->head.next; 85 | // 如果队列为空,就必然没有符合条件的结点,故直接返回NULL 86 | if (list_empty(plist)) 87 | { 88 | return NULL; 89 | } 90 | while (elem != &plist->tail) 91 | { 92 | if (func(elem, arg)) 93 | { 94 | // func返回ture则认为该元素在回调函数中符合条件,命中,故停止继续遍历 95 | return elem; 96 | } 97 | // 若回调函数func返回true,则继续遍历 98 | elem = elem->next; 99 | } 100 | return NULL; 101 | } 102 | 103 | /* 返回链表长度 */ 104 | uint32_t list_len(struct list* plist) 105 | { 106 | struct list_elem* elem = plist->head.next; 107 | uint32_t length = 0; 108 | while (elem != &plist->tail) 109 | { 110 | length++; 111 | elem = elem->next; 112 | } 113 | 114 | return length; 115 | } 116 | 117 | // 判断链表是否为空,空时返回true,否则返回false 118 | bool list_empty(struct list* plist) 119 | { 120 | // 判断队列是否为空 121 | return (plist->head.next == &plist->tail ? true : false); 122 | } -------------------------------------------------------------------------------- /lib/kernel/list.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_KERNEL_THREAD_H_ 2 | #define _LIB_KERNEL_THREAD_H_ 3 | 4 | #include "../stdint.h" 5 | #include "../../kernel/global.h" 6 | 7 | #define offset(struct_type, member) (int)(&((struct_type*)0)->member) 8 | #define elem2entry(struct_type, struct_member_name, elem_ptr) \ 9 | (struct_type*)((int)elem_ptr - offset(struct_type, struct_member_name)) 10 | 11 | struct list_elem 12 | { 13 | struct list_elem *prev; 14 | struct list_elem *next; 15 | }; 16 | 17 | struct list 18 | { 19 | struct list_elem head; 20 | struct list_elem tail; 21 | }; 22 | 23 | typedef bool (function)(struct list_elem*, int art); 24 | 25 | void list_init (struct list*); 26 | void list_insert_before(struct list_elem* before, struct list_elem* elem); 27 | void list_push(struct list* plist, struct list_elem* elem); void list_iterate(struct list* plist); 28 | void list_append(struct list* plist, struct list_elem* elem); 29 | void list_remove(struct list_elem* pelem); 30 | struct list_elem* list_pop(struct list* plist); 31 | bool list_empty(struct list* plist); 32 | uint32_t list_len(struct list* plist); 33 | struct list_elem* list_traversal(struct list* plist, function func, int arg); 34 | bool elem_find(struct list* plist, struct list_elem* obj_elem); 35 | 36 | #endif //!_LIB_KERNEL_THREAD_H_ -------------------------------------------------------------------------------- /lib/kernel/print.S: -------------------------------------------------------------------------------- 1 | ;--------------打印功能-------- 2 | ; 备份寄存器现场 3 | ; 获取光标位置 4 | ; 获取代打印的字符 5 | ; 判断是否需要滚屏 6 | ; 更新光标位置 7 | ; 恢复寄存器现场 8 | ;----------------------------- 9 | 10 | 11 | TI_GDT equ 0 12 | RPL0 equ 0 13 | SELECTOR_VIDEO equ (0x3 << 3) + TI_GDT + RPL0 14 | 15 | section .data 16 | put_int_buffer dq 0 17 | 18 | [bits 32] 19 | ;--------put_char------------- 20 | ; 把栈中的1个字符写入光标所在处 21 | ;----------------------------- 22 | section .text 23 | 24 | ;--------put_str------- 25 | global put_str 26 | put_str: 27 | push ebx 28 | push ecx 29 | xor ecx, ecx 30 | mov ebx, [esp + 12];获取待打印的字符串地址 31 | .goon: 32 | mov cl, [ebx] 33 | cmp cl, 0 34 | jz .str_over 35 | push ecx 36 | call put_char 37 | add esp, 4 38 | inc ebx 39 | jmp .goon 40 | .str_over: 41 | pop ecx 42 | pop ebx 43 | ret 44 | 45 | 46 | ;-------put_char-------- 47 | global put_char 48 | put_char: 49 | pushad 50 | mov ax, SELECTOR_VIDEO 51 | mov gs, ax 52 | 53 | ;获取光标位置 54 | ;-------------------- 55 | 56 | ;先获取高8位 57 | mov dx, 0x03d4 58 | mov al, 0x0e 59 | out dx, al 60 | mov dx, 0x03d5 61 | in al, dx 62 | mov ah, al 63 | 64 | ;再获取低8位 65 | mov dx, 0x03d4 66 | mov al, 0x0f 67 | out dx, al 68 | mov dx, 0x03d5 69 | in al, dx 70 | 71 | ;将光标存入bx 72 | mov bx, ax 73 | ; 74 | ;在栈中获取待打印的字符 pushad圧入4×8=32字节+主调函数返回地址=36 75 | mov ecx, [esp + 36] 76 | 77 | cmp cl, 0xd ;CR=0xd LF=0xa 78 | jz .is_carriage_return 79 | cmp cl, 0xa 80 | jz .is_line_feed 81 | 82 | cmp cl, 0x8 ;BS=0x8 83 | jz .is_backspace 84 | jmp .put_other 85 | 86 | 87 | .is_backspace: 88 | ;删除前一个字符,将光标前移一位,然后将当前光标所在处填充0 89 | dec bx 90 | shl bx, 1 91 | 92 | mov byte [gs:bx], 0x20 93 | inc bx 94 | mov byte [gs:bx], 0x07 95 | shr bx, 1 96 | jmp .set_cursor 97 | 98 | .put_other: 99 | shl bx, 1 100 | mov [gs:bx], cl 101 | inc bx 102 | mov byte [gs:bx], 0x7 103 | shr bx, 1 104 | inc bx 105 | cmp bx, 2000 106 | jl .set_cursor 107 | 108 | 109 | .is_line_feed: 110 | .is_carriage_return: 111 | ;CR(\r) LF(\n) 只需要将光标移到下一行行首 112 | xor dx, dx 113 | mov ax, bx 114 | mov si, 80 115 | 116 | div si 117 | sub bx, dx 118 | 119 | .is_carriage_return_end: 120 | add bx, 80 121 | cmp bx, 2000 122 | .is_line_feed_end: 123 | jl .set_cursor 124 | 125 | .roll_screen: ;超出屏幕大小时,开始滚屏,即将所有字符向上移动一行,将最后一行空出来 126 | cld 127 | 128 | mov ecx, 960 ;1920个字符, 1920×2=3840字节,一次4字节,共3840/4=960次 129 | mov esi, 0xc00b80a0 ;第一行行首 130 | mov edi, 0xc00b8000 ;第0行行首 131 | rep movsd 132 | 133 | mov ebx, 3840 134 | mov ecx, 80 135 | 136 | .cls: 137 | mov word [gs:ebx], 0x0720 138 | add ebx, 2 139 | loop .cls 140 | mov bx, 1920 141 | 142 | .set_cursor: 143 | mov dx, 0x03d4 144 | mov al, 0x0e 145 | out dx, al 146 | mov dx, 0x03d5 147 | mov al, bh 148 | out dx, al 149 | 150 | mov dx, 0x03d4 151 | mov al, 0x0f 152 | out dx, al 153 | mov dx, 0x03d5 154 | mov al, bl 155 | out dx, al 156 | .put_char_done: 157 | popad 158 | ret 159 | 160 | global cls_screen 161 | cls_screen: 162 | pushad 163 | ;;;;;;;;;;;;;;; 164 | ; 由于用户程序的cpl为3,显存段的dpl为0,故用于显存段的选择子gs在低于自己特权的环境中为0, 165 | ; 导致用户程序再次进入中断后,gs为0,故直接在put_str中每次都为gs赋值. 166 | mov ax, SELECTOR_VIDEO ; 不能直接把立即数送入gs,须由ax中转 167 | mov gs, ax 168 | 169 | mov ebx, 0 170 | mov ecx, 80*25 171 | .cls: 172 | mov word [gs:ebx], 0x0720 ;0x0720是黑底白字的空格键 173 | add ebx, 2 174 | loop .cls 175 | mov ebx, 0 176 | 177 | .set_cursor: ;直接把set_cursor搬过来用,省事 178 | ;;; ;;;; 1 先设置高8位 ;;;;;;;; 179 | mov dx, 0x03d4 ;索引寄存器 180 | mov al, 0x0e ;用于提供光标位置的高8位 181 | out dx, al 182 | mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 183 | mov al, bh 184 | out dx, al 185 | 186 | ;;; ;;;; 2 再设置低8位 ;;;;;;;;; 187 | mov dx, 0x03d4 188 | mov al, 0x0f 189 | out dx, al 190 | mov dx, 0x03d5 191 | mov al, bl 192 | out dx, al 193 | popad 194 | ret 195 | 196 | ;-------------------- 将小端字节序的数字变成对应的ascii后,倒置 ----------------------- 197 | ;输入:栈中参数为待打印的数字 198 | ;输出:在屏幕上打印16进制数字,并不会打印前缀0x,如打印10进制15时,只会直接打印f,不会是0xf 199 | ;------------------------------------------------------------------------------------------ 200 | global put_int 201 | put_int: 202 | pushad 203 | mov ebp, esp 204 | mov eax, [ebp+4*9] ; call的返回地址占4字节+pushad的8个4字节 205 | mov edx, eax 206 | mov edi, 7 ; 指定在put_int_buffer中初始的偏移量 207 | mov ecx, 8 ; 32位数字中,16进制数字的位数是8个 208 | mov ebx, put_int_buffer 209 | 210 | ;将32位数字按照16进制的形式从低位到高位逐个处理,共处理8个16进制数字 211 | .16based_4bits: ; 每4位二进制是16进制数字的1位,遍历每一位16进制数字 212 | and edx, 0x0F ; 解析16进制数字的每一位。and与操作后,edx只有低4位有效 213 | cmp edx, 9 ; 数字0~9和a~f需要分别处理成对应的字符 214 | jg .is_A2F 215 | add edx, '0' ; ascii码是8位大小。add求和操作后,edx低8位有效。 216 | jmp .store 217 | .is_A2F: 218 | sub edx, 10 ; A~F 减去10 所得到的差,再加上字符A的ascii码,便是A~F对应的ascii码 219 | add edx, 'A' 220 | 221 | ;将每一位数字转换成对应的字符后,按照类似“大端”的顺序存储到缓冲区put_int_buffer 222 | ;高位字符放在低地址,低位字符要放在高地址,这样和大端字节序类似,只不过咱们这里是字符序. 223 | .store: 224 | ; 此时dl中应该是数字对应的字符的ascii码 225 | mov [ebx+edi], dl 226 | dec edi 227 | shr eax, 4 228 | mov edx, eax 229 | loop .16based_4bits 230 | 231 | ;现在put_int_buffer中已全是字符,打印之前, 232 | ;把高位连续的字符去掉,比如把字符000123变成123 233 | .ready_to_print: 234 | inc edi ; 此时edi退减为-1(0xffffffff),加1使其为0 235 | .skip_prefix_0: 236 | cmp edi,8 ; 若已经比较第9个字符了,表示待打印的字符串为全0 237 | je .full0 238 | ;找出连续的0字符, edi做为非0的最高位字符的偏移 239 | .go_on_skip: 240 | mov cl, [put_int_buffer+edi] 241 | inc edi 242 | cmp cl, '0' 243 | je .skip_prefix_0 ; 继续判断下一位字符是否为字符0(不是数字0) 244 | dec edi ;edi在上面的inc操作中指向了下一个字符,若当前字符不为'0',要恢复edi指向当前字符 245 | jmp .put_each_num 246 | 247 | 248 | .full0: 249 | mov cl,'0' ; 输入的数字为全0时,则只打印0 250 | .put_each_num: 251 | push ecx ; 此时cl中为可打印的字符 252 | call put_char 253 | add esp, 4 254 | inc edi ; 使edi指向下一个字符 255 | mov cl, [put_int_buffer+edi] ; 获取下一个字符到cl寄存器 256 | cmp edi,8 257 | jl .put_each_num 258 | popad 259 | ret 260 | 261 | 262 | global set_cursor 263 | set_cursor: 264 | pushad 265 | mov bx, [esp+36] 266 | ;;;;;;; 1 先设置高8位 ;;;;;;;; 267 | mov dx, 0x03d4 ;索引寄存器 268 | mov al, 0x0e ;用于提供光标位置的高8位 269 | out dx, al 270 | mov dx, 0x03d5 ;通过读写数据端口0x3d5来获得或设置光标位置 271 | mov al, bh 272 | out dx, al 273 | 274 | ;;;;;;; 2 再设置低8位 ;;;;;;;;; 275 | mov dx, 0x03d4 276 | mov al, 0x0f 277 | out dx, al 278 | mov dx, 0x03d5 279 | mov al, bl 280 | out dx, al 281 | popad 282 | ret -------------------------------------------------------------------------------- /lib/kernel/print.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_KERNEL_PRINT_H_ 2 | #define _LIB_KERNEL_PRINT_H_ 3 | 4 | #include "../stdint.h" 5 | 6 | void put_char(const char ch_asci); 7 | void put_str(const char *str); 8 | void put_int(unsigned int num); 9 | void set_cursor(uint32_t cursor_pos); 10 | 11 | #endif //!_LIB_KERNEL_PRINT_H_ -------------------------------------------------------------------------------- /lib/kernel/stdio-kernel.c: -------------------------------------------------------------------------------- 1 | #include "stdio-kernel.h" 2 | #include "print.h" 3 | #include "../stdio.h" 4 | #include "../../device/console.h" 5 | #include "../../kernel/global.h" 6 | 7 | #define va_start(args, first_fix) args = (va_list)&first_fix 8 | #define va_end(args) args = NULL 9 | 10 | /* 供内核使用的格式化输出函数 */ 11 | void printk(const char* format, ...) 12 | { 13 | va_list args; 14 | va_start(args, format); 15 | char buf[1024] = {0}; 16 | vsprintf(buf, format, args); 17 | va_end(args); 18 | console_put_str(buf); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /lib/kernel/stdio-kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_KERNEL_STDIOSYS_H_ 2 | #define _LIB_KERNEL_STDIOSYS_H_ 3 | 4 | #include "../stdint.h" 5 | 6 | void printk(const char* format, ...); 7 | 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /lib/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_STDINT_H_ 2 | #define _LIB_STDINT_H_ 3 | 4 | typedef signed char int8_t; 5 | typedef signed short int int16_t; 6 | typedef signed int int32_t; 7 | typedef signed long long int int64_t; 8 | typedef unsigned char uint8_t; 9 | typedef unsigned short int uint16_t; 10 | typedef unsigned int uint32_t; 11 | typedef unsigned long long int uint64_t; 12 | 13 | 14 | #endif //!_LIB_STDINT_H_ -------------------------------------------------------------------------------- /lib/stdio.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "../kernel/interrupt.h" 3 | #include "../kernel/global.h" 4 | #include "string.h" 5 | #include "./user/syscall.h" 6 | #include "./kernel/print.h" 7 | #include "../fs/file.h" 8 | 9 | #define va_start(ap, v) ap = (va_list)&v // 把ap指向第一个固定参数v 10 | #define va_arg(ap, t) *((t *)(ap += 4)) // ap指向下一个参数并返回其值 11 | #define va_end(ap) ap = NULL // 清除ap 12 | 13 | /* 将整型转换成字符(integer to ascii) */ 14 | static void itoa(uint32_t value, char **buf_ptr_addr, uint8_t base) 15 | { 16 | uint32_t m = value % base; // 求模,最先掉下来的是最低位 17 | uint32_t i = value / base; // 取整 18 | if (i) 19 | { // 如果倍数不为0则递归调用。 20 | itoa(i, buf_ptr_addr, base); 21 | } 22 | if (m < 10) 23 | { // 如果余数是0~9 24 | *((*buf_ptr_addr)++) = m + '0'; // 将数字0~9转换为字符'0'~'9' 25 | } 26 | else 27 | { // 否则余数是A~F 28 | *((*buf_ptr_addr)++) = m - 10 + 'A'; // 将数字A~F转换为字符'A'~'F' 29 | } 30 | } 31 | 32 | /* 将参数ap按照格式format输出到字符串str,并返回替换后str长度 */ 33 | uint32_t vsprintf(char *str, const char *format, va_list ap) 34 | { 35 | char *buf_ptr = str; 36 | const char *index_ptr = format; 37 | char index_char = *index_ptr; 38 | int32_t arg_int; 39 | char *arg_str; 40 | while (index_char) 41 | { 42 | if (index_char != '%') 43 | { 44 | *(buf_ptr++) = index_char; 45 | index_char = *(++index_ptr); 46 | continue; 47 | } 48 | index_char = *(++index_ptr); // 得到%后面的字符 49 | switch (index_char) 50 | { 51 | case 's': 52 | arg_str = va_arg(ap, char *); 53 | strcpy(buf_ptr, arg_str); 54 | buf_ptr += strlen(arg_str); 55 | index_char = *(++index_ptr); 56 | break; 57 | 58 | case 'c': 59 | *(buf_ptr++) = va_arg(ap, char); 60 | index_char = *(++index_ptr); 61 | break; 62 | 63 | case 'd': 64 | arg_int = va_arg(ap, int); 65 | /* 若是负数, 将其转为正数后,再正数前面输出个负号'-'. */ 66 | if (arg_int < 0) 67 | { 68 | arg_int = 0 - arg_int; 69 | *buf_ptr++ = '-'; 70 | } 71 | itoa(arg_int, &buf_ptr, 10); 72 | index_char = *(++index_ptr); 73 | break; 74 | 75 | case 'x': 76 | arg_int = va_arg(ap, int); 77 | itoa(arg_int, &buf_ptr, 16); 78 | index_char = *(++index_ptr); // 跳过格式字符并更新index_char 79 | break; 80 | } 81 | } 82 | return strlen(str); 83 | } 84 | 85 | /* 同printf不同的地方就是字符串不是写到终端,而是写到buf中 */ 86 | uint32_t sprintf(char *buf, const char *format, ...) 87 | { 88 | va_list args; 89 | uint32_t retval; 90 | va_start(args, format); 91 | retval = vsprintf(buf, format, args); 92 | va_end(args); 93 | return retval; 94 | } 95 | 96 | /* 格式化输出字符串format */ 97 | uint32_t printf(const char *format, ...) 98 | { 99 | va_list args; 100 | va_start(args, format); // 使args指向format 101 | char buf[1024] = {0}; // 用于存储拼接后的字符串 102 | vsprintf(buf, format, args); 103 | va_end(args); 104 | return write(stdout_no, buf, strlen(buf)); 105 | } 106 | -------------------------------------------------------------------------------- /lib/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_STDIO_H_ 2 | #define _LIB_STDIO_H_ 3 | #include "stdint.h" 4 | 5 | typedef char* va_list; 6 | 7 | uint32_t printf(const char* str, ...); 8 | uint32_t vsprintf(char* str, const char* format, va_list ap); 9 | uint32_t sprintf(char *buf, const char *format, ...); 10 | #endif 11 | -------------------------------------------------------------------------------- /lib/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "../kernel/debug.h" 3 | #include "../kernel/global.h" 4 | 5 | void memset(void *dest, uint8_t value, uint32_t size) 6 | { 7 | ASSERT(dest != NULL); 8 | uint8_t *dest_tmp = (uint8_t *)dest; 9 | 10 | while(size-- > 0) 11 | { 12 | *dest_tmp++ = value; 13 | } 14 | } 15 | 16 | void memcpy(void *dest, const void *src, uint32_t size) 17 | { 18 | ASSERT(dest != NULL && src != NULL); 19 | 20 | uint8_t *dest_tmp = (uint8_t*)dest; 21 | const uint8_t *src_tmp = (const uint8_t *)src; 22 | 23 | while(size-- > 0) 24 | { 25 | *dest_tmp++ = *src_tmp++; 26 | } 27 | } 28 | 29 | int memcmp(const void *a, const void *b, uint32_t size) 30 | { 31 | const char * a_tmp = (const char *)a; 32 | const char *b_tmp = (const char *)b; 33 | 34 | ASSERT(a != NULL || b != NULL); 35 | 36 | while (size-- > 0) 37 | { 38 | if(*a_tmp != *b_tmp) 39 | { 40 | return *a_tmp > *b_tmp ? 1 : -1; 41 | } 42 | ++a_tmp; 43 | ++b_tmp; 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | char *strcpy(char *dest, const char *src) 50 | { 51 | ASSERT(dest != NULL && src != NULL); 52 | char *ret = dest; 53 | 54 | while((*dest++ = *src++)); 55 | return ret; 56 | } 57 | 58 | uint32_t strlen(const char *str) 59 | { 60 | ASSERT(str != NULL); 61 | uint32_t len = 0; 62 | 63 | while(*str++) 64 | { 65 | ++len; 66 | } 67 | return len; 68 | } 69 | 70 | int strcmp(const char *a, const char *b) 71 | { 72 | ASSERT(a != NULL && b != NULL); 73 | while(*a && *a == *b) 74 | { 75 | ++a; 76 | ++b; 77 | } 78 | 79 | return *a < *b ? -1 : *a > *b; 80 | } 81 | 82 | char *strchr(const char *str, const char ch) 83 | { 84 | ASSERT(str != NULL); 85 | while(*str) 86 | { 87 | if(*str == ch) 88 | return (char *)str; 89 | 90 | ++str; 91 | } 92 | return NULL; 93 | } 94 | 95 | char *strrchr(const char *str, const char ch) 96 | { 97 | ASSERT(str != NULL); 98 | 99 | char *last_char = NULL; 100 | 101 | while(*str) 102 | { 103 | if(*str == ch) 104 | last_char = str; 105 | 106 | ++str; 107 | } 108 | 109 | return last_char; 110 | } 111 | 112 | char *strcat(char *dest, const char *src) 113 | { 114 | ASSERT(dest != NULL && src != NULL); 115 | 116 | char *str = dest; 117 | while (*++str); 118 | 119 | --str; 120 | 121 | while((*str++ = *src++)); 122 | 123 | return dest; 124 | } 125 | 126 | uint32_t strchrs(const char *str, char ch) 127 | { 128 | ASSERT(str != NULL); 129 | 130 | uint32_t ch_cnt = 0; 131 | 132 | while (*str) 133 | { 134 | if(*str == ch) 135 | ++ch_cnt; 136 | 137 | ++str; 138 | } 139 | return ch_cnt; 140 | } -------------------------------------------------------------------------------- /lib/string.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_STRING_H_ 2 | #define _LIB_STRING_H_ 3 | 4 | #include "stdint.h" 5 | 6 | 7 | void memset(void *dest, uint8_t value, uint32_t size); 8 | 9 | void memcpy(void *dest, const void *src, uint32_t size); 10 | 11 | int memcmp(const void *a, const void *b, uint32_t size); 12 | 13 | char *strcpy(char *dest, const char *src); 14 | 15 | uint32_t strlen(const char *str); 16 | 17 | int strcmp(const char *a, const char *b); 18 | 19 | char *strchr(const char *str, const char ch); 20 | 21 | char *strrchr(const char *str, const char ch); 22 | 23 | char *strcat(char *dest, const char *src); 24 | 25 | uint32_t strchrs(const char *str, char ch); 26 | 27 | #endif //!_LIB_STRING_H_ -------------------------------------------------------------------------------- /lib/user/assert.c: -------------------------------------------------------------------------------- 1 | #include "assert.h" 2 | #include "../stdio.h" 3 | void user_spin(char* filename, int line, const char* func, const char* condition) 4 | { 5 | printf("\n\n\n\nfilename %s\nline %d\nfunction %s\ncondition %s\n", filename, line, func, condition); 6 | while(1); 7 | } 8 | -------------------------------------------------------------------------------- /lib/user/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_USER_ASSERT_H_ 2 | #define _LIB_USER_ASSERT_H_ 3 | 4 | #define NULL ((void*)0) 5 | 6 | void user_spin(char* filename, int line, const char* func, const char* condition); 7 | #define panic(...) user_spin(__FILE__, __LINE__, __func__, __VA_ARGS__) 8 | 9 | #ifdef NDEBUG 10 | #define assert(CONDITION) ((void)0) 11 | #else 12 | #define assert(CONDITION) \ 13 | if (!(CONDITION)) { \ 14 | panic(#CONDITION); \ 15 | } 16 | 17 | #endif/*NDEBUG*/ 18 | 19 | #endif/*__LIB_USER_ASSERT_H*/ 20 | -------------------------------------------------------------------------------- /lib/user/syscall.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | 3 | /* 无参数的系统调用 */ 4 | #define _syscall0(NUMBER) \ 5 | ({ \ 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 | ({ \ 18 | int retval; \ 19 | asm volatile( \ 20 | "int $0x80" \ 21 | : "=a"(retval) \ 22 | : "a"(NUMBER), "b"(ARG1) \ 23 | : "memory"); \ 24 | retval; \ 25 | }) 26 | 27 | /* 两个参数的系统调用 */ 28 | #define _syscall2(NUMBER, ARG1, ARG2) ({ \ 29 | int retval; \ 30 | asm volatile( \ 31 | "int $0x80" \ 32 | : "=a"(retval) \ 33 | : "a"(NUMBER), "b"(ARG1), "c"(ARG2) \ 34 | : "memory"); \ 35 | retval; \ 36 | }) 37 | 38 | /* 三个参数的系统调用 */ 39 | #define _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \ 40 | int retval; \ 41 | asm volatile( \ 42 | "int $0x80" \ 43 | : "=a"(retval) \ 44 | : "a"(NUMBER), "b"(ARG1), "c"(ARG2), "d"(ARG3) \ 45 | : "memory"); \ 46 | retval; \ 47 | }) 48 | 49 | /* 返回当前任务pid */ 50 | uint32_t getpid() 51 | { 52 | return _syscall0(SYS_GETPID); 53 | } 54 | 55 | uint32_t write(int32_t fd, const void *buf, uint32_t count) 56 | { 57 | return _syscall3(SYS_WRITE, fd, buf, count); 58 | } 59 | 60 | void *malloc(uint32_t size) 61 | { 62 | return (void*)_syscall1(SYS_MALLOC, size); 63 | } 64 | 65 | void free(void *ptr) 66 | { 67 | _syscall1(SYS_FREE, ptr); 68 | } 69 | 70 | int open(const char *pathname, int flags) 71 | { 72 | return _syscall2(SYS_OPEN, pathname, flags); 73 | } 74 | 75 | int close(int fd) 76 | { 77 | return _syscall1(SYS_CLOSE, fd); 78 | } 79 | 80 | int32_t read(int32_t fd, void *buf, uint32_t count) 81 | { 82 | return _syscall3(SYS_READ, fd, buf, count); 83 | } 84 | 85 | int32_t lseek(int32_t fd, int32_t offset, uint8_t whence) 86 | { 87 | return _syscall3(SYS_LSEEK, fd, offset, whence); 88 | } 89 | 90 | int32_t unlink(const char *pathname) 91 | { 92 | return _syscall1(SYS_UNLINK, pathname); 93 | } -------------------------------------------------------------------------------- /lib/user/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIB_USER_SYSCALL_H_ 2 | #define _LIB_USER_SYSCALL_H_ 3 | 4 | 5 | #include "../stdint.h" 6 | 7 | enum SYSCALL_NR 8 | { 9 | SYS_GETPID, 10 | SYS_WRITE, 11 | SYS_MALLOC, 12 | SYS_FREE, 13 | SYS_OPEN, 14 | SYS_CLOSE, 15 | SYS_READ, 16 | SYS_LSEEK, 17 | SYS_UNLINK 18 | }; 19 | 20 | uint32_t getpid(void); 21 | uint32_t write(int32_t fd, const void *buf, uint32_t count); 22 | void *malloc(uint32_t size); 23 | void free(void *ptr); 24 | int open(const char *pathname, int flags); 25 | int close(int fd); 26 | int32_t read(int32_t fd, void *buf, uint32_t count); 27 | int32_t lseek(int32_t fd, int32_t offset, uint8_t whence); 28 | int32_t unlink(const char *pathname); 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /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/ 7 | ASFLAGS = -f elf 8 | 9 | CFLAGS = -Wall $(LIB) -c -fno-builtin -W -Wstrict-prototypes \ 10 | -Wmissing-prototypes -fno-stack-protector -z execstack 11 | 12 | LDFLAGS = -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map 13 | OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/print.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \ 14 | $(BUILD_DIR)/kernel.o $(BUILD_DIR)/timer.o $(BUILD_DIR)/debug.o \ 15 | $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o $(BUILD_DIR)/string.o \ 16 | $(BUILD_DIR)/thread.o $(BUILD_DIR)/list.o $(BUILD_DIR)/switch.o \ 17 | $(BUILD_DIR)/console.o $(BUILD_DIR)/sync.o $(BUILD_DIR)/keyboard.o \ 18 | $(BUILD_DIR)/ioqueue.o $(BUILD_DIR)/tss.o $(BUILD_DIR)/process.o \ 19 | $(BUILD_DIR)/syscall.o $(BUILD_DIR)/syscall-init.o $(BUILD_DIR)/stdio.o \ 20 | $(BUILD_DIR)/ide.o $(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/fs.o 21 | 22 | ############## c代码编译 ############### 23 | $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \ 24 | lib/stdint.h kernel/init.h 25 | $(CC) $(CFLAGS) $< -o $@ 26 | 27 | $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \ 28 | lib/stdint.h kernel/interrupt.h device/timer.h 29 | $(CC) $(CFLAGS) $< -o $@ 30 | 31 | $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \ 32 | lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h 33 | $(CC) $(CFLAGS) $< -o $@ 34 | 35 | $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/stdint.h\ 36 | lib/kernel/io.h lib/kernel/print.h 37 | $(CC) $(CFLAGS) $< -o $@ 38 | 39 | $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \ 40 | lib/kernel/print.h lib/stdint.h kernel/interrupt.h 41 | $(CC) $(CFLAGS) $< -o $@ 42 | 43 | $(BUILD_DIR)/string.o : lib/string.c lib/string.h lib/stdint.h kernel/global.h kernel/debug.h 44 | $(CC) $(CFLAGS) $< -o $@ 45 | 46 | $(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h \ 47 | kernel/global.h lib/stdint.h lib/string.h lib/stdint.h \ 48 | lib/kernel/print.h kernel/interrupt.h kernel/debug.h 49 | $(CC) $(CFLAGS) $< -o $@ 50 | 51 | $(BUILD_DIR)/memory.o : kernel/memory.c kernel/memory.h lib/stdint.h \ 52 | lib/kernel/bitmap.h kernel/global.h kernel/debug.h \ 53 | lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \ 54 | lib/string.h lib/stdint.h 55 | $(CC) $(CFLAGS) $< -o $@ 56 | 57 | 58 | $(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h lib/stdint.h \ 59 | kernel/global.h lib/kernel/bitmap.h kernel/memory.h lib/string.h \ 60 | lib/kernel/print.h kernel/interrupt.h kernel/debug.h 61 | $(CC) $(CFLAGS) $< -o $@ 62 | 63 | $(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h kernel/global.h lib/stdint.h \ 64 | kernel/interrupt.h 65 | $(CC) $(CFLAGS) $< -o $@ 66 | 67 | $(BUILD_DIR)/console.o: device/console.c device/console.h lib/stdint.h \ 68 | lib/kernel/print.h thread/sync.h lib/kernel/list.h kernel/global.h \ 69 | thread/thread.h thread/thread.h 70 | $(CC) $(CFLAGS) $< -o $@ 71 | 72 | $(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h lib/kernel/list.h kernel/global.h \ 73 | lib/stdint.h thread/thread.h lib/string.h lib/stdint.h kernel/debug.h \ 74 | kernel/interrupt.h 75 | $(CC) $(CFLAGS) $< -o $@ 76 | 77 | $(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h lib/kernel/print.h \ 78 | lib/stdint.h kernel/interrupt.h lib/kernel/io.h thread/thread.h \ 79 | lib/kernel/list.h kernel/global.h thread/sync.h thread/thread.h 80 | $(CC) $(CFLAGS) $< -o $@ 81 | 82 | $(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h lib/stdint.h thread/thread.h \ 83 | lib/kernel/list.h kernel/global.h thread/sync.h thread/thread.h kernel/interrupt.h \ 84 | kernel/debug.h 85 | $(CC) $(CFLAGS) $< -o $@ 86 | 87 | $(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h thread/thread.h lib/stdint.h \ 88 | lib/kernel/list.h kernel/global.h lib/string.h lib/stdint.h \ 89 | lib/kernel/print.h 90 | $(CC) $(CFLAGS) $< -o $@ 91 | 92 | $(BUILD_DIR)/process.o: userprog/process.c userprog/process.h thread/thread.h \ 93 | lib/stdint.h lib/kernel/list.h kernel/global.h kernel/debug.h \ 94 | kernel/memory.h lib/kernel/bitmap.h userprog/tss.h kernel/interrupt.h \ 95 | lib/string.h lib/stdint.h 96 | $(CC) $(CFLAGS) $< -o $@ 97 | 98 | $(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h lib/stdint.h 99 | $(CC) $(CFLAGS) $< -o $@ 100 | 101 | $(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \ 102 | lib/stdint.h lib/user/syscall.h lib/kernel/print.h thread/thread.h \ 103 | lib/kernel/list.h kernel/global.h lib/kernel/bitmap.h kernel/memory.h 104 | $(CC) $(CFLAGS) $< -o $@ 105 | 106 | $(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h kernel/interrupt.h \ 107 | lib/stdint.h kernel/global.h lib/string.h lib/user/syscall.h lib/kernel/print.h 108 | $(CC) $(CFLAGS) $< -o $@ 109 | 110 | $(BUILD_DIR)/ide.o: device/ide.c device/ide.h lib/stdint.h thread/sync.h \ 111 | lib/kernel/list.h kernel/global.h thread/thread.h lib/kernel/bitmap.h \ 112 | kernel/memory.h lib/kernel/io.h lib/stdio.h lib/stdint.h lib/kernel/stdio-kernel.h \ 113 | kernel/interrupt.h kernel/debug.h device/console.h device/timer.h lib/string.h 114 | $(CC) $(CFLAGS) $< -o $@ 115 | 116 | $(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h lib/stdint.h \ 117 | lib/kernel/print.h lib/stdio.h lib/stdint.h device/console.h kernel/global.h 118 | $(CC) $(CFLAGS) $< -o $@ 119 | 120 | $(BUILD_DIR)/fs.o: fs/fs.c fs/fs.h lib/stdint.h device/ide.h thread/sync.h lib/kernel/list.h \ 121 | kernel/global.h thread/thread.h lib/kernel/bitmap.h kernel/memory.h fs/super_block.h \ 122 | fs/inode.h fs/dir.h lib/kernel/stdio-kernel.h lib/string.h lib/stdint.h kernel/debug.h \ 123 | kernel/interrupt.h lib/kernel/print.h 124 | $(CC) $(CFLAGS) $< -o $@ 125 | 126 | 127 | 128 | ############## 汇编代码编译 ############### 129 | $(BUILD_DIR)/kernel.o: kernel/kernel.S 130 | $(AS) $(ASFLAGS) $< -o $@ 131 | 132 | $(BUILD_DIR)/print.o: lib/kernel/print.S 133 | $(AS) $(ASFLAGS) $< -o $@ 134 | 135 | $(BUILD_DIR)/switch.o: thread/switch.S 136 | $(AS) $(ASFLAGS) $< -o $@ 137 | 138 | 139 | ############## 链接所有目标文件 ############# 140 | $(BUILD_DIR)/kernel.bin: $(OBJS) 141 | $(LD) $(LDFLAGS) $^ -o $@ 142 | 143 | .PHONY : mk_dir hd clean all 144 | 145 | hd: 146 | dd if=$(BUILD_DIR)/kernel.bin \ 147 | of=/home/ba/bochs/hd60M.img \ 148 | bs=512 count=200 seek=9 conv=notrunc 149 | 150 | hdgdb: 151 | dd if=$(BUILD_DIR)/kernel.bin of=/home/ba/bochsgdb/hd60M.img bs=512 count=200 seek=9 conv=notrunc 152 | 153 | clean: 154 | @cd $(BUILD_DIR) && rm -f ./*.bin ./*.o 155 | 156 | build: $(BUILD_DIR)/kernel.bin 157 | 158 | all: build hd clean 159 | 160 | 161 | -------------------------------------------------------------------------------- /shell/buildin_cmd.c: -------------------------------------------------------------------------------- 1 | #include "buildin_cmd.h" 2 | #include "../lib/user/syscall.h" 3 | #include "../lib/stdio.h" 4 | #include "../lib/string.h" 5 | #include "../fs/fs.h" 6 | #include "../kernel/global.h" 7 | #include "../fs/dir.h" 8 | #include "shell.h" 9 | #include "../lib/user/assert.h" 10 | 11 | /* 将路径old_abs_path中的..和.转换为实际路径后存入new_abs_path */ 12 | static void wash_path(char *old_abs_path, char *new_abs_path) 13 | { 14 | assert(old_abs_path[0] == '/'); 15 | char name[MAX_FILE_NAME_LEN] = {0}; 16 | char *sub_path = old_abs_path; 17 | sub_path = path_parse(sub_path, name); 18 | if (name[0] == 0) 19 | { // 若只键入了"/",直接将"/"存入new_abs_path后返回 20 | new_abs_path[0] = '/'; 21 | new_abs_path[1] = 0; 22 | return; 23 | } 24 | new_abs_path[0] = 0; // 避免传给new_abs_path的缓冲区不干净 25 | strcat(new_abs_path, "/"); 26 | while (name[0]) 27 | { 28 | /* 如果是上一级目录“..” */ 29 | if (!strcmp("..", name)) 30 | { 31 | char *slash_ptr = strrchr(new_abs_path, '/'); 32 | /*如果未到new_abs_path中的顶层目录,就将最右边的'/'替换为0, 33 | 这样便去除了new_abs_path中最后一层路径,相当于到了上一级目录 */ 34 | if (slash_ptr != new_abs_path) 35 | { // 如new_abs_path为“/a/b”,".."之后则变为“/a” 36 | *slash_ptr = 0; 37 | } 38 | else 39 | { // 如new_abs_path为"/a",".."之后则变为"/" 40 | /* 若new_abs_path中只有1个'/',即表示已经到了顶层目录, 41 | 就将下一个字符置为结束符0. */ 42 | *(slash_ptr + 1) = 0; 43 | } 44 | } 45 | else if (strcmp(".", name)) 46 | { // 如果路径不是‘.’,就将name拼接到new_abs_path 47 | if (strcmp(new_abs_path, "/")) 48 | { // 如果new_abs_path不是"/",就拼接一个"/",此处的判断是为了避免路径开头变成这样"//" 49 | strcat(new_abs_path, "/"); 50 | } 51 | strcat(new_abs_path, name); 52 | } // 若name为当前目录".",无须处理new_abs_path 53 | 54 | /* 继续遍历下一层路径 */ 55 | memset(name, 0, MAX_FILE_NAME_LEN); 56 | if (sub_path) 57 | { 58 | sub_path = path_parse(sub_path, name); 59 | } 60 | } 61 | } 62 | 63 | /* 将path处理成不含..和.的绝对路径,存储在final_path */ 64 | void make_clear_abs_path(char *path, char *final_path) 65 | { 66 | char abs_path[MAX_PATH_LEN] = {0}; 67 | /* 先判断是否输入的是绝对路径 */ 68 | if (path[0] != '/') 69 | { // 若输入的不是绝对路径,就拼接成绝对路径 70 | memset(abs_path, 0, MAX_PATH_LEN); 71 | if (getcwd(abs_path, MAX_PATH_LEN) != NULL) 72 | { 73 | if (!((abs_path[0] == '/') && (abs_path[1] == 0))) 74 | { // 若abs_path表示的当前目录不是根目录/ 75 | strcat(abs_path, "/"); 76 | } 77 | } 78 | } 79 | strcat(abs_path, path); 80 | wash_path(abs_path, final_path); 81 | } 82 | 83 | /* pwd命令的内建函数 */ 84 | void buildin_pwd(uint32_t argc, char **argv UNUSED) 85 | { 86 | if (argc != 1) 87 | { 88 | printf("pwd: no argument support!\n"); 89 | return; 90 | } 91 | else 92 | { 93 | if (NULL != getcwd(final_path, MAX_PATH_LEN)) 94 | { 95 | printf("%s\n", final_path); 96 | } 97 | else 98 | { 99 | printf("pwd: get current work directory failed.\n"); 100 | } 101 | } 102 | } 103 | 104 | /* cd命令的内建函数 */ 105 | char *buildin_cd(uint32_t argc, char **argv) 106 | { 107 | if (argc > 2) 108 | { 109 | printf("cd: only support 1 argument!\n"); 110 | return NULL; 111 | } 112 | 113 | /* 若是只键入cd而无参数,直接返回到根目录. */ 114 | if (argc == 1) 115 | { 116 | final_path[0] = '/'; 117 | final_path[1] = 0; 118 | } 119 | else 120 | { 121 | make_clear_abs_path(argv[1], final_path); 122 | } 123 | 124 | if (chdir(final_path) == -1) 125 | { 126 | printf("cd: no such directory %s\n", final_path); 127 | return NULL; 128 | } 129 | return final_path; 130 | } 131 | 132 | /* ls命令的内建函数 */ 133 | void buildin_ls(uint32_t argc, char **argv) 134 | { 135 | char *pathname = NULL; 136 | struct stat file_stat; 137 | memset(&file_stat, 0, sizeof(struct stat)); 138 | bool long_info = false; 139 | uint32_t arg_path_nr = 0; 140 | uint32_t arg_idx = 1; // 跨过argv[0],argv[0]是字符串“ls” 141 | while (arg_idx < argc) 142 | { 143 | if (argv[arg_idx][0] == '-') 144 | { // 如果是选项,单词的首字符是- 145 | if (!strcmp("-l", argv[arg_idx])) 146 | { // 如果是参数-l 147 | long_info = true; 148 | } 149 | else if (!strcmp("-h", argv[arg_idx])) 150 | { // 参数-h 151 | printf("usage: -l list all infomation about the file.\n-h for help\nlist all files in the current dirctory if no option\n"); 152 | return; 153 | } 154 | else 155 | { // 只支持-h -l两个选项 156 | printf("ls: invalid option %s\nTry `ls -h' for more information.\n", argv[arg_idx]); 157 | return; 158 | } 159 | } 160 | else 161 | { // ls的路径参数 162 | if (arg_path_nr == 0) 163 | { 164 | pathname = argv[arg_idx]; 165 | arg_path_nr = 1; 166 | } 167 | else 168 | { 169 | printf("ls: only support one path\n"); 170 | return; 171 | } 172 | } 173 | arg_idx++; 174 | } 175 | 176 | if (pathname == NULL) 177 | { // 若只输入了ls 或 ls -l,没有输入操作路径,默认以当前路径的绝对路径为参数. 178 | if (NULL != getcwd(final_path, MAX_PATH_LEN)) 179 | { 180 | pathname = final_path; 181 | } 182 | else 183 | { 184 | printf("ls: getcwd for default path failed\n"); 185 | return; 186 | } 187 | } 188 | else 189 | { 190 | make_clear_abs_path(pathname, final_path); 191 | pathname = final_path; 192 | } 193 | 194 | if (stat(pathname, &file_stat) == -1) 195 | { 196 | printf("ls: cannot access %s: No such file or directory\n", pathname); 197 | return; 198 | } 199 | if (file_stat.st_filetype == FT_DIRECTORY) 200 | { 201 | struct dir *dir = opendir(pathname); 202 | struct dir_entry *dir_e = NULL; 203 | char sub_pathname[MAX_PATH_LEN] = {0}; 204 | uint32_t pathname_len = strlen(pathname); 205 | uint32_t last_char_idx = pathname_len - 1; 206 | memcpy(sub_pathname, pathname, pathname_len); 207 | if (sub_pathname[last_char_idx] != '/') 208 | { 209 | sub_pathname[pathname_len] = '/'; 210 | pathname_len++; 211 | } 212 | rewinddir(dir); 213 | if (long_info) 214 | { 215 | char ftype; 216 | printf("total: %d\n", file_stat.st_size); 217 | while ((dir_e = readdir(dir))) 218 | { 219 | ftype = 'd'; 220 | if (dir_e->f_type == FT_REGULAR) 221 | { 222 | ftype = '-'; 223 | } 224 | sub_pathname[pathname_len] = 0; 225 | strcat(sub_pathname, dir_e->filename); 226 | memset(&file_stat, 0, sizeof(struct stat)); 227 | if (stat(sub_pathname, &file_stat) == -1) 228 | { 229 | printf("ls: cannot access %s: No such file or directory\n", dir_e->filename); 230 | return; 231 | } 232 | printf("%c %d %d %s\n", ftype, dir_e->i_no, file_stat.st_size, dir_e->filename); 233 | } 234 | } 235 | else 236 | { 237 | while ((dir_e = readdir(dir))) 238 | { 239 | printf("%s ", dir_e->filename); 240 | } 241 | printf("\n"); 242 | } 243 | closedir(dir); 244 | } 245 | else 246 | { 247 | if (long_info) 248 | { 249 | printf("- %d %d %s\n", file_stat.st_ino, file_stat.st_size, pathname); 250 | } 251 | else 252 | { 253 | printf("%s\n", pathname); 254 | } 255 | } 256 | } 257 | 258 | /* ps命令内建函数 */ 259 | void buildin_ps(uint32_t argc, char **argv UNUSED) 260 | { 261 | if (argc != 1) 262 | { 263 | printf("ps: no argument support!\n"); 264 | return; 265 | } 266 | ps(); 267 | } 268 | 269 | /* clear命令内建函数 */ 270 | void buildin_clear(uint32_t argc, char **argv UNUSED) 271 | { 272 | if (argc != 1) 273 | { 274 | printf("clear: no argument support!\n"); 275 | return; 276 | } 277 | clear(); 278 | } 279 | 280 | /* mkdir命令内建函数 */ 281 | int32_t buildin_mkdir(uint32_t argc, char **argv) 282 | { 283 | int32_t ret = -1; 284 | if (argc != 2) 285 | { 286 | printf("mkdir: only support 1 argument!\n"); 287 | } 288 | else 289 | { 290 | make_clear_abs_path(argv[1], final_path); 291 | /* 若创建的不是根目录 */ 292 | if (strcmp("/", final_path)) 293 | { 294 | if (mkdir(final_path) == 0) 295 | { 296 | ret = 0; 297 | } 298 | else 299 | { 300 | printf("mkdir: create directory %s failed.\n", argv[1]); 301 | } 302 | } 303 | } 304 | return ret; 305 | } 306 | 307 | /* rmdir命令内建函数 */ 308 | int32_t buildin_rmdir(uint32_t argc, char **argv) 309 | { 310 | int32_t ret = -1; 311 | if (argc != 2) 312 | { 313 | printf("rmdir: only support 1 argument!\n"); 314 | } 315 | else 316 | { 317 | make_clear_abs_path(argv[1], final_path); 318 | /* 若删除的不是根目录 */ 319 | if (strcmp("/", final_path)) 320 | { 321 | if (rmdir(final_path) == 0) 322 | { 323 | ret = 0; 324 | } 325 | else 326 | { 327 | printf("rmdir: remove %s failed.\n", argv[1]); 328 | } 329 | } 330 | } 331 | return ret; 332 | } 333 | 334 | /* rm命令内建函数 */ 335 | int32_t buildin_rm(uint32_t argc, char **argv) 336 | { 337 | int32_t ret = -1; 338 | if (argc != 2) 339 | { 340 | printf("rm: only support 1 argument!\n"); 341 | } 342 | else 343 | { 344 | make_clear_abs_path(argv[1], final_path); 345 | /* 若删除的不是根目录 */ 346 | if (strcmp("/", final_path)) 347 | { 348 | if (unlink(final_path) == 0) 349 | { 350 | ret = 0; 351 | } 352 | else 353 | { 354 | printf("rm: delete %s failed.\n", argv[1]); 355 | } 356 | } 357 | } 358 | return ret; 359 | } 360 | -------------------------------------------------------------------------------- /shell/buildin_cmd.h: -------------------------------------------------------------------------------- 1 | #ifndef __SHELL_BUILDIN_CMD_H 2 | #define __SHELL_BUILDIN_CMD_H 3 | 4 | #include "../lib/stdint.h" 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 make_clear_abs_path(char* path, char* wash_buf); 11 | void buildin_pwd(uint32_t argc, char** argv); 12 | void buildin_ps(uint32_t argc, char** argv); 13 | void buildin_clear(uint32_t argc, char** argv); 14 | #endif 15 | -------------------------------------------------------------------------------- /shell/pipe.c: -------------------------------------------------------------------------------- 1 | #include "pipe.h" 2 | #include "../kernel/memory.h" 3 | #include "../fs/fs.h" 4 | #include "../fs/file.h" 5 | #include "../device/ioqueue.h" 6 | #include "../thread/thread.h" 7 | 8 | /* 判断文件描述符local_fd是否是管道 */ 9 | bool is_pipe(uint32_t local_fd) 10 | { 11 | uint32_t global_fd = fd_local2global(local_fd); 12 | return file_table[global_fd].fd_flag == PIPE_FLAG; 13 | } 14 | 15 | /* 创建管道,成功返回0,失败返回-1 */ 16 | int32_t sys_pipe(int32_t pipefd[2]) 17 | { 18 | int32_t global_fd = get_free_slot_in_global(); 19 | 20 | /* 申请一页内核内存做环形缓冲区 */ 21 | file_table[global_fd].fd_inode = get_kernel_pages(1); 22 | 23 | /* 初始化环形缓冲区 */ 24 | ioqueue_init((struct ioqueue *)file_table[global_fd].fd_inode); 25 | if (file_table[global_fd].fd_inode == NULL) 26 | { 27 | return -1; 28 | } 29 | 30 | /* 将fd_flag复用为管道标志 */ 31 | file_table[global_fd].fd_flag = PIPE_FLAG; 32 | 33 | /* 将fd_pos复用为管道打开数 */ 34 | file_table[global_fd].fd_pos = 2; 35 | pipefd[0] = pcb_fd_install(global_fd); 36 | pipefd[1] = pcb_fd_install(global_fd); 37 | return 0; 38 | } 39 | 40 | /* 从管道中读数据 */ 41 | uint32_t pipe_read(int32_t fd, void *buf, uint32_t count) 42 | { 43 | char *buffer = buf; 44 | uint32_t bytes_read = 0; 45 | uint32_t global_fd = fd_local2global(fd); 46 | 47 | /* 获取管道的环形缓冲区 */ 48 | struct ioqueue *ioq = (struct ioqueue *)file_table[global_fd].fd_inode; 49 | 50 | /* 选择较小的数据读取量,避免阻塞 */ 51 | uint32_t ioq_len = ioq_length(ioq); 52 | uint32_t size = ioq_len > count ? count : ioq_len; 53 | while (bytes_read < size) 54 | { 55 | *buffer = ioq_getchar(ioq); 56 | bytes_read++; 57 | buffer++; 58 | } 59 | return bytes_read; 60 | } 61 | 62 | /* 往管道中写数据 */ 63 | uint32_t pipe_write(int32_t fd, const void *buf, uint32_t count) 64 | { 65 | uint32_t bytes_write = 0; 66 | uint32_t global_fd = fd_local2global(fd); 67 | struct ioqueue *ioq = (struct ioqueue *)file_table[global_fd].fd_inode; 68 | 69 | /* 选择较小的数据写入量,避免阻塞 */ 70 | uint32_t ioq_left = bufsize - ioq_length(ioq); 71 | uint32_t size = ioq_left > count ? count : ioq_left; 72 | 73 | const char *buffer = buf; 74 | while (bytes_write < size) 75 | { 76 | ioq_putchar(ioq, *buffer); 77 | bytes_write++; 78 | buffer++; 79 | } 80 | return bytes_write; 81 | } 82 | 83 | /* 将文件描述符old_local_fd重定向为new_local_fd */ 84 | void sys_fd_redirect(uint32_t old_local_fd, uint32_t new_local_fd) 85 | { 86 | task_struct *cur = running_thread(); 87 | /* 针对恢复标准描述符 */ 88 | if (new_local_fd < 3) 89 | { 90 | cur->fd_table[old_local_fd] = new_local_fd; 91 | } 92 | else 93 | { 94 | uint32_t new_global_fd = cur->fd_table[new_local_fd]; 95 | cur->fd_table[old_local_fd] = new_global_fd; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /shell/pipe.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHELL_PIPE_H_ 2 | #define _SHELL_PIPE_H_ 3 | #include "../lib/stdint.h" 4 | #include "../kernel/global.h" 5 | 6 | #define PIPE_FLAG 0xFFFF 7 | bool is_pipe(uint32_t local_fd); 8 | int32_t sys_pipe(int32_t pipefd[2]); 9 | uint32_t pipe_read(int32_t fd, void* buf, uint32_t count); 10 | uint32_t pipe_write(int32_t fd, const void* buf, uint32_t count); 11 | void sys_fd_redirect(uint32_t old_local_fd, uint32_t new_local_fd); 12 | #endif 13 | -------------------------------------------------------------------------------- /shell/shell.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | #include "../lib/stdint.h" 3 | #include "../fs/file.h" 4 | #include "../lib/user/syscall.h" 5 | #include "../lib/stdio.h" 6 | #include "../kernel/global.h" 7 | #include "../lib/user/assert.h" 8 | #include "../lib/string.h" 9 | #include "buildin_cmd.h" 10 | #include "../userprog/exec.h" 11 | 12 | #define MAX_ARG_NR 16 // 加上命令名外,最多支持15个参数 13 | 14 | /* 存储输入的命令 */ 15 | static char cmd_line[MAX_PATH_LEN] = {0}; 16 | char final_path[MAX_PATH_LEN] = {0}; // 用于洗路径时的缓冲 17 | 18 | /* 用来记录当前目录,是当前目录的缓存,每次执行cd命令时会更新此内容 */ 19 | char cwd_cache[MAX_PATH_LEN] = {0}; 20 | 21 | /* 输出提示符 */ 22 | void print_prompt(void) 23 | { 24 | printf("[rabbit@localhost %s]$ ", cwd_cache); 25 | } 26 | 27 | /* 从键盘缓冲区中最多读入count个字节到buf。*/ 28 | static void readline(char *buf, int32_t count) 29 | { 30 | assert(buf != NULL && count > 0); 31 | char *pos = buf; 32 | 33 | while (read(stdin_no, pos, 1) != -1 && (pos - buf) < count) 34 | { // 在不出错情况下,直到找到回车符才返回 35 | switch (*pos) 36 | { 37 | /* 找到回车或换行符后认为键入的命令结束,直接返回 */ 38 | case '\n': 39 | case '\r': 40 | *pos = 0; // 添加cmd_line的终止字符0 41 | putchar('\n'); 42 | return; 43 | 44 | case '\b': 45 | if (cmd_line[0] != '\b') 46 | { // 阻止删除非本次输入的信息 47 | --pos; // 退回到缓冲区cmd_line中上一个字符 48 | putchar('\b'); 49 | } 50 | break; 51 | 52 | /* ctrl+l 清屏 */ 53 | case 'l' - 'a': 54 | /* 1 先将当前的字符'l'-'a'置为0 */ 55 | *pos = 0; 56 | /* 2 再将屏幕清空 */ 57 | clear(); 58 | /* 3 打印提示符 */ 59 | print_prompt(); 60 | /* 4 将之前键入的内容再次打印 */ 61 | printf("%s", buf); 62 | break; 63 | 64 | /* ctrl+u 清掉输入 */ 65 | case 'u' - 'a': 66 | while (buf != pos) 67 | { 68 | putchar('\b'); 69 | *(pos--) = 0; 70 | } 71 | break; 72 | 73 | /* 非控制键则输出字符 */ 74 | default: 75 | putchar(*pos); 76 | pos++; 77 | } 78 | } 79 | printf("readline: can`t find enter_key in the cmd_line, max num of char is 128\n"); 80 | } 81 | 82 | /* 分析字符串cmd_str中以token为分隔符的单词,将各单词的指针存入argv数组 */ 83 | static int32_t cmd_parse(char *cmd_str, char **argv, char token) 84 | { 85 | assert(cmd_str != NULL); 86 | int32_t arg_idx = 0; 87 | while (arg_idx < MAX_ARG_NR) 88 | { 89 | argv[arg_idx] = NULL; 90 | arg_idx++; 91 | } 92 | char *next = cmd_str; 93 | int32_t argc = 0; 94 | /* 外层循环处理整个命令行 */ 95 | while (*next) 96 | { 97 | /* 去除命令字或参数之间的空格 */ 98 | while (*next == token) 99 | { 100 | next++; 101 | } 102 | /* 处理最后一个参数后接空格的情况,如"ls dir2 " */ 103 | if (*next == 0) 104 | { 105 | break; 106 | } 107 | argv[argc] = next; 108 | 109 | /* 内层循环处理命令行中的每个命令字及参数 */ 110 | while (*next && *next != token) 111 | { // 在字符串结束前找单词分隔符 112 | next++; 113 | } 114 | 115 | /* 如果未结束(是token字符),使tocken变成0 */ 116 | if (*next) 117 | { 118 | *next++ = 0; // 将token字符替换为字符串结束符0,做为一个单词的结束,并将字符指针next指向下一个字符 119 | } 120 | 121 | /* 避免argv数组访问越界,参数过多则返回0 */ 122 | if (argc > MAX_ARG_NR) 123 | { 124 | return -1; 125 | } 126 | argc++; 127 | } 128 | return argc; 129 | } 130 | 131 | /* 执行命令 */ 132 | static void cmd_execute(uint32_t argc, char **argv) 133 | { 134 | if (!strcmp("ls", argv[0])) 135 | { 136 | buildin_ls(argc, argv); 137 | } 138 | else if (!strcmp("cd", argv[0])) 139 | { 140 | if (buildin_cd(argc, argv) != NULL) 141 | { 142 | memset(cwd_cache, 0, MAX_PATH_LEN); 143 | strcpy(cwd_cache, final_path); 144 | } 145 | } 146 | else if (!strcmp("pwd", argv[0])) 147 | { 148 | buildin_pwd(argc, argv); 149 | } 150 | else if (!strcmp("ps", argv[0])) 151 | { 152 | buildin_ps(argc, argv); 153 | } 154 | else if (!strcmp("clear", argv[0])) 155 | { 156 | buildin_clear(argc, argv); 157 | } 158 | else if (!strcmp("mkdir", argv[0])) 159 | { 160 | buildin_mkdir(argc, argv); 161 | } 162 | else if (!strcmp("rmdir", argv[0])) 163 | { 164 | buildin_rmdir(argc, argv); 165 | } 166 | else if (!strcmp("rm", argv[0])) 167 | { 168 | buildin_rm(argc, argv); 169 | } 170 | else if (!strcmp("help", argv[0])) 171 | { 172 | buildin_help(argc, argv); 173 | } 174 | else 175 | { // 如果是外部命令,需要从磁盘上加载 176 | int32_t pid = fork(); 177 | if (pid) 178 | { // 父进程 179 | int32_t status; 180 | int32_t child_pid = wait(&status); // 此时子进程若没有执行exit,my_shell会被阻塞,不再响应键入的命令 181 | if (child_pid == -1) 182 | { // 按理说程序正确的话不会执行到这句,fork出的进程便是shell子进程 183 | panic("my_shell: no child\n"); 184 | } 185 | printf("child_pid %d, it's status: %d\n", child_pid, status); 186 | } 187 | else 188 | { // 子进程 189 | make_clear_abs_path(argv[0], final_path); 190 | argv[0] = final_path; 191 | 192 | /* 先判断下文件是否存在 */ 193 | struct stat file_stat; 194 | memset(&file_stat, 0, sizeof(struct stat)); 195 | if (stat(argv[0], &file_stat) == -1) 196 | { 197 | printf("my_shell: cannot access %s: No such file or directory\n", argv[0]); 198 | exit(-1); 199 | } 200 | else 201 | { 202 | execv(argv[0], argv); 203 | } 204 | } 205 | } 206 | } 207 | 208 | char *argv[MAX_ARG_NR]; // argv为全局变量,为了以后exec的程序可访问参数 209 | int32_t argc = -1; 210 | 211 | void my_shell(void) 212 | { 213 | cwd_cache[0] = '/'; 214 | while (1) 215 | { 216 | print_prompt(); 217 | memset(final_path, 0, MAX_PATH_LEN); 218 | memset(cmd_line, 0, MAX_PATH_LEN); 219 | readline(cmd_line, MAX_PATH_LEN); 220 | if (cmd_line[0] == 0) 221 | { // 若只键入了一个回车 222 | continue; 223 | } 224 | 225 | /* 针对管道的处理 */ 226 | char *pipe_symbol = strchr(cmd_line, '|'); 227 | if (pipe_symbol) 228 | { 229 | /* 支持多重管道操作,如cmd1|cmd2|..|cmdn, 230 | * cmd1的标准输出和cmdn的标准输入需要单独处理 */ 231 | 232 | /*1 生成管道*/ 233 | int32_t fd[2] = {-1}; // fd[0]用于输入,fd[1]用于输出 234 | pipe(fd); 235 | /* 将标准输出重定向到fd[1],使后面的输出信息重定向到内核环形缓冲区 */ 236 | fd_redirect(1, fd[1]); 237 | 238 | /*2 第一个命令 */ 239 | char *each_cmd = cmd_line; 240 | pipe_symbol = strchr(each_cmd, '|'); 241 | *pipe_symbol = 0; 242 | 243 | /* 执行第一个命令,命令的输出会写入环形缓冲区 */ 244 | argc = -1; 245 | argc = cmd_parse(each_cmd, argv, ' '); 246 | cmd_execute(argc, argv); 247 | 248 | /* 跨过'|',处理下一个命令 */ 249 | each_cmd = pipe_symbol + 1; 250 | 251 | /* 将标准输入重定向到fd[0],使之指向内核环形缓冲区*/ 252 | fd_redirect(0, fd[0]); 253 | /*3 中间的命令,命令的输入和输出都是指向环形缓冲区 */ 254 | while ((pipe_symbol = strchr(each_cmd, '|'))) 255 | { 256 | *pipe_symbol = 0; 257 | argc = -1; 258 | argc = cmd_parse(each_cmd, argv, ' '); 259 | cmd_execute(argc, argv); 260 | each_cmd = pipe_symbol + 1; 261 | } 262 | 263 | /*4 处理管道中最后一个命令 */ 264 | /* 将标准输出恢复屏幕 */ 265 | fd_redirect(1, 1); 266 | 267 | /* 执行最后一个命令 */ 268 | argc = -1; 269 | argc = cmd_parse(each_cmd, argv, ' '); 270 | cmd_execute(argc, argv); 271 | 272 | /*5 将标准输入恢复为键盘 */ 273 | fd_redirect(0, 0); 274 | 275 | /*6 关闭管道 */ 276 | close(fd[0]); 277 | close(fd[1]); 278 | } 279 | else 280 | { // 一般无管道操作的命令 281 | argc = -1; 282 | argc = cmd_parse(cmd_line, argv, ' '); 283 | if (argc == -1) 284 | { 285 | printf("num of arguments exceed %d\n", MAX_ARG_NR); 286 | continue; 287 | } 288 | cmd_execute(argc, argv); 289 | } 290 | } 291 | panic("my_shell: should not be here"); 292 | } 293 | -------------------------------------------------------------------------------- /shell/shell.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_SHELL_H_ 2 | #define _KERNEL_SHELL_H_ 3 | 4 | #include "../fs/fs.h" 5 | 6 | void print_prompt(void); 7 | void my_shell(void); 8 | extern char final_path[MAX_PATH_LEN]; 9 | #endif 10 | -------------------------------------------------------------------------------- /thread/switch.S: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | section .text 3 | global switch_to 4 | switch_to: 5 | ;栈中此处是返回地址 6 | push esi 7 | push edi 8 | push ebx 9 | push ebp 10 | 11 | mov eax, [esp + 20] ; 得到栈中的参数cur, cur = [esp+20] 12 | mov [eax], esp ; 保存栈顶指针esp. task_struct的self_kstack字段, 13 | ; self_kstack在task_struct中的偏移为0, 14 | ; 所以直接往thread开头处存4字节便可。 15 | ;------------------ 以上是备份当前线程的环境,下面是恢复下一个线程的环境 ---------------- 16 | mov eax, [esp + 24] ; 得到栈中的参数next, next = [esp+24] 17 | mov esp, [eax] ; pcb的第一个成员是self_kstack成员,用来记录0级栈顶指针, 18 | ; 用来上cpu时恢复0级栈,0级栈中保存了进程或线程所有信息,包括3级栈指针 19 | pop ebp 20 | pop ebx 21 | pop edi 22 | pop esi 23 | ret ; 返回到上面switch_to下面的那句注释的返回地址, 24 | ; 未由中断进入,第一次执行时会返回到kernel_thread 25 | -------------------------------------------------------------------------------- /thread/sync.c: -------------------------------------------------------------------------------- 1 | #include "sync.h" 2 | #include "../lib/kernel/list.h" 3 | #include "../kernel/global.h" 4 | #include "../kernel/debug.h" 5 | #include "../kernel/interrupt.h" 6 | 7 | /* 初始化信号量 */ 8 | void sema_init(struct semaphore *psema, uint8_t value) 9 | { 10 | psema->value = value; // 为信号量赋初值 11 | list_init(&psema->waiters); //初始化信号量的等待队列 12 | } 13 | 14 | /* 初始化锁plock */ 15 | void lock_init(struct lock *plock) 16 | { 17 | plock->holder = NULL; 18 | plock->holder_repeat_nr = 0; 19 | sema_init(&plock->semaphore, 1); // 信号量初值为1 20 | } 21 | 22 | /* 信号量down操作 */ 23 | void sema_down(struct semaphore *psema) 24 | { 25 | /* 关中断来保证原子操作 */ 26 | enum intr_status old_status = intr_disable(); 27 | while (psema->value == 0) 28 | { 29 | // 若value为0,表示已经被别人持有 30 | ASSERT(!elem_find(&psema->waiters, &running_thread()->general_tag)); 31 | /* 当前线程不应该已在信号量的waiters队列中 */ 32 | if (elem_find(&psema->waiters, &running_thread()->general_tag)) 33 | { 34 | PANIC("sema_down: thread blocked has been in waiters_list\n"); 35 | } 36 | /* 若信号量的值等于0,则当前线程把自己加入该锁的等待队列,然后阻塞自己 */ 37 | list_append(&psema->waiters, &running_thread()->general_tag); 38 | thread_block(TASK_BLOCKED); // 阻塞线程,直到被唤醒 39 | } 40 | /* 若value为1或被唤醒后,会执行下面的代码,也就是获得了锁。*/ 41 | psema->value--; 42 | ASSERT(psema->value == 0); 43 | /* 恢复之前的中断状态 */ 44 | intr_set_status(old_status); 45 | } 46 | 47 | /* 信号量的up操作 */ 48 | void sema_up(struct semaphore *psema) 49 | { 50 | /* 关中断,保证原子操作 */ 51 | enum intr_status old_status = intr_disable(); 52 | ASSERT(psema->value == 0); 53 | if (!list_empty(&psema->waiters)) 54 | { 55 | task_struct *thread_blocked = elem2entry(task_struct, general_tag, list_pop(&psema->waiters)); 56 | thread_unblock(thread_blocked); 57 | } 58 | psema->value++; 59 | ASSERT(psema->value == 1); 60 | /* 恢复之前的中断状态 */ 61 | intr_set_status(old_status); 62 | } 63 | 64 | /* 获取锁plock */ 65 | void lock_acquire(struct lock *plock) 66 | { 67 | /* 排除曾经自己已经持有锁但还未将其释放的情况。*/ 68 | if (plock->holder != running_thread()) 69 | { 70 | sema_down(&plock->semaphore); // 对信号量P操作,原子操作 71 | plock->holder = running_thread(); 72 | ASSERT(plock->holder_repeat_nr == 0); 73 | plock->holder_repeat_nr = 1; 74 | } 75 | else 76 | { 77 | plock->holder_repeat_nr++; 78 | } 79 | } 80 | 81 | /* 释放锁plock */ 82 | void lock_release(struct lock *plock) 83 | { 84 | ASSERT(plock->holder == running_thread()); 85 | if (plock->holder_repeat_nr > 1) 86 | { 87 | plock->holder_repeat_nr--; 88 | return; 89 | } 90 | ASSERT(plock->holder_repeat_nr == 1); 91 | 92 | plock->holder = NULL; // 把锁的持有者置空放在V操作之前 93 | plock->holder_repeat_nr = 0; 94 | sema_up(&plock->semaphore); // 信号量的V操作,也是原子操作 95 | } -------------------------------------------------------------------------------- /thread/sync.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREAD_SYNC_H_ 2 | #define _THREAD_SYNC_H_ 3 | 4 | #include "../lib/kernel/list.h" 5 | #include "../lib/stdint.h" 6 | #include "thread.h" 7 | 8 | /* 信号量结构 */ 9 | struct semaphore 10 | { 11 | uint8_t value; 12 | struct list waiters; 13 | }; 14 | 15 | /* 锁结构 */ 16 | struct lock { 17 | task_struct* holder; // 锁的持有者 18 | struct semaphore semaphore; // 用二元信号量实现锁 19 | uint32_t holder_repeat_nr; // 锁的持有者重复申请锁的次数 20 | }; 21 | 22 | void sema_init(struct semaphore* psema, uint8_t value); 23 | void sema_down(struct semaphore* psema); 24 | void sema_up(struct semaphore* psema); 25 | void lock_init(struct lock* plock); 26 | void lock_acquire(struct lock* plock); 27 | void lock_release(struct lock* plock); 28 | 29 | #endif // !_THREAD_SYNC_H_ 30 | -------------------------------------------------------------------------------- /thread/thread.c: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | #include "../lib/string.h" 3 | #include "../kernel/global.h" 4 | #include "../kernel/memory.h" 5 | #include "../lib/kernel/list.h" 6 | #include "../kernel/interrupt.h" 7 | #include "../kernel/debug.h" 8 | #include "../kernel/memory.h" 9 | #include "../lib/kernel/print.h" 10 | #include "../userprog/process.h" 11 | #include "../thread/sync.h" 12 | #include "../lib/kernel/bitmap.h" 13 | 14 | task_struct *main_thread; 15 | struct list thread_ready_list; 16 | struct list thread_all_list; 17 | static struct list_elem *thread_tag; 18 | 19 | task_struct *idle_thread; 20 | 21 | struct lock pid_lock; 22 | uint8_t pid_bitmap_bits[128] = {0}; 23 | 24 | struct pid_pool 25 | { 26 | struct bitmap pid_bitmap; 27 | uint32_t pid_start; // 起始pid 28 | struct lock pid_lock; 29 | }pid_pool; 30 | 31 | extern void switch_to(task_struct *cur, task_struct *next); 32 | extern void init(); 33 | 34 | static void idle(void *arg UNUSED) 35 | { 36 | while (1) 37 | { 38 | thread_block(TASK_BLOCKED); 39 | asm volatile("sti; hlt" ::: "memory"); 40 | } 41 | } 42 | 43 | static void pid_pool_init() 44 | { 45 | pid_pool.pid_start = 1; 46 | pid_pool.pid_bitmap.bits = pid_bitmap_bits; 47 | pid_pool.pid_bitmap.btmp_bytes_len = 128; 48 | bitmap_init(&pid_pool.pid_bitmap); 49 | lock_init(&pid_pool.pid_lock); 50 | } 51 | 52 | /* 分配pid */ 53 | static pid_t allocate_pid(void) 54 | { 55 | lock_acquire(&pid_pool.pid_lock); 56 | int32_t bit_idx = bitmap_scan(&pid_pool.pid_bitmap, 1); 57 | bitmap_set(&pid_pool.pid_bitmap, bit_idx, 1); 58 | lock_release(&pid_pool.pid_lock); 59 | return (bit_idx + pid_pool.pid_start); 60 | } 61 | 62 | /* 释放pid */ 63 | void release_pid(pid_t pid) 64 | { 65 | lock_acquire(&pid_pool.pid_lock); 66 | int32_t bit_idx = pid - pid_pool.pid_start; 67 | bitmap_set(&pid_pool.pid_bitmap, bit_idx, 0); 68 | lock_release(&pid_pool.pid_lock); 69 | } 70 | 71 | /* fork进程时为其分配pid*/ 72 | pid_t fork_pid(void) 73 | { 74 | return allocate_pid(); 75 | } 76 | 77 | task_struct *running_thread() 78 | { 79 | uint32_t esp; 80 | 81 | asm ("mov %%esp, %0" : "=g" (esp)); 82 | return (task_struct *)(esp & 0xfffff000); 83 | } 84 | 85 | static void kernel_thread(thread_func *function, void *func_arg) 86 | { 87 | intr_enable(); 88 | function(func_arg); 89 | } 90 | 91 | void thread_create(task_struct* pthread, thread_func function, void* func_arg) 92 | { 93 | // 先预留中断使用栈的空间 94 | pthread->self_kstack -= sizeof(intr_stack); 95 | 96 | // 再留出线程栈空间,可见thread.h中定义 97 | pthread->self_kstack -= sizeof(thread_stack); 98 | 99 | thread_stack* kthread_stack = (thread_stack*)pthread->self_kstack; 100 | kthread_stack->eip = kernel_thread; 101 | kthread_stack->function = function; 102 | kthread_stack->func_arg = func_arg; 103 | kthread_stack->ebp = kthread_stack->ebx = kthread_stack->esi = kthread_stack->edi = 0; 104 | } 105 | 106 | 107 | // 初始化线程基本信息 108 | void init_thread(task_struct* pthread, char* name, int prio) 109 | { 110 | memset(pthread, 0, sizeof(*pthread)); 111 | strcpy(pthread->name, name); 112 | pthread->pid = allocate_pid(); 113 | 114 | if (pthread == main_thread) 115 | { 116 | pthread->status = TASK_RUNNING; 117 | } 118 | else 119 | { 120 | pthread->status = TASK_READY; 121 | } 122 | 123 | pthread->priority = prio; 124 | pthread->ticks = prio; 125 | pthread->elapsed_ticks = 0; 126 | pthread->pgdir = NULL; 127 | 128 | // self_kstack是线程自己在内核态下使用的栈顶地址 129 | pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE); 130 | 131 | pthread->fd_table[0] = 0; 132 | pthread->fd_table[1] = 1; 133 | pthread->fd_table[2] = 2; 134 | 135 | uint8_t fd_idx = 3; 136 | 137 | while(fd_idx < MAX_FILES_OPEN_PER_PROC) 138 | { 139 | pthread->fd_table[fd_idx++] = -1; 140 | } 141 | 142 | 143 | pthread->stack_magic = 0x19971234; // 自定义的魔数,检查栈溢出 144 | } 145 | 146 | // 创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) 147 | task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg) 148 | { 149 | // pcb都位于内核空间,包括用户进程的pcb也是在内核空间 150 | task_struct* thread = get_kernel_pages(1); 151 | 152 | init_thread(thread, name, prio); 153 | thread_create(thread, function, func_arg); 154 | 155 | /* 确保之前不在队列中 */ 156 | ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); 157 | /* 加入就绪线程队列 */ 158 | list_append(&thread_ready_list, &thread->general_tag); 159 | 160 | /* 确保之前不在队列中 */ 161 | ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); 162 | /* 加入全部线程队列 */ 163 | list_append(&thread_all_list, &thread->all_list_tag); 164 | 165 | //asm volatile ("movl %0, %%esp; pop %%ebp; pop %%ebx; pop %%edi; pop %%esi; ret" : : "g" (thread->self_kstack) : "memory"); 166 | return thread; 167 | } 168 | 169 | static void make_main_thread() 170 | { 171 | main_thread = running_thread(); 172 | init_thread(main_thread, "main", 31); 173 | 174 | ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag)); 175 | list_append(&thread_all_list, &main_thread->all_list_tag); 176 | } 177 | 178 | void schedule() 179 | { 180 | ASSERT(intr_get_status() == INTR_OFF); 181 | 182 | task_struct* cur = running_thread(); 183 | 184 | if (cur->status == TASK_RUNNING) 185 | { 186 | // 若此线程只是cpu时间片到了,将其加入到就绪队列尾 187 | ASSERT(!elem_find(&thread_ready_list, &cur->general_tag)); 188 | list_append(&thread_ready_list, &cur->general_tag); 189 | cur->ticks = cur->priority; // 重新将当前线程的ticks再重置为其priority; 190 | cur->status = TASK_READY; 191 | } 192 | else 193 | { 194 | /* 若此线程需要某事件发生后才能继续上cpu运行, 195 | 不需要将其加入队列,因为当前线程不在就绪队列中。*/ 196 | } 197 | 198 | if(list_empty(&thread_ready_list)) 199 | { 200 | thread_unblock(idle_thread); 201 | } 202 | 203 | ASSERT(!list_empty(&thread_ready_list)); 204 | thread_tag = NULL; // thread_tag清空 205 | /* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */ 206 | thread_tag = list_pop(&thread_ready_list); 207 | task_struct* next = elem2entry(task_struct, general_tag, thread_tag); 208 | next->status = TASK_RUNNING; 209 | 210 | process_activate(next); 211 | switch_to(cur, next); 212 | 213 | } 214 | 215 | /* 当前线程将自己阻塞,标志其状态为stat. */ 216 | void thread_block(task_status stat) 217 | { 218 | /* stat取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING,也就是只有这三种状态才不会被调度*/ 219 | ASSERT(stat == TASK_BLOCKED || stat == TASK_WAITING || stat == TASK_HANGING); 220 | 221 | enum intr_status old_status = intr_disable(); 222 | task_struct* cur_thread = running_thread(); 223 | 224 | cur_thread->status = stat; // 置其状态为stat 225 | schedule(); // 将当前线程换下处理器 226 | 227 | /* 待当前线程被解除阻塞后才继续运行下面的intr_set_status */ 228 | intr_set_status(old_status); 229 | } 230 | 231 | /* 将线程pthread解除阻塞 */ 232 | void thread_unblock(task_struct* pthread) 233 | { 234 | enum intr_status old_status = intr_disable(); 235 | ASSERT((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING)); 236 | 237 | if (pthread->status != TASK_READY) 238 | { 239 | ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag)); 240 | 241 | if (elem_find(&thread_ready_list, &pthread->general_tag)) 242 | { 243 | PANIC("thread_unblock: blocked thread in ready_list\n"); 244 | } 245 | 246 | list_push(&thread_ready_list, &pthread->general_tag); // 放到队列的最前面,使其尽快得到调度 247 | pthread->status = TASK_READY; 248 | } 249 | intr_set_status(old_status); 250 | } 251 | 252 | void thread_yield() 253 | { 254 | task_struct *cur = running_thread(); 255 | enum intr_status old_status = intr_disable(); 256 | ASSERT(!elem_find(&thread_ready_list, &cur->general_tag)); 257 | 258 | list_append(&thread_ready_list, &cur->general_tag); 259 | cur->status = TASK_READY; 260 | schedule(); 261 | intr_set_status(old_status); 262 | } 263 | 264 | /* 回收thread_over的pcb和页表,并将其从调度队列中去除 */ 265 | void thread_exit(task_struct *thread_over, bool need_schedule) 266 | { 267 | /* 要保证schedule在关中断情况下调用 */ 268 | intr_disable(); 269 | thread_over->status = TASK_DIED; 270 | 271 | /* 如果thread_over不是当前线程,就有可能还在就绪队列中,将其从中删除 */ 272 | if (elem_find(&thread_ready_list, &thread_over->general_tag)) 273 | { 274 | list_remove(&thread_over->general_tag); 275 | } 276 | if (thread_over->pgdir) 277 | { 278 | // 如是进程,回收进程的页表 279 | mfree_page(PF_KERNEL, thread_over->pgdir, 1); 280 | } 281 | 282 | /* 从all_thread_list中去掉此任务 */ 283 | list_remove(&thread_over->all_list_tag); 284 | 285 | /* 回收pcb所在的页,主线程的pcb不在堆中,跨过 */ 286 | if (thread_over != main_thread) 287 | { 288 | mfree_page(PF_KERNEL, thread_over, 1); 289 | } 290 | 291 | /* 归还pid */ 292 | release_pid(thread_over->pid); 293 | 294 | /* 如果需要下一轮调度则主动调用schedule */ 295 | if (need_schedule) 296 | { 297 | schedule(); 298 | PANIC("thread_exit: should not be here\n"); 299 | } 300 | } 301 | 302 | /* 比对任务的pid */ 303 | static bool pid_check(struct list_elem *pelem, int32_t pid) 304 | { 305 | task_struct *pthread = elem2entry(task_struct, all_list_tag, pelem); 306 | if (pthread->pid == pid) 307 | { 308 | return true; 309 | } 310 | return false; 311 | } 312 | 313 | /* 根据pid找pcb,若找到则返回该pcb,否则返回NULL */ 314 | task_struct *pid2thread(int32_t pid) 315 | { 316 | struct list_elem *pelem = list_traversal(&thread_all_list, pid_check, pid); 317 | if (pelem == NULL) 318 | { 319 | return NULL; 320 | } 321 | task_struct *thread = elem2entry(task_struct, all_list_tag, pelem); 322 | return thread; 323 | } 324 | 325 | void thread_init() 326 | { 327 | put_str("thread_init start\n"); 328 | list_init(&thread_ready_list); 329 | list_init(&thread_all_list); 330 | 331 | pid_pool_init(); 332 | process_execute(init, "init"); 333 | 334 | make_main_thread(); 335 | idle_thread = thread_start("idle", 10, idle, NULL); 336 | put_str("thread_init done\n"); 337 | } -------------------------------------------------------------------------------- /thread/thread.h: -------------------------------------------------------------------------------- 1 | #ifndef _THREAD_THREAD_H_ 2 | #define _THREAD_THREAD_H_ 3 | 4 | #include "../lib/stdint.h" 5 | #include "../lib/kernel/list.h" 6 | #include "../lib/kernel/bitmap.h" 7 | #include "../kernel/memory.h" 8 | 9 | #define MAX_FILES_OPEN_PER_PROC 8 10 | 11 | typedef void thread_func(void*); 12 | 13 | typedef int16_t pid_t; 14 | 15 | // 线程的状态 16 | typedef enum tag_task_status 17 | { 18 | TASK_RUNNING, 19 | TASK_READY, 20 | TASK_BLOCKED, 21 | TASK_WAITING, 22 | TASK_HANGING, 23 | TASK_DIED 24 | }task_status; 25 | 26 | // 中断栈 27 | // 此结构用于中断发生时保护程序上下文环境 28 | // 此栈放于内核栈中的固定位置,即内核栈所在页的最顶端 29 | typedef struct tag_intr_stack 30 | { 31 | uint32_t vec_no; 32 | uint32_t edi; 33 | uint32_t esi; 34 | uint32_t ebp; 35 | uint32_t esp_dummy; 36 | 37 | uint32_t ebx; 38 | uint32_t edx; 39 | uint32_t ecx; 40 | uint32_t eax; 41 | uint32_t gs; 42 | uint32_t fs; 43 | uint32_t es; 44 | uint32_t ds; 45 | 46 | uint32_t err_code; 47 | void (*eip)(); 48 | uint32_t cs; 49 | uint32_t eflags; 50 | void *esp; 51 | uint32_t ss; 52 | }intr_stack; 53 | 54 | 55 | // 线程栈 56 | typedef struct tag_thread_stack 57 | { 58 | uint32_t ebp; 59 | uint32_t ebx; 60 | uint32_t edi; 61 | uint32_t esi; 62 | 63 | 64 | void (*eip)(thread_func *func, void *func_arg); 65 | 66 | void *unused_retaddr; 67 | thread_func *function; 68 | void *func_arg; 69 | }thread_stack; 70 | 71 | // 线程pcb 72 | typedef struct tag_task_struct 73 | { 74 | uint32_t *self_kstack; // 内核线程的栈顶地址 75 | pid_t pid; 76 | task_status status; // 当前线程的状态 77 | char name[16]; 78 | uint8_t priority; 79 | uint8_t ticks; // 线程执行的时间 80 | 81 | uint32_t elapsed_ticks; // 线程已经执行的时间 82 | 83 | int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; 84 | 85 | struct list_elem general_tag; 86 | 87 | struct list_elem all_list_tag; 88 | uint32_t *pgdir; 89 | struct virtual_addr userprog_vaddr; 90 | struct mem_block_desc u_block_desc[DESC_CNT]; 91 | uint32_t cwd_inode_pid; 92 | pid_t parent_pid; 93 | int8_t exit_status; 94 | uint32_t stack_magic; // 栈的边界标记,用来检测栈溢出 95 | }task_struct; 96 | 97 | extern struct list thread_ready_list; 98 | extern struct list thread_all_list; 99 | 100 | void thread_create(task_struct* pthread, thread_func function, void* func_arg); 101 | void init_thread(task_struct* pthread, char* name, int prio); 102 | task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg); 103 | 104 | task_struct *running_thread(); 105 | void schedule(); 106 | 107 | void thread_init(); 108 | 109 | void thread_unblock(task_struct* pthread); 110 | void thread_block(task_status stat); 111 | void thread_yield(); 112 | void release_pid(pid_t pid); 113 | pid_t fork_pid(void); 114 | #endif //!_THREAD_THREAD_H_ -------------------------------------------------------------------------------- /userprog/exec.c: -------------------------------------------------------------------------------- 1 | #include "exec.h" 2 | #include "../thread/thread.h" 3 | #include "../lib/kernel/stdio-kernel.h" 4 | #include "../fs/fs.h" 5 | #include "../lib/string.h" 6 | #include "../kernel/global.h" 7 | #include "../kernel/memory.h" 8 | 9 | extern void intr_exit(void); 10 | typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off; 11 | typedef uint16_t Elf32_Half; 12 | 13 | /* 32位elf头 */ 14 | struct Elf32_Ehdr 15 | { 16 | unsigned char e_ident[16]; 17 | Elf32_Half e_type; 18 | Elf32_Half e_machine; 19 | Elf32_Word e_version; 20 | Elf32_Addr e_entry; 21 | Elf32_Off e_phoff; 22 | Elf32_Off e_shoff; 23 | Elf32_Word e_flags; 24 | Elf32_Half e_ehsize; 25 | Elf32_Half e_phentsize; 26 | Elf32_Half e_phnum; 27 | Elf32_Half e_shentsize; 28 | Elf32_Half e_shnum; 29 | Elf32_Half e_shstrndx; 30 | }; 31 | 32 | /* 程序头表Program header.就是段描述头 */ 33 | struct Elf32_Phdr 34 | { 35 | Elf32_Word p_type; // 见下面的enum segment_type 36 | Elf32_Off p_offset; 37 | Elf32_Addr p_vaddr; 38 | Elf32_Addr p_paddr; 39 | Elf32_Word p_filesz; 40 | Elf32_Word p_memsz; 41 | Elf32_Word p_flags; 42 | Elf32_Word p_align; 43 | }; 44 | 45 | /* 段类型 */ 46 | enum segment_type 47 | { 48 | PT_NULL, // 忽略 49 | PT_LOAD, // 可加载程序段 50 | PT_DYNAMIC, // 动态加载信息 51 | PT_INTERP, // 动态加载器名称 52 | PT_NOTE, // 一些辅助信息 53 | PT_SHLIB, // 保留 54 | PT_PHDR // 程序头表 55 | }; 56 | 57 | /* 将文件描述符fd指向的文件中,偏移为offset,大小为filesz的段加载到虚拟地址为vaddr的内存 */ 58 | static bool segment_load(int32_t fd, uint32_t offset, uint32_t filesz, uint32_t vaddr) 59 | { 60 | uint32_t vaddr_first_page = vaddr & 0xfffff000; // vaddr地址所在的页框 61 | uint32_t size_in_first_page = PG_SIZE - (vaddr & 0x00000fff); // 加载到内存后,文件在第一个页框中占用的字节大小 62 | uint32_t occupy_pages = 0; 63 | /* 若一个页框容不下该段 */ 64 | if (filesz > size_in_first_page) 65 | { 66 | uint32_t left_size = filesz - size_in_first_page; 67 | occupy_pages = DIV_ROUND_UP(left_size, PG_SIZE) + 1; // 1是指vaddr_first_page 68 | } 69 | else 70 | { 71 | occupy_pages = 1; 72 | } 73 | 74 | /* 为进程分配内存 */ 75 | uint32_t page_idx = 0; 76 | uint32_t vaddr_page = vaddr_first_page; 77 | while (page_idx < occupy_pages) 78 | { 79 | uint32_t *pde = pde_ptr(vaddr_page); 80 | uint32_t *pte = pte_ptr(vaddr_page); 81 | 82 | /* 如果pde不存在,或者pte不存在就分配内存. 83 | * pde的判断要在pte之前,否则pde若不存在会导致 84 | * 判断pte时缺页异常 */ 85 | if (!(*pde & 0x00000001) || !(*pte & 0x00000001)) 86 | { 87 | if (get_a_page(PF_USER, vaddr_page) == NULL) 88 | { 89 | return false; 90 | } 91 | } // 如果原进程的页表已经分配了,利用现有的物理页,直接覆盖进程体 92 | vaddr_page += PG_SIZE; 93 | page_idx++; 94 | } 95 | sys_lseek(fd, offset, SEEK_SET); 96 | sys_read(fd, (void *)vaddr, filesz); 97 | return true; 98 | } 99 | 100 | /* 从文件系统上加载用户程序pathname,成功则返回程序的起始地址,否则返回-1 */ 101 | static int32_t load(const char *pathname) 102 | { 103 | int32_t ret = -1; 104 | struct Elf32_Ehdr elf_header; 105 | struct Elf32_Phdr prog_header; 106 | memset(&elf_header, 0, sizeof(struct Elf32_Ehdr)); 107 | 108 | int32_t fd = sys_open(pathname, O_RDONLY); 109 | if (fd == -1) 110 | { 111 | return -1; 112 | } 113 | 114 | if (sys_read(fd, &elf_header, sizeof(struct Elf32_Ehdr)) != sizeof(struct Elf32_Ehdr)) 115 | { 116 | ret = -1; 117 | goto done; 118 | } 119 | 120 | /* 校验elf头 */ 121 | if (memcmp(elf_header.e_ident, "\177ELF\1\1\1", 7) || elf_header.e_type != 2 || elf_header.e_machine != 3 || elf_header.e_version != 1 || elf_header.e_phnum > 1024 || elf_header.e_phentsize != sizeof(struct Elf32_Phdr)) 122 | { 123 | ret = -1; 124 | goto done; 125 | } 126 | 127 | Elf32_Off prog_header_offset = elf_header.e_phoff; 128 | Elf32_Half prog_header_size = elf_header.e_phentsize; 129 | 130 | /* 遍历所有程序头 */ 131 | uint32_t prog_idx = 0; 132 | while (prog_idx < elf_header.e_phnum) 133 | { 134 | memset(&prog_header, 0, prog_header_size); 135 | 136 | /* 将文件的指针定位到程序头 */ 137 | sys_lseek(fd, prog_header_offset, SEEK_SET); 138 | 139 | /* 只获取程序头 */ 140 | if (sys_read(fd, &prog_header, prog_header_size) != prog_header_size) 141 | { 142 | ret = -1; 143 | goto done; 144 | } 145 | 146 | /* 如果是可加载段就调用segment_load加载到内存 */ 147 | if (PT_LOAD == prog_header.p_type) 148 | { 149 | if (!segment_load(fd, prog_header.p_offset, prog_header.p_filesz, prog_header.p_vaddr)) 150 | { 151 | ret = -1; 152 | goto done; 153 | } 154 | } 155 | 156 | /* 更新下一个程序头的偏移 */ 157 | prog_header_offset += elf_header.e_phentsize; 158 | prog_idx++; 159 | } 160 | ret = elf_header.e_entry; 161 | done: 162 | sys_close(fd); 163 | return ret; 164 | } 165 | 166 | /* 用path指向的程序替换当前进程 */ 167 | int32_t sys_execv(const char *path, const char *argv[]) 168 | { 169 | uint32_t argc = 0; 170 | while (argv[argc]) 171 | { 172 | argc++; 173 | } 174 | int32_t entry_point = load(path); 175 | if (entry_point == -1) 176 | { // 若加载失败则返回-1 177 | return -1; 178 | } 179 | 180 | task_struct *cur = running_thread(); 181 | /* 修改进程名 */ 182 | memcpy(cur->name, path, TASK_NAME_LEN); 183 | cur->name[TASK_NAME_LEN - 1] = 0; 184 | 185 | intr_stack *intr_0_stack = (intr_stack *)((uint32_t)cur + PG_SIZE - sizeof(intr_stack)); 186 | /* 参数传递给用户进程 */ 187 | intr_0_stack->ebx = (int32_t)argv; 188 | intr_0_stack->ecx = argc; 189 | intr_0_stack->eip = (void *)entry_point; 190 | /* 使新用户进程的栈地址为最高用户空间地址 */ 191 | intr_0_stack->esp = (void *)0xc0000000; 192 | 193 | /* exec不同于fork,为使新进程更快被执行,直接从中断返回 */ 194 | asm volatile("movl %0, %%esp; jmp intr_exit" 195 | : 196 | : "g"(intr_0_stack) 197 | : "memory"); 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /userprog/exec.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_EXEC_H_ 2 | #define _USERPROG_EXEC_H_ 3 | #include "../lib/stdint.h" 4 | int32_t sys_execv(const char* path, const char* argv[]); 5 | #endif 6 | -------------------------------------------------------------------------------- /userprog/fork.c: -------------------------------------------------------------------------------- 1 | #include "fork.h" 2 | #include "process.h" 3 | #include "../kernel/memory.h" 4 | #include "../kernel/interrupt.h" 5 | #include "../kernel/debug.h" 6 | #include "../thread/thread.h" 7 | #include "../lib/string.h" 8 | #include "../fs/file.h" 9 | 10 | extern void intr_exit(void); 11 | 12 | /* 将父进程的pcb、虚拟地址位图拷贝给子进程 */ 13 | static int32_t copy_pcb_vaddrbitmap_stack0(task_struct *child_thread, task_struct *parent_thread) 14 | { 15 | /* a 复制pcb所在的整个页,里面包含进程pcb信息及特级0极的栈,里面包含了返回地址, 然后再单独修改个别部分 */ 16 | memcpy(child_thread, parent_thread, PG_SIZE); 17 | child_thread->pid = fork_pid(); 18 | child_thread->elapsed_ticks = 0; 19 | child_thread->status = TASK_READY; 20 | child_thread->ticks = child_thread->priority; // 为新进程把时间片充满 21 | child_thread->parent_pid = parent_thread->pid; 22 | child_thread->general_tag.prev = child_thread->general_tag.next = NULL; 23 | child_thread->all_list_tag.prev = child_thread->all_list_tag.next = NULL; 24 | block_desc_init(child_thread->u_block_desc); 25 | /* b 复制父进程的虚拟地址池的位图 */ 26 | uint32_t bitmap_pg_cnt = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE); 27 | void *vaddr_btmp = get_kernel_pages(bitmap_pg_cnt); 28 | if (vaddr_btmp == NULL) 29 | return -1; 30 | /* 此时child_thread->userprog_vaddr.vaddr_bitmap.bits还是指向父进程虚拟地址的位图地址 31 | * 下面将child_thread->userprog_vaddr.vaddr_bitmap.bits指向自己的位图vaddr_btmp */ 32 | memcpy(vaddr_btmp, child_thread->userprog_vaddr.vaddr_bitmap.bits, bitmap_pg_cnt * PG_SIZE); 33 | child_thread->userprog_vaddr.vaddr_bitmap.bits = vaddr_btmp; 34 | /* 调试用 */ 35 | ASSERT(strlen(child_thread->name) < 11); // pcb.name的长度是16,为避免下面strcat越界 36 | strcat(child_thread->name, "_fork"); 37 | return 0; 38 | } 39 | 40 | /* 复制子进程的进程体(代码和数据)及用户栈 */ 41 | static void copy_body_stack3(task_struct *child_thread, task_struct *parent_thread, void *buf_page) 42 | { 43 | uint8_t *vaddr_btmp = parent_thread->userprog_vaddr.vaddr_bitmap.bits; 44 | uint32_t btmp_bytes_len = parent_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len; 45 | uint32_t vaddr_start = parent_thread->userprog_vaddr.vaddr_start; 46 | uint32_t idx_byte = 0; 47 | uint32_t idx_bit = 0; 48 | uint32_t prog_vaddr = 0; 49 | 50 | /* 在父进程的用户空间中查找已有数据的页 */ 51 | while (idx_byte < btmp_bytes_len) 52 | { 53 | if (vaddr_btmp[idx_byte]) 54 | { 55 | idx_bit = 0; 56 | while (idx_bit < 8) 57 | { 58 | if ((BITMAP_MASK << idx_bit) & vaddr_btmp[idx_byte]) 59 | { 60 | prog_vaddr = (idx_byte * 8 + idx_bit) * PG_SIZE + vaddr_start; 61 | /* 下面的操作是将父进程用户空间中的数据通过内核空间做中转,最终复制到子进程的用户空间 */ 62 | 63 | /* a 将父进程在用户空间中的数据复制到内核缓冲区buf_page, 64 | 目的是下面切换到子进程的页表后,还能访问到父进程的数据*/ 65 | memcpy(buf_page, (void *)prog_vaddr, PG_SIZE); 66 | 67 | /* b 将页表切换到子进程,目的是避免下面申请内存的函数将pte及pde安装在父进程的页表中 */ 68 | page_dir_activate(child_thread); 69 | /* c 申请虚拟地址prog_vaddr */ 70 | get_a_page_without_opvaddrbitmap(PF_USER, prog_vaddr); 71 | 72 | /* d 从内核缓冲区中将父进程数据复制到子进程的用户空间 */ 73 | memcpy((void *)prog_vaddr, buf_page, PG_SIZE); 74 | 75 | /* e 恢复父进程页表 */ 76 | page_dir_activate(parent_thread); 77 | } 78 | idx_bit++; 79 | } 80 | } 81 | idx_byte++; 82 | } 83 | } 84 | 85 | /* 为子进程构建thread_stack和修改返回值 */ 86 | static int32_t build_child_stack(task_struct *child_thread) 87 | { 88 | /* a 使子进程pid返回值为0 */ 89 | /* 获取子进程0级栈栈顶 */ 90 | intr_stack *intr_0_stack = (intr_stack *)((uint32_t)child_thread + PG_SIZE - sizeof(intr_stack)); 91 | /* 修改子进程的返回值为0 */ 92 | intr_0_stack->eax = 0; 93 | 94 | /* b 为switch_to 构建 struct thread_stack,将其构建在紧临intr_stack之下的空间*/ 95 | uint32_t *ret_addr_in_thread_stack = (uint32_t *)intr_0_stack - 1; 96 | 97 | /*** 这三行不是必要的,只是为了梳理thread_stack中的关系 ***/ 98 | uint32_t *esi_ptr_in_thread_stack = (uint32_t *)intr_0_stack - 2; 99 | uint32_t *edi_ptr_in_thread_stack = (uint32_t *)intr_0_stack - 3; 100 | uint32_t *ebx_ptr_in_thread_stack = (uint32_t *)intr_0_stack - 4; 101 | /**********************************************************/ 102 | 103 | /* ebp在thread_stack中的地址便是当时的esp(0级栈的栈顶), 104 | 即esp为"(uint32_t*)intr_0_stack - 5" */ 105 | uint32_t *ebp_ptr_in_thread_stack = (uint32_t *)intr_0_stack - 5; 106 | 107 | /* switch_to的返回地址更新为intr_exit,直接从中断返回 */ 108 | *ret_addr_in_thread_stack = (uint32_t)intr_exit; 109 | 110 | /* 下面这两行赋值只是为了使构建的thread_stack更加清晰,其实也不需要, 111 | * 因为在进入intr_exit后一系列的pop会把寄存器中的数据覆盖 */ 112 | *ebp_ptr_in_thread_stack = *ebx_ptr_in_thread_stack = 113 | *edi_ptr_in_thread_stack = *esi_ptr_in_thread_stack = 0; 114 | /*********************************************************/ 115 | 116 | /* 把构建的thread_stack的栈顶做为switch_to恢复数据时的栈顶 */ 117 | child_thread->self_kstack = ebp_ptr_in_thread_stack; 118 | return 0; 119 | } 120 | 121 | /* 更新inode打开数 */ 122 | static void update_inode_open_cnts(task_struct *thread) 123 | { 124 | int32_t local_fd = 3, global_fd = 0; 125 | while (local_fd < MAX_FILES_OPEN_PER_PROC) 126 | { 127 | global_fd = thread->fd_table[local_fd]; 128 | ASSERT(global_fd < MAX_FILE_OPEN); 129 | if (global_fd != -1) 130 | { 131 | if (is_pipe(local_fd)) 132 | { 133 | file_table[global_fd].fd_pos++; 134 | } 135 | else 136 | { 137 | file_table[global_fd].fd_inode->i_open_cnts++; 138 | } 139 | } 140 | local_fd++; 141 | } 142 | } 143 | 144 | /* 拷贝父进程本身所占资源给子进程 */ 145 | static int32_t copy_process(task_struct *child_thread, task_struct *parent_thread) 146 | { 147 | /* 内核缓冲区,作为父进程用户空间的数据复制到子进程用户空间的中转 */ 148 | void *buf_page = get_kernel_pages(1); 149 | if (buf_page == NULL) 150 | { 151 | return -1; 152 | } 153 | 154 | /* a 复制父进程的pcb、虚拟地址位图、内核栈到子进程 */ 155 | if (copy_pcb_vaddrbitmap_stack0(child_thread, parent_thread) == -1) 156 | { 157 | return -1; 158 | } 159 | 160 | /* b 为子进程创建页表,此页表仅包括内核空间 */ 161 | child_thread->pgdir = create_page_dir(); 162 | if (child_thread->pgdir == NULL) 163 | { 164 | return -1; 165 | } 166 | 167 | /* c 复制父进程进程体及用户栈给子进程 */ 168 | copy_body_stack3(child_thread, parent_thread, buf_page); 169 | 170 | /* d 构建子进程thread_stack和修改返回值pid */ 171 | build_child_stack(child_thread); 172 | 173 | /* e 更新文件inode的打开数 */ 174 | update_inode_open_cnts(child_thread); 175 | 176 | mfree_page(PF_KERNEL, buf_page, 1); 177 | return 0; 178 | } 179 | 180 | /* fork子进程,内核线程不可直接调用 */ 181 | pid_t sys_fork(void) 182 | { 183 | task_struct *parent_thread = running_thread(); 184 | task_struct *child_thread = get_kernel_pages(1); // 为子进程创建pcb(task_struct结构) 185 | if (child_thread == NULL) 186 | { 187 | return -1; 188 | } 189 | ASSERT(INTR_OFF == intr_get_status() && parent_thread->pgdir != NULL); 190 | 191 | if (copy_process(child_thread, parent_thread) == -1) 192 | { 193 | return -1; 194 | } 195 | 196 | /* 添加到就绪线程队列和所有线程队列,子进程由调试器安排运行 */ 197 | ASSERT(!elem_find(&thread_ready_list, &child_thread->general_tag)); 198 | list_append(&thread_ready_list, &child_thread->general_tag); 199 | ASSERT(!elem_find(&thread_all_list, &child_thread->all_list_tag)); 200 | list_append(&thread_all_list, &child_thread->all_list_tag); 201 | 202 | return child_thread->pid; // 父进程返回子进程的pid 203 | } 204 | -------------------------------------------------------------------------------- /userprog/fork.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_FORK_H_ 2 | #define _USERPROG_FORK_H_ 3 | 4 | #include "../thread/thread.h" 5 | 6 | /* fork子进程,只能由用户进程通过系统调用fork调用, 7 | 内核线程不可直接调用,原因是要从0级栈中获得esp3等 */ 8 | pid_t sys_fork(void); 9 | #endif 10 | -------------------------------------------------------------------------------- /userprog/process.c: -------------------------------------------------------------------------------- 1 | #include "process.h" 2 | #include "../kernel/global.h" 3 | #include "../kernel/debug.h" 4 | #include "../kernel/memory.h" 5 | #include "../thread/thread.h" 6 | #include "../lib/kernel/list.h" 7 | #include "tss.h" 8 | #include "../kernel/interrupt.h" 9 | #include "../lib/string.h" 10 | #include "../device/console.h" 11 | 12 | extern void intr_exit(void); 13 | 14 | /* 构建用户进程初始上下文信息 */ 15 | void start_process(void *filename_) 16 | { 17 | void *function = filename_; 18 | task_struct *cur = running_thread(); 19 | cur->self_kstack += sizeof(thread_stack); 20 | intr_stack *proc_stack = (struct intr_stack *)cur->self_kstack; 21 | proc_stack->edi = proc_stack->esi = proc_stack->ebp = proc_stack->esp_dummy = 0; 22 | proc_stack->ebx = proc_stack->edx = proc_stack->ecx = proc_stack->eax = 0; 23 | proc_stack->gs = 0; // 用户态用不上,直接初始为0 24 | proc_stack->ds = proc_stack->es = proc_stack->fs = SELECTOR_U_DATA; 25 | proc_stack->eip = function; // 待执行的用户程序地址 26 | proc_stack->cs = SELECTOR_U_CODE; 27 | proc_stack->eflags = (EFLAGS_IOPL_0 | EFLAGS_MBS | EFLAGS_IF_1); 28 | proc_stack->esp = (void *)((uint32_t)get_a_page(PF_USER, USER_STACK3_VADDR) + PG_SIZE); 29 | proc_stack->ss = SELECTOR_U_DATA; 30 | asm volatile("movl %0, %%esp; jmp intr_exit" 31 | : 32 | : "g"(proc_stack) 33 | : "memory"); 34 | } 35 | 36 | /* 击活页表 */ 37 | void page_dir_activate(task_struct *p_thread) 38 | { 39 | /******************************************************** 40 | * 执行此函数时,当前任务可能是线程。 41 | * 之所以对线程也要重新安装页表, 原因是上一次被调度的可能是进程, 42 | * 否则不恢复页表的话,线程就会使用进程的页表了。 43 | ********************************************************/ 44 | 45 | /* 若为内核线程,需要重新填充页表为0x100000 */ 46 | uint32_t pagedir_phy_addr = 0x100000; // 默认为内核的页目录物理地址,也就是内核线程所用的页目录表 47 | if (p_thread->pgdir != NULL) 48 | { // 用户态进程有自己的页目录表 49 | pagedir_phy_addr = addr_v2p((uint32_t)p_thread->pgdir); 50 | } 51 | 52 | /* 更新页目录寄存器cr3,使新页表生效 */ 53 | asm volatile("movl %0, %%cr3" 54 | : 55 | : "r"(pagedir_phy_addr) 56 | : "memory"); 57 | } 58 | 59 | /* 击活线程或进程的页表,更新tss中的esp0为进程的特权级0的栈 */ 60 | void process_activate(task_struct *p_thread) 61 | { 62 | ASSERT(p_thread != NULL); 63 | /* 击活该进程或线程的页表 */ 64 | page_dir_activate(p_thread); 65 | 66 | /* 内核线程特权级本身就是0,处理器进入中断时并不会从tss中获取0特权级栈地址,故不需要更新esp0 */ 67 | if (p_thread->pgdir) 68 | { 69 | /* 更新该进程的esp0,用于此进程被中断时保留上下文 */ 70 | update_tss_esp(p_thread); 71 | } 72 | } 73 | 74 | /* 创建页目录表,将当前页表的表示内核空间的pde复制, 75 | * 成功则返回页目录的虚拟地址,否则返回-1 */ 76 | uint32_t *create_page_dir(void) 77 | { 78 | /* 用户进程的页表不能让用户直接访问到,所以在内核空间来申请 */ 79 | uint32_t *page_dir_vaddr = get_kernel_pages(1); 80 | if (page_dir_vaddr == NULL) 81 | { 82 | console_put_str("create_page_dir: get_kernel_page failed!"); 83 | return NULL; 84 | } 85 | 86 | /************************** 1 先复制页表 *************************************/ 87 | /* page_dir_vaddr + 0x300*4 是内核页目录的第768项 */ 88 | memcpy((uint32_t *)((uint32_t)page_dir_vaddr + 0x300 * 4), (uint32_t *)(0xfffff000 + 0x300 * 4), 1024); 89 | /*****************************************************************************/ 90 | 91 | /************************** 2 更新页目录地址 **********************************/ 92 | uint32_t new_page_dir_phy_addr = addr_v2p((uint32_t)page_dir_vaddr); 93 | /* 页目录地址是存入在页目录的最后一项,更新页目录地址为新页目录的物理地址 */ 94 | page_dir_vaddr[1023] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1; 95 | /*****************************************************************************/ 96 | return page_dir_vaddr; 97 | } 98 | 99 | /* 创建用户进程虚拟地址位图 */ 100 | void create_user_vaddr_bitmap(task_struct *user_prog) 101 | { 102 | user_prog->userprog_vaddr.vaddr_start = USER_VADDR_START; 103 | uint32_t bitmap_pg_cnt = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE); 104 | user_prog->userprog_vaddr.vaddr_bitmap.bits = get_kernel_pages(bitmap_pg_cnt); 105 | user_prog->userprog_vaddr.vaddr_bitmap.btmp_bytes_len = (0xc0000000 - USER_VADDR_START) / PG_SIZE / 8; 106 | bitmap_init(&user_prog->userprog_vaddr.vaddr_bitmap); 107 | } 108 | 109 | /* 创建用户进程 */ 110 | void process_execute(void *filename, char *name) 111 | { 112 | /* pcb内核的数据结构,由内核来维护进程信息,因此要在内核内存池中申请 */ 113 | task_struct *thread = get_kernel_pages(1); 114 | init_thread(thread, name, default_prio); 115 | create_user_vaddr_bitmap(thread); 116 | thread_create(thread, start_process, filename); 117 | thread->pgdir = create_page_dir(); 118 | 119 | block_desc_init(thread->u_block_desc); 120 | 121 | enum intr_status old_status = intr_disable(); 122 | ASSERT(!elem_find(&thread_ready_list, &thread->general_tag)); 123 | list_append(&thread_ready_list, &thread->general_tag); 124 | 125 | ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag)); 126 | list_append(&thread_all_list, &thread->all_list_tag); 127 | intr_set_status(old_status); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /userprog/process.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_PROCESS_H_ 2 | #define _USERPROG_PROCESS_H_ 3 | 4 | #include "../thread/thread.h" 5 | #include "../lib/stdint.h" 6 | 7 | 8 | #define default_prio 31 9 | #define USER_STACK3_VADDR (0xc0000000 - 0x1000) 10 | #define USER_VADDR_START 0x8048000 11 | 12 | 13 | void process_execute(void* filename, char* name); 14 | void start_process(void* filename_); 15 | void process_activate(task_struct* p_thread); 16 | void page_dir_activate(task_struct* p_thread); 17 | uint32_t* create_page_dir(void); 18 | void create_user_vaddr_bitmap(task_struct* user_prog); 19 | 20 | 21 | #endif // !_USERPROG_PROCESS_H_ 22 | -------------------------------------------------------------------------------- /userprog/syscall-init.c: -------------------------------------------------------------------------------- 1 | #include "syscall-init.h" 2 | #include "../lib/user/syscall.h" 3 | #include "../lib/stdint.h" 4 | #include "../lib/kernel/print.h" 5 | #include "../thread/thread.h" 6 | #include "../device/console.h" 7 | #include "../lib/string.h" 8 | #include "../kernel/memory.h" 9 | #include "../fs/fs.h" 10 | 11 | #define syscall_nr 32 12 | typedef void *syscall; 13 | syscall syscall_table[syscall_nr]; 14 | 15 | /* 返回当前任务的pid */ 16 | uint32_t sys_getpid(void) 17 | { 18 | return running_thread()->pid; 19 | } 20 | 21 | /* 初始化系统调用 */ 22 | void syscall_init(void) 23 | { 24 | put_str("syscall_init start\n"); 25 | syscall_table[SYS_GETPID] = sys_getpid; 26 | syscall_table[SYS_WRITE] = sys_write; 27 | syscall_table[SYS_MALLOC] = sys_malloc; 28 | syscall_table[SYS_FREE] = sys_free; 29 | syscall_table[SYS_OPEN] = sys_open; 30 | syscall_table[SYS_CLOSE] = sys_close; 31 | syscall_table[SYS_READ] = sys_read; 32 | syscall_table[SYS_LSEEK] = sys_lseek; 33 | syscall_table[SYS_UNLINK] = sys_unlink; 34 | put_str("syscall_init done\n"); 35 | } 36 | -------------------------------------------------------------------------------- /userprog/syscall-init.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_SYSCALLINIT_H_ 2 | #define _USERPROG_SYSCALLINIT_H_ 3 | 4 | #include "../lib/stdint.h" 5 | 6 | void syscall_init(void); 7 | uint32_t sys_getpid(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /userprog/tss.c: -------------------------------------------------------------------------------- 1 | #include "tss.h" 2 | #include "../lib/stdint.h" 3 | #include "../kernel/global.h" 4 | #include "../lib/string.h" 5 | #include "../lib/kernel/print.h" 6 | 7 | /* 任务状态段tss结构 */ 8 | struct tss 9 | { 10 | uint32_t backlink; 11 | uint32_t* esp0; 12 | uint32_t ss0; 13 | uint32_t* esp1; 14 | uint32_t ss1; 15 | uint32_t* esp2; 16 | uint32_t ss2; 17 | uint32_t cr3; 18 | uint32_t (*eip) (void); 19 | uint32_t eflags; 20 | uint32_t eax; 21 | uint32_t ecx; 22 | uint32_t edx; 23 | uint32_t ebx; 24 | uint32_t esp; 25 | uint32_t ebp; 26 | uint32_t esi; 27 | uint32_t edi; 28 | uint32_t es; 29 | uint32_t cs; 30 | uint32_t ss; 31 | uint32_t ds; 32 | uint32_t fs; 33 | uint32_t gs; 34 | uint32_t ldt; 35 | uint32_t trace; 36 | uint32_t io_base; 37 | }; 38 | 39 | static struct tss tss; 40 | 41 | /* 更新tss中esp0字段的值为pthread的0级线 */ 42 | void update_tss_esp(task_struct* pthread) 43 | { 44 | tss.esp0 = (uint32_t*)((uint32_t)pthread + PG_SIZE); 45 | } 46 | 47 | /* 创建gdt描述符 */ 48 | static struct gdt_desc make_gdt_desc(uint32_t* desc_addr, uint32_t limit, uint8_t attr_low, uint8_t attr_high) 49 | { 50 | uint32_t desc_base = (uint32_t)desc_addr; 51 | struct gdt_desc desc; 52 | desc.limit_low_word = limit & 0x0000ffff; 53 | desc.base_low_word = desc_base & 0x0000ffff; 54 | desc.base_mid_byte = ((desc_base & 0x00ff0000) >> 16); 55 | desc.attr_low_byte = (uint8_t)(attr_low); 56 | desc.limit_high_attr_high = (((limit & 0x000f0000) >> 16) + (uint8_t)(attr_high)); 57 | desc.base_high_byte = desc_base >> 24; 58 | return desc; 59 | } 60 | 61 | /* 在gdt中创建tss并重新加载gdt */ 62 | void tss_init() 63 | { 64 | put_str("tss_init start\n"); 65 | uint32_t tss_size = sizeof(tss); 66 | memset(&tss, 0, tss_size); 67 | tss.ss0 = SELECTOR_K_STACK; 68 | tss.io_base = tss_size; 69 | 70 | /* gdt段基址为0x900,把tss放到第4个位置,也就是0x900+0x20的位置 */ 71 | 72 | /* 在gdt中添加dpl为0的TSS描述符 */ 73 | *((struct gdt_desc*)0xc0000920) = make_gdt_desc((uint32_t*)&tss, tss_size - 1, TSS_ATTR_LOW, TSS_ATTR_HIGH); 74 | 75 | /* 在gdt中添加dpl为3的数据段和代码段描述符 */ 76 | *((struct gdt_desc*)0xc0000928) = make_gdt_desc((uint32_t*)0, 0xfffff, GDT_CODE_ATTR_LOW_DPL3, GDT_ATTR_HIGH); 77 | *((struct gdt_desc*)0xc0000930) = make_gdt_desc((uint32_t*)0, 0xfffff, GDT_DATA_ATTR_LOW_DPL3, GDT_ATTR_HIGH); 78 | 79 | /* gdt 16位的limit 32位的段基址 */ 80 | uint64_t gdt_operand = ((8 * 7 - 1) | ((uint64_t)(uint32_t)0xc0000900 << 16)); // 7个描述符大小 81 | asm volatile ("lgdt %0" : : "m" (gdt_operand)); 82 | asm volatile ("ltr %w0" : : "r" (SELECTOR_TSS)); 83 | put_str("tss_init and ltr done\n"); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /userprog/tss.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_TSS_H_ 2 | #define _USERPROG_TSS_H_ 3 | 4 | #include "../thread/thread.h" 5 | 6 | void update_tss_esp(task_struct* pthread); 7 | void tss_init(void); 8 | 9 | #endif //!_USERPROG_TSS_H_ 10 | -------------------------------------------------------------------------------- /userprog/wait_exit.c: -------------------------------------------------------------------------------- 1 | #include "wait_exit.h" 2 | #include "../kernel/global.h" 3 | #include "../kernel/debug.h" 4 | #include "../thread/thread.h" 5 | #include "../lib/kernel/list.h" 6 | #include "../lib/kernel/stdio-kernel.h" 7 | #include "../kernel/memory.h" 8 | #include "../lib/kernel/bitmap.h" 9 | #include "../fs/fs.h" 10 | 11 | /* 释放用户进程资源: 12 | * 1 页表中对应的物理页 13 | * 2 虚拟内存池占物理页框 14 | * 3 关闭打开的文件 */ 15 | static void release_prog_resource(task_struct *release_thread) 16 | { 17 | uint32_t *pgdir_vaddr = release_thread->pgdir; 18 | uint16_t user_pde_nr = 768, pde_idx = 0; 19 | uint32_t pde = 0; 20 | uint32_t *v_pde_ptr = NULL; // v表示var,和函数pde_ptr区分 21 | 22 | uint16_t user_pte_nr = 1024, pte_idx = 0; 23 | uint32_t pte = 0; 24 | uint32_t *v_pte_ptr = NULL; // 加个v表示var,和函数pte_ptr区分 25 | 26 | uint32_t *first_pte_vaddr_in_pde = NULL; // 用来记录pde中第0个pte的地址 27 | uint32_t pg_phy_addr = 0; 28 | 29 | /* 回收页表中用户空间的页框 */ 30 | while (pde_idx < user_pde_nr) 31 | { 32 | v_pde_ptr = pgdir_vaddr + pde_idx; 33 | pde = *v_pde_ptr; 34 | if (pde & 0x00000001) 35 | { // 如果页目录项p位为1,表示该页目录项下可能有页表项 36 | first_pte_vaddr_in_pde = pte_ptr(pde_idx * 0x400000); // 一个页表表示的内存容量是4M,即0x400000 37 | pte_idx = 0; 38 | while (pte_idx < user_pte_nr) 39 | { 40 | v_pte_ptr = first_pte_vaddr_in_pde + pte_idx; 41 | pte = *v_pte_ptr; 42 | if (pte & 0x00000001) 43 | { 44 | /* 将pte中记录的物理页框直接在相应内存池的位图中清0 */ 45 | pg_phy_addr = pte & 0xfffff000; 46 | free_a_phy_page(pg_phy_addr); 47 | } 48 | pte_idx++; 49 | } 50 | /* 将pde中记录的物理页框直接在相应内存池的位图中清0 */ 51 | pg_phy_addr = pde & 0xfffff000; 52 | free_a_phy_page(pg_phy_addr); 53 | } 54 | pde_idx++; 55 | } 56 | 57 | /* 回收用户虚拟地址池所占的物理内存*/ 58 | uint32_t bitmap_pg_cnt = (release_thread->userprog_vaddr.vaddr_bitmap.btmp_bytes_len) / PG_SIZE; 59 | uint8_t *user_vaddr_pool_bitmap = release_thread->userprog_vaddr.vaddr_bitmap.bits; 60 | mfree_page(PF_KERNEL, user_vaddr_pool_bitmap, bitmap_pg_cnt); 61 | 62 | /* 关闭进程打开的文件 */ 63 | uint8_t fd_idx = 3; 64 | while (fd_idx < MAX_FILES_OPEN_PER_PROC) 65 | { 66 | if (release_thread->fd_table[fd_idx] != -1) 67 | { 68 | if (is_pipe(local_fd)) 69 | { 70 | uint32_t global_fd = fd_local2global(local_fd); 71 | if (--file_table[global_fd].fd_pos == 0) 72 | { 73 | mfree_page(PF_KERNEL, file_table[global_fd].fd_inode, 1); 74 | file_table[global_fd].fd_inode = NULL; 75 | } 76 | } 77 | else 78 | { 79 | sys_close(fd_idx); 80 | } 81 | } 82 | fd_idx++; 83 | } 84 | } 85 | 86 | /* list_traversal的回调函数, 87 | * 查找pelem的parent_pid是否是ppid,成功返回true,失败则返回false */ 88 | static bool find_child(struct list_elem *pelem, int32_t ppid) 89 | { 90 | /* elem2entry中间的参数all_list_tag取决于pelem对应的变量名 */ 91 | task_struct *pthread = elem2entry(task_struct, all_list_tag, pelem); 92 | if (pthread->parent_pid == ppid) 93 | { // 若该任务的parent_pid为ppid,返回 94 | return true; // list_traversal只有在回调函数返回true时才会停止继续遍历,所以在此返回true 95 | } 96 | return false; // 让list_traversal继续传递下一个元素 97 | } 98 | 99 | /* list_traversal的回调函数, 100 | * 查找状态为TASK_HANGING的任务 */ 101 | static bool find_hanging_child(struct list_elem *pelem, int32_t ppid) 102 | { 103 | task_struct *pthread = elem2entry(task_struct, all_list_tag, pelem); 104 | if (pthread->parent_pid == ppid && pthread->status == TASK_HANGING) 105 | { 106 | return true; 107 | } 108 | return false; 109 | } 110 | 111 | /* list_traversal的回调函数, 112 | * 将一个子进程过继给init */ 113 | static bool init_adopt_a_child(struct list_elem *pelem, int32_t pid) 114 | { 115 | task_struct *pthread = elem2entry(task_struct, all_list_tag, pelem); 116 | if (pthread->parent_pid == pid) 117 | { // 若该进程的parent_pid为pid,返回 118 | pthread->parent_pid = 1; 119 | } 120 | return false; // 让list_traversal继续传递下一个元素 121 | } 122 | 123 | /* 等待子进程调用exit,将子进程的退出状态保存到status指向的变量. 124 | * 成功则返回子进程的pid,失败则返回-1 */ 125 | pid_t sys_wait(int32_t *status) 126 | { 127 | task_struct *parent_thread = running_thread(); 128 | 129 | while (1) 130 | { 131 | /* 优先处理已经是挂起状态的任务 */ 132 | struct list_elem *child_elem = list_traversal(&thread_all_list, find_hanging_child, parent_thread->pid); 133 | /* 若有挂起的子进程 */ 134 | if (child_elem != NULL) 135 | { 136 | task_struct *child_thread = elem2entry(task_struct, all_list_tag, child_elem); 137 | *status = child_thread->exit_status; 138 | 139 | /* thread_exit之后,pcb会被回收,因此提前获取pid */ 140 | uint16_t child_pid = child_thread->pid; 141 | 142 | /* 2 从就绪队列和全部队列中删除进程表项*/ 143 | thread_exit(child_thread, false); // 传入false,使thread_exit调用后回到此处 144 | /* 进程表项是进程或线程的最后保留的资源, 至此该进程彻底消失了 */ 145 | 146 | return child_pid; 147 | } 148 | 149 | /* 判断是否有子进程 */ 150 | child_elem = list_traversal(&thread_all_list, find_child, parent_thread->pid); 151 | if (child_elem == NULL) 152 | { // 若没有子进程则出错返回 153 | return -1; 154 | } 155 | else 156 | { 157 | /* 若子进程还未运行完,即还未调用exit,则将自己挂起,直到子进程在执行exit时将自己唤醒 */ 158 | thread_block(TASK_WAITING); 159 | } 160 | } 161 | } 162 | 163 | /* 子进程用来结束自己时调用 */ 164 | void sys_exit(int32_t status) 165 | { 166 | task_struct *child_thread = running_thread(); 167 | child_thread->exit_status = status; 168 | if (child_thread->parent_pid == -1) 169 | { 170 | PANIC("sys_exit: child_thread->parent_pid is -1\n"); 171 | } 172 | 173 | /* 将进程child_thread的所有子进程都过继给init */ 174 | list_traversal(&thread_all_list, init_adopt_a_child, child_thread->pid); 175 | 176 | /* 回收进程child_thread的资源 */ 177 | release_prog_resource(child_thread); 178 | 179 | /* 如果父进程正在等待子进程退出,将父进程唤醒 */ 180 | task_struct *parent_thread = pid2thread(child_thread->parent_pid); 181 | if (parent_thread->status == TASK_WAITING) 182 | { 183 | thread_unblock(parent_thread); 184 | } 185 | 186 | /* 将自己挂起,等待父进程获取其status,并回收其pcb */ 187 | thread_block(TASK_HANGING); 188 | } 189 | -------------------------------------------------------------------------------- /userprog/wait_exit.h: -------------------------------------------------------------------------------- 1 | #ifndef _USERPROG_WAITEXIT_H_ 2 | #define _USERPROG_WAITEXIT_H_ 3 | #include "../thread/thread.h" 4 | 5 | pid_t sys_wait(int32_t* status); 6 | void sys_exit(int32_t status); 7 | 8 | #endif 9 | --------------------------------------------------------------------------------