Linux libdrm中drm_syncobj的实现原理

 更新时间:2025年09月19日 09:41:51   作者:DeeplyMind  
本文分析libdrm中drm_syncobj的API实现,涵盖创建销毁、跨进程同步、信号重置及时间线操作,支持多阶段异步任务同步,适用于DMA与GPU渲染等复杂场景,提升同步灵活性与效率

1. 前言

在博文:Linux drm_syncobj 机制原理与应用中介绍了内核drm_syncobj机制的相关原理,本篇是用户态libdrm中的syncobj的API实现分析。

syncobj 的设计初衷是为用户空间提供一种高效、灵活的 GPU 任务同步原语。

它具备如下特点:

  • 对象化管理:同步状态以对象(syncobj)形式存在,用户空间可通过句柄引用和操作。
  • 多次信号/等待:支持多次信号和等待,适合异步和多阶段任务。
  • 跨进程同步:支持同步对象的导入/导出,实现进程间同步。
  • timeline 扩展:部分驱动支持 timeline syncobj,可表示多个时间点的同步状态。

传统 fence 仅能表示单次同步事件,且生命周期受限。而 syncobj 支持多次信号/等待,且可导入/导出,适合复杂的同步场景。timeline syncobj 更是将同步扩展到多时间点,极大提升了灵活性。

2. syncobj 相关 IOCTLs概览与典型流程

syncobj 的所有操作都通过 ioctl 与内核 DRM 驱动交互。

和内核实现一文中的维度划分保持一致,把ioctl划分如下。

维度IOCTL 名称功能描述
创建与销毁DRM_IOCTL_SYNCOBJ_CREATE创建同步对象,分配资源并返回句柄
DRM_IOCTL_SYNCOBJ_DESTROY销毁同步对象,释放资源
导入与导出DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD将同步对象句柄导出为文件描述符,实现跨进程同步
DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE从文件描述符导入同步对象句柄,实现跨进程同步
信号与重置DRM_IOCTL_SYNCOBJ_SIGNAL将同步对象设为已完成(signaled)状态
DRM_IOCTL_SYNCOBJ_RESET将同步对象设为未完成(unsignaled)状态
时间线操作DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL对 timeline syncobj 的指定时间点进行信号
DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT等待 timeline syncobj 的指定时间点完成
DRM_IOCTL_SYNCOBJ_TIMELINE_QUERY查询 timeline syncobj 当前时间点状态
等待与事件通知DRM_IOCTL_SYNCOBJ_WAIT等待同步对象变为已完成状态,支持阻塞/非阻塞及超时
DRM_IOCTL_SYNCOBJ_QUERY查询同步对象的当前状态

典型流程:

  • 创建 syncobj:通过 drmSyncobjCreate 创建同步对象,获得句柄。将该句柄随任务传递给内核驱动。
  • 导入/导出:如需跨进程同步,通过 drmSyncobjHandleToFd 和 drmSyncobjFdToHandle 实现句柄与 fd 的转换。
  • 信号/重置:根据任务完成情况,调用 drmSyncobjSignal 或 drmSyncobjReset 设置同步对象状态。
  • timeline 操作:如需多时间点同步,使用 timeline 相关 API 进行信号、等待和查询。
  • 等待/通知:通过 drmSyncobjWait 或 drmSyncobjTimelineWait 等待任务完成(任务完成后,会signal该任务关联的步骤1的同步对象),驱动后续处理。
  • 销毁:任务完成后,调用 drmSyncobjDestroy 释放资源。

3. 创建与销毁

3.1 创建同步对象

3.1.1 DRM_IOCTL_SYNCOBJ_CREATE

该 ioctl 用于在内核中分配一个同步对象,并返回其句柄。

用户空间通过 libdrm 的 drmSyncobjCreate API 调用。

  • 支持普通 syncobj 和 timeline syncobj 创建
  • 句柄在用户空间唯一标识同步对象
  • 可指定初始状态(已信号/未信号)
drm_public int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle)
{
    struct drm_syncobj_create args;
    int ret;

    memclear(args);
    args.flags = flags;
    args.handle = 0;
    ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_CREATE, &args);
    if (ret)
        return ret;
    *handle = args.handle;
    return 0;
}
  • fd:打开的DRM 设备文件描述符
  • flags:可选标志,如 DRM_SYNCOBJ_CREATE_SIGNALED(初始为已信号状态)、DRM_SYNCOBJ_CREATE_TIMELINE(创建 timeline syncobj)
  • handle:返回的同步对象句柄

