Linux 角度看binder原理(二)

393 阅读5分钟

Linux 角度看binder原理(二)

ioctl

在Linux中和设备的常用交互除了上一篇中重定向file_operations中的文件操作就是ioctl了。file_operations作为通用的设备交互手段,缺少了每个设备自己的特有操作,这时候就使ioctl作为补充。函数是int ioctl(int fd, ind cmd, …) 第一个参数就是打开设备的fd,第二个就是设备的控制指令,再后面的...传入当前控制指令需要的参数。

binder指令

binder指令存在BinderDriverCommandProtocol和BinderDriver ReturnProtocol两个enum中。

​
enum binder_driver_return_protocol {
  BR_ERROR = _IOR('r', 0, __s32),
  BR_OK = _IO('r', 1),
  BR_TRANSACTION_SEC_CTX = _IOR('r', 2,
                struct binder_transaction_data_secctx),
  BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
  BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
  BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
  BR_DEAD_REPLY = _IO('r', 5),
  BR_TRANSACTION_COMPLETE = _IO('r', 6),
  BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
  BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
  BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
  BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
  BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
  BR_NOOP = _IO('r', 12),
  BR_SPAWN_LOOPER = _IO('r', 13),
  BR_FINISHED = _IO('r', 14),
  BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t),
  BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t),
  BR_FAILED_REPLY = _IO('r', 17),
  BR_FROZEN_REPLY = _IO('r', 18),
  BR_ONEWAY_SPAM_SUSPECT = _IO('r', 19),
  
};
​
enum binder_driver_command_protocol {
  BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
  BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
  BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
  BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
  BC_INCREFS = _IOW('c', 4, __u32),
  BC_ACQUIRE = _IOW('c', 5, __u32),
  BC_RELEASE = _IOW('c', 6, __u32),
  BC_DECREFS = _IOW('c', 7, __u32),
  BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
  BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
  BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
  BC_REGISTER_LOOPER = _IO('c', 11),
  BC_ENTER_LOOPER = _IO('c', 12),
  BC_EXIT_LOOPER = _IO('c', 13),
  BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14,
            struct binder_handle_cookie),
  BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15,
            struct binder_handle_cookie),
  BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t),
  BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
  BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
};

从全称也就知道了为什么有的是BC有的是BR,BC是从用户空间到驱动,BR是从驱动返回用户空间。注意这里不是client和service之间的指令和返回,而是驱动和用户空间的。这里放一张gityuan的图,很好的展示了一次ioctl的过程

binder_protocol

binder 常见结构

在上一篇中,已经知道所有使用binder的进程都会调用open来打开设备,当设备打开的时候就会创建一个binder_proc结构体。

struct binder_proc {
  struct hlist_node proc_node; //proc的hash队列的的节点
  struct rb_root threads; //binder_thread红黑树
  struct rb_root nodes; //binder_node红黑树
  struct rb_root refs_by_desc;//binder_ref红黑树以desc为关键字
  struct rb_root refs_by_node;//binder_ref红黑树以node为关键字
  struct list_head waiting_threads;//等待线程
  int pid;//进程id
  struct task_struct *tsk;//进程结构体
  const struct cred *cred;//credential机制用于权限判断
  struct hlist_node deferred_work_node;//延迟工作的节点
  int deferred_work;//延迟工作的类型
  int outstanding_txns;
  bool is_dead;
  bool is_frozen;
  bool sync_recv;
  bool async_recv;
  wait_queue_head_t freeze_wait;
​
  struct list_head todo;//当前进程的工作队列
  struct binder_stats stats;
  struct list_head delivered_death;
  int max_threads;//最大线程数,这里不是总得线程,是binder驱动注册的线程,用户进程可以自己注册线程到线程池但是不占用max_threads的数量
  int requested_threads;
  int requested_threads_started;
  int tmp_ref;
  long default_priority;
  struct dentry *debugfs_entry;
  struct binder_alloc alloc;//内存相关的结构被封装成了一个结构体,在老版本中的free_buffers和allocated_buffers等都在里面
  struct binder_context *context;
  spinlock_t inner_lock;
  spinlock_t outer_lock;
  struct dentry *binderfs_entry;
  bool oneway_spam_detection_enabled;
};

再看下和内存相关最大的binder_alloc

struct binder_alloc {
  struct mutex mutex; //锁
  struct vm_area_struct *vma;//binder内存对应的VMA
  struct mm_struct *vma_vm_mm;//binder进程的内存描述符
  void __user *buffer;//mmap的用户空间虚拟地址。
  struct list_head buffers;//所有buffer的列表
  struct rb_root free_buffers;//空闲buffer的红黑树
  struct rb_root allocated_buffers;//已经分配的buffer的红黑树
  size_t free_async_space;//异步事务数据的内核缓冲区
  struct binder_lru_page *pages;//binder内存区域对应的page
  size_t buffer_size;//buffer的大小
  uint32_t buffer_free;//free的buffer大小
  int pid;//Binder proc的pid
  size_t pages_high;//内存理想的阈值
  bool oneway_spam_detected;//是否启用单向SPAM检测
};

