Binder驱动层的代码在kernel/goldfish/drivers/staging/android下的binder.cbinder.h。Android源码是不带Linux内核的,驱动正是在这个内核里,需要单独下载,出门左转参见《Anrdoid源码、内核编译》。驱动的相关知识先不在这里展开了,那又是一个庞大的体系,以后再啃。直奔我们的主题——客户端为test()组织的请求数据是:



驱动程序是如何处理这个数据包的呢?

从应用层登陆,顺流直下

为此,还需要先从应用层往下看,frameworks/native/libs/binder/IPCThreadState.cpp:548,就从这里登陆吧。客户端组织test()请求数据时,调用到IPCThreadState::transact(...)

status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{ // code=TEST, flag=0 flags |= TF_ACCEPT_FDS;
...... err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); ......
if (reply) {
err = waitForResponse(reply); // 这次重点看这里
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...... return err;
}

函数使用writeTransactionData(…)打包好数据后,接下来调用waitForResponse(…)把数据发出去。

frameworks/native/libs/binder/IPCThreadState.cpp:712

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err; while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
......
}
...... return err;
}

继续调用talkWithDriver()和驱动对话,frameworks/native/libs/binder/IPCThreadState.cpp:803

status_t IPCThreadState::talkWithDriver(bool doReceive)
{ // doReceive=true
......
binder_write_read bwr;
......
const bool needRead = mIn.dataPosition() >= mIn.dataSize();// mIn有上一轮IO读出尚未解析的数据,因此needRead=true
......
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; // outAvail=mOut.dataSize() bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
......
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
}
......
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
......
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) // 重点在这
err = NO_ERROR;
......
} while (err == -EINTR);
......
return err;
}

doReceive取默认值为true,在通过test()调用到talkWithDriver(...)之前,和驱动的对话已经做了好几轮了,比如defaultServiceManager()和ServiceManager的对话,getService(...)和Service的对话,此时mIn中应该是有之前读出尚未解析的数据,因此needRead=true,outAvail=mOut.dataSize()。可以组织一个gdb确认mIn此时的内容。

组织一个gdb确认此时mIn的内容

需要开启三个终端完成调试:

  1. Target1 在模拟器上启动server
$ adb shell /data/local/tmp/testservice/TestServer
  1. Target2 在模拟器上通过gdbserver启动客户端
$ adb shell gdbserver :1234 /data/local/tmp/testservice/TestClient
Process /data/local/tmp/testservice/TestClient created; pid = 1254
Listening on port 1234
Remote debugging from host 127.0.0.1
  1. Host1 在宿主端启动gdb
$ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-gdb out/debug/target/product/generic/obj/EXECUTABLES/TestClient_intermediates/LINKED/TestClient
......
(gdb) b main
Breakpoint 1 at 0xb6f571fc: file external/testservice/TestClient.cpp, line 14.
(gdb) c
Continuing.
......
(gdb) set solib-absolute-prefix out/debug/target/product/generic/symbols/
Reading symbols from ...... linker...done.
......
Loaded symbols for ......
......
(gdb) b IPCThreadState.cpp:846 # 在talkWithDriver(...)内下断点
Breakpoint 2 at 0xb6eaf884: file frameworks/native/libs/binder/IPCThreadState.cpp, line 846.
(gdb) c
......
然后就是若干轮的continue和backtrace,直到停在由test()调用触发的talkWithDriver(...)
...... Breakpoint 2, android::IPCThreadState::talkWithDriver (this=this@entry=0xb6c24000, doReceive=doReceive@entry=true) at frameworks/native/libs/binder/IPCThreadState.cpp:846
846 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
(gdb) bt
0 android::IPCThreadState::talkWithDriver (this=this@entry=0xb6c24000, doReceive=doReceive@entry=true) at frameworks/native/libs/binder/IPCThreadState.cpp:846
1 0xb6eafed2 in android::IPCThreadState::waitForResponse (this=0xb6c24000, reply=0xbeaa1ad4, acquireResult=0x0) at frameworks/native/libs/binder/IPCThreadState.cpp:718
2 0xb6eb0088 in android::IPCThreadState::transact (this=0xb6c24000, handle=1, code=code@entry=1, data=..., reply=reply@entry=0xbeaa1ad4, flags=16, flags@entry=0) at frameworks/native/libs/binder/IPCThreadState.cpp:604
3 0xb6eab08e in android::BpBinder::transact (this=0xb6c090c0, code=1, data=..., reply=0xbeaa1ad4, flags=0) at frameworks/native/libs/binder/BpBinder.cpp:165
4 0xb6f3e42e in android::BpTestService::test (this=<optimized out>) at external/testservice/TestClient.cpp:10
5 0xb6f3e23c in main () at external/testservice/TestClient.cpp:18
(gdb) p mIn
$1 = {mError = 0, mData = 0xb6c27000 "\fr", mDataSize = 48, mDataCapacity = 256, mDataPos = 48, mObjects = 0x0, mObjectsSize = 0, mObjectsCapacity = 0, mNextObjectHint = 0, mFdsKnown = true, mHasFds = false, mAllowFds = true, mOwner = 0x0, mOwnerCookie = 0x0,
mOpenAshmemSize = 0}
(gdb) p needRead
$2 = true

结果和我猜测的一致。

综上所述,在IPCThreadState::talkWithDriver(...)中调用

ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &brw);

传入的数据bwr即为:

进入驱动层

