4. Binder ioctl命令封装

任务目标

实现一个轻量级库函数,封装Binder驱动交互所需的ioctl系统调用(BINDER_WRITE_READ、BINDER_VERSION),对外提供简洁的读写接口,隐藏struct binder_write_read的构造细节与ioctl调用样板代码。

核心实现

// binder_ioctl_wrapper.h
#ifndef BINDER_IOCTL_WRAPPER_H
#define BINDER_IOCTL_WRAPPER_H

#include <linux/types.h>
#include <linux/binder.h>

/* 封装BINDER_WRITE_READ:向binder驱动提交写缓冲并读取响应 */
int binder_write_read(int fd, void *write_buf, size_t write_len,
                      void *read_buf, size_t read_len);

/* 封装BINDER_VERSION:获取binder驱动协议版本 */
int binder_get_version(int fd, struct binder_version *ver);

#endif
// binder_ioctl_wrapper.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "binder_ioctl_wrapper.h"

int binder_write_read(int fd, void *write_buf, size_t write_len,
                      void *read_buf, size_t read_len)
{
    struct binder_write_read bwr;

    memset(&bwr, 0, sizeof(bwr));

    if (write_buf && write_len > 0) {
        bwr.write_buffer = (unsigned long)write_buf;
        bwr.write_size = write_len;
        bwr.write_consumed = 0;
    }

    if (read_buf && read_len > 0) {
        bwr.read_buffer = (unsigned long)read_buf;
        bwr.read_size = read_len;
        bwr.read_consumed = 0;
    }

    if (ioctl(fd, BINDER_WRITE_READ, &bwr) < 0)
        return -errno;

    return 0;
}

int binder_get_version(int fd, struct binder_version *ver)
{
    if (!ver)
        return -EINVAL;

    memset(ver, 0, sizeof(*ver));

    if (ioctl(fd, BINDER_VERSION, ver) < 0)
        return -errno;

    return 0;
}

调用示例

// demo.c —— 演示封装接口的用法
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/binder.h>
#include "binder_ioctl_wrapper.h"

int main(void)
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd < 0) {
        perror("open /dev/binder");
        return 1;
    }

    struct binder_version ver;
    int ret = binder_get_version(fd, &ver);
    if (ret < 0) {
        fprintf(stderr, "BINDER_VERSION failed: %s\n", strerror(-ret));
        close(fd);
        return 1;
    }
    printf("Binder protocol version: %d\n", ver.protocol_version);

    /* 构造一个BC_ENTER_LOOPER命令写入驱动 */
    uint32_t cmd = BC_ENTER_LOOPER;
    ret = binder_write_read(fd, &cmd, sizeof(cmd), NULL, 0);
    if (ret < 0) {
        fprintf(stderr, "BINDER_WRITE_READ failed: %s\n", strerror(-ret));
        close(fd);
        return 1;
    }

    printf("BC_ENTER_LOOPER sent successfully\n");
    close(fd);
    return 0;
}

编译与运行

# 编译(需Linux内核头文件,Android系统自带或通过apt安装linux-headers获取)
gcc -o demo demo.c binder_ioctl_wrapper.c -I/usr/include

# 运行(需要root权限或设备具有binder访问权限)
sudo ./demo

验证方法

  • 预期输出:程序打印Binder protocol version: 8(或当前内核实际支持的版本号),随后打印BC_ENTER_LOOPER sent successfully。
  • 通过条件:ioctl调用返回0,无错误信息输出;若返回Operation not permitted,说明当前用户无binder设备访问权限,需使用sudo或确认设备节点权限。
  • 错误排查:若open失败,确认内核已加载binder驱动且/dev/binder节点存在(可执行ls -l /dev/binder检查)。

关键说明

  • 封装函数返回0表示成功,返回负的错误码(-errno)表示失败,便于调用方用strerror输出可读错误信息。
  • write_buf或read_buf传入NULL且长度为0时,对应字段不填充,驱动会忽略该方向的操作——这允许只读或只写场景。
  • 实际使用中write_buf通常指向一个包含struct binder_transaction_data的struct binder_transaction_data_on_stack缓冲区,本封装不关心缓冲区内部结构,仅负责传递用户缓冲区和长度。
Binder ioctl 封装调用链 用户进程 demo.c 构造命令/数据 调用封装接口 封装库 binder_write_read() binder_get_version() 填充binder_write_read 系统调用 ioctl(fd, cmd, arg) BINDER_WRITE_READ BINDER_VERSION 内核 Binder 驱动 /dev/binder 处理事务、维护线程池、节点管理 返回0/错误码 错误处理 返回 -errno 调用方用 strerror 输出可读信息