Android Binder驅動分析三 addservice原始碼分析
接著上網我們分析了如下的代碼:
- 將事務添加到
servicemanager,然后將其喚醒 - 添加一個
binder_work事務給mediaplayserice
我們這里分析mediaplayservice提交事務后會做什么
//drivers/staging/android/binder.c
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
//從用戶空間的arg的地址處拷貝到內核
//ubuf = arg
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
//因為有資料傳入所以不為0
if (bwr.write_size > 0) {
// 1. 將事務添加到`servicemanager`,然后將其喚醒
// 2. 添加一個`binder_work`事務給`mediaplayserice`
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
}
//這里不為空,表示想接收binder驅動的資訊
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
//如果還有未處理的任務喚醒執行緒,但是這里沒有沒有其他事務所以不會進入下面的代碼
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
goto out;
}
}
//binder驅動將修改過后的bwr傳回用戶空間(mediaplayservice)
//可見記憶體共享僅針對于服務端來說,客戶端的讀寫還是要拷貝滴
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
//ret = binder_thread_read(proc, thread, bwr.read_buffer,
// bwr.read_size,
// &bwr.read_consumed,
// filp->f_flags & O_NONBLOCK);
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
//這里是0所以放入BR_NOOP
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
//失敗處理
return -EFAULT;
//指標偏移
ptr += sizeof(uint32_t);
}
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
//一個程式的待處理任務會在執行緒或行程todo串列中
//這里是取出串列物件
if (!binder_worklist_empty_ilocked(&thread->todo))
list = &thread->todo;
else if (!binder_worklist_empty_ilocked(&proc->todo) &&
wait_for_proc_work)
list = &proc->todo;
//是否還有資料要讀取
if (end - ptr < sizeof(tr) + 4) {
break;
}
//取出串列物件,這里就是上篇文章的的內容tcomplete
w = binder_dequeue_work_head_ilocked(list);
switch (w->type) {
case BINDER_WORK_TRANSACTION_COMPLETE: {
cmd = BR_TRANSACTION_COMPLETE;
//放入一個命令BR_TRANSACTION_COMPLETE
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
//指標后移
ptr += sizeof(uint32_t);
kfree(w);
} break;
}
}
done:
//注意consume變成寫入的數值大小
//比如buffer是100 寫入64
//ptr初始值=100 寫入64為164.和原始地址先減去可以到寫入的位元組大小
*consumed = ptr - buffer;
//這里用于讓用戶空間新生成一個執行緒,這里我們先無視,就當沒有寫入
if (proc->requested_threads == 0 &&
list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++;
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
}
return 0;
}
可見binder驅動給mediaplayservice提交了兩個主要命令BR_NOOP 和BR_TRANSACTION_COMPLETE 就回傳用戶空間
也就是以下代碼:
// frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive=true)
{
//略
do {
//將我們的內容發送給binder驅動,這時從驅動回傳
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
} while (err == -EINTR);
//binder驅動會修改bwr
if (err >= NO_ERROR) {
//運行到這時候binder驅動會消費我們傳入bwr.write_buffer,具體消費了多個位元組就是write_consumed
if (bwr.write_consumed > 0) {
//將這個重置后繼續下次使用
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
//binder驅動會順帶回傳資訊,回傳的資訊的大小是read_consumed
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
沒有太特殊繼續向上層函式回傳
以下函式首先讀取BR_NOOP 后再進入回圈呼叫talkWithDriver()后在讀取資訊,等回傳后在處理BR_TRANSACTION_COMPLETE
// frameworks\native\libs\binder\IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
//回傳到這繼續向下走
if ((err=talkWithDriver()) < NO_ERROR) break;
//如果沒有回傳資訊那么繼續回圈讀取binder驅動
if (mIn.dataAvail() == 0) continue;
//讀取binder驅動回傳的前4位元組,這四個位元組表示binder驅動是什么訊息型別
cmd = (uint32_t)mIn.readInt32();
//根據回傳的binder回傳的命令做出操作
//首先讀取BR_NOOP
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
//這里reply不為空,acquireResult為NULL
//所以!reply && !acquireResult 為false,因此繼續回圈進入talkWithDriver等
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
//..略
case BR_FAILED_REPLY:
//..略
case BR_ACQUIRE_RESULT:
//..略
case BR_REPLY:
//..略
default:
//BR_NOOP在內部什么都不做,繼續回圈進入talkWithDriver
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
}
}
return err;
}
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch (cmd) {
//略
case BR_NOOP:
break;
}
return result;
}
第一次讀取BR_NOOP沒有太多特殊的情況于是乎繼續進入talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
binder_write_read bwr;
//因為dataPosition還有4位元組沒有讀取,
//假設dataSize為8位元組,分別存放`BR_NOOP` 和`BR_TRANSACTION_COMPLETE`
//讀取`BR_NOOP`后dataPosition為4
//因此這里為false
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
//outAvail 自然為0
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
//needRead false
//doReceive true
//這里走到false
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// bwr.write_size == 0為true
// bwr.read_size == 0 為true
// 那么整個運算式為true,因此直接回傳
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
talkWithDriver()回傳繼續讀取下一個命令BR_TRANSACTION_COMPLETE 這里由于這個命令也沒有特殊的地方,所以會重新再次進入talkWithDriver
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
binder_write_read bwr;
//資料讀取完`BR_TRANSACTION_COMPLETE` dataPosition會等于dataSize
//因此這里為true
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
//outAvail dataSize
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
//needRead true
//doReceive true
//這里走到true
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
//這里直接為false
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
//這里在進入binder驅動中
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
最后再次進入binder驅動的binder_thread_read函式.
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
//這里回傳false. binder_available_for_proc_work_ilocked如果有事務就回傳true,沒有就回傳false
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
//這里為
if (non_block) {
//;略
} else {
//阻塞由于wait_for_proc_work為fakse
//所以mediaplayservice被阻塞在這
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
//略
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/272516.html
標籤:其他
上一篇:QT的UDP通信
