4. Binder驱动与Android框架的接口

Binder驱动作为Android系统中进程间通信(IPC)的核心枢纽,其与上层框架的接口设计直接决定了系统的稳定性与扩展性。本章深入剖析Binder设备节点的创建与访问机制、驱动层提供的ioctl接口体系,以及驱动与ServiceManager的交互流程,为多平台移植适配奠定基础。

4.1 Binder设备节点:/dev/binder 的创建与访问

在Android系统中,Binder驱动通过字符设备节点 /dev/binder 暴露给用户空间。该节点的创建是驱动初始化的关键步骤,其访问权限控制着系统服务的通信能力。

4.1.1 设备节点创建流程

Binder设备节点的创建发生在驱动模块加载阶段,由内核的 misc_register() 机制完成。以下是核心代码路径:

// drivers/android/binder.c
static int __init binder_init(void)
{
    int ret;
    // 创建binder设备节点
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    if (!binder_deferred_workqueue)
        return -ENOMEM;

    // 注册misc设备,自动创建/dev/binder
    ret = misc_register(&binder_miscdev);
    if (ret < 0) {
        destroy_workqueue(binder_deferred_workqueue);
        return ret;
    }
    return 0;
}

static struct miscdevice binder_miscdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "binder",
    .fops = &binder_fops
};

设备节点的关键属性如下表所示:

属性 值 说明
设备路径 /dev/binder 标准字符设备节点
主设备号 10 (MISC_MAJOR) misc设备共用主设备号
次设备号 动态分配 由内核自动分配
文件操作 binder_fops 包含open/release/ioctl/mmap等
默认权限 0666 允许所有进程访问

4.1.2 用户空间访问控制

在Android框架中,/dev/binder 的访问权限通过SELinux策略严格控制。以下是典型的SELinux规则:

# external/sepolicy/binder.te
type binder_device, dev_type;
allow servicemanager binder_device:chr_file { open read write ioctl };
allow system_app binder_device:chr_file { open read write ioctl };
allow untrusted_app binder_device:chr_file { open read write ioctl };
移植注意: 在非Android平台(如标准Linux)移植Binder驱动时,需要手动创建设备节点并设置权限。建议使用udev规则或init脚本在系统启动时执行:mknod /dev/binder c 10 231 && chmod 0666 /dev/binder。

4.2 Binder驱动ioctl接口详解

Binder驱动通过ioctl系统调用提供丰富的控制接口,用户空间通过 ioctl(fd, cmd, arg) 与驱动交互。所有ioctl命令定义在 linux/binder.h 头文件中。

4.2.1 ioctl命令分类

Binder驱动定义了以下核心ioctl命令,按功能分为三大类:

命令宏 命令码 方向 功能描述
BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) 双向 核心读写操作,发送/接收Binder事务
BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) 写入 设置空闲超时时间(已废弃)
BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) 写入 设置线程池最大线程数
BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32) 写入 设置空闲优先级(已废弃)
BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) 写入 注册当前进程为Context Manager(ServiceManager)
BINDER_THREAD_EXIT _IOW('b', 8, __s32) 写入 通知驱动Binder线程退出
BINDER_VERSION _IOWR('b', 9, struct binder_version) 双向 获取驱动版本信息
BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) 双向 获取Binder节点调试信息
BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) 双向 根据引用获取节点信息
BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) 写入 扩展版Context Manager注册
BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) 写入 冻结/解冻Binder节点
BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) 双向 获取冻结状态信息

4.2.2 核心ioctl:BINDER_WRITE_READ

BINDER_WRITE_READ 是Binder驱动最核心的ioctl命令,它封装了所有事务的发送与接收。其数据结构如下:

struct binder_write_read {
    // 写入缓冲区
    struct binder_transaction_data *write_buffer;
    unsigned long write_size;    // 写入数据大小
    unsigned long write_consumed; // 已消耗的写入数据

    // 读取缓冲区
    struct binder_transaction_data *read_buffer;
    unsigned long read_size;     // 读取缓冲区大小
    unsigned long read_consumed; // 已读取的数据量
};

用户空间通过填充 binder_write_read 结构体,将事务数据写入驱动,同时从驱动读取待处理的事务响应。驱动内部通过 binder_ioctl_write_read() 函数处理该命令:

static long binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    struct binder_proc *proc = filp->private_data;
    struct binder_write_read bwr;
    int ret;

    // 从用户空间拷贝bwr结构
    if (copy_from_user(&bwr, (void __user *)arg, sizeof(bwr)))
        return -EFAULT;

    // 处理写入数据
    if (bwr.write_size > 0) {
        ret = binder_thread_write(proc, thread,
                      bwr.write_buffer,
                      bwr.write_size,
                      &bwr.write_consumed);
        if (ret < 0 && bwr.read_consumed == 0)
            return ret;
    }

    // 处理读取数据
    if (bwr.read_size > 0) {
        ret = binder_thread_read(proc, thread,
                     bwr.read_buffer,
                     bwr.read_size,
                     &bwr.read_consumed,
                     filp->f_flags & O_NONBLOCK);
    }

    // 将结果写回用户空间
    if (copy_to_user((void __user *)arg, &bwr, sizeof(bwr)))
        return -EFAULT;

    return 0;
}

4.2.3 ioctl调用流程

关键流程: 用户空间调用ioctl → 驱动解析命令码 → 分发到对应处理函数 → 操作内核数据结构 → 返回结果。

以下SVG图展示了Binder ioctl的完整调用链路:

Binder ioctl调用流程 Binder ioctl调用流程 用户空间进程 sys_ioctl() VFS vfs_ioctl() binder_ioctl() 命令分发器 binder_ioctl_write_read() binder_ioctl_set_ctx_mgr() binder_ioctl_get_version() 其他处理函数 说明:用户空间通过ioctl系统调用进入内核,VFS层根据文件描述符找到binder_fops, 最终调用binder_ioctl()进行命令分发,由具体处理函数完成操作。

4.3 Binder驱动与ServiceManager的交互

ServiceManager是Android系统中所有Binder服务的注册中心,它作为第一个启动的Binder服务,拥有特殊的Context Manager身份。Binder驱动为ServiceManager提供了特权接口,使其能够管理全局服务注册表。

4.3.1 ServiceManager注册机制

ServiceManager通过 BINDER_SET_CONTEXT_MGR ioctl命令向驱动注册自身为Context Manager。该操作只能执行一次,后续尝试会返回错误。

// frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
    struct binder_state *bs;
    // 打开binder设备
    bs = binder_open("/dev/binder", 128*1024);
    if (!bs) {
        ALOGE("failed to open binder device\n");
        return -1;
    }
    
    // 注册为Context Manager
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    
    // 进入事件循环
    binder_loop(bs, svcmgr_handler);
    return 0;
}

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

4.3.2 驱动侧Context Manager处理

当驱动收到 BINDER_SET_CONTEXT_MGR 命令时,执行以下关键逻辑:

static long binder_ioctl_set_ctx_mgr(struct file *filp,
                    struct binder_thread *thread)
{
    struct binder_proc *proc = filp->private_data;
    struct binder_context *context = proc->context;
    struct binder_node *new_node;
    struct binder_node *context_mgr_node;
    int ret = 0;

    // 检查是否已存在Context Manager
    mutex_lock(&context->context_mgr_node_lock);
    context_mgr_node = context->binder_context_mgr_node;
    if (context_mgr_node) {
        // 已存在,返回错误
        ret = -EBUSY;
        goto out;
    }

    // 创建新的Binder节点表示ServiceManager
    new_node = binder_new_node(proc, NULL);
    if (!new_node) {
        ret = -ENOMEM;
        goto out;
    }

    // 设置节点为Context Manager
    new_node->local_weak_refs++;
    new_node->local_strong_refs++;
    new_node->has_strong_ref = 1;
    new_node->has_weak_ref = 1;
    context->binder_context_mgr_node = new_node;

out:
    mutex_unlock(&context->binder_context_mgr_node_lock);
    return ret;
}

4.3.3 服务注册与查询流程

ServiceManager维护一个服务名称到Binder引用的映射表。服务注册和查询的交互流程如下:

ServiceManager服务注册与查询流程 ServiceManager服务注册与查询流程 服务提供者 (如MediaServer) 服务使用者 (如Camera App) Binder驱动 (内核空间) ServiceManager (Context Manager) 服务注册表 "media.player" → handle=1 "camera" → handle=2 ① 注册服务 ② 转发注册请求 ③ 存储服务信息 ④ 查询服务 ⑤ 转发查询 ⑥ 返回句柄 ⑦ 返回句柄 说明:所有服务注册和查询都通过Binder驱动中转,ServiceManager作为中心节点管理全局服务映射。

4.3.4 服务注册数据结构

ServiceManager内部使用红黑树或哈希表维护服务注册信息,核心数据结构如下:

// frameworks/native/cmds/servicemanager/service_manager.c
struct svcinfo {
    struct svcinfo *next;       // 链表指针
    struct svcinfo *prev;       // 前驱指针
    unsigned int handle;        // Binder引用句柄
    struct binder_death death;  // 死亡通知
    int allow_isolated;         // 是否允许隔离进程访问
    size_t len;                 // 服务名称长度
    uint16_t name[0];           // 服务名称(变长数组)
};
移植要点: 在多平台移植时,ServiceManager与驱动的交互接口是固定的。需要确保:
  • BINDER_SET_CONTEXT_MGR 命令在驱动中正确实现
  • 驱动能够识别并特殊处理Context Manager节点
  • 服务注册和查询的事务格式与Android框架兼容

4.3.5 关键交互场景对比

场景 发起方 Binder事务码 驱动处理 ServiceManager处理
服务注册 服务提供者 SVC_MGR_ADD_SERVICE 转发到Context Manager节点 存储名称→句柄映射
服务查询 服务使用者 SVC_MGR_GET_SERVICE 转发到Context Manager节点 查找并返回句柄
服务检查 服务使用者 SVC_MGR_CHECK_SERVICE 转发到Context Manager节点 查找并返回句柄(不等待)
服务列表 调试工具 SVC_MGR_LIST_SERVICES 转发到Context Manager节点 返回所有已注册服务名称

4.4 本章小结

Binder驱动与Android框架的接口设计体现了清晰的分层架构:

  • 设备节点层: /dev/binder 作为用户空间与内核空间的桥梁,通过misc设备机制创建,权限由SELinux控制
  • ioctl接口层: 提供12个标准命令,其中 BINDER_WRITE_READ 是核心数据通道,BINDER_SET_CONTEXT_MGR 是特权注册接口
  • ServiceManager交互层: 驱动为Context Manager提供特殊节点标识,所有服务注册/查询都通过驱动中转,形成星型拓扑结构

在多平台移植时,这三层接口必须完整实现,任何一层的缺失或行为不一致都会导致Android框架无法正常运行。