io电梯算法,网上一堆,在此不再赘述。

手上有几块厂商提供的sas的ssd,做如下实验。

考虑到没有磁头移动,ssd一般采用noop的io调度策略,结果看到如下的iostat测试数据:

Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
dm-0 84.00 0.00 600.00 0.00 342.00 0.00 1167.36 1.15 1.92 1.92 0.00 0.78 46.90
dm-1 87.00 0.00 560.00 0.00 323.00 0.00 1181.26 1.04 1.86 1.86 0.00 0.79 44.50
dm-2 88.00 0.00 576.00 0.00 332.00 0.00 1180.44 1.19 2.06 2.06 0.00 0.79 45.40
dm-3 94.00 0.00 553.00 0.00 323.50 0.00 1198.06 0.98 1.78 1.78 0.00 0.82 45.60
dm-4 98.00 0.00 576.00 0.00 337.00 0.00 1198.22 1.12 1.95 1.95 0.00 0.81 46.80
dm-5 83.00 0.00 536.00 0.00 309.00 0.00 1180.66 0.93 1.73 1.73 0.00 0.81 43.50
dm-6 101.00 0.00 572.00 0.00 337.00 0.00 1206.60 1.06 1.86 1.86 0.00 0.81 46.50
dm-7 98.00 0.00 607.00 0.00 353.00 0.00 1191.01 1.13 1.87 1.87 0.00 0.82 49.60

第一列的数据表明,read进行了merge,这个与我之前预想的不一致,我一直以为固态硬盘颗粒不会进行合并,后来想,可能跟调度算法有关系,而不是跟具体物理介质有关系,如果该驱动注册了这个调度策略,那么就可能进行merge,而不管这个merge对硬件介质是否管用。根据查看iostat的源码以及内核中genhd.c的diskstats_show,确认了这个合并就是我们理解的io merge。

我们知道对于cfq和deadline这两种调度策略,是会进行合并的,noop 的合并需要单独看noop的实现。

查看内核中noop的初始化函数指针:

static struct elevator_type elevator_noop = {
.ops = {
.elevator_merge_req_fn = noop_merged_requests,
.elevator_dispatch_fn = noop_dispatch,
.elevator_add_req_fn = noop_add_request,
.elevator_queue_empty_fn = noop_queue_empty,
.elevator_former_req_fn = noop_former_request,
.elevator_latter_req_fn = noop_latter_request,
.elevator_init_fn = noop_init_queue,
.elevator_exit_fn = noop_exit_queue,
},
.elevator_name = "noop",
.elevator_owner = THIS_MODULE,
};

从初始化函数指针可以看出:

elevator_allow_merge_fn 没有初始化,为NULL,注意这个函数的意思不是说永远是否允许merge,而是当前这次请求是否允许。

elevator_merge_fn 函数指针没有初始化,那应该为NULL,说明没有单独的合并回调。

1 noop(实现简单的FIFO),

首先来看下调用链:

Returning to : 0xffffffff812cd03b : blk_queue_bio+0x8b/0x3a0 [kernel]
0xffffffff812c8452 : generic_make_request+0xe2/0x130 [kernel]
0xffffffff812c8511 : submit_bio+0x71/0x150 [kernel]

blk_queue_bio里面有两处尝试merge,一处是blk_attempt_plug_merge,一处是elv_merge,前者不需要自旋锁,后者需要,所以前者的消耗较小。

背景知识:

bio 代表一个IO 请求,可以准确描述os提交给block层的请求。

request 是bio 提交给IO调度器产生的数据,一个request 中放着顺序排列的bio

当设备提交bio 给IO调度器时,IO调度器可能会插入bio,或者生成新的request

request_queue代表着一个物理设备,顺序的放着request

写一个stap打点脚本如下:

probe begin {
print("Started monitoring noop\n")
}
probe kernel.function("blk_attempt_plug_merge").return {
printf("\tCurr: %s(%d), Parent: %s(%d), Cmdline: %s\n", execname(), pid(), pexecname(), ppid(), cmdline_str());
aaa = kernel_string(($q)->elevator->type->elevator_name);
if(aaa=="noop")
{
    printf("Paras: blk_attempt_plug_merge bio=(%u),(%s),return is (%d)\n",$bio,aaa, $return);
    print_backtrace();
}
}

probe kernel.function("elv_merge").return {
bbb = kernel_string(($q)->elevator->type->elevator_name);
if(bbb=="noop")
{
    printf("Paras: elv_merge bio=(%u),(%s),return is (%d)\n",$bio,bbb, $return);
    print_backtrace();
}
}

先看看 blk_attempt_plug_merge 函数中,是怎么处理的。

Curr: nginx(12008), Parent: nginx(24824), Cmdline: nginx: worker process "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
Paras: blk_attempt_plug_merge bio=(18446612139773339904),(noop),return is (1)---------return 为1,则不会进行elv_merge。

