Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2122979
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-10-03 00:05:48

从硬盘中读取n个扇区的数据的流程:
sys_setup 在kernel/blk_drv/hd.c中
sys_setup中
--> bh = bread(0x300 + drive*5,0);

1. 在kernel/blk_drv/hd.c中-->调用了bread
  1. int sys_setup(void * BIOS)
  2. {
  3.     static int callable = 1;
  4.     int i,drive;
  5.     unsigned char cmos_disks;
  6.     struct partition *p;
  7.     struct buffer_head * bh;

  8.     if (!callable)
  9.         return -1;
  10.     callable = 0;
  11. //读取硬盘的参数: int head,sect,cyl,wpcom,lzone,ctl
  12.     for (drive=; drive<; drive++) {
  13.         hd_info[drive].cyl = *(unsigned short *) BIOS;
  14.         hd_info[drive].head = *(unsigned char *) (2+BIOS);
  15.         hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
  16.         hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
  17.         hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
  18.         hd_info[drive].sect = *(unsigned char *) (14+BIOS);
  19.         BIOS += 16;
  20.     }
  21.     if (hd_info[1].cyl)
  22.         NR_HD=2;
  23.     else
  24.         NR_HD=1;
  25. //通过刚才读取的硬盘参数计算总的扇区数=head*(sectors_per_track)*cylinders
  26. //hd[0]代表整个硬盘,start_sect=0,nr_sects=整个硬盘的sector
  27.     for (i=; i<NR_HD ; i++) {
  28.         hd[i*5].start_sect = 0;
  29.         hd[i*5].nr_sects = hd_info[i].head*
  30.                 hd_info[i].sect*hd_info[i].cyl;
  31.     }
  32. //以下不重要不关心,直接认NR_HD=1
  33.     if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
  34.         if (cmos_disks & 0x0f)
  35.             NR_HD = 2;
  36.         else
  37.             NR_HD = 1;
  38.     else
  39.         NR_HD = 0;
  40.     for (= NR_HD ; i < 2 ; i++) {
  41.         hd[i*5].start_sect = 0;
  42.         hd[i*5].nr_sects = 0;
  43.     }
  44. //读取硬盘的第0,1两个扇区,并判断第一个扇区是不是以0x55aa结束
  45. //将分区表的信息保存在hd中
  46.     for (drive=; drive<NR_HD ; drive++) {
  47.         if (!(bh = bread(0x300 + drive*5,0))) {                      ---->就是这里
  48.             printk("Unable to read partition table of drive %d\n\r", drive);
  49.             panic("");
  50.         }
  51.         if (bh->b_data[510] != 0x55 || (unsigned charbh->b_data[511] != 0xAA) {
  52.             printk("Bad partition table on drive %d\n\r",drive);
  53.             panic("");
  54.         }
  55.         p = 0x1BE + (void *)bh->b_data;     //在mbr的0x1be=446,446+64=510,最后的0x55aa好512
  56.         for (i=1;i<5;i++,p++) {
  57.             hd[i+5*drive].start_sect = p->start_sect;
  58.             hd[i+5*drive].nr_sects = p->nr_sects;
  59.         }
  60.         brelse(bh);
  61.     }
  62.     for (i=; i<5*MAX_HD ; i++)
  63.         hd_sizes[i] = hd[i].nr_sects>>;
  64.     blk_size[MAJOR_NR] = hd_sizes;
  65.     if (NR_HD)
  66.         printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
  67.     rd_load();
  68.     init_swapping();
  69.     mount_root();
  70.     return (0);
  71. }
关于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)
  1. struct buffer_head * bread(int dev,int block)
  2. {
  3.     struct buffer_head * bh;
  4.     //在高速缓冲区中申请一块内存,用来放读取后的数据
  5.     if (!(bh=getblk(dev,block)))                
  6.         panic("bread: getblk returned NULL\n");
  7.     if (bh->b_uptodate)
  8.         return bh;
  9.     //读取硬盘中的数据
  10.     ll_rw_block(READ,bh);     //这里是调用底层的读写函数
  11.     wait_on_buffer(bh);
  12.     if (bh->b_uptodate)
  13.         return bh;
  14.     brelse(bh);
  15.     return NULL;
  16. }
