Skip to content

Binder ioctl入口

沿 Binder ioctl 总入口追踪线程绑定、命令分派、配置状态、错误返回和 BINDER_WRITE_READ 边界。

基于android-17.0.0_r1
AndroidBinderioctl驱动源码阅读

Binder ioctl入口 ​

打开 Binder 设备并完成映射后,用户态并不会调用许多不同的系统调用来管理 Binder。线程注册、 事务读写、协议版本查询、线程数配置、context manager 注册、冻结状态查询和扩展错误读取,都会 从同一个字符设备 fd 进入 binder_ioctl()。

本文面向已经读过 Binder设备打开、Binder内存映射 和 Binder线程状态模型 的读者,回答 “一个 ioctl 到达驱动后,谁拥有状态,命令如何选择消费者,失败时如何回到用户态”。

文章不把 BINDER_WRITE_READ 的全部 BC/BR 命令展开成事务专题;它只追踪这个命令在总入口中的 位置,并把读写细节留给后续文章。也不把 UAPI 中存在但当前 switch 没有实现的命令写成可用能力。

1. 入口分层 ​

Binder ioctl 有三层边界:

层代表对象负责什么
用户态包装ProcessState、IPCThreadState选择命令、填充参数、处理 errno
总入口binder_ioctl()绑定 file/proc/thread、选择命令消费者、统一收尾
子处理器binder_ioctl_write_read() 等读取参数、改变具体状态、向用户空间回写结果

因此 binder_ioctl() 不是所有 Binder 逻辑的实现位置。它拥有的是“入口控制权”: filp->private_data 指向进程,当前 TID 找到线程,cmd 决定子函数;事务队列、node/ref、冻结 状态和扩展错误仍由各自 owner 管理。

2. 用户调用 ​

2.1 启动配置 ​

源码文件:frameworks/native/libs/binder/ProcessState.cpp

相关函数:open_driver()

cpp
static unique_fd open_driver(
        const char* driver,
        String8* error) {
    auto fd = unique_fd(
            open(driver, O_RDWR | O_CLOEXEC));
    if (!fd.ok())
        return {};

    int vers = 0;
    int result = ioctl(
            fd.get(), BINDER_VERSION, &vers);
    if (result == -1)
        return {};
    if (result != 0 ||
            vers != BINDER_CURRENT_PROTOCOL_VERSION)
        return {};

    size_t maxThreads =
            DEFAULT_MAX_BINDER_THREADS;
    result = ioctl(
            fd.get(), BINDER_SET_MAX_THREADS,
            &maxThreads);
    if (result == -1)
        ALOGE("Binder ioctl to set max threads failed");

    uint32_t enable =
            DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;
    result = ioctl(
            fd.get(),
            BINDER_ENABLE_ONEWAY_SPAM_DETECTION,
            &enable);
    if (result == -1)
        ALOGE_IF(
            ProcessState::isDriverFeatureEnabled(
                ProcessState::DriverFeature::
                    ONEWAY_SPAM_DETECTION),
            "Binder ioctl to enable ... failed");
    return fd;
}

open_driver() 展示了 ioctl 的两个重要语义:BINDER_VERSION 是失败即放弃的协商,而设置 最大线程和 oneway spam detection 是失败记录日志后继续。不能把“调用过 ioctl”统称为“配置成功”; 每个命令的失败策略由用户态调用方决定。

BINDER_VERSION 和 BINDER_SET_MAX_THREADS 发生在 ProcessState 构造期间,后续线程池启动再 通过同一个 fd 进入 BINDER_WRITE_READ。这解释了为什么总入口必须按当前 fd 找到准确的 proc。

2.2 线程读写 ​

源码文件:frameworks/native/libs/binder/IPCThreadState.cpp

相关函数:IPCThreadState::talkWithDriver()