Returning from: 0xffffffff812cc590 : blk_attempt_plug_merge+0x0/0x100 [kernel]
Returning to : 0xffffffff812cd12e : blk_queue_bio+0x17e/0x3a0 [kernel]
0xffffffff812c8452 : generic_make_request+0xe2/0x130 [kernel]
0xffffffff812c8511 : submit_bio+0x71/0x150 [kernel]
0xffffffff81220790 : do_mpage_readpage+0x2e0/0x6e0 [kernel]
0xffffffff81220c7b : mpage_readpages+0xeb/0x160 [kernel]
0xffffffffa089ba7d [xfs]
0xffffffff8117691c : __do_page_cache_readahead+0x1cc/0x250 [kernel] (inexact)
0xffffffff81176b16 : ondemand_readahead+0x116/0x230 [kernel] (inexact)
0xffffffff81176f21 : page_cache_sync_readahead+0x31/0x50 [kernel] (inexact)
0xffffffff8120f226 : __generic_file_splice_read+0x556/0x5e0 [kernel] (inexact)
0xffffffff81589a23 : tcp_v4_md5_lookup+0x13/0x20 [kernel] (inexact)
0xffffffff8120dac0 : spd_release_page+0x0/0x20 [kernel] (inexact)
0xffffffff81577bb5 : tcp_sendpage+0xe5/0x5a0 [kernel] (inexact)
0xffffffff8120da00 : pipe_to_sendpage+0x0/0xa0 [kernel] (inexact)
0xffffffff815a326e : inet_sendpage+0x6e/0xe0 [kernel] (inexact)
0xffffffff81510e8b : kernel_sendpage+0x1b/0x30 [kernel] (inexact)
0xffffffff81510ec7 : sock_sendpage+0x27/0x30 [kernel] (inexact)
0xffffffff8120dab5 : page_cache_pipe_buf_release+0x15/0x20 [kernel] (inexact)
0xffffffff8120d9a1 : splice_from_pipe_feed+0xc1/0x120 [kernel] (inexact)
0xffffffff8120da00 : pipe_to_sendpage+0x0/0xa0 [kernel] (inexact)

另外一种情况是blk_attempt_plug_merge返回0,则会调用 elv_merge

Paras: blk_attempt_plug_merge bio=(18446612139773339136),(noop),return is (0)-------------可以对比下面的bio,两者相等
Returning from: 0xffffffff812cc590 : blk_attempt_plug_merge+0x0/0x100 [kernel]
Returning to : 0xffffffff812cd12e : blk_queue_bio+0x17e/0x3a0 [kernel]
0xffffffff812c8452 : generic_make_request+0xe2/0x130 [kernel]
0xffffffff812c8511 : submit_bio+0x71/0x150 [kernel]
0xffffffff81220cb4 : mpage_readpages+0x124/0x160 [kernel]
0xffffffffa089ba7d [xfs]
0xffffffff8117691c : __do_page_cache_readahead+0x1cc/0x250 [kernel] (inexact)
0xffffffff81176b16 : ondemand_readahead+0x116/0x230 [kernel] (inexact)
0xffffffff81176f21 : page_cache_sync_readahead+0x31/0x50 [kernel] (inexact)
0xffffffff8120f226 : __generic_file_splice_read+0x556/0x5e0 [kernel] (inexact)
0xffffffff81589a23 : tcp_v4_md5_lookup+0x13/0x20 [kernel] (inexact)
0xffffffff8120dac0 : spd_release_page+0x0/0x20 [kernel] (inexact)
0xffffffff81577bb5 : tcp_sendpage+0xe5/0x5a0 [kernel] (inexact)
0xffffffff8120da00 : pipe_to_sendpage+0x0/0xa0 [kernel] (inexact)
0xffffffff815a326e : inet_sendpage+0x6e/0xe0 [kernel] (inexact)
0xffffffff81510e8b : kernel_sendpage+0x1b/0x30 [kernel] (inexact)
0xffffffff81510ec7 : sock_sendpage+0x27/0x30 [kernel] (inexact)
0xffffffff8120dab5 : page_cache_pipe_buf_release+0x15/0x20 [kernel] (inexact)
0xffffffff8120d9a1 : splice_from_pipe_feed+0xc1/0x120 [kernel] (inexact)
0xffffffff8120da00 : pipe_to_sendpage+0x0/0xa0 [kernel] (inexact)
0xffffffff8120f2ee : generic_file_splice_read+0x3e/0x80 [kernel] (inexact)
Paras: elv_merge bio=(18446612139773339136),(noop),return is (0)-------------------可以对比上面的bio,两者相等
Returning from: 0xffffffff812c4b50 : elv_merge+0x0/0xe0 [kernel]
Returning to : 0xffffffff812cd03b : blk_queue_bio+0x8b/0x3a0 [kernel]

综上所述,在noop的调度策略中,io还是正常合并的,虽然叫先来先服务,但是合并还是正常进行,除非一种情况,那就是采用none调度策略。

cat /sys/block/nvme0n1/queue/scheduler

none

Nvme调度策略就是none。

noop还是会尝试在block层合并,但是none的意思是不是说就不合并了呢?也不是,我们查看nvme的驱动,发现nvme的queue有一个合并的flag,当开启的时候,也会尝试合并,但是,nvme真的足够快,查看iostat统计发现,没看到过合并的记录打印。

