详细介绍Linux IO

1.IO概述

分析一下写操作:

char *buf = malloc(MAX_BUF_SIZE);

strncpy(buf, src, , MAX_BUF_SIZE);

fwrite(buf, MAX_BUF_SIZE, 1, fp);

fclose(fp);

以下图为例:分析数据流写入硬盘的过程

malloc的buf对于图层中的application buffer,即应用程序的buffer;

调用fwrite后,把数据从application buffer 拷贝到了 CLib buffer,即C库标准IObuffer。

fwrite返回后,数据还在CLib buffer,如果这时候进程core掉。这些数据会丢失。没有写到磁盘介质上。当调用fclose的时候,fclose调用会把这些数据刷新到磁盘介质上。

除了fclose方法外,还有一个主动刷新操作fflush函数,不过fflush函数只是把数据从CLib buffer 拷贝到page cache 中,并没有刷新到磁盘上,从page cache刷新到磁盘上可以通过调用fsync函数完成。

fwrite是系统提供的最上层接口,也是最常用的接口。它在用户进程空间开辟一个buffer,将多次小数据量相邻写操作先缓存起来,合并,最终调用write函数一次性写入(或者将大块数据分解多次write调用)。

Write函数通过调用系统调用接口,将数据从应用层copy到内核层,所以write会触发内核态/用户态切换。当数据到达page cache后,内核并不会立即把数据往下传递。而是返回用户空间。数据什么时候写入硬盘,有内核IO调度决定,所以write是一个异步调用。这一点和read不同,read调用是先检查page cache里面是否有数据,如果有,就取出来返回用户,如果没有,就同步传递下去并等待有数据,再返回用户,所以read是一个同步过程。当然你也可以把write的异步过程改成同步过程,就是在open文件的时候带上O_SYNC标记。

数据到了page cache后,内核有pdflush线程在不停的检测脏页,判断是否要写回到磁盘中。把需要写回的页提交到IO队列——即IO调度队列。IO调度队列调度策略调度何时写回。

IO队列有2个主要任务。一是合并相邻扇区的,而是排序。合并相信很容易理解,排序就是尽量按照磁盘选择方向和磁头前进方向排序。因为磁头寻道时间是和昂贵的。

这里IO队列和我们常用的分析工具IOStat关系密切。IOStat中rrqm/s wrqm/s表示读写合并个数。avgqu-sz表示平均队列长度。

内核中有多种IO调度算法。当硬盘是SSD时候,没有什么磁道磁头,人家是随机读写的,加上这些调度算法反而画蛇添足。OK,刚好有个调度算法叫noop调度算法,就是什么都不错(合并是做了)。刚好可以用来配置SSD硬盘的系统。

从IO队列出来后,就到了驱动层(当然内核中有更多的细分层,这里忽略掉),驱动层通过DMA,将数据写入磁盘cache。

至于磁盘cache时候写入磁盘介质,那是磁盘控制器自己的事情。如果想要睡个安慰觉,确认要写到磁盘介质上。就调用fsync函数吧。可以确定写到磁盘上了。

2.linux IO子系统和文件系统读写流程

I/O子系统是个层次很深的系统,数据请求从用户空间最终到达磁盘,经过了复杂的数据流动。

read系统调用的处理分为用户空间和内核空间处理两部分。其中,用户空间处理只是通过0x80中断陷入内核,接着调用其中断服务例程,即sys_read以进入内核处理流程。

对于read系统调用在内核的处理,如上图所述,经过了VFS、具体文件系统,如ext2、页高速缓冲存层、通用块层、IO调度层、设备驱动层、和设备层。其中,VFS主要是用来屏蔽下层具体文件系统操作的差异,对上提供一个统一接口,正是因为有了这个层次,所以可以把设备抽象成文件。具体文件系统,则定义了自己的块大小、操作集合等。引入cache层的目的,是为了提高IO效率。它缓存了磁盘上的部分数据,当请求到达时,如果在cache中存在该数据且是最新的,则直接将其传递给用户程序,免除了对底层磁盘的操作。通用块层的主要工作是,接收上层发出的磁盘请求,并最终发出IO请求(BIO)。IO调度层则试图根据设置好的调度算法对通用块层的bio请求合并和排序,回调驱动层提供的请求处理函数,以处理具体的IO请求。驱动层的驱动程序对应具体的物理设备,它从上层取出IO请求,并根据该IO请求中指定的信息,通过向具体块设备的设备控制器发送命令的方式,来操纵设备传输数据。设备层都是具体的物理设备。

VFS层

