《操作系统真象还原》第13章
感觉本章比较“硬”,说实话我个人对这部分也不是很感兴趣,就直接把整个实验步骤搬上来吧。
步骤:
1.创建从盘
2.创建磁盘分区表
3.编写硬盘驱动程序
1.创建从盘
hd60M.img是主盘,只充当了启动盘的作用,仅用来存储内核。我们再创建一个80MB的硬盘作为从盘来安装文件系统。
①在bochs/下输入bin/bximage
②依次输入 1 ->回车 -> 回车 -> 80 -> hd80M.img
③然后你就可以获得一个hd80M.img虚拟磁盘了
此时我们在bochs中用xp命令看下物理地址0x475的内容:
此时只有一个磁盘,就是那个hd60M.img。
咱们还要再修改bochsrc.disk,只需要在最后添加一句:
ata0-slave: type=disk, path="hd80M.img", mode=flat,cylinders=162,heads=16,spt=63
之前我在输入这个简单的一句话时竟然发现报错了:
???一脸问号,我对比着看了好一会儿,终于对比出原来第五个字符'-'是英文字符,而我打入的是中文的,两个符号长得差不多,离谱。。。
好吧,修正之后再看一眼xp/b 0x475:
ok,此时显示0x02说明有两个硬盘了。对了,顺便提一句,这个0x475既是物理地址也是虚拟地址,因为在低1MB空间内,物理地址和虚拟地址是相同的。
2.创建磁盘分区表
①键入fdisk ./hd80M.img
②键入m得到命令菜单
③键入x得到专家命令菜单
④键入c设置柱面(cylinder) ,输入162
⑤键入h设置磁头(head),输入16
⑥键入r返回主菜单
⑦键入n创建分区
⑧键入p创建主扇区/键入e创建扩展扇区
⑨键入1开始创建第一个,至于First/Last sector,看我下方的图片就知道了:
⑩最后键入t,修改分区5~6的id为0x66,此时img5~6会自动修改type为unknown:
别忘了键入w写到磁盘上。这样磁盘分区应该就算是完成了。
3.编写硬盘驱动程序
感觉像书本中一样将一点点改每个程序太麻烦了,基本上就直接放出有关程序的完整代码吧。
①修改kernel/interrupt.c:
②lib/kernel/stdio-kernel.c:

1 #include "stdio-kernel.h"
2 #include "stdint.h"
3 #include "stdio.h"
4 #include "console.h"
5
6 #define va_start(ap,v) ap=(va_list)&v;
7 #define va_arg(ap,t) *((t*)(ap+=4))
8 #define va_end(ap) ap=NULL
9
10 /* 供内核使用的格式化输出函数 */
11 void printk(const char* format, ...){
12 va_list args;
13 va_start(args,format);
14 char buf[1024]={0};
15 vsprintf(buf,format,args);
16 va_end(args);
17 console_put_str(buf);
18 }
③lib/kernel/stdio-kernel.h:

#ifndef __LIB_STDIO_KERNEL_H
#define __LIB_STDIO_KERNEL_H
void printk(const char* format, ...);
#endif
④lib/stdio.c:
注意:书上没有写sprintf()怎么编写,不过类似于printf()。

1 #include "stdio.h"
2 #include "interrupt.h"
3 #include "global.h"
4 #include "string.h"
5 #include "syscall.h"
6 #include "print.h"
7
8 #define va_start(ap,v) ap=(va_list)&v // 把ap指向第一个固定参数v
9 #define va_arg(ap,t) *((t*)(ap +=4)) // ap指向下一个参数并返回其值
10 #define va_end(ap) ap = NULL // 清除ap
11
12 /* 将整形转换成字符 */
13 static void itoa(uint32_t value,char** buf_ptr_addr,uint8_t base)
14 {
15 uint32_t m=value%base;
16 uint32_t i=value/base; //除数为0即最高位了 输出即可 没到零继续即可
17 if (i){
18 itoa(i,buf_ptr_addr,base);
19 }
20 if (m < 10){
21 *((*buf_ptr_addr)++)=m+'0';
22 }else{
23 *((*buf_ptr_addr)++)=m+'A'-10;
24 }
25 }
26
27 uint32_t vsprintf(char* str,const char* format,va_list ap)
28 {
29 char* buf_ptr=str;
30 const char* index_ptr=format;
31 char index_char=*index_ptr;
32 int32_t arg_int;
33 char* arg_str;
34 while(index_char){
35 if(index_char!='%'){
36 *(buf_ptr++)= index_char;
37 index_char=*(++index_ptr);
38 continue;
39 }
40 index_char=*(++index_ptr);
41 switch (index_char){
42 case 's':
43 arg_str=va_arg(ap,char*);
44 strcpy(buf_ptr,arg_str);
45 buf_ptr+=strlen(arg_str);
46 index_char=*(++index_ptr);
47 break;
48 case 'x':
49 arg_int=va_arg(ap,int);
50 itoa(arg_int,&buf_ptr,16);
51 index_char=*(++index_ptr);
52 break;
53 case 'd':
54 arg_int=va_arg(ap,int);
55 if(arg_int<0){
56 arg_int=0-arg_int;
57 *(buf_ptr++)='-';
58 }
59 itoa(arg_int,&buf_ptr,10);
60 index_char=*(++index_ptr);
61 break;
62 case 'c':
63 *(buf_ptr++)=va_arg(ap,char);
64 index_char=*(++index_ptr);
65 }
66 }
67 return strlen(str);
68 }
69
70 /* 将字符串写到buf中 */
71 uint32_t sprintf(char* buf,const char* format, ...){
72 va_list args;
73 uint32_t retval;
74 va_start(args,format);
75 retval=vsprintf(buf,format,args);
76 va_end(args);
77 return retval;
78 }
79
80 /* 将字符串写到终端中 */
81 uint32_t printf(const char* format, ...)
82 {
83 va_list args;
84 uint32_t retval;
85 va_start(args,format); // 使args指向format
86 char buf[1024]={0}; // 用于存储拼接后的字符串
87 retval=vsprintf(buf,format,args);
88 va_end(args);
89 write(buf);
90 return retval;
91 }
⑤device/ide.h:

1 #ifndef __DEVICE_LDE_H
2 #define __DEVICE_LDE_H
3 #include "stdint.h"
4 #include "sync.h"
5 #include "bitmap.h"
6 #include "list.h"
7
8 /* 分区结构 */
9 struct partition{
10 uint32_t start_lba; // 起始扇区
11 uint32_t sec_cnt; // 扇区数
12 struct disk* my_disk; // 分区所属的硬盘
13 struct list_elem part_tag; // 用于队列中的标记
14 char name[8]; // 分区名称
15 struct super_block* sb; // 本分区的超级块
16 struct bitmap block_bitmap; // 块位图
17 struct bitmap inode_bitmap; // i结点位图
18 struct list open_inodes; // 本分区打开的i结点队列
19 };
20
21 /* 硬盘结构 */
22 struct disk{
23 char name[8]; // 本硬盘名称
24 struct ide_channel* my_channel; // 本硬盘归属于哪个ide通道
25 uint8_t dev_no; // 本硬盘是主0还是1
26 struct partition prim_parts[4]; // 主分区顶多是4个
27 struct partition logic_parts[8]; // 逻辑分区数量无限,设置上限为8个
28 };
29
30 /* ata通道 */
31 struct ide_channel{
32 char name[8]; // 本ata通道名称
33 uint16_t port_base; // 本通道的起始端口号
34 uint8_t irq_no; // 本通道所用的中断号
35 struct lock lock; // 通道锁
36 bool expecting_intr; // 表示等待硬盘中断
37 struct semaphore disk_done; // 用于阻塞、唤醒线程
38 struct disk devices[2]; // 一个通道上连接两个硬盘,一主一从
39 };
40
41 void intr_hd_handler(uint8_t irq_no);
42 void ide_init(void);
43 extern uint8_t channel_cnt;
44 extern struct ide_channel channels[];
45 extern struct list partition_list;
46 void ide_read(struct disk* hd,uint32_t lba,void* buf,uint32_t sec_cnt);
47 void ide_write(struct disk* hd,uint32_t lba,void* buf,uint32_t sec_cnt);
48 #endif
⑥device/ide.c:
书本勘误:第28行开始的三个宏定义不是BIT_ALT_STAT_BSY形式而是BIT_STAT_BSY形式。

