Binder机制原理学习笔记(4)_ServiceManager启动Binder分析
ServiceManager启动Binder
在Framwork源码解析(1)_Zygote进程启动流程一文中了解过,Android系统启动Zygote进程然后创建SystemService,再创建其他服务进程,ServiceManager 进程也是在这里启动的。查看/system/core/rootdir/init.rc
源码,可以找到启动servicemanager:
这里启动的是/frameworks/native/cmds/servicemanager
下的service_manager.c
文件,此目录下还有servicemanager.rc
配置,就是被zygote启动的。
查看service_manager.c
源码的main方法:
int main(int argc, char** argv){// ...... if (argc > 1) { driver = argv[1]; } else { // 设置默认binder驱动文件路径 driver = "/dev/binder"; }// 打开binder文件,并设置映射文件大小为128KB bs = binder_open(driver, 128*1024); // 成为上下文管理者 if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; }// ......// 开启binder循环 binder_loop(bs, svcmgr_handler); return 0;}
主要做了三件事:
- 设置默认binder驱动文件路径,root过的手机可以在/dev/binder目录下找到Binder驱动文件
- 打开binder驱动:binder_open
- 将ServiceManager注册成为 binder 服务管理者:binder_become_context_manager
- 开启binder循环:binder_loop
binder_open
找到同级目录下的frameworks/native/cmds/servicemanager/binder.c
文件,该文件中找到binder_open
方法:
struct binder_state *binder_open(const char* driver, size_t mapsize){ struct binder_state *bs; struct binder_version vers; // 动态分配内存 bs = malloc(sizeof(*bs)); // 打开驱动文件,将文件句柄也就是c++的指针引用传给bs结构体的fd bs->fd = open(driver, O_RDWR | O_CLOEXEC); // 系统调用 if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) { fprintf(stderr, "binder: kernel driver version (%d) differs from user space version (%d)\n", vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION); goto fail_open; } bs->mapsize = mapsize; // 创建映射,mmap函数能够将用户空间的一段内存区域映射到内核空间,用户对该段内存的修改能直接反映到内核空间, // 相反,内核空间的改动也可以映射到用户空间,这里将/dev/binder映射到内核空间,并赋值给bs的mapped属性,大小是128KB bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); return bs;}
bs = malloc(sizeof(*bs));
动态分配内存bs->fd = open(driver, O_RDWR | O_CLOEXEC);
打开驱动文件,将文件句柄也就是c++的指针引用传给bs结构体的fdioctl(bs->fd, BINDER_VERSION, &vers)
ioctl(input/output control)
是一个专用于设备输入输出操作的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码。ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。
详情可查看:https://blog.csdn.net/qq_19923217/article/details/82698787bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
创建映射,mmap函数能够将用户空间的一段内存区域映射到内核空间,用户对该段内存的修改能直接反映到内核空间,相反,内核空间的改动也可以映射到用户空间,这里将/dev/binder映射到内核空间,并赋值给bs的mapped属性,大小是128KB。
注: 为什么是128KB?
这里有个小知识,磁盘的写入单位是4KB,未到4KB的会把数据放到缓存中,等满足4KB的时候再写入磁盘,所以设置写入磁盘的大小要满足是4KB的整数倍。
binder_become_context_manager
int binder_become_context_manager(struct binder_state *bs){ return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}
源码中通过系统调用将"/dev/binder"驱动的引用传给驱动层,命令符是BINDER_SET_CONTEXT_MGR
。
binder_loop
1、开启binder循环
void binder_loop(struct binder_state *bs, binder_handler func){ int res; struct binder_write_read bwr; // 32位,2的32次方就是128kb uint32_t readbuf[32]; bwr.write_size = 0; bwr.write_consumed = 0; bwr.write_buffer = 0; readbuf[0] = BC_ENTER_LOOPER; // 重置操作,将文件清空 binder_write(bs, readbuf, sizeof(uint32_t));// 开启循环,读取数据并解析 for (;;) { bwr.read_size = sizeof(readbuf); bwr.read_consumed = 0; bwr.read_buffer = (uintptr_t) readbuf;// 不断的读获取服务的请求 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);// 解析binder内容 res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); }}
2、查看binder_parse
源码,源码路径/frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio, uintptr_t ptr, size_t size, binder_handler func){ while (ptr < end) { // .... case BR_TRANSACTION: { struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; binder_dump_txn(txn); if (func) { unsigned rdata[256/4]; struct binder_io msg; struct binder_io reply; int res; bio_init(&reply, rdata, sizeof(rdata), 4); bio_init_from_txn(&msg, txn); // 执行回调处理函数,将处理结果返回 res = func(bs, txn, &msg, &reply); if (txn->flags & TF_ONE_WAY) { binder_free_buffer(bs, txn->data.ptr.buffer); } else { // 向binder驱动发送执行结果 binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); } } ptr += sizeof(*txn); break; } } return r;}
这里执行完回调函数res = func(bs, txn, &msg, &reply);
并把结果发送binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
,再找到binder_send_reply
方法:
void binder_send_reply(struct binder_state *bs, struct binder_io *reply, binder_uintptr_t buffer_to_free, int status){ // .... binder_write(bs, &data, sizeof(data));}
这里又走到了binder_write
方法:
int binder_write(struct binder_state *bs, void *data, size_t len){ struct binder_write_read bwr; int res; bwr.write_size = len; bwr.write_consumed = 0; bwr.write_buffer = (uintptr_t) data; bwr.read_size = 0; bwr.read_consumed = 0; bwr.read_buffer = 0; // 向驱动文件写入数据,read_size = 0,也就是向驱动层写入数据,BINDER_WRITE_READ指令是读写指令,主要看read_size或wite_size谁是0 res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); return res;}
binder_write方法也调用了ioctl
方法,命令符是BINDER_WRITE_READ
,表示向驱动文件读或写入数据,read_size = 0,也就是向驱动层写入数据。
总结
- 应用启动的时候,ServiceManager也启动,并创建驱动层文件;
- 将ServiceManager注册成为 binder 服务管理者:binder_become_context_manager
- 打开binder文件,创建mmap映射,并设置映射文件大小为128KB
- 开启一个loop死循环,循环里不断的监听数据,并向驱动文件写入数据,也就是向内核空间写入数据。
https://blog.csdn.net/yiranfeng/article/details/105210069
更多相关文章
- Android升级到2.3之后遇到的问题
- android 蓝牙文件
- Android使用xml文件中的array资源
- Android挂载本地硬盘为SD卡操作指南
- Android平台下简单Widget的搭建过程
- android dataBinding详解
- 通过Android命令自动编译出build.xml文件
- Android安装服务installd源码分析
- iperf3 arm交叉编译补充