内核函数sys_read是read系统调用在该层的入口点。它根据文件fd指定的索引,从当前进程描述符中取出相应的file对象,并调用vfs_read执行文件读取操作。vfs_read会调用与具体文件相关的read函数执行读取操作,file->f_op.read。然后,VFS将控制权交给了ext2文件系统。(ext2在此作为示例,进行解析)

Ext2文件系统层

通过ext2_file_operations结构知道,上述函数最终会调用到do_sync_read函数,它是系统通用的读取函数。所以说,do_sync_read才是ext2层的真实入口。该层入口函数 do_sync_read 调用函数 generic_file_aio_read ,后者判断本次读请求的访问方式,如果是直接 io (filp->f_flags 被设置了 O_DIRECT 标志,即不经过 cache)的方式,则调用 generic_file_direct_IO 函数;如果是 page cache 的方式,则调用 do_generic_file_read 函数。它会判断该页是否在页高速缓存,如果是,直接将数据拷贝到用户空间。如果不在,则调用page_cache_sync_readahead函数执行预读(检查是否可以预读),它会调用mpage_readpages。如果仍然未能命中(可能不允许预读或者其它原因),则直接跳转readpage,执行mpage_readpage,从磁盘读取数据。在mpage_readpages(一次读多个页)中,它会将连续的磁盘块放入同一个BIO,并延缓BIO的提交,直到出现不连续的块,则直接提交BIO,再继续处理,以构造另外的BIO。

page cache 结构

图5显示了一个文件的 page cache 结构。文件被分割为一个个以 page 大小为单元的数据块,这些数据块(页)被组织成一个多叉树(称为 radix 树)。树中所有叶子节点为一个个页帧结构(struct page),表示了用于缓存该文件的每一个页。在叶子层最左端的第一个页保存着该文件的前4096个字节(如果页的大小为4096字节),接下来的页保存着文件第二个4096个字节,依次类推。树中的所有中间节点为组织节点,指示某一地址上的数据所在的页。此树的层次可以从0层到6层,所支持的文件大小从0字节到16 T 个字节。树的根节点指针可以从和文件相关的 address_space 对象(该对象保存在和文件关联的 inode 对象中)中取得(更多关于 page cache 的结构内容请参见参考资料)。

mpage处理机制就是page cache层要处理的问题。

通用块层

在缓存层处理末尾,执行mpage_submit_bio之后,会调用generic_make_request函数。这是通用块层的入口函数。它将bio传送到IO调度层进行处理。

IO调度层

对bio进行合并、排序,以提高IO效率。然后,调用设备驱动层的回调函数,request_fn,转到设备驱动层处理。

设备驱动层

request函数对请求队列中每个bio进行分别处理,根据bio中的信息向磁盘控制器发送命令。处理完成后,调用完成函数end_bio以通知上层完成。

3.IO之流程与buffer概览

一般情况下,进程在io的时候,要依赖于内核中的一个buffer模块来和外存发生数据交换行为。另一个角度来说,数据从应用进程自己的buffer流动到外存,中间要先拷贝到内核的buffer中,然后再由内核决定什么时候把这些载有数据的内核buffer写出到外存。

“buffer cache”仅仅被内核用于常规文件(磁盘文件)的I/O操作。

内核中的buffer模块-“buffer cache”(buffer,cache的功能兼备)

一般情况下,read,write系统调用并不直接访问磁盘。这两个系统调用仅仅是在用户空间和内核空间的buffer之间传递目标数据。举个例子,下面的write系统调用仅仅是把3个字节从用户空间拷贝到内核空间的buffer之后就直接返回了

write(fd,”abc”,3);

在以后的某个时间点上,内核把装着“abc”三个字节的buffer写入(flush)磁盘。如果另外的进程在这个过程中想要读刚才被打开写的那个文件怎么办?答案是:内核会从刚才的buffer提供要读取的数据,而不是从磁盘读。

当前系统上第一次读一个文件时,read系统调用触发内核以blocrk为单位从磁盘读取文件数据,并把数据blocks存入内核buffer,然后read不断地从这个buffer取需要的数据,直到buffer中的数据全部被读完,接下来,内核从磁盘按顺序把当前文件后面的blocks再读入内核buffer,然后read重复之前的动作…

一般的文件访问,都是这种不断的顺序读取的行为,为了加速应用程序读磁盘,unix的设计者们为这种普遍的顺序读取行为,设计了这样的机制—-预读,来保证进程在想读后续数据的时候,这些后续数据已经的由内核预先从磁盘读好并且放在buffer里了。这么做的原因是磁盘的io访问比内存的io访问要慢很多,指数级的差别。

