sogou/workflow

Windows下基于iocp的的异步文件IO

Open

#637 opened on Nov 10, 2021

View on GitHub
 (12 comments) (0 reactions) (0 assignees)C++ (2,300 forks)batch import
help wanted

Repository metrics

Stars
 (12,016 stars)
PR merge metrics
 (Avg merge 1d) (6 merged PRs in 30d)

Description

Workflow支持异步文件IO任务,具体实现目前在Linux下是由操作系统支持的异步IO系统,在非Linux的系统下是用多线程实现的。而Windows下目前也这个需求,所以欢迎熟悉iocp开发的小伙伴可以积极参与共建~

在此把原异步文件IO的流程大概梳理如下,以供参考:

  1. 用户层接口,我们以create_pread_task()为例子:
class WFTaskFactory                                                             
{
    static WFFileIOTask *create_pread_task(const std::string& pathname,                               
                                           void *buf,                              
                                           size_t count,                           
                                           off_t offset,                           
                                           fio_callback_t callback);
    ...
  1. Workflow内部都是行为派生,所以用户拿到的都是WFFileIOTask *类型的task,而内部会根据pread行为创建一个__WFFilepreadTask
WFFileIOTask *WFTaskFactory::create_pread_task(const std::string& pathname,                            
                                               void *buf,                          
                                               size_t count,                       
                                               off_t offset,                       
                                               fio_callback_t callback)            
{                                                                                  
    return new __WFFilepreadTask(pathname, buf, count, offset,                             
                                 WFGlobal::get_io_service(),                         
                                 std::move(callback));                          
}
  1. __WFFilepreadTask需要实现prepare(),供内部IOService调用,具体是做与异步文件相关的起始操作:
class __WFFilepreadTask : public WFFilepreadTask                          
{ 
protected:                                                                         
    virtual int prepare()
    {
        // 这里调用了IOSession层的prep_preadv(),不同系统实现不一样;
    }
  1. 在Linux和Windows中,WFFileTask和IORequest的定义都是一样的。不同点在于上面提到的IOService和IOSession。 IOService是接管所有文件异步IO的服务,IOSession是一次IO请求的上下文,需要根据Windows下iocp的机制来具体实现。 虽然Linux使用了系统的libaio,但大家要做的事情是类似:
class IOService                                                                    
{                                                                                  
public:   
    int request(IOSession *session); // 用于用户提交一个文件io任务

private:                                                                           
    int event_fd;  // 用于结合libaio机制的eventfd,多个IO事件也只用一个,希望在windows下也尽量少占用系统资源
                                                                
private:                                                                        
    struct list_head session_list; // 用链表管理了此时发出的多个任务                                    
                                                               
private:                                                                        
    static void *aio_finish(void *context); // 用于结合libaio机制的回调函数,有事件通知会回到这里

    ...
};
  1. 此IOService需要通过CommScheduler::io_bind()把自己的eventfd和回调绑定到通信器中,同理也需要io_unbind()。其他内部接口需要根据iocp的机制按需添加。目的是做到当系统有异步事件的时候,会通过注册到通信器的机制来告诉框架,框架调起当时的那片上下文的handle(),即可回到task的逻辑中:
void Communicator::handle_aio_result(struct poller_result *res) 
{
    ...
    session->handle(state, error);

  1. 如果希望默认使用此异步文件服务,可以参考现在的__FileIOService, 从IOService派生,并且在全局单例中提供接口供调用,这样也可以保证不用异步文件IO的用户不会创建相应资源:
class __CommManager
{
    IOService *get_io_service()
    {
        if (!fio_flag_)
            fio_service_ = new __FileIOService(&scheduler_);
        ...
    }
}

以上是整个异步文件IO的基本流程,希望在windows下的实现同时遵循Workflow一如既往的对资源的极度节制以及对高并发的严谨。如有了解iocp的小伙伴愿意尝试欢迎随时交流。

Contributor guide