[IO系统]13 通用块层-向通用块层提交IO

前面介绍了块设备的相关概念、 buffer_head和bio结构体。接下来主要分析如何向通用块层提交IO。

1.1   分配bio

当向通用块层提交一个IO操作请求的时候,假设被请求的数据块在磁盘上是相邻的,并且内核要已经知道了他们的物理位置。那么首先第一步就是执行bio_alloc()函数        分配一个新的bio描述符,然后内核通过设置一些字段的值来初始化bio描述符。该函数主要做的工作如下:

.将bi_sector字段设置为数据的起始扇区号(如果块设备被分成了几个分区,那么扇区号是相对于分区的起始位置)。

.将bi_size字段设置为涵盖整个数据的扇区数目。

.将bi_bdev设置为块设备描述符的地址(这个是block_device的对象,代表的是一个分区或者是主设备)。

.将bi_io_vec设为bio_vec结构数组的其实地址,数组中的每个元素描述了io操作中的一个段(内存缓存),此外,将bi_vcnt设置为bio中总的段数。

.将bi_rw字段设置为被请求的操作的标志。

.将bi_end_io字段设置成为当bio上的IO操作完成时所执行的完成程序的地址。

1.2   提交bio

generic_make_request()函数会接手一个已经基本初始化好的bio,并使用make_request_fn将请求置于驱动程序的请求队列上。即把该bio传给设备对应的驱动程序。

blk_qc_t generic_make_request(struct bio *bio)

参数bio中bi_dev和bi_sector都已经设定为要进行IO的对应的设备的具体地址。其中在make_request_fn(每个设备会对应一个请求队列(request_queue),该请求队列上会有对应的make_request_fn指针)函数调用后就不应该再对bio有所改动。

但是make_request可能会递归调用generic_make_request,但是对于内核态来说,栈空间是有限的。所以为了保证递归深度不能超过1,这块做了个聪明的处理。

    if(current->bio_list) {
        bio_list_add(current->bio_list,bio);
        gotoout;
    }

其中current->bio_list是记录了由make_request_fn提交的request组成的链表,一般情况下,如果是第一次调用generic_make_request,该current->bio_list为空的。所以这个if语句并不会执行。

    BUG_ON(bio->bi_next);
    bio_list_init(&bio_list_on_stack);/* 初始化双向列表 */
    current->bio_list= &bio_list_on_stack;
    do {
        structrequest_queue *q = bdev_get_queue(bio->bi_bdev); /* 获取当前设备的队列 */

        if(likely(blk_queue_enter(q, false) == 0)) { /* 判断当前队列是否可有效响应请求 */
            ret= q->make_request_fn(q, bio); /* 提交bio */

            blk_queue_exit(q);

            bio= bio_list_pop(current->bio_list);
        } else{
            structbio *bio_next = bio_list_pop(current->bio_list);

            bio_io_error(bio);
            bio= bio_next;
        }
    } while(bio);

上面说了current->bio_list为空,所以初始化一个链表用来存后来make_request_fn提交的request。接下来获取设备的请求队列(request_queue),然后本次提交的bio可以通过make_request_fn添加到请求队列上,但是在make_request_fn函数中可能会发生递归调用generic_make_request(比如软RAID的实现,把请求拆分到其他设备上)。为了防止这种递归调用层次太深,但是逻辑上又要递归调用,就需要使用到current->bio_list,比如说现在make_request_fn已经调用了generic_make_request,那么就会进入上面提到的if块中,此时只是把该bio加入到bio_list之后就返回没有再做处理了。

这时候会回到循环中make_request_fn的下一句代码(假设整个过程只调用了一次generic_make_request),可以看见该语句又会从bio_list拿出刚刚添加的bio进行处理。这是不是就把递归问题巧妙地转变成了循环同时又保证了调用递归的方便性?

下图是generic_make_request的整个处理流程,最主要的操作是获取请求队列,然后调用对应的make_request_fn方法处理bio,make_request_fn方法会将bio放入请求队列中进行调度处理,普通磁盘介质一个最大的问题是随机读写性能很差。为了提高性能,通常的做法是聚合IO,因此在块设备层设置请求队列,对IO进行聚合操作,从而提高读写性能,对于I/O的调度将单独进行分析。

此时,通用层的基本使命已经完成,从代码流程上来看,通用层主要完成的是将应用层的读写请求构成一个或者多个I/O请求而已,后面的主要工作就交给I/O调度层。

时间: 2024-10-21 16:37:37

[IO系统]13 通用块层-向通用块层提交IO的相关文章

一文看懂java io系统 (转)

