注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

D-BUS 分析—5 代码分析(远程调用)  

2009-01-08 13:50:25|  分类: D-Bus |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

到这里应该开始做一些代码分析了,打算从三个方面进行,远程调用(消息),信号的发送和接收以及建立服务。从例子程序着手。
一、远程调用(消息)
1.我们先选择dbus-send作为分析的对象,通过跟踪到dbus内部以了解dbus内部是详细实现。
命令行参数为:--session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect
进入main函数,我们去除参数分析和一些错误处理的代码.把重点放在和dbus相关的内容上,发现调用的流程非常简单:

a. connection = dbus_bus_get (type, &error);---->  
b. message = dbus_message_new_method_call (NULL,path,name,last_dot + 1);---->
c. dbus_message_set_auto_start (message, TRUE);---->
d. dbus_message_iter_init_append (message, &iter);---->
e. reply = dbus_connection_send_with_reply_and_block (connection,message, reply_timeout,&error);---->
f. dbus_message_unref (reply);---->
f. dbus_message_unref (message);---->
f. dbus_connection_unref (connection);
2.流程:
a. 建立好dbus连接----connection = dbus_bus_get (type, &error);
b.为这dbus连接命名申请一个远程调用通道,同时提供服务器名,本次调用的接口名和方法名---- dbus_message_new_method_call();
c. 压入本次调用的参数---- dbus_message_iter_init_append (message, &iter);
d. 启动发送调用----reply = dbus_connection_send_with_reply_and_block (connection,message, reply_timeout,&error);
e.释放发送相关的消息结构----dbus_message_unref (reply);dbus_message_unref (message);dbus_connection_unref (connection);

