首页 | 新闻 | 交流 | 问吧 | 文档 | 手册 | 下载 | 博客

Linux async io 2

作者:  时间: 2011-06-11

async io 之iohandler实现

iohandler的功能:
1. 封装对fd的读写
2. delegate读写到io manager 线程

首先需要初始化iohandler.
用一个预先得到的fd作为参数打开对应文件,并设置nonblocking。
int open(int fd) {
  m_fd = fd;
  fcntl(m_fd, F_SETFL, O_NONBLOCK);
}


iohandler对外的读写接口不外乎read()和write(). 我们定义接口如下:
void async_read(Buffer *buf, int len, CB cb);
void async_write(Buffer *buf, int len, CB cb);
这里需要注意几点
1. 这里我们假设存在一种叫做Buffer的数据结构,它是对数据的封装。
2. 另外假设存在函数对象类型CB(CallBack),cb会在对写操作完成时调用。几乎可以说callback于async io是相生相伴的。一般来说,业务逻辑线程在调用完
async_read/async_write之后就很快结束了。对读写得到的数据或者说副作用的处理,全都需要封装在callback之中。也就是说业务逻辑需要意识要async io的
存在而参杂callback函数的代码。这是async io代码难以编写和维护,从而为人所诟病的地方。但是其实也是有办法将业务逻辑和async io完全分离开来。fiber技术的实现就是一个例子。
3. 函数返回void而不是通常情况下的int(以表示实际读写的数据量),这是因为这在async场景下无法做到。

由于是async操作,我们必须保存参数,以待实际读写时使用。所以,iohandler必须存在一些相应的内部数据成员。保存好参数后,需要把读写操作注册到io manager线程。
简单代码如下:
async_read(Buffer* buf, int len, CB cb){
  m_readBuf = buf;
  m_readLen = len;
  m_readCB = cb;
  m_events |= READ;
  iomanager.addHandler(this);
}

async_write(Buffer * buf, int len, CB cb) {
  m_writeBuf = buf;
  m_writeLen = len;
  m_writeCB = cb;
  m_events |= WRITE;
  write()
}

我们看到,async_read/async_write中没有做任何实际读写操作,所以我们还需要完成实际的数据读写函数。
void read();
void write();
这些函数酱油iomanager在检测到事件时调用。
read(){
  m_total_read += readv(m_readLen - m_total_read);
  if(m_total_read < m_readLen)
    iomanager.addHandler(this);
  else{
    iomanager.removeHandler(this);
    m_readCB();
  }
}

write(){
  m_total_write += writev(m_writeLen - m_total_write);
  if(m_total_write < m_writeLen)
    iomanager.addHandler(this);
  else{
    iomanager.removeHandler(this);
    m_readCB();
  }
}


以下是io manager中,addHandler和removeHandler的伪代码:
int addHandler(IOHandler *handler) {
  struct epoll_event pe;
  // 确保数据干净
  memset(&pe, 0, sizeof(pe));
  pe.data.fd = handler->m_fd;
  if (handler->m_events & IO_EVENT_READ) {
        pe.events |= EPOLLIN;
    }

  if (handler->m_events & IO_EVENT_WRITE) {
      pe.events |= EPOLLOUT;
  }
  m_handlers[handler->m_fd] = handler;
  rc = epoll_ctl(m_epollFD, EPOLL_CTL_MOD, handler->m_fd, &pe);
}


removeHandler 就更简单了,基本只要epoll_ctl(m_epollFD, EPOLL_CTL_DEL, handler->m_fd, &ep)和m_handlers[handler->m_fd] = NULL;