io调度策略noop的理解的更多相关文章

  1. linux io的cfq代码理解

    内核版本: 3.10内核. CFQ,即Completely Fair Queueing绝对公平调度器,原理是基于时间片的角度去保证公平,其实如果一台设备既有单队列,又有多队列,既有快速的NVME,又有 ...

  2. java基础之IO流及递归理解

    一.IO流(简单理解是input/output流,数据流内存到磁盘或者从磁盘到内存等) 二.File类(就是操作文件和文件夹的) 1.FIleFile类构造方法 注意:通过构造方法创建的file对象是 ...

  3. 对于IO流的个人理解

    Samuel 2018-04-21 在这之前,我给你们构造这样一个生活用水的场景: 人们日常生活需要生活用水,那么,水从哪里来呢? 大家都学过初中的物理常识,水在地表,通过蒸发,变成水蒸气去到空中,在 ...

  4. Java的IO操作,个人理解。

    先看一段代码: import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import ...

  5. 关于Blocking IO,non-Blokcing IO,async IO的区别和理解

    来源:http://shmilyaw-hotmail-com.iteye.com/blog/1896683 概括来说,一个IO操作可以分为两个部分:发出请求.结果完成.如果从发出请求到结果返回,一直B ...

  6. IO口输入输出模式理解

    1.IO输入输出模式 2.有上拉,下拉,弱上拉,推挽,开漏输出:不同的单片机有不同的输出模式 3.以最简单的51单片机为例 P0:开漏型双向IO口,通常需要添加外部上拉电阻 P1~P3:准双向IO口, ...

  7. 【代码笔记】Java文件的输入输出(1)——Java.io包的初步理解

    Java里面文件的输入输出全部在java.io包里面. Java.io包里面所有的类都需要掌握. java.io包里面所有的东西都在上面了. 包里面的相关类.异常等树关系如下 类分层结构 java.l ...

  8. IO多路复用?我所理解的IO模式

    1:IO的过程 当我们调用系统函数read时,一般会经历两个阶段: 1:等待数据准备(waiting for the data be ready) 2:将数组从内核拷贝到进程(从内核态到用户态)(co ...

  9. FPGA之IO信号类型深入理解

    在FPGA设计开发中,很多场合会遇到同一根信号既可以是输入信号,又可以是输出信号,即IO类型(Verilog定义成inout). 对于inout型的信号,我们既可以使用FPGA原语来实现,也可以使用V ...

随机推荐

  1. spring boot RESTFul API拦截 以及Filter和interceptor 、Aspect区别

    今天学习一下RESTFul api拦截 大概有三种方式 一.通过Filter这个大家很熟悉了吧,这是java规范的一个过滤器,他会拦截请求.在springboot中一般有两种配置方式. 这种过滤器拦截 ...

  2. JQuery 网页瞄点

    $("html,body").animate({ scrollTop: $("#Content1").offset().top }, 3000); 代码说明:h ...

  3. 解决linux 乌班图下使用eclipse创建类和其他各种操作进程卡死的问题的一种可能方法

    [转载]http://blog.csdn.net/u010652906/article/details/51626257Eclipse设置面板菜单可以点,左边面板不出现,创建类.创建包都会卡死的问题, ...

  4. Java 中的函数式编程(Functional Programming):Lambda 初识

    Java 8 发布带来的一个主要特性就是对函数式编程的支持. 而 Lambda 表达式就是一个新的并且很重要的一个概念. 它提供了一个简单并且很简洁的编码方式. 首先从几个简单的 Lambda 表达式 ...

  5. Django-mtv开发模式

    从著名的MVC模式开始说起 所谓的MVC就是把Web应用分为模型(M)控制器(C)和视图(V)三层,他们之间以一种插件式的.松耦合的房还是 连接在一起,模型负责业务对象与数据库的映射(ORM),视图负 ...

  6. JavaScrip:Function函数编程

    自定义函数定义 1.函数通过function关键字创建,函数创建格式: function 函数名称([参数,...]){ 代码段; return 返回值; } 注意事项: 函数名称不要包含特殊字符 函 ...

  7. nodejs+express+mysql实现restful风格的增删改查示例

    首先,放上项目github地址:https://github.com/codethereforam/express-mysql-demo 一.前言 之前学的java,一直用的ssm框架写后台.前段时间 ...

  8. Android Studio | 详细安装教程

    Windows和Mac系统下的安装教程差不多,需要注意的是确保系统中已经安装了JDK,并且JDK版本为1.7或1.7以上版本,如果没有,请自行更新下载安装,地址如下: Java SE Develop ...

  9. d3 画地图终极自适应大小方案

    d3,v4以前用d3画地图的时候,为了让地图差不多正好画在容器的上下左右正中间,不得不慢慢的这样调: const projection = d3.geoMercator() .center([108. ...

  10. django[post与get测试]

    首先先看一下代码:↓ 后台: 前端展示: 测试结果: