《操作系统真象还原》第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 }
View Code
复制代码

③lib/kernel/stdio-kernel.h

复制代码
#ifndef __LIB_STDIO_KERNEL_H
#define __LIB_STDIO_KERNEL_H

void printk(const char* format, ...);

#endif
View Code
复制代码

④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 }
View Code
复制代码

⑤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
View Code
复制代码

⑥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 }
View Code
复制代码

⑦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 }
View Code
复制代码

⑧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
View Code
复制代码

⑨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 }
View Code
复制代码

⑩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
View Code
复制代码

⑪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
View Code
复制代码

就有了结果:

中间两行奇奇怪怪的输出是我调试时加入的,分别为信号量的地址和其value,然后我发现原来是我的idle线程没有初始化加入队列。。。

写到第15章时我发现中间有一句输出有点小问题——输出了两句“ide_init done”——代码写错了,不过也不重要我就懒得改了,见谅 > <

好了,这样驱动程序就算是完成了(感觉就是草草过了一下),下一章就到文件系统了。


参考博客:

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