引言

bread 块设备读取函数,顾名思义就是读取块设备内容的函数,这个函数的第一次调用是在main函数里面进程1的init()函数里面,sys_setup函数调用的。
第一次调用主要是为了读取硬盘上的第一个扇区的内容,因为第一个扇区有着分区表等重要信息。
bread的技术路线特别长,可以说它操作系统里面的缓冲区,请求项,驱动等多个版本都结合起来,想要充分理解整个函数,是十分需要耐心的。
OK,Let GO.

Go

我们看第一次调用bread函数的上下文:

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;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);}

其中调用语句为bh=bread(0x300+drive*5,0)
传入的参数有两个:dev和block。表示读取设备号为dev,块号为block的那个块的内容。
注意这里的0x300+drive*5 ,和式的8位以上的部分0x3是硬盘的主设备号3,低8位是次设备号。
在linux-0.11中,一种存在以下7种块设备:

struct blk_dev_struct blk_dev[NR_BLK_DEV] = {{ NULL, NULL },        /* no_dev */{ NULL, NULL },     /* dev mem */{ NULL, NULL },        /* dev fd */{ NULL, NULL },     /* dev hd 主设备号为3*/ { NULL, NULL },      /* dev ttyx */{ NULL, NULL },       /* dev tty */{ NULL, NULL }     /* dev lp */
};

注意,其中blk_dev[3]就是被用作硬盘,所以硬盘的主设备号为3.
OK,我们先看bread(0x300,0)这种情况。


bread函数的定义:

struct buffer_head * bread(int dev,int block)//1.有现成的//最新的//不是最新的//2.空闲//3.没空闲//设备号?块号?来保证存活时间尽量长?不应该是先进先出
{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;
}

这个函数的技术路线是:
首先,通过getblk()函数得到一个缓冲区,这个缓冲区通过一个buffer_head的指针来维护。
然后,看这个缓存块的内容是否是最新的(或者是否已经同步),这里通过bh->b_update来反映。
如果这个缓存块里面的东西是已经同步过的,那么直接return.
否则,先同步,从磁盘中读取第一个块的内容。

到了这里,我们就要先从宏观上把握缓冲区在内核中作用。
显然,缓冲区是介于磁盘与进程之间的东西。当进程需要读取数据的时候,内核先把数据从磁盘中读到缓冲区里面,然后再从缓冲区里面把数据拷贝到进程空间去。
当进程需要写数据的时候,内核先把进程要写的数据写到缓冲区中,然后内核在把缓冲区的数据写到硬盘上去。

其中,每个缓冲区由设备号和块号来标识,一个缓冲区就对应了硬盘上一个块(这是一个逻辑块,是操作系统的概念,硬盘不存在块的概念,在真实写盘,读盘的时候,需要把这个块号映射到硬盘的扇区号上去)。

我们可能会有疑问了,为什么要有缓冲区的存在??为什么硬盘来的数据先放到Buffer中,然后再把数据从Buffer拷到进程中去。
答案就是复用。
这是因为,很有可能,同一个设备号、块号的内容在一定的时间内被多次使用。例如,同一个进程可以多次读取同一个块。考虑到硬盘与内存之间读取速度的差异,读一次盘可是需要花费很长时间的,只要能够撞上一次就撞大发了。

其实,内核缓冲区部分的核心思想就是想方设法让里面的缓冲块被复用,那么为了达到这个目的,一种可行的方法就是让里面的缓冲块存活的时间尽量的长。直观的想,存活时间越长,被复用的机会就越大。

OK,我们先看看getblk(dev,block)这个函数。

struct buffer_head * getblk(int dev,int block)
{struct buffer_head * tmp, * bh;repeat:if ((bh = get_hash_table(dev,block)))return bh;//找到相同的tmp = free_list;do {if (tmp->b_count)//找引用计数为0的,也就是找空闲的continue;if (!bh || BADNESS(tmp)<BADNESS(bh)) //找最差的,通过dirt,b_lock//dirt:进程有写这个块,改了缓冲区//b_lock:1.需要把缓冲区的内存写入到硬盘; 2.一个进程读、一个进程写{bh = tmp;if (!BADNESS(tmp))break;}
/* and repeat until we find something good */}while ((tmp = tmp->b_next_free) != free_list);if (!bh)//bh==NULL  说明没有找到空闲的{sleep_on(&buffer_wait);goto repeat;}wait_on_buffer(bh);//bh里面 dirt,lock有可能为0,这个只是相对更好而已 不是一定最好if (bh->b_count)//上面wait的时候,另外一个进程改了goto repeat;while (bh->b_dirt) {sync_dev(bh->b_dev);wait_on_buffer(bh);if (bh->b_count)goto repeat;}
/* NOTE!! While we slept waiting for this block, somebody else might */
/* already have added "this" block to the cache. check it */if (find_buffer(dev,block))goto repeat;
/* OK, FINALLY we know that this buffer is the only one of it's kind, */
/* and that it's unused (b_count=0), unlocked (b_lock=0), and clean */bh->b_count=1;bh->b_dirt=0;bh->b_uptodate=0;//???????????remove_from_queues(bh);bh->b_dev=dev;bh->b_blocknr=block;insert_into_queues(bh);return bh;
}

