http://www.cnblogs.com/palance/p/5724433.html
Binder驅動層的代碼在
kernel/goldfish/drivers/staging/android
下的
binder.c
和
binder.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的内容
需要開啟三個終端完成調試:
- Target1 在模拟器上啟動server
$ adb shell /data/local/tmp/testservice/TestServer
- 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
-
Host1 在宿主端啟動gdb
``` bash
$ ./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 ([email protected]=0xb6c24000,[email protected]=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 ([email protected]=0xb6c24000,[email protected]=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,[email protected]=1, data=..., [email protected]=0xbeaa1ad4, flags=16, [email protected]=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=) 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的資料有多大。
分類: Android