read,write从语义和概念上来说,本来是必须要直接和磁盘交互的,调用时间非常长,应用每次在使用这两个系统的时候,从表象上来说都是被卡住。而有了这些buffer,这些系统调用就直接和buffer交互就可以了,大幅的加速了应用执行。

Linux内核并没有规定”buffer cache”的尺寸上线,原则上来说,除了系统正常运行所必需和用户进程自身所必需的之外的内存都可以被”buffer cache”使用。而系统和用户进程需要申请更多的内存的时候,”buffer cache”的内存释放行为会被触发,一些长久未被读取,以及被写过的脏页就会被释放和写入磁盘,腾出内存,以便被需要的行为方使用。

”buffer cache”有五个flush的触发点:

1.pdflush(内核线程)定期flush;

2.系统和其他进程需要内存的时候触发它flush;

3.用户手工sync,外部命令触发它flush;

4.proc内核接口触发flush,”echo 3 >/proc/sys/vm/drop_caches;

5.应用程序内部控制flush。

这个”buffer cache”从概念上的理解就是这些了,实际上,更准确的说,linux从2.4开始就不再维护独立的”buffer cache”模块了,而是把它的功能并入了”page cache”这个内存管理的子系统了,”buffer cache”现在已经是一个unix系统族的普遍的历史概念了

高性能写文件

写100MB的数据

场景1,1次写1个字节,总共write 100M次;

场景2,1次写1K个字节,总共write 100K次;

场景3,1次写4K个字节,总共write 25K次;

场景4,1次写16k个字节,总共write大约不到7K次。

以上4种写入方式,内核写磁盘的次数基本相同,因为写磁盘的单位是block,而不是字节。现在的系统默认的block都是4k。

第1种性能非常差,user time和system time执行时间都很长,既然写盘次数都差不多,那他慢在哪儿呢?答案是系统调用的次数太多

第2种,user time和system time都显著降低,不过system time降低幅度更大

第2种以后,性能差别就不是很高了,第3种和第4种性能几乎一样

有兴趣的朋友可以试一试,如果你的服务器很好,可以适当放大测试样本。

总而言之,得出的结论是以block的尺寸为write(fd, sizeof(buf),buf)的调用单位就可以了,再大对性能也没什么太大的提高。

题外话:一个衡量涉及IO程序的好坏的粗略标准是“程序运行应该尽量集中在user time,避免大量的system time”以及“IO的时候肯定是需要一些应用层buf的,比如上述4个场景,匹配就可以了(比如场景3,场景1和场景2会导致系统调用次数太多,场景4使用的buf尺寸过于浪费)”

每个系统调用在返回的时候,会有一个从内核态向用户态切换的间隙,每次在这个间隙里面,系统要干两个事情—-递送信号和进程调度,其中进程调度会重新计算全部RUN状态进程的优先级。

系统调用太多的话,递送信号和进程调度引起的计算量是不容忽视的。

精确地flush “buffer cache”

在很多业务场景下,我们仅仅调用write()把需要写盘的数据推送至内核的”buffer cache”中,这是很不负责任的。或许我们应该不断地频繁地把”buffer cache”中的数据强制flush到磁盘,尽最大可能保证我们的业务数据尽量不因断电而丢失。

天下没有免费的午餐,既想要效率(写入内核buffer),又想要安全性(数据必须flush到外存介质中才安全),这似乎是很矛盾的。SUSv3(Single UNIX Specification Version 3)给了这种需求一个折中的解决方案,让OS尽量满足我们的苛刻的要求。介绍这个折中方案之前,有两个SUSv3提案的规范很重要,说明如下:

1.数据完整性同步(synchronized I/O data integrity)

一个常规文件所包含的信息有两种:文件元数据和文件内容数据。

文件元数据包括:文件所属用户、组、访问权限,文件尺寸,文件硬连接数目,最后访问时间戳,最后修改时间戳,最后文件元数据修改时间戳,文件数据块指针。

对于文件内容数据,大家应该都很清楚是什么东西。

对于写操作,这个规范规定了,写文件时保证文件内容数据和必要的文件元数据保持完整性即可。粗糙地举个例子来解释这个规范,某次flush内核中的数据到磁盘的时候,仅仅把文件内容数据写入磁盘即可,但是如果这次写文件导致了文件尺寸的变化,那么这个文件尺寸作为文件的元数据也需要被写入磁盘,必要信息保持同步。而其他的文件元数据,例如修改时间,访问时间一概略去,不需要同步。

