binder驱动框架注册服务
2017-12-12 13:57
218 查看
binder驱动框架misc驱动,Binder驱动:
ret = misc_register(&binder_miscdev);杂项驱动
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops/////////////////binder驱动向misc注册
};
misc_class = class_create(THIS_MODULE, "misc");//创建类
register_chrdev(MISC_MAJOR,"misc",&misc_fops)
它同样是做注册字符设备驱动名字"misc", 文件操作&misc_fops
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};misc向内核注册
在open函数里做了
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
} //////////根据次设备号找到那个文件操作的结构体以后只要操作文件就直接调用binder的open函数
服务注册过程:
SMmain函数打开驱动输出版本信息
然后告诉内核binder_become_context_manager(bs)自己是Handle为0 的SM
在binder_loop中 binder_write(bs, readbuf, sizeof(uint32_t));把readbuf[0] = BC_ENTER_LOOPER;写入将会嗲用binder_thread_write函数。这个函数的参数线程是怎么来的thread = binder_get_thread(proc);,,用来取在线程树下与当前线程PID相等的线程。
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"binder: %d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("binder: %d:%d ERROR:"
" BC_ENTER_LOOPER called after "
"BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
thread = rb_entry(parent, struct binder_thread, rb_node);
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
在Binder驱动层,会跟用户空间的线程关联一个struct binder_thread实例,这个结构记录了内核空间的在该线程上执行的一些IPC状态信息
struct binder_thread {
struct binder_proc *proc; //线程所属的进程
struct rb_node rb_node; //红黑树的结点,进程通过该结点将线程加入到红黑树中
int pid; //线程的pid
int looper; //线程所处的状态
struct binder_transaction *transaction_stack;//transaction session list on this thread
struct list_head todo; //在该线程上的Task列表
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait; //该线程的等待队列
struct binder_stats stats; //统计该线程上的命令数量
};
1> Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。
2>接口:
container_of(ptr, type, member)
ptr:表示结构体中member的地址
type:表示结构体类型
member:表示结构体中的成员
通过ptr的地址可以返回结构体的首地址
就是输出一些打印信息
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);///////////读操作将会调用binder_thread_read函数
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (void *)t->buffer->data +
proc->user_buffer_offset;/////////////////////////////////////////数据
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
4000
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr)))
SM ioctl里面会创建一个线程也有一个todo链表
接下来进入for循环是一个读操作调用bidner_thread_read函数
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
但是他先写入BR_NOOP这个命令,,对于所有的读操作数据头部都是BR_NOOP。
read_buffer里放着的是BR_NOOP,cmd 数据,cmd 数据,,,
当然没有数据休眠ret = wait_event_interruptible_exclusive(pr。
现在轮到test_server了
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
unsigned iodata[512/4];
struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
bio_put_obj(&msg, ptr);
对于这个iodata buffer里的数据格式存放前16 字节空开,然后用一个字节放0,然后放 bio_put_string16_x(&msg, SVC_MGR_NAME);
这个函数用前一个字节放长度bio_put_uint32(bio, len);
用两个字节放一个字符 uint16_t *ptr;
while (*str)
*ptr++ = *str++;
*ptr++ = 0;
////////////////////////////////////////////////
bio_put_obj(&msg, ptr);
struct flat_binder_object *obj;
server给驱动传入一个obj,然后内核根据它创捷一个binder_node结构体
offs的作用就是指出这个结构体的位置以便内核知道他在哪里
static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)
{
struct flat_binder_object *obj;
obj = bio_alloc(bio, sizeof(*obj));
if (obj && bio->offs_avail) {
bio->offs_avail--;
*bio->offs++ = ((char*) obj) - ((char*) bio->data0);///bio->offs是首地址。 ((char*) obj) - ((char*) bio->data0)表示i相对于空出16字节的地址的偏移。所以前16 字节的作用就是记录这个内存快的位置
return obj;
}
bio->flags |= BIO_F_OVERFLOW;
return NULL;
}
接着把构造的数据发送,调用binder_call实质就是调用binder_ioctl函数当然先构造一个驱动认识的binder_write_read结构体,cmd表明消息的类型,数据放在binder_transaction_data这
writebuf.cmd = BC_TRANSACTION;
writebuf.txn.target.handle = target;
writebuf.txn.code = code;
writebuf.txn.flags = 0;
writebuf.txn.data_size = msg->data - msg->data0;
writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
构造数据,转为binder_write_read结构体,然后把数据发送用ioctl()系统调用进入驱动,调用binder_ioctl(的作用就是把数据放入目标进程的todo链表唤醒进程
这个地方是个数组相当于放四个flag_binder_object结构体然后逐个处理switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: {/////如果是binder就是这个node = binder_new_node(proc, fp->binder, fp->cookie);创建node节点给teat_server进程ref
= binder_get_ref_for_node(target_proc, node);//创建目的进程也就是SM创建按引用binder_proc结构体里的红黑节点node用来记录进程创建的服务 struct rb_root refs_by_desc;/////////根据handle号快速找到引用 struct rb_root refs_by_node;///////////根据node快速找到ref if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;并且修改type现在在内核空间binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);//////会返回一些信息给当前线程,test_server就知道有人引用。t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list); tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); if (target_wait) wake_up_interruptible(target_wait);///////////数据写入后休眠if
(t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; t->saved_priority = task_nice(current); if (t->priority
< target_node->min_priority && !(t->flags & TF_ONE_WAY)) binder_set_nice(t->priority); else if (!(t->flags & TF_ONE_WAY) || t->saved_priority > target_node->min_priority) binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION;SM这边收到数据后开始处理res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res
= binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);////////返回到这里进行解析 在前边int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}////SM函数里就已经ioctl这个,binder_context_mgr_node
= binder_new_node(proc, NULL, NULL);然后创建了这个结构体
)把数据放入SMbinder_proc结构体的todo链表里,
ret = misc_register(&binder_miscdev);杂项驱动
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops/////////////////binder驱动向misc注册
};
misc_class = class_create(THIS_MODULE, "misc");//创建类
register_chrdev(MISC_MAJOR,"misc",&misc_fops)
它同样是做注册字符设备驱动名字"misc", 文件操作&misc_fops
static const struct file_operations misc_fops = {
.owner = THIS_MODULE,
.open = misc_open,
.llseek = noop_llseek,
};misc向内核注册
在open函数里做了
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops);
break;
}
} //////////根据次设备号找到那个文件操作的结构体以后只要操作文件就直接调用binder的open函数
服务注册过程:
SMmain函数打开驱动输出版本信息
然后告诉内核binder_become_context_manager(bs)自己是Handle为0 的SM
在binder_loop中 binder_write(bs, readbuf, sizeof(uint32_t));把readbuf[0] = BC_ENTER_LOOPER;写入将会嗲用binder_thread_write函数。这个函数的参数线程是怎么来的thread = binder_get_thread(proc);,,用来取在线程树下与当前线程PID相等的线程。
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"binder: %d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("binder: %d:%d ERROR:"
" BC_ENTER_LOOPER called after "
"BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
thread = rb_entry(parent, struct binder_thread, rb_node);
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
在Binder驱动层,会跟用户空间的线程关联一个struct binder_thread实例,这个结构记录了内核空间的在该线程上执行的一些IPC状态信息
struct binder_thread {
struct binder_proc *proc; //线程所属的进程
struct rb_node rb_node; //红黑树的结点,进程通过该结点将线程加入到红黑树中
int pid; //线程的pid
int looper; //线程所处的状态
struct binder_transaction *transaction_stack;//transaction session list on this thread
struct list_head todo; //在该线程上的Task列表
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait; //该线程的等待队列
struct binder_stats stats; //统计该线程上的命令数量
};
1> Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。
2>接口:
container_of(ptr, type, member)
ptr:表示结构体中member的地址
type:表示结构体类型
member:表示结构体中的成员
通过ptr的地址可以返回结构体的首地址
就是输出一些打印信息
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);///////////读操作将会调用binder_thread_read函数
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (void *)t->buffer->data +
proc->user_buffer_offset;/////////////////////////////////////////数据
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
4000
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr)))
SM ioctl里面会创建一个线程也有一个todo链表
接下来进入for循环是一个读操作调用bidner_thread_read函数
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
但是他先写入BR_NOOP这个命令,,对于所有的读操作数据头部都是BR_NOOP。
read_buffer里放着的是BR_NOOP,cmd 数据,cmd 数据,,,
当然没有数据休眠ret = wait_event_interruptible_exclusive(pr。
现在轮到test_server了
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
unsigned iodata[512/4];
struct binder_io msg, reply;
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
bio_put_string16_x(&msg, name);
bio_put_obj(&msg, ptr);
对于这个iodata buffer里的数据格式存放前16 字节空开,然后用一个字节放0,然后放 bio_put_string16_x(&msg, SVC_MGR_NAME);
这个函数用前一个字节放长度bio_put_uint32(bio, len);
用两个字节放一个字符 uint16_t *ptr;
while (*str)
*ptr++ = *str++;
*ptr++ = 0;
////////////////////////////////////////////////
bio_put_obj(&msg, ptr);
struct flat_binder_object *obj;
server给驱动传入一个obj,然后内核根据它创捷一个binder_node结构体
offs的作用就是指出这个结构体的位置以便内核知道他在哪里
static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)
{
struct flat_binder_object *obj;
obj = bio_alloc(bio, sizeof(*obj));
if (obj && bio->offs_avail) {
bio->offs_avail--;
*bio->offs++ = ((char*) obj) - ((char*) bio->data0);///bio->offs是首地址。 ((char*) obj) - ((char*) bio->data0)表示i相对于空出16字节的地址的偏移。所以前16 字节的作用就是记录这个内存快的位置
return obj;
}
bio->flags |= BIO_F_OVERFLOW;
return NULL;
}
接着把构造的数据发送,调用binder_call实质就是调用binder_ioctl函数当然先构造一个驱动认识的binder_write_read结构体,cmd表明消息的类型,数据放在binder_transaction_data这
writebuf.cmd = BC_TRANSACTION;
writebuf.txn.target.handle = target;
writebuf.txn.code = code;
writebuf.txn.flags = 0;
writebuf.txn.data_size = msg->data - msg->data0;
writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0);
writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0;
writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0;
构造数据,转为binder_write_read结构体,然后把数据发送用ioctl()系统调用进入驱动,调用binder_ioctl(的作用就是把数据放入目标进程的todo链表唤醒进程
if (tr->target.handle) {///////////////如果不是SM的handle就取出它的ref结构体,前边说过这个ref结构体记录了handle值和对应的binder_ndoe结构体 struct binder_ref *ref; ref = binder_get_ref(proc, tr->target.handle);
else { target_node = binder_context_mgr_node;////否则直接让他等于一个特殊的目标节点 这个在什么时候被创建 target_proc = target_node->proc;////////找到目标进程 copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)/////开始真正的复制数据 for (; offp < off_end; offp++) { struct flat_binder_object *fp; if (*offp > t->buffer->data_size - sizeof(*fp) || *offp < off_min || t->buffer->data_size < sizeof(*fp) || !IS_ALIGNED(*offp, sizeof(void *))) {///////////处理数据开头的offso t->work.type = BINDER_WORK_TRANSACTION; list_add_tail(&t->work.entry, target_list); tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); if (target_wait) wake_up_interruptible(target_wait);///最后把t放入目的的链表里,然后唤醒这个链表是todo链表target_list = &target_thread->todo; 详细分析一下驱动对数据offso的处理,,,就是flag_binder_object结构体 给test_server构造binder_node结构体,,,,构造binder_ref给SM。增加引用计数。。。 看看这个结构体: struct flat_binder_object { /* 8 bytes for large_flat_header. */ unsigned long type;////表示当前传输的这个结构体是实体还是引用,只有server提供者可以传实体,对于SM和client程序只能传引用 unsigned long flags; /* 8 bytes of data. */ union { void *binder; /* local object */ 。/////当传实体的时候联合体对应binder,当传引用的时候代表handle比如test_server中 obj->binder = (uintptr_t)ptr;即使将处理函数放在了binder中 signed long handle; /* remote object */ }; /* extra data associated with local object */ void *cookie; }; copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size); 这个结构体从用户空间复制到内核空间,,,数据在
tr->data.ptr.offsets
这个地方是个数组相当于放四个flag_binder_object结构体然后逐个处理switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: {/////如果是binder就是这个node = binder_new_node(proc, fp->binder, fp->cookie);创建node节点给teat_server进程ref
= binder_get_ref_for_node(target_proc, node);//创建目的进程也就是SM创建按引用binder_proc结构体里的红黑节点node用来记录进程创建的服务 struct rb_root refs_by_desc;/////////根据handle号快速找到引用 struct rb_root refs_by_node;///////////根据node快速找到ref if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;并且修改type现在在内核空间binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);//////会返回一些信息给当前线程,test_server就知道有人引用。t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list); tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); if (target_wait) wake_up_interruptible(target_wait);///////////数据写入后休眠if
(t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; tr.target.ptr = target_node->ptr; tr.cookie = target_node->cookie; t->saved_priority = task_nice(current); if (t->priority
< target_node->min_priority && !(t->flags & TF_ONE_WAY)) binder_set_nice(t->priority); else if (!(t->flags & TF_ONE_WAY) || t->saved_priority > target_node->min_priority) binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION;SM这边收到数据后开始处理res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); if (res < 0) { ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); break; } res
= binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);////////返回到这里进行解析 在前边int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}////SM函数里就已经ioctl这个,binder_context_mgr_node
= binder_new_node(proc, NULL, NULL);然后创建了这个结构体
BINDER_SET_CONTEXT_MGR
)把数据放入SMbinder_proc结构体的todo链表里,
相关文章推荐
- android binder机制,注册系统服务---客户端Binder驱动
- android binder机制,注册系统服务--服务端servicemanager binder驱动
- 【Android进阶】Android Binder之ServiceManager注册服务解析1
- Binder框架的一些简单总结(关于自定义服务中的Binder)
- 模拟dubbo 框架RPC调用及dubbo的服务动态注册,服务路由,负载均衡功能的思考
- Android Binder 修炼之道(四)Binder 系统C++ 发送数据过程以及Server注册服务处理数据的过程
- Dubbo分布式远程服务调用框架(告别Web Service模式中的WSdl,以服务者与消费者的方式在dubbo上注册
- Dubbo框架初探【用Spring配置声明暴露服务(可以使用multicast广播注册中心暴露服务地址或者使用zookeeper注册中心暴露服务地址)、加载Spring配置,启动服务】
- Android系统--Binder系统具体框架分析(二)Binder驱动情景分析
- 一个简单RPC框架是如何炼成的(VI)——引入服务注册机制
- Spring+Dubbo+Zookeeper框架搭建–<三>服务注册示例
- go微服务框架go-micro深度学习(三) Registry服务的注册和发现
- Binder框架 -- Binder 驱动
- 多研究些架构,少谈些框架(3)-- 微服务和事件驱动
- 《连载 | 物联网框架ServerSuperIO教程》- 14.配制工具介绍,以及设备驱动、视图驱动、服务实例的挂载
- spring-cloud 微服务框架集合 Eureke 服务注册于发现
- android binder机制,注册系统服务---native客户端
- 简单RPC框架-基于Consul的服务注册与发现
- 关于PHP-Zend framework2 框架 学习过程。 阅前须知: ZF2中的配置文件是可以静态文件配置来注册和通过相关函数动态注册。 1.EventManager(事件驱动),关于事件驱动,在ZF2相关资料没有详细说明,可以参考ANDROID的事件驱动,MFC的消息响应/事件驱动。
- 微服务框架Spring Cloud介绍 Part3: Mysteam项目结构与开发用户注册服务