linux 磁盘io技术3------libaio使用介绍

前一篇文章介绍了一下libaio与block io性能比较:http://blog.csdn.net/beginning1126/article/details/16989421

今天来仔细聊聊libaio的使用。

一、O_DIRECT

研究libaio之前,必须怎明白O_DIRECT标志是干嘛的。

Buffered I/O:

user space buffer----> kernel space buffer---->disk,这就需要用户空间数据拷贝到内核缓冲区,这就浪费了CPU和内存资源了。

DIRECT I/O:

不通过kernel内存缓存,直接将用户空间数据拷贝到磁盘。

那么使用O_DIRECT,则使用DIRECT I/O,这样会减少内存拷贝次数,cpu使用率也会减少。但是这样做就需要由用户层来做内存block对齐,并且其内存大小也必须是block整数倍。如果不这样做,用write是写不进数据的,直接报错。

通过命令dumpe2fs可以查看系统文件系统的block大小。我的系统block是1024。

但是这个地方我做过实验,不需要是block对齐,或block整数倍,只需要512对齐,512整数倍就可以。512是正好一个扇区的大小。这个地方很奇怪,不知道哪位高手能帮忙解惑下,不胜感激。

保证512对齐可以采用如下方法:

方法1:

void * buf = NULL;
posix_memalign(&buf, 512, BUF_SIZE);


方法2:

#include <stdint.h>

#define CHUNK_ALIGNMENT       512    // align to 512-byte boundary
#define CHUNK_ALIGNMENT_MASK  (~(CHUNK_ALIGNMENT - 1))

static inline size_t
align_size (size_t unaligned)
{
    return((unaligned + CHUNK_ALIGNMENT - 1 ) &
            CHUNK_ALIGNMENT_MASK);
}

static inline void *
align_buf (void *unaligned)
{
    return((void *)((intptr_t)(unaligned + CHUNK_ALIGNMENT - 1) &
         CHUNK_ALIGNMENT_MASK));
}

static inline bool
buf_aligned (void *ptr)
{
    return(((intptr_t)(ptr) & (~CHUNK_ALIGNMENT_MASK)) == 0);
}

<注> 最后说明一下,在使用libaio,是必须将 O_DIRECT标识置上的,如果不置,可能会报错,也可能会直接采用同步block读写的方式,其行为不确定。

可以参照说明:http://lse.sourceforge.net/io/aio.html


二、libaio接口说明

接口文件为/usr/include/libaio.h,可以直接打开看,这里捡些主要的来说明一下。

接口结构体:

struct io_iocb_common {
	PADDEDptr(void	*buf, __pad1); 	//buf start ptr
	PADDEDul(nbytes, __pad2);	//buf size
	long long	offset;		//file offset
	long long	__pad3, __pad4;
};

这里面buf和nbytes没啥好说的,buf和buf size,offset是相对于文件的偏移量。但有一点必须强调下,buf、buf size、offset必须都是512的整数倍。否则读或写都会失败的。

typedef enum io_iocb_cmd {
	IO_CMD_PREAD = 0,
	IO_CMD_PWRITE = 1,

	IO_CMD_FSYNC = 2,
	IO_CMD_FDSYNC = 3,

	IO_CMD_POLL = 5,
	IO_CMD_NOOP = 6,
} io_iocb_cmd_t;

struct iocb {
	PADDEDptr(void *data, __pad1);	/* Return in the io completion event */
	PADDED(unsigned key, __pad2);	/* For use in identifying io requests */

	short		aio_lio_opcode;	
	short		aio_reqprio;
	int		aio_fildes;

	union {
		struct io_iocb_common		c;
		struct io_iocb_vector		v;
		struct io_iocb_poll		poll;
		struct io_iocb_sockaddr	saddr;
	} u;
};

data:可以自定义类型,通常在这里定义call back ptr和call back ptr data,在io_getevents返回之后,通过此回调函数完成读写完成之后的后续处理

aio_fildes:file fd

aio_lio_opcode:io_iocb_cmd下的命令,常用的就是读写。

union联合中,常用的是io_iocb_common。

这个结构是不是很复杂,其实是不用我们挨个赋值的,libaio提供了一些内联函数,来帮我们做这些事情,内联函数在头文件libaio.h中有定义,这里不赘述了

struct io_event {
	PADDEDptr(void *data, __pad1);
	PADDEDptr(struct iocb *obj,  __pad2);
	PADDEDul(res,  __pad3);
	PADDEDul(res2, __pad4);
};

data:没太搞懂,没怎么明白有啥用?

obj: iocb

res:为完成的读、写字节数

res2:读写成功状态,0表示成功


接口函数:

extern int io_setup(int maxevents, io_context_t *ctxp);
extern int io_destroy(io_context_t ctx);
extern int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
extern int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
extern int io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);

io_setup:maxevents表示能处理的最大时间数,同时初始化结构io_context_t,调用之前ctxp必须。

io_destroy:和io_setup对应

io_submint:该函数把nr个iocb放到ctx对应的执行队列中之后返回

io_cancel:该函数尝试取消之前通过io_submit传入的iocb。

io_getevents:该函数尝试从完成队列中去的至少min_nr个,至多nr个的消息。我们通过检查io_event的信息可以做我们想做的事情。


简单示例:

<span style="font-size:14px;">#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <libaio.h>
#include <errno.h>
#include <unistd.h>
#include <unistd.h>




#define MAX_COUNT 10 * 1024
#define BUF_SIZE  1 * 1024 * 1024

#ifndef O_DIRECT
#define O_DIRECT         040000 /* direct disk access hint */
#endif

int main(int args, void *argv[]){
    int fd;
    void * buf = NULL;

    int pagesize = sysconf(_SC_PAGESIZE);  
    posix_memalign(&buf, pagesize, BUF_SIZE);  
    
    io_context_t ctx;
    struct iocb io,*p=&io;
    struct io_event e[10];
    struct timespec timeout;
    
    memset(&ctx,0,sizeof(ctx)); 
    if(io_setup(10,&ctx)!=0){
        printf("io_setup error\n"); 
        return -1; 
    }   
    
    if((fd = open("/home/aio.d", O_WRONLY | O_CREAT | O_APPEND | O_DIRECT, 0644))<0) {   
        perror("open error");
        io_destroy(ctx);
        return -1; 
    }   

    int n = MAX_COUNT;

    while(n > 0) {
        io_prep_pwrite(&io, fd, buf, BUF_SIZE, 0);
        
        if(io_submit(ctx, 1, &p)!=1) {
            io_destroy(ctx);
            printf("io_submit error\n");    
            return -1; 
        }   

        int ret = io_getevents(ctx, 1, 10, e, NULL);
        if (ret != 1) {
            perror("ret != 1");
            break;
        }
        n--;
    }

    close(fd);
    io_destroy(ctx);
    return 0;
}
</span>


写到这里,libaio基本知识也都说的差不多了,但是现在有个问题,如果当前进程是基于epoll的,那么是否可以将libaio当成普通fd,加入到epoll当中来,和网络io结合到一起呢,这样对于一些网络服务,是不是特别方便,特别帅呢。答案是ok的。网上有一个epoll和libaio结合的例子供大家参考,作者偷个懒,这里不做单独讨论,例子如下:

http://blog.chinaunix.net/uid-16979052-id-3840266.html


  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值