Linux 角度看binder原理(二)

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博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值