首页 > 其他分享 >DBus API的使用(讲的很清晰,底层概念,基本api)

DBus API的使用(讲的很清晰,底层概念,基本api)

时间:2023-04-17 18:23:41浏览次数:56  
标签:Name api DBus DBusMessage API msg dbus name

DBus是用来进行进程间通信的。下面这张图展示了一些DBus的大部分东西,但是它太复杂了:


DBus 本身是构建在 Socket 机制之上。真正的通信还是由 Socket 来完成的。DBus 则是在这之上,制定了一些通信的协议,并提供了更高一层的接口,以更方便应用程序之间进行数据的交互。
在DBus的体系中,有一个常驻的进程 Daemon,所有进程间的交互都通过它来进行分发和管理。所有希望使用 DBus 进行通信的进程,都必须事先连上 Daemon,并将自己的名字注册到 Daemon 上,之后,Daemon会根据需要把消息以及数据发到相应的进程中。
首先使用
1 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
让应用程序和 DBus 之间取得连接。之后,使用函数
1 ret = dbus_bus_request_name(conn, "test.method.server",2 DBUS_NAME_FLAG_REPLACE_EXISTING3 , &err);
将自己的进程名字注册到 Daemon 上。(参考前篇的[共通用代码])。这样通信就有了基础了。
DBus 提供的最简单的一种通信方式是信号(Signal),应用程序可以发送一个信号到 Daemon 上,之后,Daemon 会根据信号的种类和谁希望得到信号等信息,把相应的数据发给每个希望得到信号的进程。也就是 Signal 具有广播的功能。信号具有两个基本的属性,一个是名称,用来标识各个不同的信号,一个是数据,信号是可以带一定的数据的。Signal 的通信过程可以用下面的图大概展示出来:

如果一个进程(比如 B )想接收接口名为 test.signal.Type 的信号,那么可以使用下面的函数向 Daemon 添加匹配信号,让 Daemon 知道自己对这种信号感兴趣。
1 // add a rule for which messages we want to see2 dbus_bus_add_match(conn,3 "type='signal',interface='test.signal.Type'",4 &err); // see signals from the given interface
然后, 进程 B 可以使用下面的函数来进行等待:
1 dbus_connection_read_write(conn, 0);2 msg = dbus_connection_pop_message(conn);
一旦有消息发送过来,进程 B 就可以通过 msg 取到相应的数据了。(参考前篇代码段[接收消息1、2] )
现在有一个进程 A,

1 dbus_uint32_t serial = 0; // unique number to associate replies with requests 2 DBusMessage* msg; 3 4 // create a signal and check for errors 5 msg = dbus_message_new_signal("/test/signal/Object", // object name of the signal 6 "test.signal.Type", // interface name of the signal 7 "Test"); // name of the signal 8 9 // append arguments onto signal10 dbus_message_iter_init_append(msg, &args);11 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &sigvalue)) {12 fprintf(stderr, "Out Of Memory!\n");13 exit(1);14 }15 16 // send the message and flush the connection17 if (!dbus_connection_send(conn, msg, &serial)) {18 fprintf(stderr, "Out Of Memory!\n");19 exit(1);20 }

到这里为止,已经涉及到了几个基本而又核心的概念,搞清楚它们,几乎就大概知道了 DBus 的使用方法了。
DBusMessage 是 DBus 中的核心数据结构。可以这样理解:DBus中传递消息数据的时候,就是通过它来传递的。对于使用者来说,DBusMessage 中存储了两种重要的信息,一种是为通信机制服务的各种 Name,一种是通信的数据本身。

各种名字(Name)
在前面用到的很多接口中都有名称/路径字符串作为参数。
DBus Name: 在 DBus 中最为重要的名字是“Bus Name”,Bus Name 是应用程序(或是通信对象)用来标识自己用的。几乎可以当成是“IP”地址来理解。Bus Name有两种,一种是“Unique Connection Name”,是以冒号开头的,是全局唯一但“人类不友好”的命名,一种是“Well-know Name”,人类友好的。Bus Name 的命名规则是:
1 Bus name 就像网址一样,由“.”号分割的多个子字符串组成,每个子字符串都必须至少有一个以上的字符。2 每个子字符串都只能由“[A-Z][a-z][0-9]-”这些 ASCII 字符组成,只有 Unique Name 的子串可以以数字开头。3 每个 Bus name 至少要有一个“.”,和两个子字符串,不能以“.”开头4 Bus name 不能超过 255 个字符
几个例子是:Unique Name :392-2.33 org.freedesktop.DBus 等等
DBus Name 是用来给应用程序进行标识自己的,所以每当程序连上 DBus Daemon 后,就会分配到一个 Unique Name,同时应用程序还可以要求自己分配另一个 Well-know name (通过 dbus_bus_request_name 函数)。
Interface name: DBus 也有 interface 这个概念,主要是用来为更高一层的框架使用方面而设定的。在 C API 这一层,你几乎可以无视这个概念,只需要知道这是一个“字符串”,并在消息匹配时被 DBus 使用到,会随着消息在不同的进程之前传递,从进程 A 发送一个消息或是数据到进程 B 时,其中必定会带这个字符串,至于 B 进程怎么用(或是无视它)都可以。它的命名规则与 DBus Name 几乎是一样的,只有一点要注意,interface name 中不能带有“-”字符。
Object path:DBus 中的 object path,与 interface 一样,也只是个概念在更高一层的框架(QT Dbus)中才比较有用,在 C API 这一层,几乎可以无视这个概念,把它当成一个普通的字符串,根据通信的需要,用来做一种标识和区分。Object path 的命名规则是:/com/example/MusicPlayer1
1 object path 可以是任意长度的2 以'/'开头,并以以'/'分隔的若干子字符串组成3 每个子串必须由“[A-Z][a-z][0-9]
”中的字符组成4 不能有空子串(也就是不能连续两个'/'符)5 除了“root path”('/')之外,不能再有 object path 是以 '/' 结尾的了。
一个 object path 的例子:/com/example/MusicPlayer1
Member name:Member 包含两种类型,一种是 Signal,一种是 Method。在大多数方面,他们几乎是一样的,除了两点:1. Signal是在总线中进行广播的,而Method是指定发给某个进程的。2. Signal 不会有返回,而 Method 一定会有返回(同步的或是异步的)。Member name的命名规则是这样的:
1 只能包含"[A-Z][a-z][0-9]_"这些字符,且不能以数字开头。不能包含“.”。2 不能超过255个字符
以 C API 的层面来看,Member name最大的作用就是在两个进程间共享“发出的消息的类型信息”。DBus 只能以 Signal / Method 来进行消息通信,这两种方式都允许在消息发出之前,在消息中 append 各种类型的数据,当通信的对方收到消息后,它就可以通过 Signal / Method 的名称知道如何把各种数据再解析出来。
到现在为止,已经介绍了三种最为重要的 Name,如果与 windows 消息机制对比的话,我大概觉得,DBus Name 就是进程的 ID,有了这个你才能把消息发给指定的进程,object path ,interface等概念在“(QT等)高层框架中才有意义”,C API 级别的使用的话,可以无视它的概念,当成消息甄别用的信息就好了。 Member name 相当于 Message type,有了它你才知道怎么去解析发过来的数据。
下篇将会记录 DBusMessage 另一个主要的组成部分:通信数据。
通信数据的设置和获取
DBus 提供了一个 DBusMessageIter 的类型,使用这个类型的变量,我们就可以向 DBusMessage 中很容易地加入数据,也可以很容易地从中取出数据。
一、向 DBusMessage 中加入数据
1 DBusMessage* msg; 2 DBusMessageIter args; 3 4 // msg... 5 6 dbus_message_iter_init_append(msg, &args); 7 dbus_uint32_t my_data = 10; 8 if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_data)) { 9 printf("Out of memory\n");10 return RES_FAILED;11 }12 13 dbus_connection_flush(conn);14 dbus_message_unref(msg);

第 2 行的代码声明了一个 DBusMessageIter 的对象 args;
第 6 行的函数,对 args 进行初始化,这可以让一个 DBusMessageIter 对象与对应的 DBusMessage 关联起来,后面再使用 DBusMessageIter的时候(设置或者取得数据),就是对相应的 DBusMessage 进行处理。
第 8 行的函数,将一个 uint32 的数据 my_data 追加到 msg 中了。如果还要追加新的参数的话,只需要继续调用该函数,并传入适当的参数就可以了。
二、从 DBusMessage 中取出数据
1 DBusMessage* msg; 2 DBusMessageIter args; 3 4 // get a DBusMessage from process A 5 6 if(!dbus_message_iter_init(msg, &args)) { 7 printf("dbus_message_iter_init error, msg has no arguments!\n"); 8 } 9 else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){10 printf("not a uint 32 type !\n");11 }12 else {13 dbus_uint32_t my_age = 0;14 dbus_message_iter_get_basic(&args, &my_age);15 printf("Got signal with value %d\n", my_age);16 }
很简单,一样的先使用 dbus_messge_iter_init 先把 DBusMessage 对象和从 DBus 总线中取到的 msg 关联起来。
这样,使用第 9 行的函数先取得第一个通信数据中第一个参数的类型,如果类型无误的话可以进而使用第 14 行的函数取得参数值本身。
那么如何将 DBusMessage 在进程之间传递呢?
消息的发送和获取
消息的发送其实比较简单,当进程 A 准备申请好一个 DBusMessage对象,设置好它的“类型”(就是各种名字),放好需要通信的数据,之后,使用下面的代码就可以将数据发送到总线上:

1 dbus_uint32_t serial = 0;2 3 if(!dbus_connection_send(conn, msg, &serial)) {4 printf("Out of memory");5 return RES_FAILED;6 }7 dbus_connection_flush(conn);

