Linux–I/O介绍
I/O概述
首先分析一下写操作,具体代码和数据流图如下所示:
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库的标准I/O buffer;
fwrite返回后,数据依然还在CLib buffer,如果这个时候进程core掉,那么这些数据就会丢失,并没有写到磁盘上;当调用fclose的时候,会把这些数据刷新到磁盘介质;
除了fclose方法外,还有一个主动刷新操作fflush函数,但是fflush函数只是把数据从Clib buffer拷贝到page cache中,并没有刷新到磁盘上,从page cache刷新到磁盘上可以通过调用fsync函数完成。
fwrite是系统提供的最上层接口,也是最常用的接口。它在用户进程空间开辟一个buffer,将多次小数据量相邻写操作先缓存起来,合并后调用write函数一次性写入(或者将大数据块分解为多次write调用)write函数通过调用系统调用接口,将数据从应用层copy到内核层,所以write会触发内核态/用户态切换。
当数据到达page cache后,内核并不会立即将数据写回,而是返回用户空间。数据什么时候写入磁盘,由内核I/O调度决定,所以write是一个异步调用。这一点和read不同,read调用时先检查page cache里面是否有数据,如果有就取出来返回用户,如果没有就同步传递下去并等待数据读取上来,再返回给用户,所以read是一个同步过程,当然write也可以通过在open时设置标记O_SYNC改成同步过程。
数据到了page cache后,内核有pdflush线程在不停的检测脏页,判断是否要写会到磁盘中,把需要写回的页提交到I/O队列,即I/O调度队列,由I/O调度策略决定何时写回。
I/O队列有两个任务,一是合并相邻扇区,二是写回排序。合并是为了将相邻的写回合并,减少寻道的次数,而排序就是尽量按照磁盘选择方向和磁头前进方向排序,减少磁头寻时间。
从I/O队列出来后,就到驱动层,驱动层通过DMA将数据写入磁盘cache,而从磁盘cache写入磁盘介质就由磁盘控制器决定。