2.文件完整性同步(synchronized I/O file integrity)

相对于数据完整性同步而言,这个规范规定了,所有内容数据以及元数据都要同步。

下面来介绍linux提供的几种flush内核缓冲数据的几种方案,相信看完之后,大家应该知道上述提及的折中方案是怎样的:)

1.int fsync(int fd);

文件完整性同步;

2.int fdatasync(int fd);

数据完整性同步。

fdatasync相对于fsync的意义在于,fdatasync大致仅需要一次磁盘操作,而fsync需要两次磁盘操作。举例说明一下,假如文件内容改变了,但是文件尺寸并没有发生变化,那调用fdatasync仅仅是把文件内容数据flush到磁盘,而fsync不仅仅把文件内容flush刷入磁盘,还要把文件的last modified time也同步到磁盘文件系统。last modified time属于文件的元数据,一般情况下文件的元数据和文件内容数据在磁盘上不是连续存放的,写完内容数据再写元数据,必然涉及到磁盘的seek,而seek又是机械硬盘速度慢的根源。。。

在某些业务场景下,fdatasync和fsync的这点微小差别会导致应用程序性能的大幅差异。

3.sync_file_range()

这个接口是linux从2.6.17之后实现的,是linux独有的非标准接口。这个接口提供了比fdatasync更为精准的flush数据的能力。详细请参照man。

4.void sync(void);

强制”buffer cache”中的数据全部flush到磁盘,并且要遵循文件完整性同步。

上面4种方式介绍完毕,open()系统调用的打开文件的标志位,比如O_DSYNC诸如此类的标志,对flush数据的影响和上面几个接口作用类似。

预读

上面介绍了写buffer以及如何控制buffer的flush,下面来讲一讲如何控制读cache的行为。

读cache这一块,基本上,我们可以控制的就是文件的预读。

我们从POSIX规定的一个接口来论述一下如何控制文件的预读以及控制它的意义。接口原型如下:

int posix_fadvise(int fd, off_t offset, off_t len, int advice);

fd:打开文件的描述符其实;

offset和len:指明文件区域;

advice:预读的方式。预读方式及其意义如下:

POSIX_FADV_NORMAL:内核默认的预读方式;

POSIX_FADV_RANDOM:内核禁用预读。适合随机读文件的业务,每次按业务要求的量读取数据,不多读;

POSIX_FADV_SEQUENTIALP:内核把默认的预读量(POSIX_FADV_NORMAL)扩大一倍;

POSIX_FADV_WILLNEED:读取出来的内容会被应用程序多次访问(就是应用程序会不断的调用read()对这些内容不断的读);

POSIX_FADV_NOREUSE:读取出来的内容只会被应用程序访问一次,访问一次之后就清理掉并且释放内存。cache服务器,比如memcache或者redis启动时,把文件内容加载到应用层cache,就是这个参数存在的典型场景;

POSIX_FADV_DONTNEED:应用程序后续不打算访问指定范围中的文件内容,内核从”page cache(buffer cache)”中删除指定范围的文件内容,释放内存。

对于POSIX_FADV_WILLNEED这种方式,linux自己有一个特定接口,原型如下:

ssize_t readahead(int fd, off64_t offset, size_t count);

linux的”buffer cache”默认预读128k。

实际上,OS全局控制”buffer cache”的操作接口不仅仅是上面提及的几种,/proc/sys/vm/目录下还有几个参数可以从其他一些方面来控制”buffer cache”的行为,这部分内容在之后我整理笔记之后会介绍。

IO之标准C库buffer

在论述这个主题之前,先介绍一下标准C库和linux系统调用以及windows API之间的关系。

拿写文件来举个例子

linux下写文件用write()

windows下写文件用WriteFile()

这说明不同操作系统实现同样的系统功能的接口应该是不一样的。造成这种现状是操作系统发展的历史原因造成的,无法在操作系统的层面统一系统函数接口。同样功能的程序在linux上写一套,windows上又得写另外一套,毫无移植性可言。如果要开发一个既能在linux跑,又能在windows上跑的程序,开发成本飙升!

为了解决这个移植性的问题,标准C库利用了封装技术,扮演了一个重要的角色,统一了部分基本功能接口。

标准C规定的写文件的函数是fwrite(),就是不管在linux还是在windows上,各自都有一个标准C库,库函数封装的下层细节不一样,但是接口完全一样,提供的功能完全一样。

这是怎么做到的?猜一猜大致实现就知道了