这很简单,只是 dbus_connection_flush 这个函数有点突兀,它的作用是“Blocks until the outgoing message queue is empty.”,调用这个函数可以使用得发送进程一直等消息发送完了才能继续运行。
接受方的代码也很简单:

1 dbus_connection_read_write(conn, 0);2 msg = dbus_connection_pop_message(conn);3 4 if(NULL == msg) {5 sleep(1);6 continue;7 }

使用 1 和 2 行的代码就可以取出发送到本进程的消息,之后就可以使用 msg (如果 msg 不是 NULL 的话)来获取通信数据了。
到这里,基本概念就有了。后面,应该对 DBus 的细节再深入的探索。

标签:Name,api,DBus,DBusMessage,API,msg,dbus,name
From: https://www.cnblogs.com/ruizhang0822/p/17326715.html

相关文章

  • API 调用次数限制实现
    在开发接口服务器的过程中,为了防止客户端对于接口的滥用,保护服务器的资源,通常来说我们会对于服务器上的各种接口进行调用次数的限制。比如对于某个用户,他在一个时间段(interval)内,比如1分钟,调用服务器接口的次数不能够大于一个上限(limit),比如说100次。如果用户调用接口的次数......
  • 如何利用 IP 归属地查询 API 精准锁定用户位置
    引言在互联网时代,IP地址扮演着非常重要的角色,它可以帮助我们追踪网站访问者、优化网络服务等等。而IP归属地则更进一步,它可以帮助我们精确地定位IP地址所在的地理位置,为数据分析、网络安全、市场调研等领域提供了极大的帮助。本文将介绍一种IP归属地-IPv4区县级的API,帮......
  • 如何获取ChatGPT的API keys
    首先,登录openai官网(https://openai.com/)然后,访问这个链接https://platform.openai.com/account/api-keys点击Createnewsecretkey创建key,就ok了箴言:因为这些东西是非常简单的。不要抱怨自己学不会,那是因为你没有足够用心。......
  • 如何在 DevOps 中进行 API 全生命周期管理?
    随着DevOps理念在中国企业当中的普及和发展,中国企业DevOps落地成熟度不断提升,根据中国信通院的数据已有近6成企业向全生命周期管理迈进。而在研发全生命周期管理之中,API管理的地位愈发显得重要。随着API数量的大幅增长,也带来了新的API管理需求。如何在DevOps工作流中......
  • 拼多多根据ID获取商品详情页简版数据 API 接口返回值说明
    准备工作:1)获取一个key和secret。 2)然后登录请求地址选择需要该接口。 3)输入自己的key和secret,对接口进行测试。从而了解返回信息,方便程序获取。 item_get_easy-根据ID获取商品详情页简版数据公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在......
  • 业务扩展:图灵机器人调用(API)接口模式的开发流程及其注意事项
    业务场景:在网站添加一个可以回答用户问题的对话机器人;解决方案:使用三方图灵机器人(turing)进行语料库控制,数据统计,问题反馈等;操作流程:1、申请图灵开发者账号权限,官网网站:http://www.tuling123.com/2、购买相应套餐获取机器人功能,完成认证之后可获得免费版,未完成认证则需要完成套......
  • 如何玩转国产神器:接口一体化协作平台Apifox!
    前言:Apifox是什么?简介:简单来说,Apifox=swagger+mock+postman+Jmeter,是API文档、API调试、APIMock、API自动化测试一体化协作平台。可以把接口开发过程中各角色的工作,例api设计者、后端开发、前端开发、测试人员协同到一个Apifox平台完成。工具使用界面的各个功能都直......
  • 有效避免骚扰:从用户体验角度看空号检测 API 的优势!
    引言电话营销和短信营销是企业吸引客户和促进销售的重要手段,但是无效号码的存在会导致资源浪费和用户不满。利用空号检测API可以帮助企业识别出有效和无效的电话号码,从而避免向无效号码发送营销信息,降低营销成本。本文将从用户体验、合规风险和数据分析三个角度介绍空号检测AP......
  • 从 HTTP 到 gRPC:APISIX 中 etcd 操作的迁移之路
    罗泽轩,API7.ai 技术专家/技术工程师,ApacheAPISIXPMC成员。原文链接ApacheAPISIX现有基于HTTP的etcd操作的局限性etcd在2.x版本的时候,对外暴露的是HTTP1(以下简称HTTP)的接口。etcd升级到3.x版本后,其对外API的协议从普通的HTTP切换到了gRPC。为了兼顾......
  • 免费可用的热门 API 大全整理分享
    天气/日落日出分钟级降水预报:支持国内任一经纬度的预报数据,实时调取某地未来2小时内天气预报,可做到分钟级、公里级,可准确提醒下一场雨何时出现,何时变大,何时停止等预报信息。日出日落:支持国内3400+个城市以及国际4万个城市,获取指定城市/地点每日日出时间、日落时间。月出月落和......