cpp
status_t IPCThreadState::talkWithDriver(
        bool doReceive) {
    binder_write_read bwr;
    const size_t outAvail = mOut.dataSize();

    bwr.write_size = outAvail;
    bwr.write_buffer =
        reinterpret_cast<binder_uintptr_t>(
            mOut.data());
    if (doReceive) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer =
            reinterpret_cast<binder_uintptr_t>(
                mIn.data());
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    if (ioctl(mProcess->getDriverFD(),
              BINDER_WRITE_READ, &bwr) >= 0) {
        if (bwr.write_consumed > 0)
            mOut.remove(bwr.write_consumed);
        if (bwr.read_consumed > 0)
            mIn.setDataSize(bwr.read_consumed);
        return NO_ERROR;
    }
    return -errno;
}

talkWithDriver() 的 owner 是线程级 IPCThreadState:它提供命令缓冲、接收缓冲和进程级 driver fd;内核 binder_ioctl() 再把同一个调用绑定到 binder_thread。write_consumed 和 read_consumed 是双向契约,用户态据此移动自己的命令游标。

这里的 BINDER_WRITE_READ 不是普通“写命令 ioctl”。同一次调用可以只写、只读,或先写后读; 具体顺序由 binder_ioctl_write_read() 根据两个 size 字段决定。

3. 线程绑定 ​

3.1 入口准备 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl()

c
static long binder_ioctl(
        struct file *filp,
        unsigned int cmd,
        unsigned long arg) {
    int ret;
    struct binder_proc *proc =
            filp->private_data;
    struct binder_thread *thread;
    void __user *ubuf =
            (void __user *)arg;

    trace_binder_ioctl(cmd, arg);
    ret = wait_event_interruptible(
            binder_user_error_wait,
            binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;

    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

入口有两个 owner:proc 来自 file,thread 来自当前 Linux TID。binder_get_thread() 首次看到 这个 TID 时才在 proc 的线程红黑树中创建 binder_thread;因此一次 BINDER_VERSION ioctl 也可能是某线程第一次在驱动建立线程记录的时刻。

binder_user_error_wait 是调试暂停阀。当 binder_stop_on_user_error >= 2 时,ioctl 在命令 分派前等待;这不是业务请求超时,也不是 binder_thread_read() 的工作等待。

3.2 查找创建 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_get_thread()、binder_get_thread_ilocked()

c
static struct binder_thread *binder_get_thread(
        struct binder_proc *proc) {
    struct binder_thread *thread;
    struct binder_thread *new_thread;

    binder_inner_proc_lock(proc);
    thread = binder_get_thread_ilocked(
            proc, NULL);
    binder_inner_proc_unlock(proc);
    if (!thread) {
        new_thread = kzalloc(
                sizeof(*thread), GFP_KERNEL);
        if (new_thread == NULL)
            return NULL;
        binder_inner_proc_lock(proc);
        thread = binder_get_thread_ilocked(
                proc, new_thread);
        binder_inner_proc_unlock(proc);
        if (thread != new_thread)
            kfree(new_thread);
    }
    return thread;
}

先查找、再无锁分配、重新加锁插入,避免在 proc inner lock 下执行可能睡眠的内存分配。第二次 查找还处理竞态:若其他路径已经插入同 TID 的对象,临时分配的 new_thread 会被释放。

这也是总入口的隐藏结果:命令即使只读取版本或设置线程上限,也可能创建 binder_thread。 不能用“只有发送事务才有线程状态”解释驱动中的线程统计。

3.3 初始字段 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_get_thread_ilocked()

c
thread = new_thread;
binder_stats_created(BINDER_STAT_THREAD);
thread->proc = proc;
thread->pid = current->pid;
get_task_struct(current);
thread->task = current;
atomic_set(&thread->tmp_ref, 0);
init_waitqueue_head(&thread->wait);
INIT_LIST_HEAD(&thread->todo);
rb_link_node(&thread->rb_node, parent, p);
rb_insert_color(&thread->rb_node,
                &proc->threads);
thread->looper_need_return = true;
thread->return_error.work.type =
        BINDER_WORK_RETURN_ERROR;
thread->return_error.cmd = BR_OK;
thread->reply_error.work.type =
        BINDER_WORK_RETURN_ERROR;
thread->reply_error.cmd = BR_OK;
spin_lock_init(&thread->prio_lock);
thread->prio_state = BINDER_PRIO_SET;
thread->ee.command = BR_OK;
INIT_LIST_HEAD(
        &new_thread->waiting_thread_node);

looper_need_return = true 是新线程的初始特殊工作状态;命令收尾时总入口会把它清为 false。 return_error、reply_error 和 extended error 先处于 BR_OK,避免第一次读循环看到未初始化 的错误命令。

4. 命令分派 ​

4.1 UAPI集合 ​

源码文件:kernel/common/include/uapi/linux/android/binder.h

相关定义:Binder ioctl 枚举

c
enum {
    BINDER_WRITE_READ =
        _IOWR('b', 1,
              struct binder_write_read),
    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),
    BINDER_THREAD_EXIT =
        _IOW('b', 8, __s32),
    BINDER_VERSION =
        _IOWR('b', 9,
              struct binder_version),
    BINDER_GET_NODE_DEBUG_INFO =
        _IOWR('b', 11,
              struct binder_node_debug_info),
    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),
    BINDER_FREEZE =
        _IOW('b', 14,
             struct binder_freeze_info),
    BINDER_GET_FROZEN_INFO =
        _IOWR('b', 15,
              struct binder_frozen_status_info),
    BINDER_ENABLE_ONEWAY_SPAM_DETECTION =
        _IOW('b', 16, __u32),
    BINDER_GET_EXTENDED_ERROR =
        _IOWR('b', 17,
              struct binder_extended_error),
};