出处:  一文看懂java io系统 学习java IO系统,重点是学会IO模型,了解了各种IO模型之后就可以更好的理解java IO Java IO 是一套Java用来读写数据(输入和输出)的API.大部分程序都要处理一些输入,并由输入产生一些输出.Java为此提供了java.io包 java中io系统可以分为Bio,Nio,Aio三种io模型 关于Bio,我们需要知道什么是同步阻塞IO模型,Bio操作的对象:流,以及如何使用Bio进行网络编程,使用Bio进行网络编程的问题 关于Nio,我们需

块设备之设备驱动层

块设备是通过generic_make_request提交请求给I/O调度层,然后驱动层通过调用blk_init_queue来准备请求,这节来看看怎么样写一个块设备驱动程序.一个块设备的是由一个gendisk结构体来描述,每一个gendisk可以支持多个分区,内核对于块设备的访问,都是基于这个结构体展开 struct gendisk { int major; //主设备号 int first_minor; //第一个次设备号 int minors; //次设备个数,每个分区都需要一个次设备号 ch

Java:IO系统与装饰模式

Java的IO流有三种分法: ①输入流.输出流:输入输出流都是以Java程序为参照的. ②字节流.字符流:字节是存储单位,占8位,其他基本数据类型都用字节来衡量大小.字符是数字字母等字符,ASCII和Unicode都是字符编码集,ASCII码是8位一个字节的,Unicode是16位两个字节的,而Java字符编码是采用Unicode的.字节流后缀是Stream,字符流后缀是Reader,Writer. ③节点流.处理流:节点流可以理解为真正处理数据的流,处理流是在节点流的基础上的修饰. 关于各种流

磁盘io系统压力测试工具fio

FIO概述 fio是一个基于GPLV2授权的开源压力测试工具,主要是用来测试磁盘io性能,也有cpu,nic的io测试功能. 1.安装FIO yum -y install wget yum -y install libaio-devel wget http://brick.kernel.dk/snaps/fio-2.1.tar.gz tar zxvf fio-2.1.tar.gz cd fio-2.1 make make install 2.fio测试命令参数 filename=/dev/sda

java中的io系统详解

java中的io系统详解 分类: JAVA开发应用 笔记(读书.心得)2009-03-04 11:26 46118人阅读 评论(37) 收藏 举报 javaiostreamconstructorstringbyte 相关读书笔记.心得文章列表 Java 流在处理上分为字符流和字节流.字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符.字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组. Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他

Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6642463 在前面几篇文章中,我们详细介绍了Android系统进程间通信机制Binder的原理,并且深入分析了系统提供的Binder运行库和驱动程序的 源代码.细心的读者会发现,这几篇文章分析的Binder接口都是基于C/C++语言来实现的,但是我们在编写应用程序都是基于Java语言的,那么,我 们如何使用Java语言来使用系统的Binder机

Java的IO系统

Java的IO操作 最近想用Java写一个爬虫,知乎了一下,很多人推荐如果业务逻辑不太复杂,都推荐使用国内大牛写的的一个框架webmagic,这个是java实现的,思路参照谷歌的Scrapy .但是实现爬虫需要用到很多关于IO操作和多线程,发现这两项一直都是我java比较模糊的地方,这次就顺便学习一下,我看的是<java编程思想>. 对于IO的存取,不仅存在与各种I/O源端和想与之通信的接收端(接收端包括文件.控制台.网络链接等等),而且可能还需要有多种不同的方式与它们通信(顺序.随机存取.缓

java编程思想——java IO系统

一.什么是IO io在本质上是单个字节的移动.而流能够说是字节移动的载体和方式,它不停的向目标处移动数据.我们要做的就是依据流的方向从流中读取数据或者向流中写入数据. 二.java中支持IO操作的库类 1.依照数据类型分为两类: (1)字节类型:InputStream和OutputStream (2)字符类型:Writer和Reader 2.依照数据的流动方向,主要分为两类: (1)基于磁盘操作的io接口:File (2)基于网络的io接口:socket 三.字节流.字符流的io接口说明 字节流

网络OSI七层模型及各层作用 与 TCP/IP

背景 虽然说以前学习计算机网络的时候,学过了,但为了更好地学习一些物联网协议(MQTT.CoAP.LWM2M.OPC),需要重新复习一下. OSI七层模型 七层模型,亦称OSI(Open System Interconnection).参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系,一般称为OSI参考模型或七层模型. 它是一个七层的.抽象的模型体,不仅包括一系列抽象的术语或概念,也包括具体的协议. OSI七层模型 功能 对应的网络协议 应用层 应用层是网络体系中