Binder学习笔记(十二)—— binder_transaction(...)都干了什么?

本文详细解析了Android中Binder通信的核心函数binder_transaction,包括binder_open和binder_mmap的准备工作,以及binder_transaction的执行过程,涉及到binder_proc、binder_node、binder_ref等关键数据结构的建立和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

binder_open(…)都干了什么?

在回答binder_transaction(…)之前,还有一些基础设施要去探究,比如binder_open(…),binder_mmap(…),这些调用是在打开设备文件/dev/binder之后必须完成的程式化操作,而在它们内部需要做一些数据结构的准备。首先来看binder_open(…)
kernel/drivers/staging/android/binder.c:2979

static int binder_open(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc;
    ......
    proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 创建binder_proc结构体
    ......
    get_task_struct(current);
    proc->tsk = current;
    INIT_LIST_HEAD(&proc->todo);  // 初始化链表头
    init_waitqueue_head(&proc->wait);   
    proc->default_priority = task_nice(current);

    ......
    // 将proc_node串入全局链表binder_procs中
    hlist_add_head(&proc->proc_node, &binder_procs); 
    proc->pid = current->group_leader->pid;
    INIT_LIST_HEAD(&proc->delivered_death);
    filp->private_data = proc;

    ......
    return 0;
}

binder_open(…)生成并初始化binder_proc结构体如下:
binder_open(...)初始化的binder_proc结构体

struct binder_proc

struct binder_proc描述一个“正在使用Binder进程间通信机制”的进程,它的定义参见kernel/goldfish/drivers/staging/android/binder.c:286

struct binder_proc {
    // 进程打开设备文件/dev/binder时,Binder驱动会为它创建一个binder_proc结构体,并将它
    // 保存在全局hash列表中,proc_node是该hash列表的节点。
    struct hlist_node proc_node;

    // 每个使用了Binder机制的进程都有一个Binder线程池,用来处理进程间通信请求。threads以
    // 线程ID作为key来组织进程的Binder线程池。进程可以调用ioctl将线程注册到Binder驱动中
    // 当没有足够的空闲线程处理进程间通信请求时,驱动可以要求进程注册更多的线程到Binder线程
    // 池中
    struct rb_root threads; 

    struct rb_root nodes;           // 组织Binder实体对象,它以成员ptr作为key
    struct rb_root refs_by_desc;    // 组织Binder引用对象,它以成员desc作为key
    struct rb_root refs_by_node;    // 组织Binder引用对象,它以成员node作为key
    int pid;                        // 指向进程组ID
    struct vm_area_struct *vma;     // 内核缓冲区的用户空间地址,供应用程序使用
    struct mm_struct *vma_vm_mm;
    struct task_struct *tsk;        // 指向进程任务控制块
    struct files_struct *files;     // 指向进程打开文件结构体数组

    // 一个hash表,保存进程可以延迟执行的工作项,这些延迟工作有三种类型
    // BINDER_DEFERRED_PUT_FILES、BINDER_DEFERRED_FLUSH、BINDER_DEFERRED_RELEASE
    // 驱动为进程分配内核缓冲区时,会为该缓冲区创建一个文件描述符,进程可以通过该描述符将该内
    // 核缓冲区映射到自己的地址空间。当进程不再需要使用Binder机制时,就会通知驱动关闭该文件
    // 描述符并释放之前所分配的内核缓冲区。由于这不是一个马上就要完成的操作,因此驱动会创建一
    // 个BINDER_DEFERRED_PUT_FILES类型的工作来延迟执行;
    // Binder线程池中的空闲Binder线程是睡眠在一个等待队列中的,进程可以通过调用函数flush
    // 来唤醒这些线程,以便它们可以检查进程是否有新的工作项需要处理。此时驱动会创建一个
    // BINDER_DEFERRED_FLUSH类型的工作项,以便延迟执行唤醒空闲Binder线程的操作;
    // 当进程不再使用Binder机制时,会调用函数close关闭文件/dev/binder,此时驱动会释放之
    // 前为它分配的资源,由于资源释放是个比较耗时的操作,驱动会创建一个
    // BINDER_DEFERRED_RELEASE类型的事务来延迟执行
    struct hlist_node deferred_work_node;

    int deferred_work;              // 描述该延迟工作项的具体类型
    void *buffer;                   // 内核缓冲区的内核空间地址,供驱动程序使用
    ptrdiff_t user_buffer_offset;   // vma和buffer之间的差值

    // buffer指向一块大的内核缓冲区,驱动程序为方便管理,将它划分成若干小块,这些小块的内核缓
    // 冲区用binder_buffer描述保存在列表中,按地址从小到大排列。buffers指向该列表的头部。
    struct list_head buffers; 

    struct rb_root free_buffers;      // buffers中的小块有的正在使用,被保存在此红黑树
    struct rb_root allocated_buffers;   // buffers中的空闲小块被保存在此红黑树
    size_t free_async_space;            // 当前可用的保存异步事务数据的内核缓冲区的大小