UAPI 有命令号不等于驱动当前实现了该命令。当前 binder_ioctl() switch 没有处理 BINDER_SET_IDLE_TIMEOUT 和 BINDER_SET_IDLE_PRIORITY,会落入 default 返回 -EINVAL。 这类“定义存在、入口不消费”的差异必须从 switch 和测试一起确认。

4.2 总分派 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl()

c
switch (cmd) {
case BINDER_WRITE_READ:
    ret = binder_ioctl_write_read(
            filp, arg, thread);
    if (ret)
        goto err;
    break;
case BINDER_SET_MAX_THREADS: {
    u32 max_threads;

    if (copy_from_user(&max_threads,
                       ubuf,
                       sizeof(max_threads))) {
        ret = -EINVAL;
        goto err;
    }
    binder_inner_proc_lock(proc);
    proc->max_threads = max_threads;
    binder_inner_proc_unlock(proc);
    break;
}
case BINDER_THREAD_EXIT:
    binder_thread_release(proc, thread);
    thread = NULL;
    break;
default:
    ret = -EINVAL;
    goto err;
}

switch 内部的状态 owner 不同:BINDER_SET_MAX_THREADS 写 proc,BINDER_THREAD_EXIT 收束 thread,BINDER_WRITE_READ 同时读写 proc/thread 工作队列。thread = NULL 是线程退出分支的 安全标记,防止统一尾部在已释放的 thread 上写字段。

5. 读写子入口 ​

5.1 参数复制 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_write_read()

c
static int binder_ioctl_write_read(
        struct file *filp,
        unsigned long arg,
        struct binder_thread *thread) {
    int ret = 0;
    struct binder_proc *proc =
            filp->private_data;
    void __user *ubuf =
            (void __user *)arg;
    struct binder_write_read bwr;
    bool has_special_work = false;

    if (copy_from_user(&bwr, ubuf,
                       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;
            goto out;
        }
    }

binder_write_read 是用户态和驱动之间的双向信封,不是事务本身。驱动先复制结构体,再按 write_size 选择写路径;写失败时把 read_consumed 清零,避免用户态把未执行的读阶段当作 有效结果。

5.2 读取回写 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_write_read()

c
    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);

        binder_inner_proc_lock(proc);
        trace_android_vh_binder_has_proc_work_ilocked(
                thread, true,
                &has_special_work);
        if (!binder_worklist_empty_ilocked(
                    &proc->todo) ||
                has_special_work)
            binder_wakeup_proc_ilocked(proc);
        binder_inner_proc_unlock(proc);
        if (ret < 0)
            goto out;
    }