缓存结构体binder_buffer

struct binder_buffer {
  struct list_head entry; /* free and allocated entries by address */
  struct rb_node rb_node; /* free entry by size or allocated entry */
        /* by address */
  unsigned free:1;
  unsigned clear_on_free:1;
  unsigned allow_user_free:1;
  unsigned async_transaction:1;
  unsigned oneway_spam_suspect:1;
  unsigned debug_id:27;
​
  struct binder_transaction *transaction;//当前buffer交给哪个事务使用struct binder_node *target_node;//这个buffer当前交给哪个node使用
  size_t data_size;//数据区域的大小
  size_t offsets_size;//offset区域的大小
  size_t extra_buffers_size;
  void __user *user_data;//用户空间地址
  int    pid;
};

到这里内存相关的结构体都已经展示完了。在binder_proc通过threads来管理线程池,在binder中线程的结构是binder_thread。

struct binder_thread {
  struct binder_proc *proc;//所属进程
  struct rb_node rb_node;//binder_proc中线程红黑树的中的节点
  struct list_head waiting_thread_node;//空闲时binder_proc中的等待队列的节点
  int pid;
  int looper;  //线程的状态
  bool looper_need_return; 
  struct binder_transaction *transaction_stack;//事务栈
  struct list_head todo;//任务队列
  bool process_todo;
  struct binder_error return_error;
  struct binder_error reply_error;
  wait_queue_head_t wait;
  struct binder_stats stats;
  atomic_t tmp_ref;
  bool is_dead;
};

binder_thread就已经到了比较具体执行任务的地方了,它的field都是和具体任务相关。一次进程间的通讯这个过程被封装成binder_transaction。

struct binder_transaction {
  int debug_id;
  struct binder_work work;//transaction涉及的binder work,就是进程或者线程中的todo中保存的任务结构体
  struct binder_thread *from;//发起binder通信的线程
  struct binder_transaction *from_parent;//执行本事务所依赖的其他事务
  struct binder_proc *to_proc;//处理binder请求的进程
  struct binder_thread *to_thread;//处理binder请求的线程
  struct binder_transaction *to_parent;//
  unsigned need_reply:1;//是否有返回
  /* unsigned is_dead:1; */       /* not used at the moment */struct binder_buffer *buffer;//本次事务分配的buffer
  unsigned int    code;
  unsigned int    flags;
  long    priority;
  long    saved_priority;
  kuid_t  sender_euid;
  struct list_head fd_fixups;
  binder_uintptr_t security_ctx;
  spinlock_t lock;
};

binder通讯是一个cs架构,有client和service。在bidner驱动中就对应了binder_ref和binder_node。每一个service注册到binder之后都会有一个binder_node对应。

struct binder_node {
  int debug_id;
  spinlock_t lock;
  struct binder_work work; //node的任务
  union {
    struct rb_node rb_node;//binder_proc中的node红黑树中的节点
    struct hlist_node dead_node;
  };
  struct binder_proc *proc;//所属的binder proc
  struct hlist_head refs;//这里保存了所有使用当前service的客户端的binder_ref
  int internal_strong_refs;//内部强引用
  int local_weak_refs;//弱引用
  int local_strong_refs;////强引用
  int tmp_refs;
  binder_uintptr_t ptr;//service指向service内部一个引用对象
  binder_uintptr_t cookie;//service用户空间地址
  struct {
    /*
     * bitfield elements protected by
     * proc inner_lock
     */
    u8 has_strong_ref:1;
    u8 pending_strong_ref:1;
    u8 has_weak_ref:1;
    u8 pending_weak_ref:1;
  };
  struct {
    /*
     * invariant after initialization
     */
    u8 accept_fds:1;
    u8 txn_security_ctx:1;
    u8 min_priority;
  };
  bool has_async_transaction;//是否是异步任务
  struct list_head async_todo;//异步任务队列
};

binder_node最大的作用就是binder驱动可以通过binder_node来访问service,同时通过强弱引用来控制他的生命周期。每一个client在驱动中就是一个binder_ref。

struct binder_ref {
  /* Lookups needed: */
  /*   node + proc => ref (transaction) */
  /*   desc + proc => ref (transaction, inc/dec ref) */
  /*   node => refs + procs (proc exit) */
  struct binder_ref_data data;//一个封装类,老版本中的desc等
  struct rb_node rb_node_desc;//binder_proc中的desc红黑树的成员
  struct rb_node rb_node_node;//binder_proc中的红黑树的成员
  struct hlist_node node_entry;//binder_node中的refs中的成员
  struct binder_proc *proc;//所属进程
  struct binder_node *node;//引用的是哪个node
  struct binder_ref_death *death;
};
​
struct binder_ref_data {
  int debug_id;
  uint32_t desc;//引用描述符,client中持有的就是描述符,需要传递消息的时候通过传递描述符,让驱动找到引用对象
  int strong;//强引用计数
  int weak;//弱引用计数
};

到这里一次IPC通讯里常见的结构都已经清楚了

参考

《Android系统源代码情景分析》

gityuan博客