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是最慢的,一定是先决绝瓶颈问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qianbo_insist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值