疯狂内核之输入输出
文章平均质量分 85
yunsongice
这个作者很懒,什么都没留下…
展开
-
Linux I/O体系结构
<br />为了确保计算机能够正常工作,必须提供数据通路,让信息在连接到个人计算机的CPU、RAM和I/O设备之间流动。这些数据通路总称为总线,担当计算机内部主通信通道的作用。<br /><br />所有计算机都拥有一条系统总线,它连接大部分内部硬件设备。一种典型的系统总线是PCI(Peripheral Component Interconnect)总线。目前使用其他类型的总线也很多,如ISA、EISA、MCA、SCSI和USB。<br /><br />典型的情况是,一台计算机包括几种不同类型的总线原创 2010-06-07 12:03:00 · 5589 阅读 · 1 评论 -
scsi块设备驱动层处理
1.6.3 scsi块设备驱动层处理<br />好了,了解完必要的scsi设备驱动知识以后,我们就可以安心分析scsi_request_fn函数了。大家回忆一下对,这个函数指针通过几次传递并最终在blk_init_queue_node()中被赋予了q->request_fn。所以这一层的重点就是这个scsi_request_fn函数。<br /> <br />在看scsi_request_fn之前,注意回忆一下scsi_alloc_queue函数的1598行至1560行还赋了三个函数指针:<br /> <b原创 2011-02-01 01:01:00 · 10482 阅读 · 3 评论 -
scsi命令的执行
1.6.4 scsi命令的执行<br />负责执行具体scsi命令的函数是scsi_dispatch_cmd,来自drivers/scsi/scsi.c:<br /> <br /> 468 int scsi_dispatch_cmd(struct scsi_cmnd *cmd)<br /> 469 {<br /> 470 struct Scsi_Host *host = cmd->device->host;<br /> 471 unsigned lon原创 2011-02-01 01:24:00 · 4270 阅读 · 0 评论 -
scsi命令的执行
1.6.4 scsi命令的执行<br />负责执行具体scsi命令的函数是scsi_dispatch_cmd,来自drivers/scsi/scsi.c:<br /> <br /> 468 int scsi_dispatch_cmd(struct scsi_cmnd *cmd)<br /> 469 {<br /> 470 struct Scsi_Host *host = cmd->device->host;<br /> 471 unsigned lon原创 2011-02-01 01:56:00 · 3779 阅读 · 0 评论 -
scsi命令的第一次转变
1.6.5 scsi命令的第一次转变<br />前面介绍了读写文件时,是通过sd_init_command设置的scsi_cmnd命令结构,其实我们也可以通过scsi_execute_req函数直接发送scsi命令,不过需要两次转变。<br /> <br />仍然以scsi磁盘举例,最初scsi这边发送的是scsi命令,可是从block走就得变成request,然而走到磁盘那边又得变回scsi命令,换言之,这整个过程scsi命令要变两次身。<br /> <br />比如,我们想获得磁盘的容量,就可以直接调用原创 2011-02-01 01:59:00 · 5632 阅读 · 0 评论 -
scsi命令的第二次转变
1.6.6 scsi命令的第二次转变<br />一旦这种关系建立好了以后,就可以开始执行请求了。来看blk_execute_rq(),来自block/ll_rw_blk.c:<br /> <br /> 2616 int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,<br /> 2617 struct request *rq, int at_head)<br /> 2618 {<br /原创 2011-02-01 02:02:00 · 3102 阅读 · 0 评论 -
块设备驱动应用之文件读写
1 读文件<br />其实Linux驱动程序最重要,也是难点就是那个块设备驱动程序。要全面研究这个问题不是那么容易,从本博开始,我们独辟蹊径,从一个文件读写的角度把这个问题阐述干净。<br /><br />大部分程序员可能会有这样的疑问:当我们在应用程序中调用库函数 read 时,这个请求是经过哪些处理最终到达磁盘的呢,数据又是怎么被拷贝到用户缓存区的呢?我们就从 read 系统调用发出到结束处理的全过程,来解密整个内核块设备驱动的内幕。<br /> 1.1 系统调用VFS层的处理<br />用户要读写文件原创 2011-01-31 21:32:00 · 4283 阅读 · 0 评论 -
Ext2的超级块对象
1.2.2 Ext2的超级块对象<br />当安装Ext2文件系统时(执行诸如mount -t ext2 /dev/sda2 /mnt/test的命令),存放在Ext2分区的磁盘数据结构中的大部分信息将被拷贝到RAM中,从而使内核避免了后来的很多读操作。那么一些数据结构如何经常更新呢?因为所有的Ext2磁盘数据结构都存放在Ext2磁盘分区的块中,因此,内核利用页高速缓存来保持它们最新。<br /> <br />前面谈到,安装ext2文件系统时,最终会调用ext2_fill_super()函数来为数据结构分配原创 2011-01-31 21:37:00 · 4371 阅读 · 1 评论 -
Ext2索引节点对象的创建
1.2.3 Ext2索引节点对象的创建<br />现在还是从一个普通文件的角度来分析上面的过程,比如说,当我门在根目录下调用fd = open("file", O_CREAT)打开(创建)一个文件时,会启动do_sys_open系统调用,并根据路径“file”去触发do_filp_open函数返回一个file结构。do_filp_open主要调用的两个函数(详细的过程请参考博客“VFS系统调用的实现”<br />http://blog.csdn.net/yunsongice/archive/2010/06/原创 2011-01-31 21:42:00 · 3673 阅读 · 0 评论 -
创建一个bio请求
1.3 页高速缓存层的处理<br />从上文得知:ext2_readpage 函数是该层的入口点,传给它的参数是文件的file,以及需要读入页高速缓存的页面。这个页面不是别的,正是刚才page_cache_alloc_cold分配的那个空白页面,其现在位于文件的基树中,基树中索引为index:<br /> <br />static int ext2_readpage(struct file *file, struct page *page)<br />{<br /> return mpage_r原创 2011-01-31 21:58:00 · 5274 阅读 · 0 评论 -
文件的预读
1.3.5 文件的预读<br />文件预读的内容我是把ULK-3一书中的内容全盘拷贝下来了。如果大家感兴趣可以根据源代码深入了解一下。<br /> <br />很多磁盘的访问都是顺序的。普通文件以相邻扇区成组存放在磁盘上,因此很少移动磁头就可以快速检索到文件。当程序读或拷贝一个文件时,它通常从第一个字节到最后一个字节顺序地访问文件。因此,在处理进程对同一文件的一系列读请求时,可以从磁盘上很多相邻的扇区读取。<br /> <br />预读(read-ahead)是一种技术,这种技术在于在实际请求前读普通文件或原创 2011-01-31 23:32:00 · 3514 阅读 · 0 评论 -
块设备的初始化
1.5 块设备I/O调度层的处理<br />下面进入块设备I/O调度层,来看看q->make_request_fn方法。不过这个方法的具体函数是什么呢?别着急,要弄清这个问题还需要再补充一下块设备驱动的基础知识,不然就又走不下去了。块设备驱动程序是Linux块子系统中的最底层组件。它们从I/O调度程序中获得请求,然后按要求处理这些请求。<br /> <br />当然,块设备驱动程序是设备驱动程序模型的组成部分(也就是在sysfs中能够看到它)。因此,每个块设备驱动程序对应一个device_driver类型的原创 2011-02-01 00:03:00 · 3665 阅读 · 2 评论 -
关联block_device结构
1.5.3 关联block_device结构<br />接下来是register_disk函数,来自fs/partitions/check.c:<br /> <br /> 473 /* Not exported, helper to add_disk(). */<br /> 474 void register_disk(struct gendisk *disk)<br /> 475 {<br /> 476 struct block_device *bdev;<br /原创 2011-02-01 00:11:00 · 5334 阅读 · 0 评论 -
scsi设备驱动体系架构
1.6.2 scsi设备驱动体系架构<br />从这一层开始,整个文件读写的中心将由request转向scsi的命令结构scsi_cmnd。那么这个命令结构到底是怎么一回事呢,这还得从SCSI架构谈起。SCSI 实现了一种客户机/服务器风格的通信架构,发起者向目标设备发送命令请求。该目标处理此请求并向发起者返回响应。发起者可以是托管计算机中的一个 SCSI 设备,而 SCSI 目标则可以是一个磁盘、光盘和磁带设备或特殊设备(比如箱体设备)。<br /> <br />这里要提到一个概念——Lower Leve原创 2011-02-01 00:37:00 · 11854 阅读 · 1 评论 -
scsi总线驱动的初始化
1.6.1 scsi总线驱动的初始化<br />块设备底层驱动的核心是scsi总线层驱动,在总线层驱动之上为各种不同的scsi设备驱动,在总线层驱动之下为scsi host驱动。其在内核中的位置如下图所示:<br /><br /> <br /><br /><br />前面我们已经知道了上三层的工作,接下来大部分知识来自底下三层。<br /> <br />在Linux中scsi驱动基本分为三大层:top level,middle level以及lower level。top level为具体的scsi设备驱动原创 2011-02-01 00:30:00 · 17515 阅读 · 5 评论 -
真实的I/O调度层处理
1.5.6 真实的I/O调度层处理<br />现在我们块设备也有了,队列也有了,要提交请求也就可以开始提交了。那就让我们回到generic_make_request来研究一下如何提交请求如何处理请求吧。我们看到,函数最后调用q->make_request_fn(q, bio)。对 make_request_fn 函数的调用可以认为是 IO调度层的入口,该函数用于向请求队列中添加请求。该函数是在创建请求队列时指定的,代码如下(blk_init_queue 函数中):<br />q->request_fn =原创 2011-02-01 00:22:00 · 7148 阅读 · 0 评论 -
linux设备模型 —— sysfs
1 sysfs初探<br />"sysfs is a ram-based filesystem initially based on ramfs. It provides a means to export kernel data structures, their attributes, and the linkages between them to userspace.” --- documentation/filesystems/sysfs.txt<br />可以先把documentation/fi转载 2011-01-31 16:34:00 · 3467 阅读 · 1 评论 -
设备驱动程序共性
<br />设备驱动程序是一组内核例程的集合,它使得硬件设备响应控制设备的编程接口,最关键的是该接口是一组规范的VFS函数集(open, read, lseek, ioctl等等)。这些函数的实际实现由设备驱动程序全权负责。由于每个设备都有一个唯一的I/O控制器,因此就有唯一的命令和唯一的状态信息,所以大部分I/O设备都有自己的驱动程序。<br /> <br />设备驱动程序的种类有很多。它们在对用户态应用程序提供支持的级别上有很大的不同,也对来自硬件设备的数据采集有不同的缓冲策略。这些选择极大地影响了设备原创 2011-01-31 16:39:00 · 3679 阅读 · 0 评论 -
Ext2索引节点对象的读取
1.2.4 Ext2索引节点对象的读取<br />上一节我们提到了当open("file", O_CREAT)创建一个文件时,其对应的Ext2磁盘索引节点是如何建立的,又是如何与系统中的其他数据结构联系的。现在还是从一个普通文件的角度来分析,当我门在根目录下调用fd = open("file", O_RDONLY)打开一个已经存在文件时,同样也会启动do_sys_open系统调用,并根据路径“file”去触发do_filp_open函数返回一个file结构。<br /> <br />而这个时候,do_fil原创 2011-01-31 21:48:00 · 3539 阅读 · 0 评论 -
Ext2层读文件入口函数
1.2.5 Ext2层读文件入口函数<br />好了,我们知道了Ext2文件系统的磁盘布局,以及始终缓存的磁盘超级拷贝块结构ext2_super_block和动态缓存的已分配磁盘索引节点结构ext2_inode这些预备知识。接下来就假设一个文件的inode已经分配好,并且包含该文件所有块号的对应宿主ext2_inode_info结构也在内存中初始化好了。那么如何读这个文件?<br /> <br />前面讲了,ext2层,也就是第二扩展文件系统的入口函数 generic_file_read,下面我们就从它开始原创 2011-01-31 21:54:00 · 3074 阅读 · 0 评论 -
得到文件的逻辑块号
1.3.2 得到文件的逻辑块号<br />继续走,233行,设置map_bh的b_page字段为当前page。随后进入循环,对于页中的每一块,调用ext2文件系统的get_block函数,作为参数传递page的inode、相对于文件起始位置的块索引block_in_file、map_bh进去,最后返回相对于磁盘分区开始位置的逻辑块号,即相对于磁盘或分区开始位置的块索引,存放在结果参数map_bh的b_blocknr字段中。所以这里我们重点关注get_block的原型,ext2_get_block函数,来自f原创 2011-01-31 22:08:00 · 7382 阅读 · 2 评论 -
文件的readpage方法
1.3.3 普通文件的readpage方法<br />回到do_mpage_readpage中,从mpage_readpage传进来的buffer_head类型的map_bh参数的b_bdev、b_blocknr和b_size字段就被赋上值了,然后245~271行对这个map_bh进行一系列的检查,检查可能发生的异常条件。具体有这几种情况:当一些块在磁盘上不相邻时,或某块落如“文件洞”内时,或一个块缓冲区已经由get_block函数写入时。那么跳到confused标号处,用一次读一块的方式读该页。有关“文件原创 2011-01-31 23:15:00 · 6138 阅读 · 0 评论 -
块设备的基础知识
1.4 通用块层的处理<br />在前面普通文件和块设备文件的readpage方法中介绍到,对于一个普通文件,要读取相对于文件头的ppos处开始size个连续的字节,就必须计算成对应的页面缓存在内存中;如果存在不连续的情况,如“文件的洞”,就调用块设备的readpage方法建立块设备页高速缓存存放不来连续的块。<br /> <br />不管怎样,最终都将封装一个bio结构,并把请求传递给函数 generic_make_request ,并由 generic_make_request 函数将请求提交给通用块层原创 2011-01-31 23:43:00 · 6214 阅读 · 2 评论 -
通用块层相关数据结构
1.4.2 通用块层相关数据结构<br />好了,通用块层的一些比较重要的基础知识大家都知道了,下面我们着重来看bio这个东西。大家在前面“创建一个bio请求”一节中已经见过这个结构了,但是,由于它太重要了,所以我们这里有必要对它进行进一步的介绍。<br /> <br />每个bio结构都包含一个磁盘存储区标识符(存储区中的起始扇区号和扇区数目)和一个或多个描述与I/O操作相关的内存区的段。bio由bio数据结构描述:<br /> <br />struct bio {<br /> sector_原创 2011-01-31 23:47:00 · 4079 阅读 · 0 评论 -
提交I/O传输请求
1.4.3 提交I/O传输请求<br />好了,bio这个数据我们建立好了,随后调用generic_make_request 函数。这个函数是通用块层的入口点,该层只有这一个函数处理请求:<br /> <br />3020void generic_make_request(struct bio *bio)<br />3021{<br />3022 request_queue_t *q;<br />3023 sector_t maxsector;<br />3024原创 2011-01-31 23:53:00 · 3447 阅读 · 0 评论 -
请求队列描述符
1.4.4 请求队列描述符<br />make_request_fn方法属于块设备I/O调度层的内容,要继续往下走,需要介绍一下通用块层的体系架构,这里需要从磁盘和磁盘分区开始说起。磁盘是一个由通用块层处理的逻辑块设备,是块设备驱动中最重要的一个概念。通常一个磁盘对应一个硬件块设备,例如硬盘、软盘或光盘。但是,磁盘也可以是一个虚拟设备,可以建立在几个物理磁盘分区之上或一些RAM专用页中的内存区上。在任何情形中,借助通用块层提供的服务,上层内核组件可以以同样的方式工作在所有的磁盘上。<br /> <br />原创 2011-01-31 23:59:00 · 3735 阅读 · 2 评论 -
建立块设备驱动环境
1.5.2 建立块设备驱动环境<br />上一节内容反映的是系统上电后,Linux初始化块设备子系统并注册了相应的块设备驱动的内容。而当我们把一块300G的硬盘插入总线的SCSI插槽中时,SCSI设备对应的probe程序就会调用alloc_disk()函数为其分配gendisk结构。<br /> <br />那么SCSI设备对应的probe程序到底是什么呢?设备驱动的研究是有一定套路的,比如说我们这里的scsi吧,必须在drivers/scsi目录下看它的Kconfig和Makefile文件。根据文件中的内原创 2011-02-01 00:08:00 · 3645 阅读 · 0 评论 -
块设备I/O调度程序
1.5.5 块设备I/O调度程序<br />我们建立请求队列建的目录是,当向请求队列增加一条新的请求,即产生一个request数据结构时,通用块层会调用I/O调度程序来确定该新request将在请求队列中的确切位置。I/O调度程序试图通过扇区将请求队列排序。如果顺序地从链表中提取要处理的请求,那么就会明显减少磁头寻道的次数,因为磁头是按照直线的方式从内磁道移向外磁道(反之亦然),而不是随意地从一个磁道跳跃到另一个磁道。<br /> <br />这就是著名的电梯算法,回想一下,电梯算法处理来自不同层的上下请求原创 2011-02-01 00:17:00 · 9166 阅读 · 2 评论 -
为设备建立请求队列
1.5.4 为设备建立请求队列<br />好啦,磁盘和分区建立好了,block_device数据结构也关联起来了,回到add_disk中,我们需要调用第三个函数了,也就是blk_register_queue(disk),来建立请求队列与bio等数据结构了,让我们来仔细分析。<br /> <br /> 4079 int blk_register_queue(struct gendisk *disk)<br /> 4080 {<br /> 4081 int ret;<br />原创 2011-02-01 00:14:00 · 3312 阅读 · 0 评论