3. 我们再进入上面每个函数看看dbus到底是如何实现的
a. connection = dbus_bus_get (type, &error)
这个函数是获得一个指定类型的dbus的连接。我们知道应用程序和dbus的通信全部是通过应用程序与dbus建立的connection来完成了。
dbus_bus_get函数只是简单的调用了static DBusConnection *internal_bus_get (DBusBusType  type,dbus_bool_t  private,DBusError*error)。我们仍然剔除一些无关紧要的代码来看internal_bus_get函数
{
    init_connections_unlocked ()---->
    {
        只是简单的初始化了bus_connection_addresses[]数组,从变量名上看是保存三种bus连接地址名称。分别是:
            DBUS_BUS_SESSION=="autolaunch:",
            DBUS_BUS_SYSTEM=="tcp:host=localhost,port=12434",
            DBUS_BUS_STARTER=="autolaunch:"
    }
    connection = dbus_connection_open (address, error);---->
    {
        只是调用了DBusConnection*_dbus_connection_open_internal (const char     *address,dbus_bool_t     shared,DBusError      *error)
        {
            dbus_parse_address (address, &entries, &len, error))---->
            {给传入的地址做分析,可以传入多个地址,该函数将地址分离,每个地址形成一个DBusAddressEntry *entry,多个地址时形成一个环形entry列表。
            }
            对于每一个地址进行如下操作,只要找到或创建了一个地址相关的connection立即返回
            {
                connection_lookup_shared (entries[i], &connection)---->
                {建立了一个hash表用于存储connection和地址条目的对应,首先查找,如果已经创建了就直接返回查找到的connection}
                假如没有找到执行
                    connection = connection_try_from_address_entry (entries[i],&tmp_error);
                {
                    transport = _dbus_transport_open (entry, error);---->
                    {
                        打开一个客户端型的链接通道,DBusTransport* transport表示通道(比如socket)的对象,通道可以传送消息从A点到B点,这是connection的后台实现。
                            内部包含DBusMessageLoader,DBusMessageLoader对象封装了字节流到消息的转换过程
                            用于真正创建transport的方法是定义在数组:
                            open_funcs[] = {
                                { _dbus_transport_open_socket },//method为tcp
                                { _dbus_transport_open_platform_specific },//未实现
                                { _dbus_transport_open_autolaunch }//method为autolaunch
#ifdef DBUS_BUILD_TESTS
                                , { _dbus_transport_open_debug_pipe }//method为debug-pipe
#endif
                        };
                        这里调用的是_dbus_transport_open_autolaunch--->
                        {
                            static DBusTransport*_dbus_transport_new_for_autolaunch (DBusError      *error)

                            {
                                dbus_bool_t _dbus_get_autolaunch_address (DBusString *address, DBusError *error)---->
                                {
                                    通过文件映射检测dbus_daemon.exe是否已经运行,同时通过_dbus_get_autolaunch_shm( address )获得地址:"tcp:host=localhost,port=12434"
                                }
                                result = check_address (_dbus_string_get_const_data (&address), error);
                                {这个函数有点容易被函数名称误导,这个函数不是检查地址用的,而是根据地址打开一个DBusTransport,因为是tcp所以调用_dbus_transport_open_socket创建一个套接字,并连接套接字在指定主机和端口。根据创建的套接字创建DBusTransport,在这里还包括了授信和其他一些信息,另一个比较重要的就是message loader,他封装了字节流转换成一系列的DBusMessage的操作过程}

                            }
                        }               
                    }
                    connection = _dbus_connection_new_for_transport (transport);
                    {
                        通过给定的transport创建一个连接(connection),一个transport表示一个利用具体机制(比如socket)实现的消息流。

                    }

                }

            }
        }
    }

    dbus_bus_register (connection, error)
    {
        注册一个连接到dbus,这是应用程序连接到消息总线之前必须做的第一个事情。如果成功,ID将被设置,可以通过dbus_bus_get_unique_name()获得。这个函数是被block直到注册成功。
            ensure_bus_data (connection);---->
        {
            dbus_connection_allocate_data_slot (&bus_data_slot))--->
                分配一个整数(插槽)用于存储与和DBusConnection关联的应用数据
                bd = dbus_connection_get_data (connection, bus_data_slot);--->
                if (bd == NULL)
                {
                    bd = dbus_new0 (BusData, 1);--->
                        dbus_connection_set_data (connection, bus_data_slot, bd,bus_data_free)--->

                }
        }

        message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,DBUS_PATH_DBUS,DBUS_INTERFACE_DBUS,"Hello"); ---->
        {
            在一个应用程序能够将信息发送到其他应用程序必须发送org.freedesktop.DBus.Hello信息,消息总线获得一个唯一的名称.
                dbus_message_new_empty_header ();
            消息具有消息缓存,首先会从消息缓存中获取,如果没有就新建一个并进行消息头初始化,但是还不能直接用
                _dbus_header_create (&message->header,DBUS_MESSAGE_TYPE_METHOD_CALL,destination, path, interface, method, NULL)
                填充消息头的主要域,至此,消息可以被使用

        }
        reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);--->
        {
            发送一条消息,并阻止等待答复.这个函数会导致不进入主消息循环,除了回复的其他消息会进入队列但是不会被处理
                dbus_connection_send_with_reply (connection, message,&pending, timeout_milliseconds))--->
            {
                发送一个消息到队列,返回DBusPendingCall用于接收消息的回复,如果在timeout_milliseconds之内没有收到回复,结束pending回复并生成一个综合错误回复(本进程生成不是远程生成)
                    在其他filter或注册对象路径处理之前DBusPendingCall 可以接受回复
                    _dbus_connection_get_is_connected_unlocked (connection)---->
                {
                    查看connection是否已经结束

                }

                pending = _dbus_pending_call_new_unlocked (connection,timeout_milliseconds,reply_handler_timeout);--->
                {
                    创建一个新的pending回复对象

                }

                serial = dbus_message_get_serial (message);--->
                {
                    返回一个消息的序列号,序列号是应用程序提供的用于接收回复,所有从connection接收到的消息都有一个由远程提供的序列号,你发送的消息由dbus_connection_send() 产生序列号并返回给你,序列号是放在消息头里面的
                        if (serial == 0)
                        {
                            serial = _dbus_connection_get_next_client_serial (connection);
                            _dbus_message_set_serial (message, serial);
                        }
                        如果没有序列号就产生一个
                }产生序列号

                _dbus_pending_call_set_timeout_error_unlocked (pending, message, serial)---->
                        {
                            设置回复消息的超时的错误信息{"Did not receive a reply. Possible causes include: "
                                "the remote application did not send a reply, "
                                "the message bus security policy blocked the reply, "
                                "the reply timeout expired, or "
                                "the network connection was broken."}

                        }

                        _dbus_connection_attach_pending_call_unlocked (connection,pending)---->
                        {
                            加入超时和序列号到pending回复
                        }

                        _dbus_connection_send_unlocked_no_update (connection, message, NULL)
                        {
                            _dbus_connection_preallocate_send_unlocked (connection)--->
                            {
                                创建DBusPreallocatedSend,主要用到其中的queue_link和counter_link

                            }

                            _dbus_connection_send_preallocated_unlocked_no_update (connection,preallocated,message,client_serial);
                            {
                                sig = dbus_message_get_signature (message);
                                {
                                    获得消息的类型签名,签字只包括DBUS_MESSAGE_TYPE_METHOD_CALL 类型in参数的DBUS_MESSAGE_TYPE_METHOD_RETURN的输出参数,签字是个字符串

                                }
                                _dbus_connection_do_iteration_unlocked (connection,DBUS_ITERATION_DO_WRITING,-1);
                                {
                                    将进来的消息放入队列,并发送出去的消息,可以选择block,每个_dbus_connection_do_iteration_unlocked()都会调用select()或poll()
                                        一次并且读入或写入数据。这个函数的目的是把进来的消息放入队列,发送出去的消息。但是不返回给应用程序控制以免重入。
                                        _dbus_connection_acquire_io_path (connection,(flags & DBUS_ITERATION_BLOCK) ? timeout_milliseconds : 0)
                                    {获取传输者的路径}

                                    _dbus_transport_do_iteration (connection->transport,flags, timeout_milliseconds);
                                    {
                                        执行一个select/poll,读入/写入数据。将进来的消息放入队列,并发送出去的消息。这是_dbus_connection_do_iteration()的真正实现。
                                            (* transport->vtable->do_iteration) (transport, flags,timeout_milliseconds);
                                        {
                                            实际调用了static  void socket_do_iteration (DBusTransport *transport,unsigned int   flags,int timeout_milliseconds)
                                            {
                                                _dbus_transport_get_is_authenticated (DBusTransport *transport)
                                                {
                                                    是否经过认证,如果没有会重新认证(只是检查buffer内容)

                                                }
                                                do_authentication (transport, need_read, need_write,&authentication_completed);
                                                {
                                                    进行认证通讯,与server通信,完成认证工作,

                                                }

                                            }

                                        }

                                    }
                                    _dbus_connection_release_io_path (connection);
                                    {
                                        用完后释放io路径

                                    }

                                }

                            }

                        }//至此,认证信息发送完毕
                        status = _dbus_connection_get_dispatch_status_unlocked (connection);---->
                        {
                            查询派发状态
                                会调用_dbus_transport_queue_messages进行消息派发(outgoing)

                        }

                        _dbus_connection_update_dispatch_status_and_unlock (connection, status);
                        {
                            更新派发状态

                        }

            }

            dbus_pending_call_block (pending);--->
            {
                等待pending call完成,主循环停止
                    _dbus_connection_flush_unlocked (connection);
                {
                    等待发出消息队列空为止
                        对每个发出消息进行:
                        _dbus_connection_do_iteration_unlocked (connection,DBUS_ITERATION_DO_READING |DBUS_ITERATION_DO_WRITING |DBUS_ITERATION_BLOCK,-1);
                    {
                        发送和接受消息:
                            接受到认证回复消息“OK”,
                            发送“BEGIN”消息,
                            发送队列里的“HELLO”消息

                    }

                }
            }

            reply = dbus_pending_call_steal_reply (pending);
            {
                获得pending回复
                    收到hello消息的回复
            }

        }

    }

}//至此,消息连接创建完毕,从创建的过程中可以看到远程调用(消息)的使用(Hello),因此,远程调用(消息)的分析我们不再继续做,其实都是一样的。

  评论这张
 
阅读(1949)| 评论(1)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017