3.2 销毁同步对象

3.2.1 DRM_IOCTL_SYNCOBJ_DESTROY

该 ioctl 用于释放同步对象资源。libdrm 封装为 drmSyncobjDestroy

drm_public int drmSyncobjDestroy(int fd, uint32_t handle)
{
    struct drm_syncobj_destroy args;

    memclear(args);
    args.handle = handle;
    return drmIoctl(fd, DRM_IOCTL_SYNCOBJ_DESTROY, &args);
}
  • fd:DRM 设备文件描述符
  • handle:要销毁的同步对象句柄

后续的API我不再给出具体实现,感兴趣的朋友请查看源码。

4. 导入与导出

4.1 导出同步对象到文件描述符

4.1.1 DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD

该 ioctl 用于将 syncobj 句柄导出为文件描述符,实现跨进程同步。

libdrm 封装为 drmSyncobjHandleToFd

int drmSyncobjHandleToFd(int fd, uint32_t handle, int *out_fd);

4.2 从文件描述符导入同步对象

4.2.1 DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE

该 ioctl 用于将文件描述符导入为 syncobj 句柄。

libdrm 封装为 drmSyncobjFdToHandle

drmSyncobjFdToHandle(fd, syncobj_fd, &imported_handle);

5. 信号与重置

5.1 信号同步对象

5.1.1 DRM_IOCTL_SYNCOBJ_SIGNAL

该 ioctl 用于将同步对象设为已完成(signaled)状态。

libdrm 封装为 drmSyncobjSignal

int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t count);

5.2 重置同步对象

5.2.1 DRM_IOCTL_SYNCOBJ_RESET

该 ioctl 用于将同步对象设为未完成(unsignaled)状态。

libdrm 封装为 drmSyncobjReset

int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t count);

6. 时间线操作

这个高级用法,我还没有在项目中用到过,有用例的朋友欢迎分享。

6.1 timeline syncobj 简介

timeline syncobj 是对传统同步对象的扩展,允许一个 syncobj 维护多个时间点(value),每个 value 都可单独信号和等待。

适用于多帧渲染、批量任务调度等复杂场景。

6.2 timeline 信号

6.2.1 DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL

该 ioctl 用于对 timeline syncobj 的指定时间点进行信号。

libdrm 封装为 drmSyncobjTimelineSignal

int drmSyncobjTimelineSignal(int fd, const uint32_t *handles,
                       const uint64_t *points, uint32_t count);
  • fd:DRM 设备文件描述符
  • handles:syncobj 句柄数组
  • points:对应时间点数组
  • count:数量

6.3 timeline 等待

6.3.1 DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT

该 ioctl 用于等待 timeline syncobj 的指定时间点完成。

libdrm 封装为 drmSyncobjTimelineWait

int drmSyncobjTimelineWait(int fd, const uint32_t *handles,
                           const uint64_t *points, uint32_t count,
                           uint64_t timeout_nsec, uint32_t flags,
                           uint32_t *first_signaled);
  • fd:DRM 设备文件描述符
  • handles:syncobj 句柄数组
  • points:对应时间点数组
  • count:数量
  • timeout_nsec:超时时间(纳秒)
  • flags:等待标志
  • first_signaled:返回第一个完成的 syncobj 索引

等待与事件通知

7.1 普通等待

7.1.1 DRM_IOCTL_SYNCOBJ_WAIT

该 ioctl 用于等待普通 syncobj 变为已完成状态。

libdrm 封装为 drmSyncobjWait

  • 支持阻塞和非阻塞等待
  • 可批量等待多个同步对象
int drmSyncobjWait(int fd, const uint32_t *handles, 
                uint32_t count, uint64_t timeout_nsec,
                uint32_t flags, uint32_t *first_signaled);
  • fd:DRM 设备文件描述符
  • handles:syncobj 句柄数组
  • count:数量
  • timeout_nsec:超时时间
  • flags:等待标志(如阻塞/非阻塞)
  • first_signaled:返回第一个完成的 syncobj 索引

8. 应用示例