bread 块设备读取函数解析(1)相关推荐

  1. Linux内核:一文读懂文件系统、缓冲区高速缓存和块设备、超级块

    目录 前言 第一部分 Linux文件系统堆栈 VFS数据结构 文件系统初始化顺序 Dentries 打开文件-说起来容易做起来难! 虚拟文件系统 前言 第二部分 Linux文件系统堆栈 当我们键入&q ...

  2. 写一个块设备驱动程序

    ----------------------- Page 1----------------------- 第 1章 +---------------------------------------- ...

  3. 转:写一个块设备驱动

    ----------------------- Page 1----------------------- 第 1章 +---------------------------------------- ...

  4. Linux驱动开发--写一个块设备驱动

    原文地址:[原创] 写一个块设备驱动 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2017377&fromuid=288 ...

  5. Linux串口读取与解析

    串口协议包的接收及解析处理 对于串口接收问题前面之前有文章介绍过串口缓存机制的应用.当然这里不应用缓存机制也是完全可行的.这里我们讲解基于不带串口缓存机制的处理.对于串口接收我们最常用的方式就是在串口 ...

  6. [转]linux 块设备驱动

    转自 linux块设备IO栈 http://www.sysnote.org/2015/08/06/linux-io-stack/ linux块设备IO流程 驱动 https://www.cnblogs ...

  7. Linux块设备驱动总结

    <Linux设备驱动程序>第十六章 块设备驱动程序读书笔记 简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linu ...

  8. [原] KVM 虚拟化原理探究(6)— 块设备IO虚拟化

    目录 KVM 虚拟化原理探究(6)- 块设备IO虚拟化 块设备IO虚拟化简介 传统块设备架构 块设备IO协议栈 块设备IO流程 块设备IO虚拟化 总结 KVM 虚拟化原理探究(6)- 块设备IO虚拟化 ...

  9. Smart210学习记录------块设备

    转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动. 所谓的能用,是指我们可以对这个驱动生成 ...

最新文章

  1. 何恺明大神新作:一种用于目标检测的主流ViT架构,效果SOTA
  2. aehyok.com的成长之路二——技术选型
  3. 第十天2017/04/21(2、泛型编程:模板 / 全特化、偏特化)
  4. C指针原理(35)-Ncurses-文本终端的图形
  5. xcopy复制文件夹及其子文件_嗨学习:如何给电脑中文件夹设置密码
  6. Linux 命令之 touch -- 创建文件
  7. Java8 Stream详解~归约(reduce)
  8. 多iframe下的html同名id,获得同级iframe页面的指定ID元素的几种实现方法
  9. 共享单车或开启了物联网的大门,新的变革即将开启
  10. Vbs脚本编程简明教程之十五
  11. 计算机学科导论-2013级教学材料
  12. [情感] 纯情女生给老实木讷男孩的恋爱建议
  13. DDD战略建模在重构业务系统时的实践
  14. vue 仿写微信公众号自定义菜单
  15. Linux中的各种栈:进程栈 线程栈 内核栈 中断栈
  16. 2022-07-17 Linux group与user基础概念
  17. 小白如何选择采集器-爬虫
  18. Python环境搭建-自用
  19. 设计模式 考试题+答案
  20. 获取滑动条位置,动态调整对话框显示位置;为动态添加的button添加click事件

热门文章

  1. oracle数据如何导入pg库,【oracle数据库如何导入到pg库中】-其它论坛-ZOL中关村在线...
  2. weblogic的集群与配置--架构师第九天
  3. 基于php的校园公寓管理系统设计与实现
  4. SpringBoot整合阿里云OSS,传入文件为MultipartFile格式文件
  5. php开发discuz插件,discuz x3插件开发傻瓜图文教程,用demo说话
  6. 上海计算机考试分值,2019年上海中考总分是多少 考试科目及分值
  7. 亚马逊常见的专业词汇你都知道吗?建议小白耐心看
  8. 「迁怒」也许不只是没忍住
  9. 数据结构头歌实验梳理
  10. 浅谈雷达在气象领域的基本应用