终于可以有此穿越到驱动层了!binder驱动层对接ioctl的函数是binder_ioctl(...)。kernel/goldfish/drivers/staging/android/binder.c:2716

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{ // cmd=BINDER_WRITE_READ
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg; ......
thread = binder_get_thread(proc);
...... switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
......
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {// 把上图总用户空间的bwr复制到内核
......
}
......
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
trace_binder_write_done(ret);
......
}
// bwr.read_size来自IPCThreadState::talkWithDriver,当读缓冲区为空,会将
// mIn缓冲区交给它,bwr.read_size=mIn.dataCapacity()
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
... ...
}
......
}
......
}
ret = 0;
......
return ret;
}

先尽可能地剪掉细枝末节,来看重点调用

binder_thread_write(proc, thread, (void __user *)bwr.write_buffer,
bwr.write_size, &bwr.write_consumed);

kernel/goldfish/drivers/staging/android/binder.c:1837

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed)
{
uint32_t cmd;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size; while (ptr < end && thread->return_error == BR_OK) {
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
binder_stats.bc[_IOC_NR(cmd)]++;
proc->stats.bc[_IOC_NR(cmd)]++;
thread->stats.bc[_IOC_NR(cmd)]++;
}
switch (cmd) {
......
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr; if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr); // 对照前面的图逆向拆解
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
......
}
*consumed = ptr - buffer;
}
return 0;
}

这个cmd的值是BC_TRANSACTION,因此应该继续binder_transaction(proc, thread, &tr, false)。这个函数实在太长了,后面再花一节的篇幅深入该函数。

不过很清晰的一点:该函数仅在出错的时候才返回小于零的整数,如果一切正常就返回0。函数binder_ioctl(...)在case BINDER_WRITE_READ这一枝上,如果没有发生错误,则返回binder_thread_write(...)。也就是说:如果一切正常,binder_ioctl(...)会返回0,不管io的数据有多大。

binder学习笔记(十)—— 穿越到驱动层的更多相关文章

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

    binder_open(...)都干了什么? 在回答binder_transaction(...)之前,还有一些基础设施要去探究,比如binder_open(...),binder_mmap(...) ...

  2. Binder学习笔记(九)—— 服务端如何响应Test()请求 ?

    从服务端代码出发,TestServer.cpp int main() { sp < ProcessState > proc(ProcessState::self()); sp < I ...

  3. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  4. Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...

  5. python3.4学习笔记(十八) pycharm 安装使用、注册码、显示行号和字体大小等常用设置

    python3.4学习笔记(十八) pycharm 安装使用.注册码.显示行号和字体大小等常用设置Download JetBrains Python IDE :: PyCharmhttp://www. ...

  6. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  7. python3.4学习笔记(十六) windows下面安装easy_install和pip教程

    python3.4学习笔记(十六) windows下面安装easy_install和pip教程 easy_install和pip都是用来下载安装Python一个公共资源库PyPI的相关资源包的 首先安 ...

  8. python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)

    python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...

  9. python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL

    python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...

随机推荐

  1. 在阿里云服务器上安装git

    https://git-scm.com/book/zh/v1/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git 有yum的系统执行下列命令(已测试) $ yum in ...

  2. mysql 查找表的auto_increment和修改

    1.查看最大的AUTO_INCREMENT SELECT AUTO_INCREMENT from  information_schema.tables where table_schema='cont ...

  3. Xmodem通信协议实例

    在工作时串口通信的过程中需要传输文件,这里就就需要使用通信协议,此时选择的是Xmodem协议作简要研究 1.什么是Xmodem协议 Xmodem协议是串口通信中广泛使用到的异步文件传输协议.以128字 ...

  4. 数据格式化和ModelAttribute注解的介绍

    关于数据传递: 客户端传递数据到服务端: 1.使用普通的形式 A.传递简单的数据 如果是说你传递的数据的名称跟控制层中的形参的名称不一致的情况下需要使用 注解: @RequestParam()如果存在 ...

  5. PostgreSQL 管理数据库

    管理数据库每个正在运行的PostgreSQL 服务器实例都管理着一个或多个数据库.因此,在组织SQL对象(“数据库对象”)的层次中,数据库位于最顶层. 本章描述数据库的属性,以及如何创建.管理.删除它 ...

  6. 关于Java中集合的讲解~

    http://blog.csdn.net/zccst/article/details/5092816 comparable& Comparator 都是用来实现集合中的排序的,只是Compar ...

  7. 开发环境入门 linux基础 (部分)awk 赋值变量 if

    awk 常用于处理格式非常明显的文件 awk -F: '{print $1}' /etc/passwd  含义:取冒号分隔符的第一段内容 $0 指取所有! NF 指有几段内容 $NF 取最后一段内容 ...

  8. 谈谈开发文本转URL小工具的思路

    URL提供了一种定位互联网上任意资源的手段,由于采用HTTP协议的URL能在互联网上自由传播和使用,所以能大行其道.在软件开发.测试甚至部署的环节,URL几乎可以说无处不再,其中用来定位文本的URL数 ...

  9. DDD学习笔录——提炼问题域之知识提炼与协作

    提炼问题域的意义 理解一个复杂问题域以便创建简单且有用的模型需要深入详尽的知识以及深刻的见解,这些只能通过与从内到外理解该领域的人协作得到.对模型的设计进行连续实验和探究正是DDD的能力所能实现的.只 ...

  10. 使用LaTeX按IEEE模板写论文时的参考文献管理方法(BibTeX使用小结)

    之前用LaTeX写论文时,参考文献都是手动添加管理的,真是让人很抓狂.所以这次趁着假期,简单看了一下怎么使用BibTeX对参考文献进行管理,这里以IEEE的最新模板为例. 首先说明,我之前用的是MiK ...