out:
    if (copy_to_user(ubuf, &bwr,
                     sizeof(bwr)))
        ret = -EFAULT;
    return ret;
}

读路径把 read_buffer 交给当前 binder_thread;如果读完后 proc 仍有工作或特殊工作,驱动会 唤醒其他合格消费者。O_NONBLOCK 只改变 binder_thread_read() 的等待行为,不改变总入口分派。

即使内核已经消费部分写命令,尾部 copy_to_user() 失败仍会把 ioctl 变成 -EFAULT。因此返回值 与两个 consumed 字段共同构成契约,不能只看其中一个。

5.3 用户游标 ​

源码文件:frameworks/native/libs/binder/IPCThreadState.cpp

相关函数:IPCThreadState::talkWithDriver()

cpp
if (ioctl(mProcess->getDriverFD(),
          BINDER_WRITE_READ, &bwr) >= 0) {
    if (bwr.write_consumed > 0)
        mOut.remove(bwr.write_consumed);
    if (bwr.read_consumed > 0)
        mIn.setDataSize(
                bwr.read_consumed);
    return NO_ERROR;
}
return -errno;

驱动只报告 consumed,IPCThreadState 才移动 mOut/mIn 游标。一次 ioctl 可以只消费一部分 BC 命令,下次调用继续从未消费位置发送;把返回 0 理解成“所有输出已经发完”会破坏命令缓冲契约。

6. 配置状态 ​

6.1 线程上限 ​

源码文件:kernel/common/drivers/android/binder.c

相关命令:BINDER_SET_MAX_THREADS

c
case BINDER_SET_MAX_THREADS: {
    u32 max_threads;

    if (copy_from_user(&max_threads,
                       ubuf,
                       sizeof(max_threads))) {
        ret = -EINVAL;
        goto err;
    }
    binder_inner_proc_lock(proc);
    proc->max_threads = max_threads;
    binder_inner_proc_unlock(proc);
    break;
}

消费者是后续 looper 读路径:驱动根据 proc->max_threads、已启动线程和等待线程决定是否返回 BR_SPAWN_LOOPER。这个 ioctl 只写一个 proc 字段,不直接创建线程,也不保证线程数量立即变化。

6.2 Oneway开关 ​

源码文件:kernel/common/drivers/android/binder.c

相关命令:BINDER_ENABLE_ONEWAY_SPAM_DETECTION

c
case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: {
    uint32_t enable;

    if (copy_from_user(&enable, ubuf,
                       sizeof(enable))) {
        ret = -EFAULT;
        goto err;
    }
    binder_inner_proc_lock(proc);
    proc->oneway_spam_detection_enabled =
            (bool)enable;
    binder_inner_proc_unlock(proc);
    break;
}

该状态属于 proc,而不是 thread 或 node。后续异步 buffer 分配会结合 allocator 空间读取它; ioctl 返回成功只表示字段更新,不表示已经发现或清除了某个 oneway 事务。

6.3 版本查询 ​

源码文件:kernel/common/drivers/android/binder.c

相关命令:BINDER_VERSION

c
case BINDER_VERSION: {
    struct binder_version __user *ver =
            ubuf;

    if (put_user(
            BINDER_CURRENT_PROTOCOL_VERSION,
            &ver->protocol_version)) {
        ret = -EINVAL;
        goto err;
    }
    break;
}

版本查询只回写协议字段,不读取事务状态。它仍然经过 binder_get_thread(),所以“只读版本”不 意味着“不会建立线程记录”。

7. 管理命令 ​

7.1 Context Manager ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_set_ctx_mgr()

c
static int binder_ioctl_set_ctx_mgr(
        struct file *filp,
        struct flat_binder_object *fbo) {
    int ret = 0;
    struct binder_proc *proc =
            filp->private_data;
    struct binder_context *context =
            proc->context;
    struct binder_node *new_node;
    kuid_t curr_euid = current_euid();

    guard(mutex)(
            &context->context_mgr_node_lock);
    if (context->binder_context_mgr_node)
        return -EBUSY;
    ret = security_binder_set_context_mgr(
            proc->cred);
    if (ret < 0)
        return ret;
    if (uid_valid(
            context->binder_context_mgr_uid)) {
        if (!uid_eq(
                context->binder_context_mgr_uid,
                curr_euid))
            return -EPERM;
    } else {
        context->binder_context_mgr_uid =
                curr_euid;
    }
    new_node = binder_new_node(proc, fbo);
    if (!new_node)
        return -ENOMEM;
    context->binder_context_mgr_node =
            new_node;
    binder_put_node(new_node);
    return ret;
}

BINDER_SET_CONTEXT_MGR 与扩展版进入同一 helper。所有权路径是 proc → context → binder_context_mgr_node;锁、SELinux hook、UID 约束和“只能设置一次”都在 helper 中发生。总入口只负责把不同参数形态转成 fbo 或 NULL。

7.2 线程退出 ​

源码文件:kernel/common/drivers/android/binder.c

相关命令:BINDER_THREAD_EXIT、binder_thread_release()

c
case BINDER_THREAD_EXIT:
    binder_debug(BINDER_DEBUG_THREADS,
            "%d:%d exit\\n",
            proc->pid, thread->pid);
    binder_thread_release(proc, thread);
    thread = NULL;
    break;

线程退出不只是从红黑树删除一个节点。binder_thread_release() 会标记死亡、处理 transaction stack、释放 thread todo 和 poll wait queue,并用临时引用保证清理期间对象仍存活。局部指针置 NULL 是总入口与释放 helper 之间的生命周期协议。

7.3 冻结目标 ​

源码文件:kernel/common/drivers/android/binder.c

相关命令:BINDER_FREEZE

c
case BINDER_FREEZE: {
    struct binder_freeze_info info;
    struct binder_proc **target_procs = NULL;
    struct binder_proc *target_proc;
    int target_procs_count = 0, i = 0;

    if (copy_from_user(&info, ubuf,
                       sizeof(info))) {
        ret = -EFAULT;
        goto err;
    }

    mutex_lock(&binder_procs_lock);
    hlist_for_each_entry(
            target_proc, &binder_procs,
            proc_node) {
        if (target_proc->pid == info.pid)
            target_procs_count++;
    }
    /* 分配数组,并为同 PID 的每个 proc 增加 tmp_ref */
    mutex_unlock(&binder_procs_lock);

    for (i = 0; i < target_procs_count; i++) {
        if (ret >= 0)
            ret = binder_ioctl_freeze(
                    &info, target_procs[i]);
        binder_proc_dec_tmpref(
                target_procs[i]);
    }
    kfree(target_procs);
}

冻结目标不是当前 proc,而是用户传入 PID 对应的所有 binder_proc。驱动在全局锁内收集目标并 增加临时引用,解锁后逐个处理,避免耗时操作占用全局表锁,也防止目标在处理期间被释放。

8. 查询命令 ​

8.1 节点引用 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_get_node_info_for_ref()

c
static int binder_ioctl_get_node_info_for_ref(
        struct binder_proc *proc,
        struct binder_node_info_for_ref *info) {
    struct binder_node *node;
    struct binder_context *context =
            proc->context;
    __u32 handle = info->handle;

    if (info->strong_count ||
            info->weak_count ||
            info->reserved1 ||
            info->reserved2 ||
            info->reserved3)
        return -EINVAL;

    mutex_lock(
            &context->context_mgr_node_lock);
    if (!context->binder_context_mgr_node ||
            context->binder_context_mgr_node->proc
                    != proc) {
        mutex_unlock(
                &context->context_mgr_node_lock);
        return -EPERM;
    }
    mutex_unlock(
            &context->context_mgr_node_lock);

    node = binder_get_node_from_ref(
            proc, handle, true, NULL);
    if (!node)
        return -EINVAL;
    info->strong_count =
            node->local_strong_refs +
            node->internal_strong_refs;
    info->weak_count =
            node->local_weak_refs;
    binder_put_node(node);
    return 0;
}

