首页
社区
课程
招聘
[原创]ADB学习笔记2——adb_main和其相关数据结构
2014-6-10 01:05 8483

[原创]ADB学习笔记2——adb_main和其相关数据结构

2014-6-10 01:05
8483
之前看adb_main的loop循环,总是很迷糊,后来发现,这货得先从其全局变量看起。。。。。
几个结构体下来,瞬间感觉豁然开朗可有木有。。。。
1.首先是结构体FH及其相关全局变量_win32_fhs
static  FHRec _win32_fhs[ WIN32_MAX_FHS ];                //这个表是用来记录系统所有的FH结构。
static  int        _win32_fh_count;                                        //记录此表中已有fh的数目。
typedef struct FHRec_
{
    FHClass    clazz;                                                        //这货记录了此FH具体的操作函数
    int        used;                                                        //此FH是否已使用
    int        eof;
    union {                                                                //FH的具体内容,可以使一个句柄,一个socket,或者一个socketpair
        HANDLE         handle;       
        SOCKET         socket;
        SocketPair  pair;
    } u;
    HANDLE  event;                                                        //读还是写事件
    int         mask;
    char  name[32];                                                        //用来唯一匹配fh的
} FHRec;
其中:
typedef const struct FHClassRec_*   FHClass;
typedef struct FHClassRec_
{
    void (*_fh_init) ( FH  f );
    int  (*_fh_close)( FH  f );
    int  (*_fh_lseek)( FH  f, int  pos, int  origin );
    int  (*_fh_read) ( FH  f, void*  buf, int  len );
    int  (*_fh_write)( FH  f, const void*  buf, int  len );
    void (*_fh_hook) ( FH  f, int  events, EventHook  hook );

} FHClassRec;
FH可以说代表的是一种抽象的数据通信句柄,在程序中的一个socket,一个文件句柄,一个SocketPair都统一用一个FH结构体来表示,对三种数据的接口函数也是相同的,但调用时具体调用的是哪个函数,是通过FH的分配函数_fh_alloc中通过clazz设定的。
Adb在全局变量中针对socket,handle和socketpair初始化了三个接口的实例,_fh_file_class,_fh_socket_class,_fh_socket_pair_class如图

这里以socket为例,如果要将一个socket 转换为FH,其大体步骤如下:
FH  f = _fh_alloc( &_fh_socket_class );
    SOCKET  s;
    bind(s)
listen(s, 1);
    return _fh_to_int(f);

先看一下FH的空间分配函数_fh_alloc,此函数的主要操作可以简写为:
static FH _fh_alloc( FHClass  clazz )
{
FH f = &_win32_fhs[ _win32_fh_count++ ];                //在全局_win32_fhs数组中的末尾为FH指定空间
f->clazz = clazz;                                //设置此FH具体操作函数
clazz->_fh_init(f);                                //调用初始化函数,这里调用的就是socket的初始化函数_fh_socket_init
return f;
}
再看_fh_to_int函数,此函数主要是将fh指针转换为一个整数fd,
_fh_to_int( FH  f )
{
    if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS)
        return (int)(f - _win32_fhs) + WIN32_FH_BASE;
    return -1;
}
对应还有一个_fh_from_int,此函数就是将一个整数转换为对应的fd指针,二者就跟句柄和指针的关系一样。

好了,综上所述,我们可知,一个FH内部其实是封装了一个socket或一个handle或一个socketpair,然后通过fh->clazz对应的init,read,write等函数可以对这三者进行操作(比如说如果一个socket,调用read函数最终调用的是_fh_socket_read,一个handle最终调用的是_fh_file_read函数)。然后fh还被封装成了一个句柄,叫fd,一个fd对应一个fh。

        好吧。。。到这里我想说,后面还有好几个这样的东西呢,为嘛这么麻烦,因为最终一个消息循环fdevent_loop处理了所有的消息啊 ,各种回调,各种参数,各种读写函数都不一样,不这么封装没法实现啊。

2.接着是结构体fdevent及其相关全局变量fd_table:
static fdevent **fd_table = 0;                //这个表示用来记录fdevent结构的指针
static int         fd_table_max = 0;                //表中元素个数
struct fdevent
{
    fdevent *next;                                //这货用来连自身的,这里先不用管
    fdevent *prev;
    int fd;                                        //对应fd
    int force_eof;
    unsigned short state;
    unsigned short events;
    fd_func func;                                //回调函数
    void *arg;                                //回调函数参数
};
前面墨迹一大堆,主要是想说这种封装的思想,后面用一句话解释fdevent,就是说fh封装了socket,handle,socketpair,提供了其读写函数,但异步通信中这三者都是需要有回调的,fdevent封装了fh,主要是让一个fh对应上一个回调函数,和相应的参数,为嘛回调函数和参数不卸载fh里面?因为不同socket,handle可能对应不同回调,没法封装在fh里面,具体的实现细节就写来浪费大家眼神了。
Fdevent有一个重要函数叫fdevent_register(fdevent *fde),此函数在fd_table中记录此fde的指针,程序中的所有fdevent结构都存在全局变量_fd_table中。