在linux上,标准C接口fwrite()的实现伪代码

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){ ... ... return write(stream->fd,buffer,count);}

在windows上,标准C接口fwrite()的实现伪代码

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){#define OUT BOOL ret = false; OUT int optnum; ... ... ret = WriteFile(stream->filehandle, buffer, count, &;optnum,...); if( ret == true) return optnum; else return -1;}

内部实现不一致,没关系,接口一样就可以,不管在linux还是windows上,写文件都用fwrite(),分别在各自平台上编译就可以了。

标准C就是这样一个处于系统层面之上的应用层标准函数库,为了统一各个操作系统上的函数接口而生。

回到我们的主题—-IO之应用层buffer

什么是应用层buffer?

回想一下我之前介绍的《IO之内核buffer”buffer cache”》,既然write()能把需要写文件的数据推送到一个内核buffer来偷工减料欺骗应用层(为了加速I/O),说“我已经写完文件并返回了”。那应用层的标准C库的fwrite()按道理也可以为了加速,在真正调用write()之前,把数据放到(FILE*)stream->buffer中,等到多次调用fwrite(),直至(FILE*)stream->buffer中积攒的数据量达到(FILE*)stream->bufferlen这么多的时候,一次性的把这些数据全部送入write()接口,写入内核,这是多么美妙啊。。。

实际上,标准C库就是这么做的!

把fwrite()的linux实现再细致一下

过程其实仍然很粗糙,为了突出buffer的重点,计算stream->buffer是否满,拷贝多少,填充多少这样的细节和主题无关的东西我略去了

size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream){
…

if( stream->buffer满 ){
write(stream->fd,stream->buffer,stream->bufferlen);

} else{
拷贝buffer内容至stream->buffer

}

…

return count;

//过程很粗糙,为了突出buffer的重点,计算stream->buffer是否满,拷贝多少,填充多少这样的细节和主题无关的东西我略去了

}

fwrite()在windows平台的实现也基本上是这样的,也有buffer。

值得一说的是,fread()也有一个读cache来完成预读。

setvbuf()和setbuf()都是控制这个标准C库的buffer的。

还有fflush()是C库用于flush数据的函数。

以上三个函数,如果大家有兴趣,可以去看看linux上对应的man文档。

重点是要知道不仅系统的内核有buffer,应用层的C库同样也有buffer。这些buffer的唯一作用就是为了加速应用,不让应用老是卡在和磁盘交互上。

说个题外话,实际上对于磁盘、RAID卡、盘阵这样的外存介质而言,他们各自在硬件上也都有一层前端的buffer,有时也叫cache,用来缓冲读写加速。cache越多,价格越贵,性能越好。大型存储设备一般拥有多层cache,用的是昂贵的SSD。 
需要分享的一点经验是,不管是标准C库的buffer也好,内核的”buffer cache”也罢,我们终究对它们的控制力度是有限的。我们在做服务器程序的时候,如果业务上涉及太大的I/O量,需要做服务整体加速的时候,我们一般自己在业务层做一层自己的”buffer”,把业务数据buffer住,攒成以文件系统或者磁盘的block块单位的大块数据,然后集中写,然后集中写又有集中写的策略。。。 
再引申一点内容,做高性能大流量的大站的架构,其中最重要几个架构角色之一就是cache。前端CDN、后端memcache、redis、mysql内部cache等等,都是cache的应用场景,可以说”buffer cache”在服务器领域从软件实现到硬件加速再到架构,真的是无处不在。

4.IO队列和IO调度

IO调度和IO队列

1.向块设备写入数据块或是从块设备读出数据块时,IO请求要先进入IO队列,等待调度。

2.这个IO队列和调度的目标是针对某个块设备而言的,换句话说就是每个块设备都有一个独立的IO队列。 

3.本篇所涉及的所谓的块设备就是iostat命令里面列出的形如sda,sdb这样的块设备,并不是指物理磁盘。假如一个盘被分成5个分区,那么在这个主题下,5个分区代表5个块设备,每个块设备都有自己独立的IO队列。 

4.I/O 调度程序维护这些队列,以便更有效地利用外存设备。简单来说,IO调度程序将无序的IO操作变为大致有序的IO请求。比如调度的时候调整几个IO请求的顺序,合并那些写盘区域相邻的请求,或者按照写磁盘的位置排序这些请求,以降低磁头在磁盘上来回seek的操作,继而加速IO。 

5.每个队列的每一次调度都会把整个队列过一遍,类似于进程调度的时候每次调度都要计算RUN队列的全部进程的优先级。

 IO队列深度