该查询只能由 context manager proc 使用。入口复制结构体,helper 验证 reserved 字段、权限和 handle,再回写 node 引用状态。它不是任何客户端都能使用的通用引用计数 API。

8.2 节点遍历 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_get_node_debug_info()

c
static int binder_ioctl_get_node_debug_info(
        struct binder_proc *proc,
        struct binder_node_debug_info *info) {
    struct rb_node *n;
    binder_uintptr_t ptr = info->ptr;

    memset(info, 0, sizeof(*info));
    binder_inner_proc_lock(proc);
    for (n = rb_first(&proc->nodes);
            n != NULL; n = rb_next(n)) {
        struct binder_node *node =
            rb_entry(n, struct binder_node,
                     rb_node);
        if (node->ptr > ptr) {
            info->ptr = node->ptr;
            info->cookie = node->cookie;
            info->has_strong_ref =
                    node->has_strong_ref;
            info->has_weak_ref =
                    node->has_weak_ref;
            break;
        }
    }
    binder_inner_proc_unlock(proc);
    return 0;
}

用户把上次返回的 ptr 作为下一次输入,helper 查找更大的 node 指针。memset 让“没有下一个 节点”表现为 ptr == 0。这是一种逐项遍历协议,不是一次性导出整棵树。

8.3 扩展错误 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl_get_extended_error()

c
static int binder_ioctl_get_extended_error(
        struct binder_thread *thread,
        void __user *ubuf) {
    struct binder_extended_error ee;

    binder_inner_proc_lock(thread->proc);
    ee = thread->ee;
    binder_set_extended_error(
            &thread->ee, 0, BR_OK, 0);
    binder_inner_proc_unlock(thread->proc);

    if (copy_to_user(ubuf, &ee,
                     sizeof(ee)))
        return -EFAULT;
    return 0;
}

扩展错误是 thread owner 的一次性槽位:读取时复制当前值,随后清成 BR_OK。读取成功只表示 错误详情已经取走,不代表原事务已重试或恢复。

9. 统一收尾 ​

9.1 返回尾部 ​

源码文件:kernel/common/drivers/android/binder.c

相关函数:binder_ioctl() 结尾

c
    ret = 0;
    trace_android_vh_binder_ioctl_end(
            current, cmd, arg,
            thread, proc, &ret);
err:
    if (thread)
        thread->looper_need_return = false;
    wait_event_interruptible(
            binder_user_error_wait,
            binder_stop_on_user_error < 2);
    if (ret && ret != -EINTR)
        pr_info("%d:%d ioctl %x returned %d\\n",
                proc->pid, current->pid,
                cmd, ret);
err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;

成功路径也经过名为 err: 的标签,标签名不能代替控制流判断。只要 thread 仍存在,入口就清除 looper_need_return;线程退出分支已经将局部指针置 NULL,因此不会访问释放后的对象。

trace_android_vh_binder_ioctl_end 可以修改 ret,是 vendor hook 边界;trace_binder_ioctl_done 则是所有返回路径的共同观测点。文章不能把 switch 中的初始结果当成最终返回值而忽略尾部。

9.2 错误层次 ​

层示例返回方式
等待层调试暂停被信号中断-EINTR,不创建 thread
线程层binder_get_thread() 分配失败-ENOMEM
参数层用户指针或结构体字段无效-EFAULT 或 -EINVAL
状态层manager 已存在、目标不存在-EBUSY、-EINVAL
权限层SELinux、UID、manager 身份不符hook errno 或 -EPERM

这些 errno 直接作为 ioctl 系统调用错误返回。事务协议中的 BR_FAILED_REPLY、BR_DEAD_REPLY 属于 BINDER_WRITE_READ 内的 BR 字节流,不能与总入口 errno 互换。

9.3 两次等待 ​

入口前后各有一次 wait_event_interruptible(binder_user_error_wait, ...)。前一次阻止命令进入 switch;后一次在错误调试状态解除后再返回。后一次的返回值没有覆盖原 ret,不是业务层的自动重试。

10. 测试输入 ​

10.1 版本参数 ​

源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

相关测试:Version、VersionNull、WriteReadNull、SetMaxThreadsNull

