DBUS源码剖析之DBusMessage数据结构

DBUS源码剖析之DBusMessage数据结构

DBusMessage 概述和数据结构

1. 概述

DBusMessage 是 D-Bus 通信的基本单元,用于在应用程序之间传递数据。每个消息由两部分组成:

  • 消息头(Header):包含路由信息、消息类型、标志等元数据
  • 消息体(Body):包含实际的数据参数

1.1 消息在 D-Bus 中的作用

D-Bus 是一个进程间通信(IPC)系统,DBusMessage 是通信的基本载体:

  1. 方法调用(Method Call):客户端调用服务端的方法
    • 包含目标服务、对象路径、接口、方法名
    • 包含输入参数
    • 期望收到方法返回或错误消息
  2. 方法返回(Method Return):服务端返回方法调用的结果
    • 包含回复序列号(用于匹配原始方法调用)
    • 包含输出参数或返回值
  3. 信号(Signal):应用程序广播事件通知
    • 不期望回复
    • 可以被多个接收者订阅
    • 用于事件通知和状态变化
  4. 错误(Error):方法调用失败时返回错误信息
    • 包含错误名和错误消息
    • 包含回复序列号(用于匹配原始方法调用)

1.2 消息的生命周期

消息从创建到销毁的完整生命周期:

创建 → 填充 → 锁定 → 序列化 → 传输 → 反序列化 → 使用 → 释放 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ new set lock marshal send demarshal get unref 

详细步骤

  1. 创建(Creation)
    • 使用 dbus_message_new_* 函数创建
    • 分配内存并初始化结构
    • 引用计数初始化为 1
  2. 填充(Population)
    • 设置头字段(路径、接口、成员等)
    • 添加参数到消息体
    • 可以多次修改
  3. 锁定(Locking)
    • 调用 dbus_message_lock() 锁定消息
    • 锁定后不允许修改
    • 确保网络数据的一致性
  4. 序列化(Marshaling)
    • 转换为网络字节流格式
    • 处理字节序对齐
    • 准备传输
  5. 传输(Transport)
    • 通过 DBusConnection 发送
    • 使用底层传输机制(Unix socket、TCP 等)
  6. 反序列化(Demarshaling)
    • 接收端解析字节流
    • 重建 DBusMessage 结构
    • 验证消息完整性
  7. 使用(Usage)
    • 应用程序读取消息内容
    • 处理消息参数
    • 执行相应操作
  8. 释放(Destruction)
    • 调用 dbus_message_unref() 减少引用计数
    • 引用计数为 0 时自动释放
    • 清理所有关联资源

2. DBusMessage 数据结构

2.1 公开接口

DBusMessage 是一个不透明类型(opaque type),应用程序只能通过 API 函数访问:

typedefstructDBusMessage DBusMessage;

设计原因

  • 封装:隐藏内部实现细节
  • 稳定性:允许内部结构变化而不影响 API
  • 安全性:防止应用程序直接修改内部状态

2.2 内部结构

实际的 DBusMessage 结构定义在 dbus-message-private.h 中:

structDBusMessage{ DBusAtomic refcount;/**< 引用计数 */ DBusHeader header;/**< 消息头(网络数据和缓存) */ DBusString body;/**< 消息体(网络数据) */unsignedint locked :1;/**< 消息已锁定,不允许修改 */#ifndefDBUS_DISABLE_CHECKSunsignedint in_cache :1;/**< 已在缓存中(调试特性) */#endif DBusList *counters;/**< 0-N 个 DBusCounter,用于跟踪消息大小/unix fds */long size_counter_delta;/**< 增加的大小计数器的增量 */dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS;/**< 迭代器失效时递增 */ DBusDataSlotList slot_list;/**< 通过整数 ID 存储的数据 */#ifndefDBUS_DISABLE_CHECKSint generation;/**< 消息创建时的 _dbus_current_generation */#endif#ifdefHAVE_UNIX_FD_PASSINGint*unix_fds;/**< 与此消息关联的 Unix 文件描述符 */unsigned n_unix_fds;/**< 数组中有效 fd 的数量 */unsigned n_unix_fds_allocated;/**< 数组的分配大小 */long unix_fd_counter_delta;/**< unix fd 计数器的增量 */#endif};

2.3 字段详解

2.3.1 refcount(引用计数)
DBusAtomic refcount;

类型DBusAtomic(原子整数)

作用:管理消息的生命周期,支持多个引用共享同一消息

操作

  • dbus_message_ref():增加引用计数
  • dbus_message_unref():减少引用计数
  • 引用计数为 0 时自动释放消息

使用场景

  • 消息需要被多个地方使用
  • 消息需要延迟释放
  • 消息需要传递给回调函数

示例

DBusMessage *msg =dbus_message_new_method_call(...);dbus_message_ref(msg);// refcount = 2// ... 使用消息dbus_message_unref(msg);// refcount = 1dbus_message_unref(msg);// refcount = 0, 消息被释放
2.3.2 header(消息头)
DBusHeader header;

类型DBusHeader 结构

作用:存储消息的路由信息和元数据

内容

  • 消息类型(METHOD_CALL、METHOD_RETURN、ERROR、SIGNAL)
  • 消息标志(如 NO_REPLY_EXPECTED)
  • 路由信息(路径、接口、成员、目标、发送者)
  • 序列号和回复序列号
  • 消息体类型签名

存储方式

  • 使用 DBusString 存储网络字节流格式的数据
  • 包含字段位置缓存(DBusHeaderField 数组)
  • 支持延迟解析和缓存优化
2.3.3 body(消息体)
DBusString body;

类型DBusString(D-Bus 的字节缓冲区)

作用:存储消息的实际参数数据

内容

  • 按照 D-Bus 类型系统序列化的参数
  • 支持基本类型(整数、字符串等)
  • 支持复杂类型(数组、结构体、字典等)

格式

  • 使用 D-Bus 的编组(marshaling)格式
  • 8 字节对齐
  • 支持字节序转换
2.3.4 locked(锁定标志)
unsignedint locked :1;

类型:位域(1 位布尔值)

作用:标记消息是否已锁定,锁定后不允许修改

锁定时机

  • 调用 dbus_message_lock() 显式锁定
  • 调用 dbus_connection_send() 发送时自动锁定
  • 调用 dbus_message_marshal() 序列化时自动锁定

锁定后的限制

  • 不允许修改头字段
  • 不允许添加或修改参数
  • 不允许删除字段

设计原因

  • 确保网络数据的一致性
  • 防止在发送过程中修改消息
  • 支持消息缓存和重用
2.3.5 in_cache(缓存标志)
#ifndefDBUS_DISABLE_CHECKSunsignedint in_cache :1;#endif

类型:位域(1 位布尔值,仅在调试模式下存在)

作用:标记消息是否在缓存中(调试特性)

使用场景

  • 检测消息是否被意外释放
  • 调试内存管理问题
  • 验证消息生命周期

注意:仅在 DBUS_DISABLE_CHECKS 未定义时编译

2.3.6 counters(计数器列表)
DBusList *counters;long size_counter_delta;

类型

  • countersDBusList*(链表,每个节点包含一个 DBusCounter*
  • size_counter_deltalong(消息大小增量)

作用:跟踪消息的大小和 Unix 文件描述符数量

DBusCounter 的作用

  • 跟踪所有活跃消息的总大小
  • 跟踪所有活跃消息的 Unix fd 总数
  • 用于资源限制和监控

工作原理

  1. 消息添加到计数器时,增加计数器的值
  2. 消息释放时,减少计数器的值
  3. 计数器可以设置阈值,超过时触发通知

使用场景

  • 限制连接的消息大小
  • 监控资源使用
  • 防止内存耗尽

示例代码

// 添加计数器_dbus_message_add_counter(message, counter);// 消息大小变化时,计数器自动更新// size_counter_delta = header.size + body.size// 消息释放时,自动减少计数器
2.3.7 changed_stamp(变更戳)
dbus_uint32_t changed_stamp : CHANGED_STAMP_BITS;// 21 位

类型:位域(21 位无符号整数)

作用:检测迭代器是否失效

CHANGED_STAMP_BITS:定义为 21,意味着可以表示 2^21 = 2,097,152 个不同的状态

工作原理

  1. 消息创建时,changed_stamp 初始化为某个值
  2. 消息修改时(添加参数、修改字段等),changed_stamp 递增
  3. 迭代器创建时,保存当前的 changed_stamp
  4. 使用迭代器时,检查 changed_stamp 是否匹配
  5. 如果不匹配,说明消息已被修改,迭代器失效

失效场景

  • 消息被锁定后修改
  • 消息被复制
  • 消息被序列化/反序列化

设计原因

  • 防止使用无效的迭代器
  • 检测并发修改
  • 提供调试信息
2.3.8 slot_list(数据槽列表)
DBusDataSlotList slot_list;

类型DBusDataSlotList(数据槽列表)

作用:通过整数 ID 存储任意数据

数据槽机制

  • 每个消息可以分配多个数据槽
  • 每个数据槽有一个唯一的整数 ID
  • 可以存储任意指针数据
  • 支持设置释放函数

使用场景

  • 存储消息相关的上下文信息
  • 实现消息过滤和路由
  • 缓存解析结果
  • 关联用户数据

API

// 分配数据槽dbus_message_allocate_data_slot(&slot_id);// 设置数据dbus_message_set_data(message, slot_id, data, free_func);// 获取数据void*data =dbus_message_get_data(message, slot_id);// 释放数据槽dbus_message_free_data_slot(&slot_id);
2.3.9 generation(生成号)
#ifndefDBUS_DISABLE_CHECKSint generation;#endif

类型int(仅在调试模式下存在)

作用:标记消息创建时的全局生成号

使用场景

  • 检测消息是否来自旧的生命周期
  • 调试内存管理问题
  • 验证消息有效性

注意:仅在 DBUS_DISABLE_CHECKS 未定义时编译

2.3.10 unix_fds(Unix 文件描述符)
#ifdefHAVE_UNIX_FD_PASSINGint*unix_fds;unsigned n_unix_fds;unsigned n_unix_fds_allocated;long unix_fd_counter_delta;#endif

类型

  • unix_fdsint*(文件描述符数组)
  • n_unix_fdsunsigned(有效 fd 数量)
  • n_unix_fds_allocatedunsigned(数组分配大小)
  • unix_fd_counter_deltalong(fd 计数器增量)

作用:支持通过消息传递 Unix 文件描述符

生命周期

  • 添加 fd 时,需要 dup() 文件描述符
  • 消息销毁时,自动关闭所有关联的 fd
  • 传递 fd 时,使用 SCM_RIGHTS 控制消息

使用场景

  • 传递文件句柄
  • 传递 socket
  • 传递管道

限制

  • 仅在支持 Unix fd 传递的系统上可用
  • 需要特殊的传输机制(Unix socket)
  • 有最大数量限制

2.4 内存布局

DBusMessage 结构的内存布局(简化):

+-------------------+ | refcount (4/8) | <- 引用计数(原子整数) +-------------------+ | header | <- DBusHeader 结构 | - data | - DBusString(网络数据) | - fields[11] | - DBusHeaderField 数组(缓存) | - padding | - 填充和字节序 | - byte_order | +-------------------+ | body | <- DBusString(参数数据) +-------------------+ | locked (1 bit) | <- 锁定标志 | in_cache (1 bit) | <- 缓存标志(调试) +-------------------+ | counters | <- DBusList*(计数器链表) | size_counter_delta| <- long(大小增量) +-------------------+ | changed_stamp | <- 21 位(变更戳) +-------------------+ | slot_list | <- DBusDataSlotList(数据槽) +-------------------+ | generation | <- int(生成号,调试) +-------------------+ | unix_fds | <- int*(文件描述符数组) | n_unix_fds | <- unsigned(有效数量) | n_unix_fds_alloc | <- unsigned(分配大小) | unix_fd_counter | <- long(fd 计数器增量) +-------------------+ 

大小估算(64 位系统):

  • 基本结构:约 100-200 字节
  • header.data:可变(取决于字段数量)
  • body:可变(取决于参数大小)
  • unix_fds:可变(取决于 fd 数量)

2.5 字段之间的关系

2.5.1 header 和 body
  • 分离存储:header 和 body 使用独立的 DBusString
  • 独立管理:可以独立重新分配内存
  • 网络格式:直接使用网络字节流格式,无需转换
2.5.2 locked 和修改操作
  • 锁定前:可以自由修改 header 和 body
  • 锁定后:所有修改操作都会失败或触发断言
2.5.3 counters 和资源管理
  • 添加消息:增加计数器的值
  • 释放消息:减少计数器的值
  • 大小跟踪size_counter_delta 存储消息大小
2.5.4 changed_stamp 和迭代器
  • 创建迭代器:保存当前的 changed_stamp
  • 修改消息:递增 changed_stamp
  • 使用迭代器:检查 changed_stamp 是否匹配

3. 设计原则

3.1 不透明类型

  • 封装:隐藏内部实现细节
  • 稳定性:允许内部结构变化
  • 安全性:防止直接修改

3.2 引用计数

  • 共享:多个引用可以共享同一消息
  • 自动管理:引用计数为 0 时自动释放
  • 线程安全:使用原子操作

3.3 网络格式存储

  • 零拷贝:直接使用网络字节流格式
  • 高效:无需格式转换
  • 对齐:支持 8 字节对齐

3.4 延迟解析

  • 缓存:字段位置缓存
  • 按需解析:只在需要时解析字段
  • 性能优化:避免不必要的解析

Read more

DeepFace深度学习库+OpenCV实现——情绪分析器

DeepFace深度学习库+OpenCV实现——情绪分析器

目录 应用场景 实现组件 1. 硬件组件 2. 软件库与依赖 3. 功能模块 代码详解(实现思路) 导入必要的库 打开摄像头并初始化变量 主循环 FPS计算 情绪分析及结果展示 显示FPS和图像 退出条件 编辑 完整代码 效果展示 自然的 开心的 伤心的 恐惧的 惊讶的  效果展示 自然的 开心的 伤心的 恐惧的 惊讶的   应用场景         应用场景比较广泛,尤其是在需要了解和分析人类情感反应的场合。: 1. 心理健康评估:在心理健康领域,可以通过长期监控和分析一个人的情绪变化来辅助医生进行诊断或治疗效果评估。 2. 用户体验研究:在产品设计、广告制作或网站开发过程中,通过观察用户在使用过程中的情绪反应,来优化产品的用户体验。 3. 互动娱乐:在游戏或虚拟现实应用中,根据玩家的情绪状态动态调整游戏难度或故事情节,以增加沉浸感和互动性。

By Ne0inhk
[DeepSeek] 入门详细指南(上)

[DeepSeek] 入门详细指南(上)

前言 今天的是 zty 写DeepSeek的第1篇文章,这个系列我也不知道能更多久,大约是一周一更吧,然后跟C++的知识详解换着更。 来冲个100赞兄弟们 最近啊,浙江出现了一匹AI界的黑马——DeepSeek。这个名字可能对很多人来说还比较陌生,但它已经在全球范围内引发了巨大的关注,甚至让一些科技巨头感到了压力。简单来说这 DeepSeek足以改变世界格局                                                   先   赞   后   看    养   成   习   惯  众所周知,一篇文章需要一个头图                                                   先   赞   后   看    养   成   习   惯   上面那行字怎么读呢,让大家来跟我一起读一遍吧,先~赞~后~看~养~成~习~惯~ 想要 DeepSeek从入门到精通.pdf 文件的加这个企鹅群:953793685(

By Ne0inhk
最全java面试题及答案(208道)

最全java面试题及答案(208道)

本文分为十九个模块,分别是:「Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM」 ,如下图所示: 共包含 208 道面试题,本文的宗旨是为读者朋友们整理一份详实而又权威的面试清单,下面一起进入主题吧。 Java 基础 1. JDK 和 JRE 有什么区别? * JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java

By Ne0inhk
10分钟打造专属AI助手!ToDesk云电脑/顺网云/海马云操作DeepSeek哪家强?

10分钟打造专属AI助手!ToDesk云电脑/顺网云/海马云操作DeepSeek哪家强?

文章目录 * 一、引言 * 云计算平台概览 * ToDesk云电脑:随时随地用上高性能电脑 * 二 .云电脑初体验 * DeekSeek介绍 * 版本参数与特点 * 任务类型表现 * 1、ToDesk云电脑 * 2、顺网云电脑 * 3、海马云电脑 * 三、DeekSeek本地化实操和AIGC应用 * 1. ToDesk云电脑 * 2. 海马云电脑 * 3、顺网云电脑 * 四、结语 * 总结:云电脑如何选择? 一、引言 DeepSeek这些大模型让 AI 开发变得越来越有趣,但真要跑起来,可没那么简单! * 本地配置太麻烦:显卡不够、驱动难装、环境冲突,光是折腾这些就让人心态崩了。 * 云端性能参差不齐:选错云电脑,可能卡到爆、加载慢,还容易掉线,搞得效率直线下降。 * 成本难控:有的平台按小时计费,价格一会儿一个样,

By Ne0inhk