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的内部