linux 高级文件IO
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 高级文件IO相关的知识,希望对你有一定的参考价值。
1、I/O 概念
首先明确概念:I/O 的意思是:输入 输出,任何流程也都是输入输出。任何流也是输入输出,如内存,文件,网络
注意在linux里面大部分设备都可以称为文件,所以这里叫做文件I/O.
1.1 分散 聚集 I/O
允许一次调用同时从多个缓冲区读取数据或者写入多个缓冲区。
**适用场景:**聚集多个不同的数据结构变量,完成每次的I/O事务。
和我们普通的读写文件程序不一样,分散聚集I/O称为向量 读写, 一次可以写入多个缓冲区,这个对于存入不同内存区域的缓冲区写入文件来说非常方便
函数:
writev
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/uio.h>
int main()
{
struct iovec iov[3];
ssize_t nr;
int fd,i;
char *buf[] ={
"钱波测试\\n",
"ab测试\\n",
"test\\n"
};
fd =open("/test.yml",O_WRONLY|O_CREAT|O_TRUNC);
if(fd==-1)
{
perror("open");
return 1;
}
for(int i =0;i<3;i++)
{
iov[i].iov_base = buf[i];
iov[i].iov_len = strlen(buf[i])+1;
}
nr = writev(fd,iov,3);
if(nr == -1)
{
return -1;
}
printf("write over\\n");
if(close(fd))
{
perror("close");
return 1;
}
return 0;
}
使用windows的wsl 进入ubuntu系统,
gcc test.c -o out
生成out程序,执行时因为写文件需要切换到root下
1.2 Epoll
poll() 和 select() 的改进,
适用场景: 一个线程需要处理数百个文件描述符的场景
Epoll 在2.6 内核引进
#include <sys/epoll.h>
int epoll_create1(int flags);
int epoll_create(int size);
触发为两种模式:
边缘触发和条件触发
1.3 内存映射 I/O
将文件映射到内存,支持通过简单的内存管理方式来处理文件I/O,
适用场景: 大文件映射分段读取,也可以用聚集I/O 读取
#include <sys/mmap.h>
void * mmap(
void *addr,size_t len,
int prot,
int flags,
int fd,
off_t offset);
取消映射,使用mummap()
使用mmap的好处是:避免多余的数据拷贝操作
不足之处:需要的映射区域的大小总是页的整数倍,可能浪费内存。
windows linux 内存文件映射
1.4 文件提示
进程将文件I/O期望适用方式的提示信息提供给内核,提升I/O性能
函数:
posix_fadvise()
readahead()
1.5 异步I/O
允许进程发起多个I/O 请求而不必等待请求完成,适用场景:不使用线程来处理比较高的I/O 负载
需要内核在最底层的支持,aio库提供了一些函数来实现异步I/O 并在完成时收到通知。
#include <aio.h>
struct aiocb{
int aio_fildes;
omt aop_lio_opcode;
int aio_reqprio;
volatile void *aio_buf;
size_t aio_nbytes;
struct sigevent aio_sigevent;
}
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);
int aio_error(const struct aiocb *aiocbp);
int aio_return(struct aiocb *aiocb *aiocbp);
int aio_cancel(int fd,struct aiocb *aiocb *aiocbp);
int aio_fsync(int op,struct aiocb *aiocb *aiocbp);
int aio_suspend(const struct aiocb * const cblist[], int n, const struct timespec * timeout);
1.6 Linux 5.1合入了一个新的异步IO框架和实现:io_uring
io_uring 由Jens Axboe开发。
围绕高效进行设计。为了避免在提交和完成事件中存在内存拷贝,io_uring设计了一对共享的ring buffer用于应用和内核之间的通信。其中,针对提交队列(SQ),应用是IO提交的生产者(producer),内核是消费者(consumer);反过来,针对完成队列(CQ),内核是完成事件的生产者,应用是消费者。
2 、io 调度器和性能
I/O 调度器实现的两个基本操作就是合并和排序,为什么需要合并? 多个I/O请求合并为一个,可以更节省资源,可以说吞吐量没有变化,但是I/O的次数可以减少很多。
2.1 调度器
2.1.1 deadline I/O调度器
解决的问题: 写恶死读,解决了传统的电梯算法调度的问题,改进的地方为:增加了读FIFO队列和写FIFO队列。
2.1.2 Anticipatory I/O 调度器
比起deadline I/O 调度器,多了一个预测机制,有人会问这怎么预测?又不是算卦的,在I/O世界里,预测意味着等待,所有算法和逻辑都是空间和时间的交互而已,世界也是如此,需要预测就是在极小的时间状态下等待一个时隔,但是又感觉不到,这就叫预测。
在磁头不断交互,寻找,读取中任务的尽头,给它一定的时间,如6毫秒等待,如果还是需要往下读就不会发生移动,也就是在6毫秒的时间内如果是对硬盘的同一部分发起同样的读请求,那么就是预测成功了!
2.1.3 CFQ I/O 调度器
每个进程都有自己的队列,叫做完全公平队列(Complete Fair Queeing) ,每个队列分配一个时间片,应对于进程而言,因为进程都有自己的时间片,调度器默认的空转时间为10毫秒,如果预测成功,则避免查找操作。
2.1.4 Noop I/O 调度器
简单合并,不排序的调度器,有很多人会有疑惑,为什么有这种调度器呢,因为现在固态硬盘使用的是闪存,不存在磁头旋转的问题,因为合并功能更重要而不是排序,如果期望的是优化交互操作,则用采用CFQ I/O调度器。
2.2 选择调度器
可以使用以下命令:
#echo cfq > /sys/block/hda/queue/scheduler
解释: 选择cfq 调度器 -->为hda设备,当然这个一定需要root权限
3、优化I/O性能
优化I/O 性能:
1、 选择合适的调度器
2 、在应用程序的写法中,尽量自己合并写和读操作
3 、减少内存的拷贝 减少,memset memcpy操作
4 、网络流的队列尽量减少,甚至可以去除
5 、尽量理解操作系统,理解机制,周密思考
6、研究合适的I/O 选择,合并线程,合并交互
7、磁盘的I/O是最慢的,一定是先决绝瓶颈问题
以上是关于linux 高级文件IO的主要内容,如果未能解决你的问题,请参考以下文章