这儿先不关心如何从高速缓冲区中申请内存的,先来看看是如何读取硬盘的数据的
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)
  1. void ll_rw_block(int rw, struct buffer_head * bh)
  2. {
  3.     unsigned int major;
  4. //hd->b_dev=0x300,主设备号是3,判断读写处理函数是存在的,则继续
  5.     if ((major=MAJOR(bh->b_dev)) >= NR_BLK_DEV ||
  6.     !(blk_dev[major].request_fn)) {
  7.         printk("Trying to read nonexistent block-device\n\r");
  8.         return;
  9.     }
  10.     make_request(major,rw,bh);  //构造硬盘请求
  11. }
a.  构造硬盘请求的过程
kernel/blk_drv/ll_rw_blk.c中 L106
  1. static void make_request(int major,int rw, struct buffer_head * bh)
  2. {
  3.     struct request * req;
  4.     int rw_ahead;

  5. /* WRITEA/READA is special case - it is not really needed, so if the */
  6. /* buffer is locked, we just forget about it, else it's a normal read */
  7.     if (rw_ahead = (rw == READA || rw == WRITEA)) {
  8.         if (bh->b_lock)
  9.             return;
  10.         if (rw == READA)
  11.             rw = READ;
  12.         else
  13.             rw = WRITE;
  14.     }
  15.     if (rw!=READ && rw!=WRITE)
  16.         panic("Bad block dev command, must be R/W/RA/WA");
  17.     lock_buffer(bh);                       //这儿进行加锁
  18.     if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
  19.         unlock_buffer(bh);
  20.         return;
  21.     }
  22. repeat:
  23. /* we don't allow the write-requests to fill up the queue completely:
  24.  * we want some room for reads: they take precedence. The last third
  25.  * of the requests are only for reads.
  26.  */
  27. //查找空亲的request-->从request的最后一项开始查找,只要找到req->dev=-1的一项说明是空闲的request
  28.     if (rw == READ)
  29.         req = request+NR_REQUEST;              //req指向request的最后一项
  30.     else
  31.         req = request+((NR_REQUEST*2)/3);
  32. /* find an empty request */
  33.     while (--req >= request)                   //从后往前查找一个空闲的request项
  34.         if (req->dev<0)
  35.             break;
  36. /* if none found, sleep on new requests: check for rw_ahead */
  37.     if (req < request) {
  38.         if (rw_ahead) {
  39.             unlock_buffer(bh);
  40.             return;
  41.         }
  42.         sleep_on(&wait_for_request);
  43.         goto repeat;                             //如果没有空闲的request项,则sleep之后再查找
  44.     }
  45. /* fill up the request-info, and add it to the queue */
  46. //找到了一个空闲的request项之后,初始化这个request
  47.     req->dev = bh->b_dev;
  48.     req->cmd = rw;
  49.     req->errors=0;
  50.     req->sector = bh->b_blocknr<<1;
  51.     req->nr_sectors = 2;
  52.     req->buffer = bh->b_data;
  53.     req->waiting = NULL;
  54.     req->bh = bh;
  55.     req->next = NULL;                      //硬盘的bld_dev是在hd_init中初始化的
  56.     add_request(major+blk_dev,req);        //在hd_init中有blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;=do_hd_request 
  57. }                                          //从这儿可以看出主设备号的用途-->作下标用的
b.  构造硬盘请求的过程
这个参数dev是blk_dev[3],即硬盘的blk_dev

  1. static void add_request(struct blk_dev_struct * dev, struct request * req)
  2. {
  3.     struct request * tmp;

  4.     req->next = NULL;
  5.     cli();                                 //关中断
  6.     if (req->bh)
  7.         req->bh->b_dirt = 0;
  8.     if (!(tmp = dev->current_request)) {
  9.         dev->current_request = req;
  10.         sti();                              //开中断
  11.         (dev->request_fn)();               //对于硬盘调用的是do_hd_request
  12.         return;
  13.     }
  14.     for ( ; tmp->next ; tmp=tmp->next) {
  15.         if (!req->bh)
  16.             if (tmp->next->bh)
  17.                 break;
  18.             else
  19.                  continue;
  20.         if ((IN_ORDER(tmp,req) ||
  21.          !IN_ORDER(tmp,tmp->next)) &&
  22.          IN_ORDER(req,tmp->next))
  23.             break;
  24.     }
  25.     req->next=tmp->next;
  26.     tmp->next=req;
  27.     sti();
  28. }