    struct page **pages;    // buffer和vma都是虚拟地址,它们对应的物理页面保存在pages
                            // 中,这是一个数组,每个元素指向一个物理页面
    size_t buffer_size;     // 进程调用mmap将它映射到进程地址空间,实际上是请求驱动为它
                            // 分配一块内核缓冲区,缓冲区大小保存在该成员中
    uint32_t buffer_free;   // 空闲内核缓冲区的大小
    struct list_head todo;  // 当进程接收到一个进程间通信请求时,Binder驱动就将该请求封
                            // 装成一个工作项,并且加入到进程的待处理工作向队列中,该队列
                            // 使用成员变量todo来描述。
    wait_queue_head_t wait; // 线程池中空闲Binder线程会睡眠在由该成员所描述的等待队列中
                            // 当宿主进程的待处理工作项队列增加新工作项后,驱动会唤醒这
                            // 些线程,以便处理新的工作项
    struct binder_stats stats;  // 用来统计进程数据

    // 当进程所引用的Service组件死亡时,驱动会向该进程发送一个死亡通知。这个正在发出的通知被
    // 封装成一个类型为BINDER_WORK_DEAD_BINDER或BINDER_WORK_DEAD_BINDER_AND_CLEAR
    // 的工作项,并保存在由该成员描述的队列中删除
    struct list_head delivered_death;  

    int max_threads;        // 驱动程序最多可以主动请求进程注册的线程数
    int requested_threads;
    int requested_threads_started;
    int ready_threads;      // 进程当前的空闲Binder线程数
    long default_priority;  // 进程的优先级,当线程处理一个工作项时,线程优先级可能被
                            // 设置为宿主进程的优先级
    struct dentry *debugfs_entry;
};

binder_proc中的链表

在binder_proc内部有若干个list_head类型的字段,用来把binder_proc串到不同的链表中去。一般写链表的做法是在链表节点结构体中追加业务逻辑字段,顺着链表的prev、next指针到达指定节点,然后再访问业务逻辑字段:
一般的链表做法
在Linux代码中则常常反过来,先定义业务逻辑的结构体,在其内部嵌入链表字段list_head,顺着该字段遍历链表,在每个节点上根据该字段与所在结构体的偏移量找到所在结构体,访问业务逻辑字段:
Linux中常用的链表做法
这样做的好处是让业务逻辑和链表逻辑分离,Linux还定义了宏用于操作链表,以及根据链表字段找到所在结构体。如binder_proc结构体内部盛放多个list_head,表示把该结构体串入了不同的链表。
具体技巧可参见《Linux内核设计与实现》第6章。

INIT_LIST_HEAD(&proc->todo)

回到binder_open(…),除了直接字段赋值,需要解释的是几个链表字段的处理。
INIT_LIST_HEAD(&proc->todo)用于将todo的next、prev指针指向自己,该宏的定义在kernel/goldfish/include/linux/lish.t:24

static inline void INIT_LIST_HEAD(struct list_head *list)
{
    list->next = list;
    list->prev = list;
}

init_waitqueue_head(&proc->wait)

init_waitqueue_head(&proc->wait)这个宏定义在kernel/goldfish/include/linux/wait.h:81

#define init_waitqueue_head(q)              \
    do {                        \
        static struct lock_class_key __key; \
                            \
        __init_waitqueue_head((q), #q, &__key); \
    } while (0)

__init_waitqueue_head(...)定义在kernel/goldfish/kernel/wait.c:13,主要完成了对task_list字段的初始化:

void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
// q=(&proc->todo)
{
    spin_lock_init(&q->lock);
    lockdep_set_class_and_name(&q->lock, key, name);
    INIT_LIST_HEAD(&q->task_list);  // 为什么使用符号->来提领task_list呢?
}

说到底还是初始化proc->wait->task_list字段。不过有点奇怪task_list是wait内的结构体,而不是结构体指针,为什么对task_list的提领使用符号->呢?

struct binder_proc {
    ......
    wait_queue_head_t wait;
    ......
};

kernel/goldfish/include/linux/wait.h:53

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_lis
::ndk::ScopedAStatus BpCameraService::addListener(const std::shared_ptr<::aidl::android::frameworks::cameraservice::service::ICameraServiceListener>& in_listener, std::vector<::aidl::android::frameworks::cameraservice::service::CameraStatusAndId>* _aidl_return) { binder_status_t _aidl_ret_status = STATUS_OK; ::ndk::ScopedAStatus _aidl_status; ::ndk::ScopedAParcel _aidl_in; ::ndk::ScopedAParcel _aidl_out; _aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR()); if (_aidl_ret_status != STATUS_OK) goto _aidl_error; _aidl_ret_status = ::ndk::AParcel_writeData(_aidl_in.get(), in_listener); if (_aidl_ret_status != STATUS_OK) goto _aidl_error; _aidl_ret_status = AIBinder_transact( asBinder().get(), (FIRST_CALL_TRANSACTION + 0 /*addListener*/), _aidl_in.getR(), _aidl_out.getR(), 0 #ifdef BINDER_STABILITY_SUPPORT | FLAG_PRIVATE_LOCAL #endif // BINDER_STABILITY_SUPPORT ); if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && ICameraService::getDefaultImpl()) { _aidl_status = ICameraService::getDefaultImpl()->addListener(in_listener, _aidl_return); goto _aidl_status_return; } if (_aidl_ret_status != STATUS_OK) goto _aidl_error; _aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR()); if (_aidl_ret_status != STATUS_OK) goto _aidl_error; if (!AStatus_isOk(_aidl_status.get())) goto _aidl_status_return; _aidl_ret_status = ::ndk::AParcel_readData(_aidl_out.get(), _aidl_return); if (_aidl_ret_status != STATUS_OK) goto _aidl_error; _aidl_error: _aidl_status.set(AStatus_fromStatus(_aidl_ret_status)); _aidl_status_return: return _aidl_status; }
最新发布
08-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值