下面这肯定是目前最后一个结构体了。。。。
3. 结构体EventHook及其相关全局变量win32_looper:
typedef struct EventHookRec_*  EventHook;
static EventLooperRec  win32_looper;                        //最终的事件检测,检测的就是这个表

typedef struct EventHookRec_
{
    EventHook    next;
    FH           fh;                        //socket有关的
    HANDLE       h;                        //异步通信中与socket绑定的触发事件的句柄
    int          wanted;           /* wanted event flags */
    int          ready;            /* ready event flags  */
    void*        aux;
    void        (*prepare)( EventHook  hook );
    int         (*start)  ( EventHook  hook );
    void        (*stop)   ( EventHook  hook );
    int         (*check)  ( EventHook  hook );
    int         (*peek)   ( EventHook  hook );
} EventHookRec;
typedef struct EventHookRec_*  EventHook;
typedef struct EventLooperRec_
{
    EventHook    hooks;
    HANDLE       htab[ MAX_LOOPER_HANDLES ];        //这货是个句柄表,记录每个hook对应fde里面的一个句柄
    int          htab_count;                                                //如果没有事件发生,就通过这个句柄表,等待相应事件

} EventLooperRec;

好吧,这个结构体有点长,还是一句话简言之,一个EventHook封装了一个fh的状态监测函数,和fh的状态,最终也是存在一个表中,这个表就是win32_looper。还是同样的问题,为嘛这么封装,如果说一个fh封装的是三种不同的句柄(socket,handle,socketpair),一个fdevent对每一种类型的fh设置回调,那么一个EventHook就是制定某一个特定的fh目前的状态怎么样了,是否ready,比如后面listen后accept的socket,这些socket有同样的回调函数,但具体是哪个socket事件触发了,就需要调用EventHook中的函数来检测了。

好吧 接下来进入正题,消息循环:
首先是一个fh怎么进入消息循环链表win32_looper的,看这个函数
void fdevent_install(                //此函数将一个fd指定的fh记录到win32_looper和fd_table中
                fdevent *fde,                //当前fde的指针
                int fd,                        //fh对应的fd
                fd_func func,                //fde的回调函数
                void *arg                        //回调函数的参数
                )
{
    memset(fde, 0, sizeof(fdevent));        //初始化fde结构
    fde->state = FDE_ACTIVE;
    fde->fd         = fd;
    fde->func = func;
fde->arg = arg;
    fdevent_register(fde);                //在fd_table中记录fde的指针
    fdevent_connect(fde);                //在win_looper.hook链表中为fd创建一个hook
    fde->state |= FDE_ACTIVE;
}
此函数输入的fde指针指向的应该是fd_table中的一个位置,然后fd指定是对哪个fh的注册,func,arg是回调函数和参数。此函数通过fdevent_register函数在fd_table中注册fde和对应回调,参数,通过fdevent_connect函数在win_looper.hook链表中为fd创建一个hook(这个名字起得太怪异了吧。。。。)。ok,注册完了之后前面提到的fdevent_loop()循环中就能扫到此fh的状态了,如果ready,就可以调用相应的回调函数并传入对应参数了。

其实这里才是正题。。。。。消息循环的主要代码:
void fdevent_loop()
{
    fdevent *fde;
    for(;;) {
    fdevent_process();                                                       
while((fde = fdevent_plist_dequeue()))
{       
            unsigned events = fde->events;
fde->func(fde->fd, events, fde->arg);
    }
}
Fdevent_process这货比较长,大概描述一下吧
1)        循环所有looper_hook[i](后面称为hook),并调用其函数hook.prepare,查看是否有事件出发了,如果有则把所有这样的hook加入list_pending列表,退出。这个list_pending是fdevent_process已经发现的,有消息需要处理的fh所在的列表。
2)        如果一个hook也没有加入到list_pending列表,则循环所有looper_hook[i](后面称为hook),把所有hook.h有效地(就是说等待的事件句柄有效地)加入到win32_loop.htab_count表中,然后调用WaitForMultipleObjects等待其中的一个事件触发。这个htab_cout记录的是,程序中所有有等待句柄的fh的句柄表。
3)        有事件触发后,再执行第一步,循环列表找到所有prepare成功的,加入list_pending列表中
4)        最终清空htab_cout,返回

再简言之,fdevent_process就是去找那些fh有事件需要处理,只要有一个fh有事件需要处理就返回,否则就一直等到有一个fh有事件,此时再扫一遍,看看哪些fh需要处理(因为可能等待到了多个需要处理的fh),然后返回。

之后就比较好理解了,while循环处理所有的可处理消息,fdevent_plist_dequeue是从list_pending中取出一个消息,然后内部调用其回调函数fde->func,传入fd,传入event(read/write),传入参数。

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
  • 1.jpg (101.09kb,11次下载)
收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 4759
活跃值: (3334)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
ashimida 5 2014-6-10 01:06
2
0
好吧 还是图的问题。。。。
游客
登录 | 注册 方可回帖
返回