这个参数是iostat里面呈现的,字面意思显而易见,就是IO队列的深度,这个参数有何意义呢? 
针对每个机械物理盘,如果这个盘对应的IO队列深度超过3,那么基本上表示这个盘处理IO硬件请求有点吃紧,这个盘对应的IO队列深度怎么算呢? 
还拿上面一个盘被切成5个分区说事儿,5个分区对应5个块设备,5个块设备对应5个IO队列,这5个IO队列的深度总和就是这个机械物理盘的IO队列深度了。 
如何解决这个盘的IO请求吃紧呢,最简单的办法硬件加速,把这个盘换成SSD盘:) 
说到这儿,我想提一提RAID卡。咱们使用RAID卡把几个硬盘放在一起,让系统只能看见一个块设备。这个时候,假如有4个盘被放在RAID后面。那么这个RAID卡对应的块设备的IO队列深度允许超过12(4个磁盘,每个盘承受深度为3)。 
SSD盘可承受的IO队列深度值很大,这个多少深度合适,我没有注意具体观察过。

 iostat另一个参数—-“%util”

实际生产系统上,我观察IO设备是否吃紧,其实是看这个util的。这个值长期高于60,咱们就得考虑物理磁盘IO吃不消了。 
如果是使用机械硬盘的服务器上这个值达到90以上,最简单的解决方案仍然是换SSD盘,换完之后这个值会下降到20左右,非常有效。

 IO调度算法

IO调度算法存在的意义有两个:一是提高IO吞吐量,二是降低IO响应时间。然而IO吞吐量和IO响应时间往往是矛盾的,为了尽量平衡这两者,IO调度器提供了多种调度算法来适应不同的IO请求场景。 
以下几个算法介绍是网上抄来的,说的很详细,作者水平很高:) 

1、NOOP 

该算法实现了最简单的FIFO队列,所有IO请求大致按照先来后到的顺序进行操作。之所以说”大致”,原因是NOOP在FIFO的基础上还做了相邻IO请求的合并,并不是完完全全按照先进先出的规则满足IO请求。 
假设有如下的io请求序列: 
100,500,101,10,56,1000 
NOOP将会按照如下顺序满足: 
100(101),500,10,56,1000

2、CFQ 

CFQ算法的全写为Completely Fair Queuing。该算法的特点是按照IO请求的地址进行排序,而不是按照先来后到的顺序来进行响应。 
假设有如下的io请求序列: 
100,500,101,10,56,1000 
CFQ将会按照如下顺序满足: 
100,101,500,1000,10,56 
在传统的SAS盘上,磁盘寻道花去了绝大多数的IO响应时间。CFQ的出发点是对IO地址进行排序,以尽量少的磁盘旋转次数来满足尽可能多的IO请求。在CFQ算法下,SAS盘的吞吐量大大提高了。但是相比于NOOP的缺点是,先来的IO请求并不一定能被满足,可能会出现饿死的情况。

3、DEADLINE 

DEADLINE在CFQ的基础上,解决了IO请求饿死的极端情况。除了CFQ本身具有的IO排序队列之外,DEADLINE额外分别为读IO和写IO提供了FIFO队列。读FIFO队列的最大等待时间为500ms,写FIFO队列的最大等待时间为5s。FIFO队列内的IO请求优先级要比CFQ队列中的高,而读FIFO队列的优先级又比写FIFO队列的优先级高。优先级可以表示如下: 
FIFO(Read) > FIFO(Write) > CFQ 
这个算法特别适合数据库这种随机读写的场景。

4、ANTICIPATORY 

CFQ和DEADLINE考虑的焦点在于满足离散IO请求上。对于连续的IO请求,比如顺序读,并没有做优化。为了满足随机IO和顺序IO混合的场景,Linux还支持ANTICIPATORY调度算法。ANTICIPATORY的在DEADLINE的基础上,为每个读IO都设置了6ms的等待时间窗口。如果在这6ms内OS收到了相邻位置的读IO请求,就可以立即满足。

IO调度器算法的选择,既取决于硬件特征,也取决于应用场景。 
在传统的SAS盘上,CFQ、DEADLINE、ANTICIPATORY都是不错的选择;对于专属的数据库服务器,DEADLINE的吞吐量和响应时间都表现良好。然而在新兴的固态硬盘比如SSD、Fusion IO上,最简单的NOOP反而可能是最好的算法,因为其他三个算法的优化是基于缩短寻道时间的,而固态硬盘没有所谓的寻道时间且IO响应时间非常短。

 IO调度算法的查看和设置