1.3 add_request-->do_hd_requst
在kernel/blk_drv/hd.c中
  1. void do_hd_request(void)
  2. {
  3.     int i,r;
  4.     unsigned int block,dev;
  5.     unsigned int sec,head,cyl;
  6.     unsigned int nsect;

  7.     INIT_REQUEST;                 //到这儿req这个都没有用
  8.     dev = MINOR(CURRENT->dev);    //到这一步才算是用到了这个make_request中申请到的request
  9.     block = CURRENT->sector;
  10. //(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}
  11.     if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
  12.         end_request(0);
  13.         goto repeat;
  14.     }
  15.     block += hd[dev].start_sect;           //加上分区的起始扇区号就是要读取的实际的扇区号
  16.     dev /= 5;                              //dev现在是minor,次设备号/5=0-->第1块硬盘 ==1是第2块硬盘
  17. //计算扇区 柱面 和 磁头号
  18.     __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0),
  19.         "r" (hd_info[dev].sect));
  20.     __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0),
  21.         "r" (hd_info[dev].head));
  22.     sec++;
  23.     nsect = CURRENT->nr_sectors;
  24.     if (reset) {
  25.         recalibrate = 1;
  26.         reset_hd();
  27.         return;
  28.     }
  29.     if (recalibrate) {
  30.         recalibrate = 0;
  31.         hd_out(dev,hd_info[CURRENT_DEV].sect,0,0,0,
  32.             WIN_RESTORE,&recal_intr);
  33.         return;
  34.     }    
  35.     if (CURRENT->cmd == WRITE) {
  36.         hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
  37.         for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
  38.             /* nothing */ ;
  39.         if (!r) {
  40.             bad_rw_intr();
  41.             goto repeat;
  42.         }
  43.         port_write(HD_DATA,CURRENT->buffer,256);
  44.     } else if (CURRENT->cmd == READ) {                         //如果当前是读命令
  45.         hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);    //
  46.     } else
  47.         panic("unknown hd-command");
  48. }

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
  1. static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
  2.         unsigned int head,unsigned int cyl,unsigned int cmd,
  3.         void (*intr_addr)(void))
  4. {
  5.     register int port;

  6.     if (drive>1 || head>15)
  7.         panic("Trying to write bad sector");
  8.     if (!controller_ready())
  9.         panic("HD controller not ready");
  10.     SET_INTR(intr_addr);                           //对于读命令来说,这句就是-->do_hd=read_intr
  11.     outb_p(hd_info[drive].ctl,HD_CMD);
  12.     port=HD_DATA;
  13.     outb_p(hd_info[drive].wpcom>>2,++port);
  14.     outb_p(nsect,++port);
  15.     outb_p(sect,++port);
  16.     outb_p(cyl,++port);
  17.     outb_p(cyl>>8,++port);
  18.     outb_p(0xA0|(drive<<4)|head,++port);
  19.     outb(cmd,++port);
  20. }
命令发送完成之后,就会沿着上述一路返回到bread(dev=0x300, block=0x0)中-->然后进入wait_on_buffer
1.5 在fs/buffer.c中 L37 
  1. static inline void wait_on_buffer(struct buffer_head * bh)
  2. {
  3.     cli();                      //关中断
  4.     while (bh->b_lock)           //如果加了锁就进入进程调度,直到解锁为止才会继续执行,
  5.         sleep_on(&bh->b_wait);   //即直到把所有的数据都读出来才会解锁往下执行
  6.     sti();                      //开中断
  7. }
这儿为什么要关中断呢?不担心关了中断硬盘的中断收不到吗?

加锁是在make_request中调用的 --> 在kernel/blk_drv/ll_rw_blk.c中
  1. static inline void lock_buffer(struct buffer_head * bh)
  2. {
  3.     cli();
  4.     while (bh->b_lock)
  5.         sleep_on(&bh->b_wait);
  6.     bh->b_lock=1;
  7.     sti();
  8. }



1.6 add_request-->do_hd_requst-->hd_out完成后生产生一个硬盘中断

在kernel/blk_drv/hd.c中 L379
  1. void hd_init(void)
  2. {
  3.     blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
  4.     set_intr_gate(0x2E,&hd_interrupt);         //定义了硬盘中断处理函数是hd_interrupt
  5.     outb_p(inb_p(0x21)&0xfb,0x21);
  6.     outb(inb_p(0xA1)&0xbf,0xA1);
  7. }