应用场景:要使用DMA引擎给一个大buffer填充数据,即buffer填充任务;然后把该buffer交给GPU的渲染任务,渲染任务要等待填充任务完成后在进行。

填充任务的执行是一个DMA引擎设备;渲染任务的执行是一个GPU设备。CPU负责给这两个设备发送任务和数据。涉及到跨设备、跨驱动的异步操作的同步问题。

该场景的实现代码如下。

  • 用户态:
uint32_t dma_syncobj;

//创建drm_syncobj对象,获取句柄
drmSyncobjCreate(fd, flags, &dma_syncobj);
//将句柄随transfer任务传递给内核
dma_transfer(bo, size, data, dma_syncobj);

//其他准备工作
.....

//等待transfer的任务完成,即dma_syncobj同步对象被signal
drmSyncobjWait(fd, &dma_syncobj, 1, ...);

submit_render_jobs(fd, jobs);
  • 内核驱动:
do_dma_transfer(bo, size, data, dma_syncobj)
{
    //使用dma引擎传输数据到bo
    dma_transfer(bo, size, data);

    //signal dma_syncobj同步对象
    drm_syncobj_replace_fence(syn_obj, signaled_fence);
}

9. 总结

drm_syncobj 作为 libdrm 用户空间的同步对象抽象,通过一系列 DRM_IOCTL_SYNCOBJ_* ioctl,为开发者提供了高效、灵活的 GPU 任务同步机制。其支持创建与销毁、导入与导出、信号与重置、时间线操作、等待与事件通知等多种操作,适用于多队列渲染、跨进程同步、异步任务调度等多种应用场景。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Ubuntu环境安装Anaconda3完整步骤

    Ubuntu环境安装Anaconda3完整步骤

    大家好,本篇文章主要讲的是Ubuntu环境安装Anaconda3完整步骤,感兴趣的同学快来看一看吧,对你有帮助的话记得收藏一下哦
    2021-11-11
  • Centos7远程桌面 vnc/vnc-server的设置详解

    Centos7远程桌面 vnc/vnc-server的设置详解

    这篇文章主要介绍了Centos7远程桌面 vnc/vnc-server的设置详解的相关资料,需要的朋友可以参考下
    2016-10-10
  • Linux crontab定时任务执行失败处理方案

    Linux crontab定时任务执行失败处理方案

    这篇文章主要介绍了Linux crontab定时任务执行失败处理方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Centos7搭建sftp服务流程

    Centos7搭建sftp服务流程

    本篇文章给大家详细分析了Centos7搭建sftp服务的全过程以及相关命令分享,一起需要下。
    2018-02-02
  • Apache虚拟主机VirtualHost配置项详解

    Apache虚拟主机VirtualHost配置项详解

    在Apache中,VirtualHost容器用于定义一个虚拟主机的配置,它允许在单一的物理服务器上托管多个不同的网站,每个网站可以有自己的域名、文档根目录、错误日志等,这篇文章主要介绍了Apache虚拟主机VirtualHost配置项详解,需要的朋友可以参考下
    2024-07-07
  • 让Apache Shiro保护你的应用

    让Apache Shiro保护你的应用

    本文介绍的Apache Shiro,是一个不同寻常的Java安全框架,为保护应用提供了简单而强大的方法。本文还解释了Apache Shiro的项目目标、架构理念以及如何使用Shiro为应用安全保驾护航
    2014-10-10
  • ubuntu系统下禁用utc时间的设置方法

    ubuntu系统下禁用utc时间的设置方法

    这篇文章主要给大家介绍了在ubuntu系统下禁用utc时间的设置方法,需要的朋友可以参考下
    2017-05-05
  • Linux LVM卷组移除PV的操作过程

    Linux LVM卷组移除PV的操作过程

    这篇文章主要介绍了Linux LVM卷组移除PV的操作过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • shell免交互的实现

    shell免交互的实现

    本文主要介绍了shell免交互的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Linux虚拟机不显示IP地址的解决方法(亲测有效)

    Linux虚拟机不显示IP地址的解决方法(亲测有效)

    本文主要介绍了通过VMware新装的Linux系统没有IP地址的解决方法,主要步骤包括:关闭虚拟机、打开VM虚拟网络编辑器、还原VMnet8或修改网关,解决后,通过ifconfig命令查看IP地址,需要的朋友可以参考下
    2025-03-03

最新评论