3. Display 设备枚举与热插拔监听

Display 设备枚举与热插拔监听 学习卡片

任务说明

本案例实现 Display HAL 模块的 close 函数,用于释放 open 时分配的资源并正确关闭显示设备。该函数是 hw_module_methods_t 中定义的设备关闭入口,由 SurfaceFlinger 在不再需要显示设备时调用。

核心实现

// display_hal.cpp
#include <hardware/hardware.h>
#include <hardware/display.h>
#include <cutils/log.h>
#include <cstdlib>

// 自定义显示设备上下文结构
struct display_device_context {
    int fd;              // 设备文件描述符
    void* mapped_mem;    // mmap 映射的显存地址
    size_t mem_size;     // 映射大小
    bool initialized;    // 初始化标志
};

// 设备关闭函数
static int display_device_close(struct hw_device_t* dev) {
    if (dev == nullptr) {
        ALOGE("display_device_close: null device pointer");
        return -EINVAL;
    }

    display_device_context* ctx = reinterpret_cast<display_device_context*>(dev);
    
    // 1. 释放 mmap 映射
    if (ctx->mapped_mem != nullptr && ctx->mem_size > 0) {
        munmap(ctx->mapped_mem, ctx->mem_size);
        ctx->mapped_mem = nullptr;
        ctx->mem_size = 0;
        ALOGI("display close: munmap done");
    }

    // 2. 关闭设备文件描述符
    if (ctx->fd >= 0) {
        close(ctx->fd);
        ctx->fd = -1;
        ALOGI("display close: fd closed");
    }

    // 3. 释放上下文结构
    ctx->initialized = false;
    free(ctx);
    ALOGI("display close: context freed");

    return 0;
}

// open 函数中注册 close 回调(供参考)
int display_device_open(const struct hw_module_t* module, 
                        const char* name,
                        struct hw_device_t** device) {
    display_device_context* ctx = 
        (display_device_context*)calloc(1, sizeof(display_device_context));
    if (ctx == nullptr) return -ENOMEM;

    ctx->fd = open("/dev/fb0", O_RDWR);
    if (ctx->fd < 0) {
        free(ctx);
        return -errno;
    }

    // 实际项目中在此处进行 mmap 等操作
    ctx->initialized = true;

    // 注册 close 回调
    ctx->common.tag = HARDWARE_DEVICE_TAG;
    ctx->common.version = HARDWARE_DEVICE_API_VERSION(1, 0);
    ctx->common.module = const_cast<hw_module_t*>(module);
    ctx->common.close = display_device_close;

    *device = &ctx->common;
    return 0;
}

关键设计说明

上述实现中,display_device_close 接收 hw_device_t* 指针,通过 reinterpret_cast 将其还原为自定义的 display_device_context 结构。关闭流程按逆序执行:先解除显存映射,再关闭文件描述符,最后释放上下文内存。每个步骤都进行了空指针和有效性检查,避免重复关闭或释放野指针。

SVG:close 调用时序与资源释放流程

SurfaceFlinger 调用 close() display_device_close hw_device_t* 参数 ① 空指针检查 dev == nullptr ? ② munmap 显存 mapped_mem → nullptr ③ close(fd) fd → -1 ④ free(ctx) initialized = false return 0 成功关闭 资源状态跟踪 fd: 有效 → 关闭 mmap: 映射 → 解除 ctx: 已分配 → 已释放 ✓ 无泄漏 验证观测点 dmesg 查看日志 /proc/meminfo 检查 再次 open 可成功

编译与验证

// Android.bp 或 Android.mk 中编译
cc_library_shared {
    name: "display.default",
    srcs: ["display_hal.cpp"],
    shared_libs: ["libhardware", "liblog", "libcutils"],
}

// 验证命令
adb shell dmesg | grep "display close"
// 预期输出(示例):
// display close: munmap done
// display close: fd closed
// display close: context freed

判定标准

通过以下条件判定实现正确:

  • 调用 close() 后返回 0,无段错误或 double-free
  • dmesg 中依次出现 munmap、fd closed、context freed 三条日志
  • 关闭后再次调用 open() 能成功,说明资源已完全释放
  • 使用 valgrind 或 AddressSanitizer 检查无内存泄漏