从硬盘中读取n个扇区的数据的流程:
sys_setup 在kernel/blk_drv/hd.c中
sys_setup中
--> bh = bread(0x300 + drive*5,0);
1. 在kernel/blk_drv/hd.c中-->调用了bread
-
int sys_setup(void * BIOS)
-
{
-
static int callable = 1;
-
int i,drive;
-
unsigned char cmos_disks;
-
struct partition *p;
-
struct buffer_head * bh;
-
-
if (!callable)
-
return -1;
-
callable = 0;
-
//读取硬盘的参数: int head,sect,cyl,wpcom,lzone,ctl
-
for (drive=0 ; drive<2 ; drive++) {
-
hd_info[drive].cyl = *(unsigned short *) BIOS;
-
hd_info[drive].head = *(unsigned char *) (2+BIOS);
-
hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
-
hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
-
hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
-
hd_info[drive].sect = *(unsigned char *) (14+BIOS);
-
BIOS += 16;
-
}
-
if (hd_info[1].cyl)
-
NR_HD=2;
-
else
-
NR_HD=1;
-
//通过刚才读取的硬盘参数计算总的扇区数=head*(sectors_per_track)*cylinders
-
//hd[0]代表整个硬盘,start_sect=0,nr_sects=整个硬盘的sector
-
for (i=0 ; i<NR_HD ; i++) {
-
hd[i*5].start_sect = 0;
-
hd[i*5].nr_sects = hd_info[i].head*
-
hd_info[i].sect*hd_info[i].cyl;
-
}
-
//以下不重要不关心,直接认为NR_HD=1即可
-
if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
-
if (cmos_disks & 0x0f)
-
NR_HD = 2;
-
else
-
NR_HD = 1;
-
else
-
NR_HD = 0;
-
for (i = NR_HD ; i < 2 ; i++) {
-
hd[i*5].start_sect = 0;
-
hd[i*5].nr_sects = 0;
-
}
-
//读取硬盘的第0,1两个扇区,并判断第一个扇区是不是以0x55aa结束
-
//将分区表的信息保存在hd中
-
for (drive=0 ; drive<NR_HD ; drive++) {
-
if (!(bh = bread(0x300 + drive*5,0))) { ---->就是这里
-
printk("Unable to read partition table of drive %d\n\r", drive);
-
panic("");
-
}
-
if (bh->b_data[510] != 0x55 || (unsigned char) bh->b_data[511] != 0xAA) {
-
printk("Bad partition table on drive %d\n\r",drive);
-
panic("");
-
}
-
p = 0x1BE + (void *)bh->b_data; //分区表的信息在mbr的0x1be=446,446+64=510,还有最后的0x55aa正好512字节
-
for (i=1;i<5;i++,p++) {
-
hd[i+5*drive].start_sect = p->start_sect;
-
hd[i+5*drive].nr_sects = p->nr_sects;
-
}
-
brelse(bh);
-
}
-
for (i=0 ; i<5*MAX_HD ; i++)
-
hd_sizes[i] = hd[i].nr_sects>>1 ;
-
blk_size[MAJOR_NR] = hd_sizes;
-
if (NR_HD)
-
printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
-
rd_load();
-
init_swapping();
-
mount_root();
-
return (0);
-
}
关于bread(0x300 + drive*5,0),
第0个参数0x300代表整个的硬盘,
第1个参数0,hd[0].start_sect=0,所以会读取了硬盘上的第0,1两个扇区
1.1 在fs/buffer.c中
sys_setup -->bread(dev=0x300, block=0x0)
-
struct buffer_head * bread(int dev,int block)
-
{
-
struct buffer_head * bh;
-
//在高速缓冲区中申请一块内存,用来放读取后的数据
-
if (!(bh=getblk(dev,block)))
-
panic("bread: getblk returned NULL\n");
-
if (bh->b_uptodate)
-
return bh;
-
//读取硬盘中的数据
-
ll_rw_block(READ,bh); //这里是调用底层的读写函数
-
wait_on_buffer(bh);
-
if (bh->b_uptodate)
-
return bh;
-
brelse(bh);
-
return NULL;
-
}
这儿先不关心如何从高速缓冲区中申请内存的,先来看看是如何读取硬盘的数据的
1.2 ll_rw_block会构造一个请求
在kernel/blk_drv/ll_rw_blk.c中 L198
sys_setup -->bread(dev=0x300, block=0x0)-->ll_rw_block(rw=READ=0,bh)
-
void ll_rw_block(int rw, struct buffer_head * bh)
-
{
-
unsigned int major;
-
//hd->b_dev=0x300,主设备号是3,判断读写处理函数是存在的,则继续
-
if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
-
!(blk_dev[major].request_fn)) {
-
printk("Trying to read nonexistent block-device\n\r");
-
return;
-
}
-
make_request(major,rw,bh); //构造硬盘请求
-
}
a. 构造硬盘请求的过程
在kernel/blk_drv/ll_rw_blk.c中 L106
-
static void make_request(int major,int rw, struct buffer_head * bh)
-
{
-
struct request * req;
-
int rw_ahead;
-
-
/* WRITEA/READA is special case - it is not really needed, so if the */
-
/* buffer is locked, we just forget about it, else it's a normal read */
-
if (rw_ahead = (rw == READA || rw == WRITEA)) {
-
if (bh->b_lock)
-
return;
-
if (rw == READA)
-
rw = READ;
-
else
-
rw = WRITE;
-
}
-
if (rw!=READ && rw!=WRITE)
-
panic("Bad block dev command, must be R/W/RA/WA");
-
lock_buffer(bh); //这儿进行加锁
-
if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
-
unlock_buffer(bh);
-
return;
-
}
-
repeat:
-
/* we don't allow the write-requests to fill up the queue completely:
-
* we want some room for reads: they take precedence. The last third
-
* of the requests are only for reads.
-
*/
-
//查找空亲的request-->从request的最后一项开始查找,只要找到req->dev=-1的一项说明是空闲的request
-
if (rw == READ)
-
req = request+NR_REQUEST; //req指向request的最后一项
-
else
-
req = request+((NR_REQUEST*2)/3);
-
/* find an empty request */
-
while (--req >= request) //从后往前查找一个空闲的request项
-
if (req->dev<0)
-
break;
-
/* if none found, sleep on new requests: check for rw_ahead */
-
if (req < request) {
-
if (rw_ahead) {
-
unlock_buffer(bh);
-
return;
-
}
-
sleep_on(&wait_for_request);
-
goto repeat; //如果没有空闲的request项,则sleep之后再查找
-
}
-
/* fill up the request-info, and add it to the queue */
-
//找到了一个空闲的request项之后,初始化这个request
-
req->dev = bh->b_dev;
-
req->cmd = rw;
-
req->errors=0;
-
req->sector = bh->b_blocknr<<1;
-
req->nr_sectors = 2;
-
req->buffer = bh->b_data;
-
req->waiting = NULL;
-
req->bh = bh;
-
req->next = NULL; //硬盘的bld_dev是在hd_init中初始化的
-
add_request(major+blk_dev,req); //在hd_init中有blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;=do_hd_request
-
} //从这儿可以看出主设备号的用途-->作下标用的
b. 构造硬盘请求的过程
这个参数dev是blk_dev[3],即硬盘的blk_dev
-
static void add_request(struct blk_dev_struct * dev, struct request * req)
-
{
-
struct request * tmp;
-
-
req->next = NULL;
-
cli(); //关中断
-
if (req->bh)
-
req->bh->b_dirt = 0;
-
if (!(tmp = dev->current_request)) {
-
dev->current_request = req;
-
sti(); //开中断
-
(dev->request_fn)(); //对于硬盘调用的是do_hd_request
-
return;
-
}
-
for ( ; tmp->next ; tmp=tmp->next) {
-
if (!req->bh)
-
if (tmp->next->bh)
-
break;
-
else
-
continue;
-
if ((IN_ORDER(tmp,req) ||
-
!IN_ORDER(tmp,tmp->next)) &&
-
IN_ORDER(req,tmp->next))
-
break;
-
}
-
req->next=tmp->next;
-
tmp->next=req;
-
sti();
-
}
1.3 add_request-->do_hd_requst
在kernel/blk_drv/hd.c中
-
void do_hd_request(void)
-
{
-
int i,r;
-
unsigned int block,dev;
-
unsigned int sec,head,cyl;
-
unsigned int nsect;
-
-
INIT_REQUEST; //到这儿req这个都没有用
-
dev = MINOR(CURRENT->dev); //到这一步才算是用到了这个make_request中申请到的request
-
block = CURRENT->sector;
-
//(gdb) p /x *blk_dev[3].current_request
//{dev = 0x300, cmd = 0x0, errors = 0x0, sector = 0x0, nr_sectors = 0x2, buffer = 0x3ffc00, waiting = 0x0, bh = 0x41c0c, next = 0x0}
-
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
-
end_request(0);
-
goto repeat;
-
}
-
block += hd[dev].start_sect; //加上分区的起始扇区号就是要读取的实际的扇区号
-
dev /= 5; //dev现在是minor,次设备号/5=0-->第1块硬盘 ==1是第2块硬盘
-
//计算扇区 柱面 和 磁头号
-
__asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
-
"r" (hd_info[dev].sect));
-
__asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
-
"r" (hd_info[dev].head));
-
sec++;
-
nsect = CURRENT->nr_sectors;
-
if (reset) {
-
recalibrate = 1;
-
reset_hd();
-
return;
-
}
-
if (recalibrate) {
-
recalibrate = 0;
-
hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0,
-
WIN_RESTORE,&recal_intr);
-
return;
-
}
-
if (CURRENT->cmd == WRITE) {
-
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
-
for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
-
/* nothing */ ;
-
if (!r) {
-
bad_rw_intr();
-
goto repeat;
-
}
-
port_write(HD_DATA,CURRENT->buffer,256);
-
} else if (CURRENT->cmd == READ) { //如果当前是读命令
-
hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); //
-
} else
-
panic("unknown hd-command");
-
}
1.4 add_request-->do_hd_requst-->hd_out
在kernel/blk_drv/hd.c中 L188
hd_out (drive=0, nsect=2, sect=1, head=0, cyl=0, cmd=32, intr_addr=0x179f5 ) at hd.c:191
-
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
-
unsigned int head,unsigned int cyl,unsigned int cmd,
-
void (*intr_addr)(void))
-
{
-
register int port;
-
-
if (drive>1 || head>15)
-
panic("Trying to write bad sector");
-
if (!controller_ready())
-
panic("HD controller not ready");
-
SET_INTR(intr_addr); //对于读命令来说,这句就是-->do_hd=read_intr
-
outb_p(hd_info[drive].ctl,HD_CMD);
-
port=HD_DATA;
-
outb_p(hd_info[drive].wpcom>>2,++port);
-
outb_p(nsect,++port);
-
outb_p(sect,++port);
-
outb_p(cyl,++port);
-
outb_p(cyl>>8,++port);
-
outb_p(0xA0|(drive<<4)|head,++port);
-
outb(cmd,++port);
-
}
命令发送完成之后,就会沿着上述一路返回到bread(dev=0x300, block=0x0)中-->然后进入wait_on_buffer
1.5 在fs/buffer.c中 L37
-
static inline void wait_on_buffer(struct buffer_head * bh)
-
{
-
cli(); //关中断
-
while (bh->b_lock) //如果加了锁就进入进程调度,直到解锁为止才会继续执行,
-
sleep_on(&bh->b_wait); //即直到把所有的数据都读出来才会解锁往下执行
-
sti(); //开中断
-
}
这儿为什么要关中断呢?不担心关了中断硬盘的中断收不到吗?
加锁是在make_request中调用的 --> 在kernel/blk_drv/ll_rw_blk.c中
-
static inline void lock_buffer(struct buffer_head * bh)
-
{
-
cli();
-
while (bh->b_lock)
-
sleep_on(&bh->b_wait);
-
bh->b_lock=1;
-
sti();
-
}
1.6 add_request-->do_hd_requst-->hd_out完成后生产生一个硬盘中断
在kernel/blk_drv/hd.c中 L379
-
void hd_init(void)
-
{
-
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
-
set_intr_gate(0x2E,&hd_interrupt); //定义了硬盘中断处理函数是hd_interrupt
-
outb_p(inb_p(0x21)&0xfb,0x21);
-
outb(inb_p(0xA1)&0xbf,0xA1);
-
}
1.7
在kernel/sys_call.s中
-
hd_interrupt:
-
pushl %eax
-
pushl %ecx
-
pushl %edx
-
push %ds
-
push %es
-
push %fs
-
movl $0x10,%eax
-
mov %ax,%ds
-
mov %ax,%es
-
movl $0x17,%eax
-
mov %ax,%fs
-
movb $0x20,%al
-
outb %al,$0xA0 # EOI to interrupt controller #1
-
jmp 1f # give port chance to breathe
-
1: jmp 1f
-
1: xorl %edx,%edx
-
movl %edx,hd_timeout
-
xchgl do_hd,%edx
-
testl %edx,%edx
-
jne 1f
-
movl $unexpected_hd_interrupt,%edx
-
1: outb %al,$0x20
-
call *%edx # "interesting" way of handling intr.
-
pop %fs
-
pop %es
-
pop %ds
-
popl %edx
-
popl %ecx
-
popl %eax
-
iret
在kernel/blk_drv/hd.c中 L275
-
static void read_intr(void)
-
{
-
if (win_result()) {
-
bad_rw_intr();
-
do_hd_request();
-
return;
-
}
-
port_read(HD_DATA,CURRENT->buffer,256);
-
CURRENT->errors = 0;
-
CURRENT->buffer += 512;
-
CURRENT->sector++;
-
if (--CURRENT->nr_sectors) {
-
SET_INTR(&read_intr);
-
return;
-
}
-
end_request(1);
-
do_hd_request();
-
}
在kernel/blk_drv/blk.h中 L118
-
static inline void end_request(int uptodate)
-
{
-
DEVICE_OFF(CURRENT->dev);
-
if (CURRENT->bh) {
-
CURRENT->bh->b_uptodate = uptodate;
-
unlock_buffer(CURRENT->bh); -->解锁了bread可以往下执行了
-
}
-
if (!uptodate) {
-
printk(DEVICE_NAME " I/O error\n\r");
-
printk("dev %04x, block %d\n\r",CURRENT->dev,
-
CURRENT->bh->b_blocknr);
-
}
-
wake_up(&CURRENT->waiting);
-
wake_up(&wait_for_request);
-
CURRENT->dev = -1;
-
CURRENT = CURRENT->next;
-
}
附录1.关于要读取的扇区号的确定
bread(0x300 + drive*5,0);
a. 第1步在getblk()中将block=0,存在bh->b_blocknr=block;中
b. 在 ll_rw_block(int rw, struct buffer_head * bh)
--> make_request(int major,int rw, struct buffer_head * bh)
中有 req->sector = bh->b_blocknr<<1; 将块号转为扇区号
c. 最后 do_hd_request(void)中
block = CURRENT->sector; 将这个扇区号取出来
block += hd[dev].start_sect; 再加上
分区的起始扇区号,这就是最终要读取的扇区号
附录2.关于主次设备号-->以0x300为例
2.1 主设备号 -->在bread的第二步ll_rw_block-->make_request
中使用
-
void ll_rw_page(int rw, int dev, int page, char * buffer)
-
{
-
unsigned int major = MAJOR(dev); //取出主设备号=0x3
-
-
if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) { //这儿己经有应用了,判断blk_dev[3]的
-
printk("Trying to read nonexistent block-device\n\r");
-
return;
-
}
-
add_request(major+blk_dev,req); //将blk_dev[3]也就是硬盘的读写伟给add_request
-
schedule();
-
}
其中上述的blk_dev[3]的初始化在
-
void hd_init(void)
-
{
-
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; //硬盘的MAJOR_NR=3
-
//这个DEVICE_REQUEST=do_hd_request
-
}
即0x300的读写请求是在do_hd_request中
2.2 次设备号 -->do_hd_request中用
-
void do_hd_request(void)
-
{
-
dev = MINOR(CURRENT->dev); //取出次设备号=0x0
-
block = CURRENT->sector;
-
if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
-
end_request(0);
-
goto repeat;
-
}
-
//这儿就看到次设备号的作用了,作为hd的下标应用
-
block += hd[dev].start_sect; //hd[0]在sys_setup中存放了整个硬盘的参数,start_sect=0代表起始
-
//即要读取的扇区号从0开始
-
}
当次设备号为1时,上面的block就是从硬盘的第1个分区开始的start_sect开始读取。
阅读(1900) | 评论(0) | 转发(0) |