1.7 

在kernel/sys_call.s中
  1. hd_interrupt:
  2.     pushl %eax
  3.     pushl %ecx
  4.     pushl %edx
  5.     push %ds
  6.     push %es
  7.     push %fs
  8.     movl $0x10,%eax
  9.     mov %ax,%ds
  10.     mov %ax,%es
  11.     movl $0x17,%eax
  12.     mov %ax,%fs
  13.     movb $0x20,%al
  14.     outb %al,$0xA0        # EOI to interrupt controller #1
  15.     jmp 1f            # give port chance to breathe
  16. 1:    jmp 1f
  17. 1:    xorl %edx,%edx
  18.     movl %edx,hd_timeout
  19.     xchgl do_hd,%edx
  20.     testl %edx,%edx
  21.     jne 1f
  22.     movl $unexpected_hd_interrupt,%edx
  23. 1:    outb %al,$0x20
  24.     call *%edx        # "interesting" way of handling intr.
  25.     pop %fs
  26.     pop %es
  27.     pop %ds
  28.     popl %edx
  29.     popl %ecx
  30.     popl %eax
  31.     iret


在kernel/blk_drv/hd.c中 L275
  1. static void read_intr(void)
  2. {
  3.     if (win_result()) {
  4.         bad_rw_intr();
  5.         do_hd_request();
  6.         return;
  7.     }
  8.     port_read(HD_DATA,CURRENT->buffer,256);
  9.     CURRENT->errors = 0;
  10.     CURRENT->buffer += 512;
  11.     CURRENT->sector++;
  12.     if (--CURRENT->nr_sectors) {
  13.         SET_INTR(&read_intr);
  14.         return;
  15.     }
  16.     end_request(1);
  17.     do_hd_request();
  18. }

在kernel/blk_drv/blk.h中 L118
  1. static inline void end_request(int uptodate)
  2. {
  3.     DEVICE_OFF(CURRENT->dev);
  4.     if (CURRENT->bh) {
  5.         CURRENT->bh->b_uptodate = uptodate;    
  6.         unlock_buffer(CURRENT->bh);             -->解锁了bread可以往下执行
  7.     }
  8.     if (!uptodate) {
  9.         printk(DEVICE_NAME " I/O error\n\r");
  10.         printk("dev %04x, block %d\n\r",CURRENT->dev,
  11.             CURRENT->bh->b_blocknr);
  12.     }
  13.     wake_up(&CURRENT->waiting);
  14.     wake_up(&wait_for_request);
  15.     CURRENT->dev = -1;
  16.     CURRENT = CURRENT->next;
  17. }





附录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中使用
  1. void ll_rw_page(int rw, int dev, int page, char * buffer)
  2. {    
  3.     unsigned int major = MAJOR(dev);       //取出主设备号=0x3

  4.     if (major >= NR_BLK_DEV || !(blk_dev[major].request_fn)) {   //这儿己经有应用了,判断blk_dev[3]的
  5.         printk("Trying to read nonexistent block-device\n\r");
  6.         return;
  7.     }
  8.     add_request(major+blk_dev,req);     //将blk_dev[3]也就是硬盘的读写伟给add_request
  9.     schedule();
  10. }
其中上述的blk_dev[3]的初始化在
  1. void hd_init(void)
  2. {
  3.     blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;   //硬盘的MAJOR_NR=3
  4.                         //这个DEVICE_REQUEST=do_hd_request
  5. }
即0x300的读写请求是在do_hd_request中
2.2 次设备号 -->do_hd_request中用
  1. void do_hd_request(void)
  2. {
  3.     dev = MINOR(CURRENT->dev);  //取出次设备号=0x0
  4.     block = CURRENT->sector;
  5.     if (dev >= 5*NR_HD || block+2 > hd[dev].nr_sects) {
  6.         end_request(0);
  7.         goto repeat;
  8.     }
  9. //这儿就看到次设备号的作用了,作为hd的下标应用
  10.     block += hd[dev].start_sect;     //hd[0]在sys_setup中存放了整个硬盘的参数,start_sect=0代表起始
  11.                                      //即要读取的扇区号从0开始
  12. }
当次设备号为1时,上面的block就是从硬盘的第1个分区开始的start_sect开始读取。

阅读(1787) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~