pjsip库学习笔记 五(I/O queue)

一. 引言

1. 该模块包括:

ioqueue.h:

ioqueue_common_abs.h/c:

ioqueue_epoll.c:

ioqueue_linux_kernel.c:

ioqueue_select.c:

2. 该模块实现I/O时,采用的Proactor模式(关于I/O设计模式,参考:http://www.cnblogs.com/dawen/archive/2011/05/18/2050358.html)

 二. 源码分析

1. 该模块主要实现I/O多路复用机制,并且采用PROACTOR设计模式
2. 该模块实现一种I/O机制,自动检测用户注册的socket句柄,并且在触发相应事件时调用相应的回调
   1)主要检测如下三种事件
     可读: read,recv,recvfrom,accept
     可写: write,send,sendto,connect
     异常:
   2)主要支持如下的用户注册回调
     a. on_read_complete
     b. on_write_complete
     c. on_accept_complete
     d. on_connect_complete
3. 该模块目前实现的I/O方式有:
  select:ioqueue_select.c
  epoll:ioqueue_epoll.c
  以上两个文件分别实现了select及epoll方式的I/O检测接口,包括:
  1)ioqueue结构体,根据选择的方式包括select或者epoll,实现了两种方式的移植操作
    对于用户来说,不需要直到结构体中的具体实现,可以使用提供的接口无差别的使用;
    虽然实现方式不同,但是大体来说都包括以下的数据段:
    a. 文件描述符集合(包括读、写、异常)
    union
    {
     struct select
     {
         pj_fd_set_t  rfdset;
        pj_fd_set_t  wfdset;
        pj_fd_set_t  xfdset;
    }
    struct epoll
    {
      struct epoll_event *events;
    }
   }
   b. 用来存放有效文件描述符、关闭文件描述符、已释放文件描述符三类信息的链表
    c. 用于同步操作的锁机制
    此外,在ioqueue中并没有直接使用文件描述符,而是使用一个自定义的结构体:
    struct pj_ioqueue_key_t
    {                      
       PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t);  
       pj_mutex_t             *mutex;                    
       pj_sock_t      fd;                       
       void     *user_data;    //用户数据        
       pj_ioqueue_callback     cb;                    
       int                     connecting;            
       struct read_operation   read_list;             
       struct write_operation  write_list;            
       struct accept_operation accept_list;    
      }
   
  1) pj_ioqueue_name
     对于epoll来说,根据当前模式,返货"epoll"或者"kernel_epoll"
     对于select来说,返回"select"
  2) pj_ioqueue_create
     创建ioqueue数据结构及其中各个字段(锁、链表...)
  3)pj_ioqueue_destroy
  4)pj_ioqueue_register_sock
     主要需要检测的socket,并且返回对应的pj_ioqueue_key_t实例
     a. 将文件描述符添加到poll中
     b. 将socket添加到ioqueue中的链表active_list中
  5)pj_ioqueue_unregister
     从ioqueue中移除指定pj_ioqueue_key_t实例
     a. 从poll中移除对应的socket文件描述符
     b. 将key对应的实例从链表active_list中移除
     c. 关闭key对应的socket连接
  6)pj_ioqueue_poll
    这个是改模块中最重要的一个接口,几乎所有的机制都在模块中实现,包括检测socket触发的事件,调用相应的注册函数
    a. 调用poll_wait/select,检测当前触发事件的socket句柄,并且将触发socket对应的pj_ioqueue_key_t实例(在poll中注册时设置的附加参数)
    b. 根据每个触发的socket的事件类型,调用用户注册的回调函数
       ioqueue_dispatch_read_event
       ioqueue_dispatch_write_event
       ioqueue_dispatch_exception_event
4. ioqueue_common_abs.h/c主要实现根据poll检测到的事件类型,调用相关的用户回调,实现如下接口:
  1) ioqueue_dispatch_read_event
    a. 检测当前key的accept_list列表,如过有项,则执行accept用户回调
    b. 检测当前key的read_list列表,如过有项,则执行read用户回调
  2)ioqueue_dispatch_write_event
    a. 如果当前key的connecting处于connect状态,则执行connect回调
    b. 如果当前key的write_list存在项,则执行wirte回调
  3)ioqueue_dispatch_exception_event 
    a. 从ioqueue中移除该key对应的socket信息
    b. 在某种条件下触发connect用户回调
   
  4)此外,在该文件中定义了一些接口,用来填充pj_ioqueue_key_t实例中的各个列表,包括:accept_list,read_list,write_list,connect状态
    而这些接口则由使用ioqueue的应用层来调用;
    所以ioqueue通常是作为【Active模块】及【fileio模块】的内部机制来使用的

    这些接口包括:

       pj_ioqueue_connect

       pj_ioqueue_accept

       pj_ioqueue_recv

   pj_ioqueue_recvfrom

       pj_ioqueue_send

   pj_ioqueue_sendto

     这些接口的流程基本类似,首先调用一次socket基本操作,如果成功则直接返回;如果失败则调用接口ioqueue_add_to_set,向ioqueue中添加适当的事件,等到

     【on_completed系列回调】中再执行相关的操作

  5) 提供了一些检测接口,用来检测ioqueue中是否存在悬而未决(pending)的操作(accept,read,write,connect),通常用在ioqueue的内部

posted @ 2012-06-05 15:31  坐看风起云涌  阅读(3220)  评论(0编辑  收藏  举报