查看和修改IO调度器的算法非常简单。假设我们要对sda进行操作,如下所示: 
cat /sys/block/sda/queue/scheduler 
echo ‘cfq' >/sys/block/sda/queue/scheduler 
还有持久化设置,不一一列举了。

以上就是Linux IO介绍的详细内容,更多关于Linux IO的资料请关注我们其它相关文章!

(0)

相关推荐

  • 如何使用iostat查看linux硬盘IO性能

    TOP 观察:IO等待所占用的CPU时间的百分比,高过30%时IO压力高其次.用iostat -x 1 10 [root@controller ~]#iostat -d -k 1 10 Device: tps kB_read/s kB_wrtn/s kB_read kB_wrtn sda 19.00 0.00 112.00 0 112 sda1 0.00 0.00 0.00 0 0 sda2 0.00 0.00 0.00 0 0 sda3 0.00 0.00 0.00 0 0 sda4 0.00

  • Linux IO多路复用之epoll网络编程

    前言 本章节是用基本的Linux基本函数加上epoll调用编写一个完整的服务器和客户端例子,可在Linux上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输出到客户端 客户端收到服务端的响应,输出这一行到标准输出 服务端 代码如下: #include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h&

  • Linux 查看磁盘IO并找出占用IO读写很高的进程

    背景-线上告警 线上一台服务器告警,磁盘利用率 disk.util > 90,并持续告警. 登录该服务器后通过iostat -x 1 10查看了相关磁盘使用信息.相关截图如下: # 如果没有 iostat 命令,那么使用 yum install sysstat 进行安装 # iostat -x 1 10 由上图可知,vdb磁盘的 %util[IO]几乎都在100%,原因是频繁的读取数据造成的. 其他字段说明 Device:设备名称 tps:每秒的IO读.写请求数量,多个逻辑请求可以组合成对设备的

  • Linux IO的水平触发和边缘触发的区别

    Linux IO的水平触发和边缘触发的区别 在linux的IO多路复用中有水平触发,边缘触发两种模式,这两种模式的区别如下: 水平触发:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知.允许在任意时刻重复检测IO的状态.select,poll就属于水平触发. 边缘触发:如果文件描述符自上次状态改变后有新的IO活动到来,此时会触发通知.在收到一个IO事件通知后要尽可能多的执行IO操作,因为如果在一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取到就绪的描述符.信号

  • 详解Linux命令iostat

    Linux系统出现了性能问题,一般我们可以通过top.iostat.free.vmstat等命令来查看初步定位问题.在一个以前看到系统监控工具,总在想那些监控工具的代理,如何收集系统性能信息,io性能,cpu使用,带宽使用等信息,偶然发现,不同系统均提供有性能分析工具的,代理可通过这些命令获取系统性能信息,个人猜测,不知道具体是不是这样的.其中iostat可以给我们提供丰富的IO状态数据,下边就来看一下iostat如何使用,命令能够输出那些信息. 简述 Linux系统中通过iostat我们能查看

  • Linux的Socket IO模型趣解

    前言 之前有看到用很幽默的方式讲解Windows的socket IO模型,借用这个故事,讲解下linux的socket IO模型: 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系. 他们的信会被邮递员投递到他们小区门口的收发室里.这和Socket模型非常类似. 下面就以老陈接收信件为例讲解linux的 Socket I/O模型. 一.同步阻塞模型 老陈的女儿第一次去外地工作,送走她之后,老陈非常的挂心她安全到达没有: 于是老陈什么也不干,一直在小区门口收发室里等着她女儿的报平安的

  • Linux shell编程中IO和条件及循环处理的细节问题讨论

    > 與 < 差在哪? 谈到I/O redirection,不妨先让我们认识一下File Descriptor(FD).程序的运算,在大部份情况下都是进行数据(data)的处理,这些数据从哪读进?又,送出到哪里呢?这就是File descriptor(FD)的功用了. 在shell程序中,最常使用的FD大概有三个,分别为: 0: Standard Input(STDIN) 1: Standard Output(STDOUT) 2: Standard Error Output(STDERR) 在标

  • 详细介绍Linux IO

    1.IO概述 分析一下写操作: char *buf = malloc(MAX_BUF_SIZE); strncpy(buf, src, , MAX_BUF_SIZE); fwrite(buf, MAX_BUF_SIZE, 1, fp); fclose(fp); 以下图为例:分析数据流写入硬盘的过程 malloc的buf对于图层中的application buffer,即应用程序的buffer; 调用fwrite后,把数据从application buffer 拷贝到了 CLib buffer,即

  • 详细介绍Linux的定时任务crontab

    一.前言 crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中(/var/spool/cron/以用户命名的文件),以供之后读取和执行.该词来源于希腊语 chronos(χρνο),原意是时间. 通常,crontab储存的指令被守护进程 - crond激活在后台运行,每一分钟检查是否有预定的作业需要执行.这类作业一般称为cron jobs. 二.启动crond进程 service cr

  • 详细介绍linux动态网络和静态网络和克隆后的网络配置

    建议设置网卡NAT模式 动态网络配置: 1.一定要开启本地DHCP服务 2.在虚拟网络编辑器中选择NAT模式选中DHCP项如下图 3.ifup eth0 静态网络配置 : 注释:ifcfg-eth0部分参数说明 DEVICE=eth0#描述网卡对应的设备别名 HWADDR=00:0C:29:42:DD:51#对应的网卡物理地址 TYPE=Ethernet#网络类型(通常是Ethemet) UUID=514fda06-1fc6-4d19-8a74-74030ed83956 ONBOOT=yes#系

  • Linux shell脚本基础学习详细介绍(完整版)第1/2页

    Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头.注释.变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提. 1. Linux 脚本编写基础 ◆1.1 语法基本介绍1.1.1 开头程序必须以下面的行开始(必须方在文件的第一行):#!/bin/sh符号#!用来告诉系统它后面的参数是用来执行该文件的程序.在这个例子中我们使用/bin/sh来执行程序.当编辑好脚本时,如果要执行该脚本,还必须使其可执行.要使脚本可执行:编译

  • linux系统安装字体详细介绍

    linux系统安装字体详细介绍 前言: 使用linux做桌面系统有很多好处,比如内存可以节约不少啊.系统垃圾减少啊.软件自动启动完全受控制啊.病毒那些大大减少啊什么的,但是也有不少问题,比如一个问题就是很多windows上的字体就没有-- 这个也不怪linux,因为很多字体都是有版权的,linux发行版不敢随意打包进安装包啊,特别是中文经常用到的"微软雅黑",一看名字就知道,是微软制作出来的. 但是,发行版不能打包,我们可以偷偷的进行搬运安装啊 ,可以在网上下载其他人制作的字体文件来安

  • linux命令scp和sftp详细介绍

    linux命令scp和sftp详细介绍 1. 使用 scp 命令传输文件: 使用 scp 命令可以用来通过安全.加密的连接在不同主机之间传输文件. (1)把本地文件传输到远程主机: 一般语法:scp [本地文件] [用户名@远程主机IP地址:/目标文件夹] (2) 把远程文件传输到本地主机: 一般语法:scp [用户名@远程主机IP地址:/源文件] [本地目录] Note: scp后加 -r 选项可以传输文件夹. 2.使用sftp命令建立FTP会话: 使用sftp命令可以用来打开安全互动的.加密

  • CentOS Linux系统搭建Android开发环境详细介绍

    CentOS Linux系统搭建Android开发环境详细介绍 很多人都是在Windows下进行Android开发,但是对于Linux,Android开发环境方面的资料比较少,今天在网上找到了一位网友分享的在CentOS Linux系统中搭建Android开发环境的过程.下面就是其介绍的配置的详细步骤原文: 由于我最近每天使用的是CentOS 5.5,所以选择CentOS5.5作为我的开发环境. 主要包括以下步骤: 1.JDK安装 2.Eclipse安装 3.ADT安装 4.Android SD

  • Linux的路由表详细介绍

    Linux的路由表详细介绍 一 在Linux下执行route命令 [root@localhost backup]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.0.1 0.0.0.0 UG 100 0 0 enp0s3 192.168.0.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s3 192.168

  • Linux启动过程详细介绍

    Linux启动过程详细介绍 启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬盘信息.内存信息.时钟信息.PnP特性等等.在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了. 启动第二步--读取MBR 众所周知,硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大

  • linux防墙iptables详细介绍、配置方法与案例

    1.1 iptables防火墙简介 Netfilter/Iptables(以下简称Iptables)是unix/linux自带的一款优秀且开放源代码的安全自由的**基于包过滤的防火墙工具**,它的功能十分强大,使用非常灵活,可以对流入和流出服务器的数据包进行很精细的控制.特别是它可以在一台非常低的硬件配置下跑的非常好 Iptables是Linux2.4及2.6内核中集成的服务.其功能与安全性比其**ipfwadm,ipchains**强大的多,iptables主要工作在OSI七层的二.三.四层,

随机推荐