1 #include "ide.h"
2 #include "sync.h"
3 #include "io.h"
4 #include "stdio.h"
5 #include "stdio-kernel.h"
6 #include "interrupt.h"
7 #include "memory.h"
8 #include "debug.h"
9 #include "console.h"
10 #include "timer.h"
11 #include "string.h"
12 #include "list.h"
13
14 /* 定义硬盘各寄存器的端口号 */
15 #define reg_data(channel) (channel->port_base+0)
16 #define reg_rerror(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+0x26)
25 #define reg_ctl(channel) reg_alt_status(channel)
26
27 /* reg_alt_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 uint8_t bootable; // 是否可引导
58 uint8_t start_head; // 起始磁头号
59 uint8_t start_sec; // 起始扇区号
60 uint8_t start_chs; // 起始柱面号
61 uint8_t fs_type; // 分区类型
62 uint8_t end_head; // 结束磁头号
63 uint8_t end_sec; // 结束扇区号
64 uint8_t end_chs; // 结束柱面号
65 /* 更需要关注以下 */
66 uint32_t start_lba; // 本分区起始扇区的lba地址
67 uint32_t sec_cnt; // 本分区的扇区数目
68 } __attribute__ ((packed)); // 保证此结构是16字节大小
69
70 /* 引导扇区,mbr或ebr所在的扇区 */
71 struct boot_sector{
72 uint8_t other[446]; // 引导代码
73 struct partition_table_entry partition_table[4]; // 分区表中有4项,共64字节
74 uint16_t signature; // 启动扇区的结束标志是0x55 0xaa
75 } __attribute__ ((packed));
76
77 /* 选择读写的硬盘 */
78 static void select_disk(struct disk* hd){
79 uint8_t reg_device=BIT_DEV_MBS|BIT_DEV_LBA;
80 if (hd->dev_no==1){
81 reg_device|=BIT_DEV_DEV;
82 }
83 outb(reg_dev(hd->my_channel),reg_device);
84 }
85
86 /* 向硬盘控制器写入起始扇区地址及要读写的扇区数 */
87 static void select_sector(struct disk* hd, uint32_t lba, uint8_t sec_cnt) {
88 ASSERT(lba<=max_lba);
89 struct ide_channel* channel=hd->my_channel;
90
91 /* 写入要读写的扇区数*/
92 outb(reg_sect_cnt(channel),sec_cnt); // 如果sec_cnt为0,则表示写入256个扇区
93
94 /* 写入lba地址(即扇区号) */
95 outb(reg_lba_l(channel),lba); // lba地址的低8位,不用单独取出低8位.outb函数中的汇编指令outb %b0, %w1会只用al。
96 outb(reg_lba_m(channel),lba>>8); // lba地址的8~15位
97 outb(reg_lba_h(channel),lba>>16); // lba地址的16~23位
98
99 /* 因为lba地址的24~27位要存储在device寄存器的0~3位,
100 * 无法单独写入这4位,所以在此处把device寄存器再重新写入一次*/
101 outb(reg_dev(channel),BIT_DEV_MBS|BIT_DEV_LBA|(hd->dev_no==1?BIT_DEV_DEV:0)|lba>>24);
102 }
103
104 /* 向通道channel发命令cmd */
105 static void cmd_out(struct ide_channel* channel,uint8_t cmd){
106 /* 只要向硬盘发出了命令便将此标记置为true,硬盘中断处理程序需要根据它来判断 */
107 channel->expecting_intr=true;
108 outb(reg_cmd(channel),cmd);
109 }
110
111 /* 硬盘读入sec_cnt个扇区的数据到buf */
112 static void read_from_sector(struct disk* hd,void* buf,uint8_t sec_cnt){
113 uint32_t size_in_byte;
114 if (sec_cnt==0){
115 /* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */
116 size_in_byte=256*512;
117 } else{
118 size_in_byte=sec_cnt*512;
119 }
120 insw(reg_data(hd->my_channel),buf,size_in_byte/2);
121 }
122
123 /* 将buf中sec_cnt扇区的数据写入硬盘 */
124 static void write2sector(struct disk* hd,void* buf,uint8_t sec_cnt){
125 uint32_t size_in_byte;
126 if (sec_cnt==0){
127 /* 因为sec_cnt是8位变量,由主调函数将其赋值时,若为256则会将最高位的1丢掉变为0 */
128 size_in_byte=256*512;
129 } else{
130 size_in_byte=sec_cnt*512;
131 }
132 outsw(reg_data(hd->my_channel),buf,size_in_byte/2);
133 }
134
135 /* 等待30秒 */
136 static bool busy_wait(struct disk* hd){
137 struct ide_channel* channel=hd->my_channel;
138 uint16_t time_limit=30*1000; // 可以等待30000毫秒
139 while (time_limit-=10>=0){
140 if (!(inb(reg_status(channel)) & BIT_STAT_BSY)){
141 return (inb(reg_status(channel)) & BIT_STAT_DRQ);
142 } else{
143 mtime_sleep(10); // 睡眠10毫秒
144 }
145 }
146 return false;
147 }
148
149 /* 从硬盘读取sec_cnt个扇区到buf */
150 void ide_read(struct disk* hd,uint32_t lba,void* buf,uint32_t sec_cnt){
151 ASSERT(lba<=max_lba);
152 ASSERT(sec_cnt > 0);
153 lock_acquire (&hd->my_channel->lock);
154
155 /* 1 先选择操作的硬盘 */
156 select_disk(hd);
157
158 uint32_t secs_op; // 每次操作的扇区数
159 uint32_t secs_done=0; // 已完成的扇区数
160 while(secs_done<sec_cnt){
161 if ((secs_done + 256)<=sec_cnt){
162 secs_op=256;
163 } else{
164 secs_op=sec_cnt-secs_done;
165 }
166
167 /* 2 写入待读入的扇区数和起始扇区号 */
168 select_sector(hd,lba+secs_done,secs_op);
169
170 /* 3 执行的命令写入reg_cmd寄存器 */
171 cmd_out(hd->my_channel,CMD_READ_SECTOR); // 准备开始读数据
172
173 /********************* 阻塞自己的时机 ***********************
174 * 在硬盘已经开始工作(开始在内部读数据或写数据)后才能阻塞自己,现在硬盘已经开始忙了,
175 * 将自己阻塞,等待硬盘完成读操作后通过中断处理程序唤醒自己*/
176 sema_down(&hd->my_channel->disk_done);
177 /*************************************************************/
178
179 /* 4 检测硬盘状态是否可读 */
180 /* 醒来后开始执行下面代码*/
181 if (!busy_wait(hd)){ // 若失败
182 char error[64];
183 sprintf(error,"%s read sector %d failed!!!!!!\n",hd->name,lba);
184 PANIC(error);
185 }
186
187 /* 5 把数据从硬盘的缓冲区中读出 */
188 read_from_sector(hd,(void*)((uint32_t)buf+secs_done*512),secs_op);
189 secs_done+=secs_op;
190 }
191 lock_release(&hd->my_channel->lock);
192 }
193
194 /* 将buf中sec_cnt扇区数据写入硬盘 */
195 void ide_write(struct disk* hd,uint32_t lba,void* buf,uint32_t sec_cnt){
196 ASSERT(lba<=max_lba);
197 ASSERT(sec_cnt>0);
198 lock_acquire (&hd->my_channel->lock);
199
200 /* 1 先选择操作的硬盘 */
201 select_disk(hd);
202
203 uint32_t secs_op; // 每次操作的扇区数
204 uint32_t secs_done=0; // 已完成的扇区数
205 while(secs_done<sec_cnt){
206 if ((secs_done+256)<=sec_cnt){
207 secs_op=256;
208 } else{
209 secs_op=sec_cnt-secs_done;
210 }
211
212 /* 2 写入待写入的扇区数和起始扇区号 */
213 select_sector(hd,lba+secs_done,secs_op);
214
215 /* 3 执行的命令写入reg_cmd寄存器 */
216 cmd_out(hd->my_channel,CMD_WRITE_SECTOR); // 准备开始写数据
217
218 /* 4 检测硬盘状态是否可读 */
219 if (!busy_wait(hd)){ // 若失败
220 char error[64];
221 sprintf(error,"%s write sector %d failed!!!!!!\n",hd->name,lba);
222 PANIC(error);
223 }
224
225 /* 5 将数据写入硬盘 */
226 write2sector(hd,(void*)((uint32_t)buf+secs_done*512),secs_op);
227
228 /* 在硬盘响应期间阻塞自己 */
229 sema_down(&hd->my_channel->disk_done);
230 secs_done+=secs_op;
231 }
232 /* 醒来后开始释放锁*/
233 lock_release(&hd->my_channel->lock);
234 }
235
236 /* 将dst中len个相邻字节交换位置后存入buf */
237 static void swap_pairs_bytes(const char* dst,char* buf,uint32_t len){
238 uint8_t idx;
239 for (idx=0;idx<len;idx+=2){
240 /* buf中存储dst中两相邻元素交换位置后的字符串 */
241 buf[idx+1]=*dst++;
242 buf[idx]=*dst++;
243 }
244 buf[idx]='\0';
245 }
246
247 /* 获得硬盘参数信息 */
248 static void identify_disk(struct disk* hd){
249 char id_info[512];
250 select_disk(hd);
251 cmd_out(hd->my_channel,CMD_IDENTIFY);
252 /* 向硬盘发送指令后便通过信号量阻塞自己,
253 * 待硬盘处理完成后,通过中断处理程序将自己唤醒 */
254 printk("+++++%d\n",&hd->my_channel->disk_done);
255 printk("+++++%d\n",(&hd->my_channel->disk_done)->value);
256 sema_down(&hd->my_channel->disk_done);
257
258 /* 醒来后开始执行下面代码 */
259 if (!busy_wait(hd)){ // 若失败
260 char error[64];
261 sprintf(error,"%s identify failed!!!!!!\n",hd->name);
262 PANIC(error);
263 }
264 read_from_sector(hd,id_info,1);
265
266 char buf[64];
267 uint8_t sn_start=10*2,sn_len=20,md_start=27*2,md_len=40;
268 swap_pairs_bytes(&id_info[sn_start],buf,sn_len);
269 printk(" disk %s info:\n SN: %s\n",hd->name,buf);
270 memset(buf,0,sizeof(buf));
271 swap_pairs_bytes(&id_info[md_start],buf,md_len);
272 printk(" MODULE: %s\n",buf);
273 uint32_t sectors=*(uint32_t*)&id_info[60*2];
274 printk(" SECTORS: %d\n",sectors);
275 printk(" CAPACITY: %dMB\n",sectors*512/1024/1024);
276 }
277
278 /* 扫描硬盘hd中地址为ext_lba的扇区中的所有分区 */
279 static void partition_scan(struct disk* hd,uint32_t ext_lba){
280 struct boot_sector* bs=sys_malloc(sizeof(struct boot_sector));
281 ide_read(hd,ext_lba,bs,1);
282 uint8_t part_idx=0;
283 struct partition_table_entry* p=bs->partition_table;
284
285 /* 遍历分区表4个分区表项 */
286 while (part_idx++<4){
287 if (p->fs_type==0x5){ // 若为扩展分区
288 if (ext_lba_base!=0){
289 /* 子扩展分区的start_lba是相对于主引导扇区中的总扩展分区地址 */
290 partition_scan(hd,p->start_lba+ext_lba_base);
291 } else{ // ext_lba_base为0表示是第一次读取引导块,也就是主引导记录所在的扇区
292 /* 记录下扩展分区的起始lba地址,后面所有的扩展分区地址都相对于此 */
293 ext_lba_base=p->start_lba;
294 partition_scan(hd,p->start_lba);
295 }
296 } else if (p->fs_type!=0){ // 若是有效的分区类型
297 if (ext_lba==0){ // 此时全是主分区
298 hd->prim_parts[p_no].start_lba=ext_lba+p->start_lba;
299 hd->prim_parts[p_no].sec_cnt=p->sec_cnt;
300 hd->prim_parts[p_no].my_disk=hd;
301 list_append(&partition_list, &hd->prim_parts[p_no].part_tag);
302 sprintf(hd->prim_parts[p_no].name,"%s%d",hd->name,p_no+1);
303 p_no++;
304 ASSERT(p_no<4); // 0,1,2,3
305 } else{
306 hd->logic_parts[l_no].start_lba=ext_lba+p->start_lba;
307 hd->logic_parts[l_no].sec_cnt=p->sec_cnt;
308 hd->logic_parts[l_no].my_disk=hd;
309 list_append(&partition_list,&hd->logic_parts[l_no].part_tag);
310 sprintf(hd->logic_parts[l_no].name,"%s%d",hd->name,l_no+5); // 逻辑分区数字是从5开始,主分区是1~4.
311 l_no++;
312 if (l_no>=8) // 只支持8个逻辑分区,避免数组越界
313 return;
314 }
315 }
316 p++;
317 }
318 sys_free(bs);
319 }
320
321 /* 打印分区信息 */
322 static bool partition_info(struct list_elem* pelem){
323 struct partition* part=elem2entry(struct partition,part_tag,pelem);
324 printk(" %s start_lba:0x%x, sec_cnt:0x%x\n",part->name,part->start_lba, part->sec_cnt);
325
326 /* 在此处return false与函数本身功能无关,
327 * 只是为了让主调函数list_traversal继续向下遍历元素 */
328 return false;
329 }
330
331 /* 硬盘中断处理程序 */
332 void intr_hd_handler(uint8_t irq_no){
333 ASSERT(irq_no==0x2e || irq_no==0x2f);
334 uint8_t ch_no=irq_no-0x2e;
335 struct ide_channel* channel=&channels[ch_no];
336 ASSERT(channel->irq_no==irq_no);
337 /* 不必担心此中断是否对应的是这一次的expecting_intr,
338 * 每次读写硬盘时会申请锁,从而保证了同步一致性 */
339 if (channel->expecting_intr){
340 channel->expecting_intr=false;
341 sema_up(&channel->disk_done);
342
343 /* 读取状态寄存器使硬盘控制器认为此次的中断已被处理,
344 * 从而硬盘可以继续执行新的读写 */
345 inb(reg_status(channel));
346 }
347 }
348
349 /* 硬盘数据结构初始化 */
350 void ide_init(){
351 printk("ide_init start\n");
352 uint8_t hd_cnt=*((uint8_t*)(0x475)); // 在地址0x475处可获取硬盘数量
353 ASSERT(hd_cnt>0);
354 channel_cnt=DIV_ROUND_UP(hd_cnt,2); // 一个ide通道上有两个硬盘,根据硬盘数量反推有几个ide通道
355 list_init(&partition_list);
356 struct ide_channel* channel;
357 uint8_t channel_no=0,dev_no=0;
358
359 /* 处理每个通道上的硬盘 */
360 while (channel_no<channel_cnt){
361 channel=&channels[channel_no];
362 sprintf(channel->name,"ide%d",channel_no);
363
364 /* 为每个ide通道初始化端口基址及中断向量 */
365 switch (channel_no){
366 case 0:
367 channel->port_base=0x1f0;
368 channel->irq_no=0x20+14; // ide0的中断向量号
369 break;
370 case 1:
371 channel->port_base=0x170;
372 channel->irq_no=0x20+15; // ide1的中断向量号
373 break;
374 }
375
376 channel->expecting_intr=false; // 未向硬盘写入指令时不期待硬盘的中断
377 lock_init(&channel->lock);
378
379 /* 初始化为0,目的是向硬盘控制器请求数据后,硬盘驱动sema_down此信号阻塞线程,
380 * 直到硬盘完成后通过发中断,由中断处理程序将此信号量sema_up唤醒线程 */
381 sema_init(&channel->disk_done,0);
382
383 register_handler(channel->irq_no,intr_hd_handler);
384
385 /* 分别获取两个硬盘的参数及分区信息 */
386 while (dev_no<2){
387 struct disk* hd=&channel->devices[dev_no];
388 hd->my_channel=channel;
389 hd->dev_no=dev_no;
390 sprintf(hd->name,"sd%c",'a'+channel_no*2+dev_no);
391 identify_disk(hd); // 获取硬盘参数
392 if (dev_no!=0){ // 内核本身的裸硬盘(hd60M.img)不处理
393 partition_scan(hd,0); // 扫描该硬盘上的分区
394 }
395 p_no=0,l_no=0;
396 ++dev_no;
397 }
398 dev_no=0;
399 ++channel_no;
400 }
401
402 printk("ide_init done\n");
403 /* 打印所有的分区信息 */
404 list_traversal(&partition_list,partition_info,(int)NULL);
405 printk("ide_init done\n");
406 }
⑦thread/thread.c:

1 #include "thread.h"
2 #include "stdint.h"
3 #include "string.h"
4 #include "global.h"
5 #include "memory.h"
6 #include "interrupt.h"
7 #include "print.h"
8 #include "debug.h"
9 #include "process.h"
10 #include "sync.h"
11
12 struct task_struct* main_thread; // 主线程PCB
13 struct task_struct* idle_thread; // idle线程
14 struct list thread_ready_list; // 就绪队列
15 struct list thread_all_list; // 所有任务队列
16 static struct list_elem* thread_tag;// 用于保存队列中的线程结点
17 struct lock pid_lock; // 分配pid锁
18
19 extern void switch_to(struct task_struct* cur,struct task_struct* next);
20
21 /* 系统空闲时运行的线程 */
22 static void idle(void){
23 while (1){
24 thread_block(TASK_BLOCKED);
25 // 执行hlt时必须要保证目前处在开中断情况下
26 asm volatile("sti;hlt":::"memory");
27 }
28 }
29
30 /* 获取当前pcb指针 */
31 struct task_struct* running_thread(){
32 uint32_t esp;
33 asm("mov %%esp,%0":"=g"(esp));
34 /* 取asm整数部分,即pcb起始地址 */
35 return (struct task_struct*)(esp & 0xfffff000);
36 }
37
38 /* 由kernel_thread去执行function(func_arg) */
39 void kernel_thread(thread_func* function,void* func_arg){
40 /* 执行function前要开中断,避免后面的时钟中断被屏蔽,而无法调度其它进程 */
41 intr_enable();
42 function(func_arg);
43 }
44
45 /* 分配pid */
46 static pid_t allocate_pid(void){
47 static pid_t next_pid=0;
48 lock_acquire(&pid_lock);
49 ++next_pid;
50 lock_release(&pid_lock);
51 return next_pid;
52 }
53
54 /* 初始化线程栈thread_stack,将待执行的函数和参数方法到thread_stack中相应的位置 */
55 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg){
56 /* 先预留中断使用栈的空间,可见thread.h中定义的结构 */
57 pthread->self_kstack-=sizeof(struct intr_stack);
58
59 /* 再留出线程栈空间,可见thread.h中定义 */
60 pthread->self_kstack-=sizeof(struct thread_stack);
61 struct thread_stack* kthread_stack=(struct thread_stack*)pthread->self_kstack;
62 kthread_stack->eip=kernel_thread;
63 kthread_stack->function=function;
64 kthread_stack->func_arg=func_arg;
65 kthread_stack->ebp=kthread_stack->ebx=kthread_stack->esi=kthread_stack->edi=0;
66 }
67
68 /* 初始化线程基本信息 */
69 void init_thread(struct task_struct* pthread,char* name,int prio){
70 memset(pthread,0,sizeof(*pthread));
71 pthread->pid=allocate_pid();
72 strcpy(pthread->name,name);
73
74 if (pthread==main_thread){
75 /* 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */
76 pthread->status=TASK_RUNNING;
77 }else{
78 pthread->status=TASK_READY;
79 }
80
81 /* self_kstack是线程自己在内核态下使用的栈顶地址 */
82 pthread->self_kstack=(uint32_t*)((uint32_t)pthread+PG_SIZE);
83 pthread->priority=prio;
84 pthread->ticks=prio;
85 pthread->elapsed_ticks=0;
86 pthread->pgdir=NULL;
87 pthread->stack_magic=0x19870916; // 自定义魔数
88 }
89
90 /* 创建一个优先级为prio的线程,线程名为name,线程所执行的函数是funciton(func_arg) */
91 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg){
92 /* pcb都位于内核空间,包括用户进程pcb也是在内核空间 */
93 struct task_struct* thread=get_kernel_pages(1);
94
95 init_thread(thread,name,prio);
96 thread_create(thread,function,func_arg);
97
98 /* 确保之前不再队列中 */
99 ASSERT(!elem_find(&thread_ready_list,&thread->general_tag));
100 /* 加入就绪队列 */
101 list_append(&thread_ready_list,&thread->general_tag);
102
103 /* 确保之前不再队列中 */
104 ASSERT(!elem_find(&thread_all_list,&thread->all_list_tag));
105 list_append(&thread_all_list,&thread->all_list_tag);
106
107 return thread;
108 }
109
110 /* 将kernel中的main函数完善为主线程 */
111 static void make_main_thread(void){
112 /* 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000,
113 * 就是为其预留pcb的,因此pcb地址为0xc009e000,不需要勇敢get_kernel_page另分配一页 */
114 main_thread=running_thread();
115 init_thread(main_thread,"main",31);
116
117 /* main函数是当前线程,当前线程不在thread_ready_list中,
118 * 所以只将其加在thead_all_list中 */
119 ASSERT(!elem_find(&thread_all_list,&main_thread->all_list_tag));
120 list_append(&thread_all_list,&main_thread->all_list_tag);
121 }
122
123 /* 实现任务调度 */
124 void schedule(){
125 ASSERT(intr_get_status()==INTR_OFF);
126 struct task_struct* cur=running_thread();
127 if (cur->status==TASK_RUNNING){
128 // 若此线程只是cpu时间片到了,将其加入就绪队列队尾
129 ASSERT(!elem_find(&thread_ready_list,&cur->general_tag));
130 list_append(&thread_ready_list,&cur->general_tag);
131 cur->ticks=cur->priority;
132 // 重新将当前进程的ticks重置为priority
133 cur->status=TASK_READY;
134 }else{
135 /* 若此线程需要某事件发生后才能继续上cpu运行,
136 * 不需要将其加入队列,因为当前不在就绪队列中 */
137 }
138
139 /* 如果就绪队列中没有可运行的任务,就唤醒idle */
140 if (list_empty(&thread_ready_list)){
141 thread_unblock(idle_thread);
142 }
143
144 ASSERT(!list_empty(&thread_ready_list));
145 thread_tag=NULL; // thread_tag清空
146 /* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu */
147 thread_tag=list_pop(&thread_ready_list);
148 struct task_struct* next=elem2entry(struct task_struct,general_tag,thread_tag);
149 next->status=TASK_RUNNING;
150
151 /* 激活任务页表等 */
152 process_activate(next);
153
154 switch_to(cur,next);
155 }
156
157 /* 初始化线程环境 */
158 void thread_init(void){
159 put_str("thread_init start\n");
160 list_init(&thread_ready_list);
161 list_init(&thread_all_list);
162 lock_init(&pid_lock);
163 /* 将当前main函数创建为线程 */
164 make_main_thread();
165
166 /* 创建idle线程 */
167 idle_thread=thread_start("idle",10,idle,NULL);
168
169 put_str("thread_init done\n");
170 }
171
172 /* 当前线程将自己阻塞,标记状态为status */
173 void thread_block(enum task_status stat){
174 /* stat取值为TASK_BLOCKED、TASK_WATING、TASK_HANGING时不会被调度 */
175 ASSERT((stat==TASK_BLOCKED) || (stat==TASK_WAITING) || (stat==TASK_HANGING));
176 enum intr_status old_status=intr_disable();
177 struct task_struct* cur_thread=running_thread();
178 cur_thread->status=stat; // 置其状态为stat
179 schedule();
180 /* 待当前线程被解除阻塞状态后才能继续下面的intr_set_status */
181 intr_set_status(old_status);
182 }
183
184 /* 解除pthread的阻塞状态 */
185 void thread_unblock(struct task_struct* pthread){
186 enum intr_status old_status=intr_disable();
187 ASSERT((pthread->status==TASK_BLOCKED) || (pthread->status==TASK_WAITING) || (pthread->status==TASK_HANGING));
188 if (pthread->status!=TASK_READY){
189 ASSERT(!elem_find(&thread_ready_list,&pthread->general_tag));
190 if (elem_find(&thread_ready_list,&pthread->general_tag)){
191 PANIC("thread_unblock:blocked thread in ready_list\n"); // 想要解除阻塞状态的thread已经在ready_list中了,有问题
192 }
193 list_push(&thread_ready_list,&pthread->general_tag); // 放到队列最前面,使其尽快得到调度
194 pthread->status=TASK_READY;
195 }
196 intr_set_status(old_status);
197 }
198
199 /* 主动让出cpu,换其它线程运行 */
200 void thread_yield(void){
201 struct task_struct* cur=running_thread();
202 enum intr_status old_status=intr_disable();
203 ASSERT(!elem_find(&thread_ready_list,&cur->general_tag));
204 list_append(&thread_ready_list,&cur->general_tag);
205 cur->status=TASK_READY;
206 schedule();
207 intr_set_status(old_status);
208 }
⑧thread/thread.h:

1 #ifndef __THREAD_THREAD_H
2 #define __THREAD_THREAD_H
3 #include "stdint.h"
4 #include "list.h"
5 #include "bitmap.h"
6 #include "memory.h"
7
8 /* 自定义通用函数类型,它将在很多线程程序中作为参数类型 */
9 typedef void thread_func(void*);
10 typedef int16_t pid_t;
11
12 /* 进程或线程状态 */
13 enum task_status{
14 TASK_RUNNING,
15 TASK_READY,
16 TASK_BLOCKED,
17 TASK_WAITING,
18 TASK_HANGING,
19 TASK_DIED
20 };
21
22 /***************** 中断栈intr_stack ******************
23 * 此结构用于中断发生时保护程序(线程或进程)的上下文环境
24 * 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文
25 * 寄存器,intr_exit中的出栈操作是此结构的逆操作
26 * 此栈在线程自己的内核栈中的位置固定,所在页的最顶端
27 *****************************************************/
28 struct intr_stack{
29 uint32_t vec_no; // kenrel.S宏VECTOR中push %1压入的中断号
30 uint32_t edi;
31 uint32_t esi;
32 uint32_t ebp;
33 uint32_t esp_dummy; // 虽然pushad会压入esp,但esp是不断变化的,所以会被popad忽略
34 uint32_t ebx;
35 uint32_t edx;
36 uint32_t ecx;
37 uint32_t eax;
38 uint32_t gs;
39 uint32_t fs;
40 uint32_t es;
41 uint32_t ds;
42
43 /* 以下由cpu从低特权级进入高特权级时压入 */
44 uint32_t err_code; // error_code会被压入在eip之后
45 void (*eip)(void);
46 uint32_t cs;
47 uint32_t eflags;
48 void* esp;
49 uint32_t ss;
50 };
51
52 /*****************线程栈thread_stack****************
53 * 线程自己的栈,用于存储线程中待执行的函数
54 * 此结构在线程自己的内核栈中位置不固定,
55 * 仅用在switch_to时保存线程环境。
56 * 实际位置取决于实际运行情况。
57 *************************************************/
58 struct thread_stack{
59 uint32_t ebp;
60 uint32_t ebx;
61 uint32_t edi;
62 uint32_t esi;
63
64 /* 线程第一次执行时,eip指向待调用的函数kernel_thread
65 * 其它时候,eip是指向switch_to的返回地址 */
66 void (*eip)(thread_func* func,void* func_arg);
67
68 /****** 以下仅供第一次被调度上cpu时使用 ******/
69
70 /* 参数unused_ret只为占位置充数为返回地址 */
71 void (*unused_retaddr);
72 thread_func* function;// 由kernel_thread所调用的函数名
73 void* func_arg; // 由kernel_thread所调用的函数所需的参数
74 };
75
76 /* 进程或线程的pcb,程序控制块 */
77 struct task_struct{
78 uint32_t* self_kstack;// 各内核线程都有自己的内核栈
79 pid_t pid; // 任务id
80 enum task_status status;
81 uint8_t priority; // 线程优先级
82 char name[16];
83 uint8_t ticks; // 每次在处理器上执行的时间滴答数
84
85 /* 此任务自上cpu运行后占用了多少cpu嘀嗒数,也就是此任务执行了多久 */
86 uint32_t elapsed_ticks;
87
88 /* genernal_tag的作用是用于线程在一般的队列中的结点 */
89 struct list_elem general_tag;
90
91 /* all_list_tag的作用是用于线程队列thread_all_list中的结点 */
92 struct list_elem all_list_tag;
93
94 uint32_t* pgdir; // 进程自己页表的虚拟地址
95
96 struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址
97 struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程的内存块描述符
98
99 uint32_t stack_magic; // 栈的边界标记,用于检测栈的溢出
100 };
101
102
103 extern struct list thread_ready_list;
104 extern struct list thread_all_list;
105
106 void kernel_thread(thread_func* function,void* func_arg);
107 void thread_create(struct task_struct* pthread,thread_func function,void* func_arg);
108 void init_thread(struct task_struct* pthread,char* name,int prio);
109 struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg);
110 struct task_struct* running_thread(void);
111 void schedule(void);
112 void thread_init(void);
113 void thread_block(enum task_status);
114 void thread_unblock(struct task_struct*);
115 void thread_yield(void);
116 #endif
⑨device/timer.c:

1 #include "timer.h"
2 #include "io.h"
3 #include "print.h"
4 #include "../thread/thread.h"
5 #include "../kernel/interrupt.h"
6 #include "debug.h"
7
8 #define IRQ0_FREQUENCY 100
9 #define INPUT_FREQUENCY 1193180
10 #define COUNTER0_VALUE INPUT_FREQUENCY / IRQ0_FREQUENCY
11 #define COUNTER0_PORT 0X40
12 #define COUNTER0_NO 0
13 #define COUNTER_MODE 2
14 #define READ_WRITE_LATCH 3
15 #define PIT_COUNTROL_PORT 0x43
16
17 #define mil_seconds_per_intr (1000/IRQ0_FREQUENCY) // 没多少ms发生一次中断
18
19 uint32_t ticks; // ticks是内核自中断开启以来总共的嘀嗒数
20
21 static void frequency_set(uint8_t counter_port,\
22 uint8_t counter_no,\
23 uint8_t rwl,\
24 uint8_t counter_mode,\
25 uint16_t counter_value)
26 {
27 outb(PIT_COUNTROL_PORT,(uint8_t) (counter_no<<6|rwl<<4|counter_mode<<1));
28 outb(counter_port,(uint8_t)counter_value);
29 outb(counter_port,(uint8_t)counter_value>>8);
30 }
31
32 static void intr_timer_handler(void){
33 struct task_struct* cur_thread=running_thread();
34
35 ASSERT(cur_thread->stack_magic==0x19870916); // 检查栈是否溢出
36
37 cur_thread->elapsed_ticks++; // 记录此线程占用的cpu时间
38 ++ticks; // 从内核第一次处理时间中断后开始至今的嘀嗒数,内核态和用户态总共的嘀嗒数
39
40 if (cur_thread->ticks==0){
41 schedule();
42 }else{
43 --cur_thread->ticks;
44 }
45 }
46
47 /* 以tick为单位的sleep,任何时间形式的sleep会转换成此ticks形式 */
48 static void ticks_to_sleep(uint32_t sleep_ticks){
49 uint32_t start_tick=ticks;
50
51 /* 若间隔的ticks数不够则让出cpu */
52 while (ticks-start_tick<sleep_ticks){
53 thread_yield();
54 }
55 }
56
57 /* 以毫秒为单位的sleep 1s=1000ms */
58 void mtime_sleep(uint32_t m_seconds){
59 uint32_t sleep_ticks=DIV_ROUND_UP(m_seconds,mil_seconds_per_intr);
60 ASSERT(sleep_ticks>0);
61 ticks_to_sleep(sleep_ticks);
62 }
63
64 /* 初始化PIT8253 */
65 void timer_init(void)
66 {
67 put_str("timer_init start!\n");
68 frequency_set(COUNTER0_PORT,\
69 COUNTER0_NO,\
70 READ_WRITE_LATCH,\
71 COUNTER_MODE,\
72 COUNTER0_VALUE);
73 register_handler(0x20,intr_timer_handler);
74 put_str("timer_init done!\n");
75 }
⑩device/timer.h:

1 #ifndef __DEVICE_TIME_H
2 #define __DEVICE_TIME_H
3 #include "stdint.h"
4 void timer_init(void);
5 void mtime_sleep(uint32_t m_seconds);
6 #endif
⑪kernel/main.c,在init_all()下加一句:
while (1);
⑫kernel/init.c中加一句:
ide_init();
⑬最后的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 CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
9 LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
10 OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \
11 $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \
12 $(BUILD_DIR)/switch.o $(BUILD_DIR)/debug.o $(BUILD_DIR)/string.o \
13 $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o $(BUILD_DIR)/thread.o \
14 $(BUILD_DIR)/list.o $(BUILD_DIR)/sync.o $(BUILD_DIR)/console.o \
15 $(BUILD_DIR)/keyboard.o $(BUILD_DIR)/ioqueue.o $(BUILD_DIR)/tss.o \
16 $(BUILD_DIR)/process.o $(BUILD_DIR)/syscall.o $(BUILD_DIR)/syscall-init.o \
17 $(BUILD_DIR)/stdio.o $(BUILD_DIR)/stdio-kernel.o $(BUILD_DIR)/ide.o
18 ############## c代码编译 ###############
19 $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \
20 lib/stdint.h kernel/init.h lib/string.h kernel/memory.h \
21 thread/thread.h kernel/interrupt.h device/console.h \
22 device/keyboard.h device/ioqueue.h userprog/process.h \
23 lib/user/syscall.h userprog/syscall-init.h lib/stdio.h \
24 lib/kernel/stdio-kernel.o
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 kernel/memory.h \
29 thread/thread.h device/console.h device/keyboard.h userprog/tss.h \
30 userprog/syscall-init.h
31 $(CC) $(CFLAGS) $< -o $@
32
33 $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \
34 lib/stdint.h kernel/global.h lib/kernel/io.h lib/kernel/print.h
35 $(CC) $(CFLAGS) $< -o $@
36
37 $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/io.h \
38 lib/kernel/print.h kernel/interrupt.h thread/thread.h kernel/debug.h
39 $(CC) $(CFLAGS) $< -o $@
40
41 $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \
42 lib/kernel/print.h lib/stdint.h kernel/interrupt.h
43 $(CC) $(CFLAGS) $< -o $@
44
45 $(BUILD_DIR)/string.o: lib/string.c lib/string.h \
46 kernel/debug.h kernel/global.h
47 $(CC) $(CFLAGS) $< -o $@
48
49 $(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h \
50 lib/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h \
51 thread/sync.h thread/thread.h lib/kernel/list.h kernel/interrupt.h
52 $(CC) $(CFLAGS) $< -o $@
53
54 $(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h kernel/global.h \
55 lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.h
56 $(CC) $(CFLAGS) $< -o $@
57
58 $(BUILD_DIR)/thread.o: thread/thread.c thread/thread.h \
59 lib/stdint.h lib/string.h kernel/global.h kernel/memory.h \
60 kernel/debug.h kernel/interrupt.h lib/kernel/print.h userprog/process.h
61 $(CC) $(CFLAGS) $< -o $@
62
63 $(BUILD_DIR)/list.o: lib/kernel/list.c lib/kernel/list.h \
64 kernel/interrupt.h lib/stdint.h kernel/debug.h
65 $(CC) $(CFLAGS) $< -o $@
66
67 $(BUILD_DIR)/sync.o: thread/sync.c thread/sync.h \
68 lib/stdint.h thread/thread.h kernel/debug.h kernel/interrupt.h
69 $(CC) $(CFLAGS) $< -o $@
70
71 $(BUILD_DIR)/console.o: device/console.c device/console.h \
72 lib/kernel/print.h thread/sync.h
73 $(CC) $(CFLAGS) $< -o $@
74
75 $(BUILD_DIR)/keyboard.o: device/keyboard.c device/keyboard.h \
76 lib/kernel/print.h lib/kernel/io.h kernel/interrupt.h \
77 kernel/global.h lib/stdint.h device/ioqueue.h
78 $(CC) $(CFLAGS) $< -o $@
79
80 $(BUILD_DIR)/ioqueue.o: device/ioqueue.c device/ioqueue.h \
81 kernel/interrupt.h kernel/global.h kernel/debug.h
82 $(CC) $(CFLAGS) $< -o $@
83
84 $(BUILD_DIR)/tss.o: userprog/tss.c userprog/tss.h \
85 kernel/global.h thread/thread.h lib/kernel/print.h
86 $(CC) $(CFLAGS) $< -o $@
87
88 $(BUILD_DIR)/process.o: userprog/process.c userprog/process.h \
89 lib/string.h kernel/global.h kernel/memory.h lib/kernel/print.h \
90 thread/thread.h kernel/interrupt.h kernel/debug.h device/console.h
91 $(CC) $(CFLAGS) $< -o $@
92
93 $(BUILD_DIR)/syscall.o: lib/user/syscall.c lib/user/syscall.h \
94 lib/stdint.h
95 $(CC) $(CFLAGS) $< -o $@
96
97 $(BUILD_DIR)/syscall-init.o: userprog/syscall-init.c userprog/syscall-init.h \
98 lib/user/syscall.h lib/stdint.h lib/kernel/print.h device/console.h \
99 thread/thread.h lib/string.h
100 $(CC) $(CFLAGS) $< -o $@
101
102 $(BUILD_DIR)/stdio.o: lib/stdio.c lib/stdio.h lib/stdint.h lib/string.h \
103 lib/user/syscall.h
104 $(CC) $(CFLAGS) $< -o $@
105
106 $(BUILD_DIR)/stdio-kernel.o: lib/kernel/stdio-kernel.c lib/kernel/stdio-kernel.h \
107 lib/stdio.h device/console.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 \
113 lib/kernel/stdio-kernel.h kernel/interrupt.h kernel/debug.h \
114 device/console.h device/timer.h lib/string.h
115 $(CC) $(CFLAGS) $< -o $@
116
117 ############## 汇编代码编译 ###############
118 $(BUILD_DIR)/kernel.o: kernel/kernel.S
119 $(AS) $(ASFLAGS) $< -o $@
120
121 $(BUILD_DIR)/print.o: lib/kernel/print.S
122 $(AS) $(ASFLAGS) $< -o $@
123
124 $(BUILD_DIR)/switch.o: thread/switch.S
125 $(AS) $(ASFLAGS) $< -o $@
126
127 ############## 链接所有目标文件 #############
128 $(BUILD_DIR)/kernel.bin: $(OBJS)
129 $(LD) $(LDFLAGS) $^ -o $@
130
131 .PHONY : mk_dir hd clean all
132
133 mk_dir:
134 if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
135
136 hd:
137 dd if=$(BUILD_DIR)/kernel.bin \
138 of=/home/zbb/bochs/hd60M.img \
139 bs=512 count=200 seek=9 conv=notrunc
140
141 clean:
142 cd $(BUILD_DIR) && rm -f ./*
143
144 build: $(BUILD_DIR)/kernel.bin
145
146 all: mk_dir build hd
就有了结果:
中间两行奇奇怪怪的输出是我调试时加入的,分别为信号量的地址和其value,然后我发现原来是我的idle线程没有初始化加入队列。。。
写到第15章时我发现中间有一句输出有点小问题——输出了两句“ide_init done”——代码写错了,不过也不重要我就懒得改了,见谅 > <
好了,这样驱动程序就算是完成了(感觉就是草草过了一下),下一章就到文件系统了。
参考博客:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库