cpp
TEST_F(BinderDriverInterfaceTest, Version) {
    struct binder_version version;
    binderTestIoctl(
            BINDER_VERSION, &version);
    ASSERT_EQ(
            BINDER_CURRENT_PROTOCOL_VERSION,
            version.protocol_version);
}

TEST_F(BinderDriverInterfaceTest, WriteReadNull) {
    binderTestIoctlErr1(
            BINDER_WRITE_READ,
            nullptr, EFAULT);
}

TEST_F(BinderDriverInterfaceTest,
       SetMaxThreadsNull) {
    binderTestIoctlErr2(
            BINDER_SET_MAX_THREADS,
            nullptr, EFAULT, EINVAL);
}

测试证明有效版本结构体会被回写,NULL 参数不会被静默接受。SetMaxThreadsNull 接受当前实现的 EFAULT 或 EINVAL 差异,因此不能把某一个 errno 写成跨版本永恒 ABI。

10.2 未实现命令 ​

源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

相关测试:SetIdleTimeoutNoTest、SetIdlePriorityNoTest

cpp
TEST_F(BinderDriverInterfaceTest,
       SetIdleTimeoutNoTest) {
    int64_t idle_timeout = 100000;
    binderTestIoctlUnimplemented(
            BINDER_SET_IDLE_TIMEOUT,
            &idle_timeout);
}

TEST_F(BinderDriverInterfaceTest,
       SetIdlePriorityNoTest) {
    int idle_priority = 0;
    binderTestIoctlUnimplemented(
            BINDER_SET_IDLE_PRIORITY,
            &idle_priority);
}

这两个测试把“UAPI 定义存在”和“当前 switch 实现”分开。helper 允许 -1/EINVAL,与 default 分支相互验证,但不能证明未来版本或厂商驱动仍未实现。

10.3 线程退出 ​

源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

相关测试:ThreadExit

cpp
TEST_F(BinderDriverInterfaceTest, ThreadExit) {
    int32_t dummy = 0;
    binderTestIoctl(
            BINDER_THREAD_EXIT, &dummy);
    static_cast<
            BinderDriverInterfaceTestEnv *>
            (binder_env)->EnterLooper();
}

测试先让当前 thread 离开驱动线程状态,再通过 BC_ENTER_LOOPER 重新进入。它验证 thread exit 后 fd/proc 仍可使用;没有覆盖活动同步事务、嵌套调用或进程 release 期间的线程收束。

11. 源码复现 ​

命令分流 ​

入口、命令分支和用户回写是三层错误边界:命令未知、参数地址错误、子函数权限/状态拒绝会产生不同 errno。

本文主线是:

text
ProcessState/IPCThreadState ioctl
→ binder_fops.unlocked_ioctl
→ binder_ioctl
→ file对应binder_proc
→ 当前TID对应binder_thread
→ switch(cmd)
→ 命令专属owner
→ consumed、状态或errno
→ looper标志与trace收尾

可以在源码 checkout 中执行:

bash
rg -n "talkWithDriver|BINDER_WRITE_READ|ioctl\\(" \\
  frameworks/native/libs/binder/IPCThreadState.cpp \\
  frameworks/native/libs/binder/ProcessState.cpp

rg -n "binder_ioctl\\(|binder_get_thread\\(|case BINDER_" \\
  kernel/common/drivers/android/binder.c

rg -n "BINDER_WRITE_READ|BINDER_SET_IDLE|BINDER_GET_EXTENDED_ERROR" \\
  kernel/common/include/uapi/linux/android/binder.h

rg -n "VersionNull|WriteReadNull|SetIdleTimeoutNoTest|ThreadExit" \\
  frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp

如果能够解释为什么 BINDER_VERSION 也可能创建 binder_thread、未实现 ioctl 为什么返回 errno 而不是 BR 命令,以及 BINDER_THREAD_EXIT 为什么必须把局部 thread 置 NULL,就已经掌握总入口 的控制流。下一篇将沿这里的 binder_ioctl_write_read() 继续拆解写入、读取、部分消费和阻塞边界。