編輯:關於android開發
Android 系統中大量采用了組件化的設計思路,將大量的核心服務以 Service 組件來對外提供。Service 只有注冊到 servicemanager 後,Client 端才能通過 servicemanager 獲取到 Service 的代理對象,從而使用到 Service 提供的服務。由於 Service 組件的注冊過程異常復雜,因此以系統中的 MediaPlayerService 為例,分為兩篇文章來介紹 Service 的注冊流程。這篇文章從 MediaPlayerService 的角度來分析如何發送注冊請求,下一篇文章從 servicemanager 的角度來分析如何處理注冊請求。
1. MediaPlayerService 用戶空間
MediaPlayerService 的注冊過程從調用 instantiate 函數開始,如下所示:
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
defaultServiceManager 函數用於獲得 servicemanager 的代理對象 BpServiceManager,詳細過程請查看上一篇文件 Binder之servicemanager代理對象。接下來調用代理對象 BpServiceManager 的 addService 函數,實現如下:
代碼路徑:frameworks/native/libs/binder/IServiceManager.cpp
virtual status_t addService(const String16& name, const sp& service,
bool allowIsolated)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); [1]
data.writeString16(name); [2]
data.writeStrongBinder(service); [3]
data.writeInt32(allowIsolated ? 1 : 0);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); [4]
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
[1] 通過 writeInterfaceToken 函數寫入頭部信息,內容為 STRICT_MODE_PENALTY_GATHER 和 android.os.IServiceManager
。
[2] 使用 writeString16 函數寫入正在注冊的服務名,內容為 media.player
。
[3] 使用 writeStrongBinder 函數將 MediaPlayerService 對象保存進 flat_binder_object 結構體中。關於 flat_binder_object 結構體,請參考 Binder之數據結構(二)。
Binder 通信機制使用 Parcel 類來序列化需要進行遠程傳輸的內容。Parcel 是包裹的意思,可以理解成發送方將要傳輸的內容 flattened,然後通過 Binder 驅動進行傳輸,到達接收方後再 unflattened。更多內容請查閱官網 Parcel 小節。最終 Parcel 類型變量 data 中內容如下圖所示:
[4] 通過 remote 函數得到保存在 BpRefBase 中的 BpBinder 對象,然後調用其 transact 函數,參數 ADD_SERVICE_TRANSACTION 是注冊 Service 的命令,參數 data 是上述步驟准備好的通信請求數據,參數 reply 是請求應答結果。transact 函數定義如下所示:
代碼路徑:frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
transact 函數實際上是調用 IPCThreadState 的同名函數來完成 addService 請求,函數定義如下:
代碼路徑:frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
......
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); [1]
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply); [2]
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
......
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
[1] writeTransactionData 函數初始化要傳輸給 Binder 驅動的 binder_transaction_data 結構體,並將該結構體寫入 IPCThreadState 類的成員變量 mOut 中。mOut 變量的類型是 Parcel,其內容如下圖所示:
注意這裡使用的事務命令是 BC_TRANSACTION,Binder 驅動中的 binder_thread_write 函數會處理此命令。關於 binder_transaction_data 結構體,請參考 Binder之數據結構(二)。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxwPlsyXSB3YWl0Rm9yUmVzcG9uc2Uguq/K/c/yIEJpbmRlciDH/bavt6LLzcfrx/OjrLKitci0/dOmtPC94bn7o6zTprTwveG5+82ouf0gcmVwbHkgseTBv7e1u9ihozwvcD4NCjxwPr3Tz8LAtLfWzvYgd2FpdEZvclJlc3BvbnNlILqvyv21xL7fzOXKtc/Wo6y0+sLryOfPwqO6PC9wPg0KPHByZSBjbGFzcz0="brush:java;">
代碼路徑:frameworks/native/libs/binder/IPCThreadState.cpp status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { int32_t cmd; int32_t err; while (1) { if ((err=talkWithDriver()) < NO_ERROR) break; err = mIn.errorCheck(); if (err < NO_ERROR) break; if (mIn.dataAvail() == 0) continue; cmd = mIn.readInt32(); switch (cmd) { case BR_TRANSACTION_COMPLETE: if (!reply && !acquireResult) goto finish; break; ...... case BR_REPLY: { binder_transaction_data tr; err = mIn.read(&tr, sizeof(tr)); if (err != NO_ERROR) goto finish; if (reply) { if ((tr.flags & TF_STATUS_CODE) == 0) { reply->ipcSetDataReference( reinterpret_cast
(tr.data.ptr.buffer), tr.data_size, reinterpret_cast (tr.data.ptr.offsets), tr.offsets_size/sizeof(size_t), freeBuffer, this); } ...... } ...... } goto finish; default: err = executeCommand(cmd); if (err != NO_ERROR) goto finish; break; } } finish: if (err != NO_ERROR) { if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; } return err; } waitForResponse 函數在 while 循環中通過 talkWithDriver 來與 Binder 驅動進行通信,實際上是通過 ioctl 系統調用來完成與 Binder 驅動的通信,如下所示:
代碼路徑:frameworks/native/libs/binder/IPCThreadState.cpp status_t IPCThreadState::talkWithDriver(bool doReceive) { if (mProcess->mDriverFD <= 0) { return -EBADF; } binder_write_read bwr; // Is the read buffer empty? const bool needRead = mIn.dataPosition() >= mIn.dataSize(); // We don't want to write anything if we are still reading // from data left in the input buffer and the caller // has requested to read the next data. const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; [1] bwr.write_size = outAvail; [2] bwr.write_buffer = (long unsigned int)mOut.data(); [3] // This is what we'll read. if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); [4] bwr.read_buffer = (long unsigned int)mIn.data(); [5] } else { bwr.read_size = 0; bwr.read_buffer = 0; } // Return immediately if there is nothing to do. 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) [6] err = NO_ERROR; if (mProcess->mDriverFD <= 0) { err = -EBADF; } } while (err == -EINTR); if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { if (bwr.write_consumed < (ssize_t)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 函數會初始化一個 binder_write_read 結構體變量 bwr,之前也對通信數據進行過多次封裝,這裡是進入 Binder 驅動之前的最後一次封裝。
[1] 由於 doReceive 和 needRead 的值都為1,因此 outAvail 的值為 mOut 中保存的數據量。
[2] 初始化成員變量 write_size,寫入 Binder 驅動的數據量為 outAvail 。
[3] 初始化成員變量 write_buffer,寫入 Binder 驅動數據的首地址為mOut.data()
。
[4] 初始化成員變量 read_size,讀取 Binder 驅動的數據量最大為mIn.dataCapacity()
。
[5] 初始化成員變量 read_buffer,將 Binder 驅動數據讀取到mIn.data()
地址處。
[6] 通過 ioctl 系統調用進入 Binder 驅動,ioctl 使用命令為 BINDER_WRITE_READ,傳入參數為 bwr。關於 binder_write_read 結構體,請參考 Binder之數據結構(二)。
2. Binder 驅動
ioctl 系統調用最終會進入 Binder 驅動的 binder_ioctl 函數,binder_ioctl 函數處理所有用戶空間傳入的命令,其中與 BINDER_WRITE_READ 命令有關的代碼如下所示:代碼路徑:linux/drivers/staging/android/binder.c static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int ret; struct binder_proc *proc = filp->private_data; [1] struct binder_thread *thread; unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; trace_binder_ioctl(cmd, arg); ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret) goto err_unlocked; binder_lock(__func__); thread = binder_get_thread(proc); [2] if (thread == NULL) { ret = -ENOMEM; goto err; } switch (cmd) { case BINDER_WRITE_READ: { struct binder_write_read bwr; if (size != sizeof(struct binder_write_read)) { ret = -EINVAL; goto err; } if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto err; } if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); [3] trace_binder_write_done(ret); if (ret < 0) { bwr.read_consumed = 0; if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } 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); [4] trace_binder_read_done(ret); if (!list_empty(&proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) ret = -EFAULT; goto err; } } if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; goto err; } break; } ...... }
[1] 通過 filp 結構體的 private_data 變量獲得用於描述進程的 binder_proc 結構體,該結構體在進程打開設備文件
/dev/binder
時創建。
[2] 調用 binder_get_thread 函數獲取用於描述當前線程的 binder_thread 結構體。第一次調用 binder_get_thread 時由於不存在,因此會創建一個 binder_thread 結構體,並保存在紅黑樹proc->threads
中。
[3] 如果bwr.write_size
的值大於0,調用 binder_thread_write 函數寫入請求內容。參數bwr.write_buffer
為之前writeTransactionData 函數中初始化的內容。
[4] 如果bwr.read_size
的值大於0,調用 binder_thread_read 函數讀取處理結果。由上述分析過程可知,
bwr.write_size
的值大於0,而且事務命令是 BC_TRANSACTION,因此 binder_thread_write 中會調用 binder_transaction 函數進一步處理通信數據。binder_transaction 函數是 Binder 驅動的核心處理函數,該函數定義如下:代碼路徑:linux/drivers/staging/android/binder.c static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { struct binder_transaction *t; struct binder_work *tcomplete; size_t *offp, *off_end; struct binder_proc *target_proc; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; struct list_head *target_list; wait_queue_head_t *target_wait; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; uint32_t return_error; ...... if (reply) { ...... } else { if (tr->target.handle) { ...... } else { target_node = binder_context_mgr_node; [1] if (target_node == NULL) { return_error = BR_DEAD_REPLY; goto err_no_context_mgr_node; } } target_proc = target_node->proc; [2] ...... } if (target_thread) { ...... } else { target_list = &target_proc->todo; [3] target_wait = &target_proc->wait; [4] } /* TODO: reuse incoming transaction for reply */ t = kzalloc(sizeof(*t), GFP_KERNEL); [5] if (t == NULL) { return_error = BR_FAILED_REPLY; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); [6] ...... if (!reply && !(tr->flags & TF_ONE_WAY)) t->from = thread; [7] else t->from = NULL; t->sender_euid = proc->tsk->cred->euid; t->to_proc = target_proc; t->to_thread = target_thread; t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); [8] if (t->buffer == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_alloc_buf_failed; } t->buffer->allow_user_free = 0; t->buffer->debug_id = t->debug_id; t->buffer->transaction = t; t->buffer->target_node = target_node; if (target_node) binder_inc_node(target_node, 1, 0, NULL); offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); [9] if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { [10] ...... } if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { [11] ...... } ...... off_end = (void *)offp + tr->offsets_size; for (; offp < off_end; offp++) { struct flat_binder_object *fp; ...... fp = (struct flat_binder_object *)(t->buffer->data + *offp); switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { struct binder_ref *ref; struct binder_node *node = binder_get_node(proc, fp->binder); [12] if (node == NULL) { node = binder_new_node(proc, fp->binder, fp->cookie); [13] if (node == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_new_node_failed; } node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } if (fp->cookie != node->cookie) { ...... } if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } ref = binder_get_ref_for_node(target_proc, node); [14] if (ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } if (fp->type == BINDER_TYPE_BINDER) fp->type = BINDER_TYPE_HANDLE; [15] else fp->type = BINDER_TYPE_WEAK_HANDLE; fp->handle = ref->desc; [16] binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo); ...... } break; ...... } } if (reply) { ...... } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); t->need_reply = 1; t->from_parent = thread->transaction_stack; [17] thread->transaction_stack = t; [18] } else { ...... } t->work.type = BINDER_WORK_TRANSACTION; list_add_tail(&t->work.entry, target_list); [19] tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); [20] if (target_wait) wake_up_interruptible(target_wait); [21] return; ...... }
binder_transaction 函數中參數 tr 的類型是 binder_transaction_data 結構體,裡面保存的實際上是用戶空間的 writeTransactionData 函數初始化的內容。
[1] 由於
tr->target.handle
值為0,因此 target_node 變量賦值為 binder_context_mgr_node,也就是賦值為 servicemanager 的 Binder 實體。binder_context_mgr_node 在 servicemanager 注冊成為上下文管理者過程被賦值,具體過程請參考 Binder之守護進程servicemanager。
[2] 根據 target_node 的值初始化目標進程 target_proc 為 servicemanager 的宿主進程 。
[3] 通過 target_proc 獲取 servicemanager 進程的待處理工作隊列 target_list。
[4] 通過 target_proc 獲取 servicemanager 進程的等待隊列 target_wait。
[5] 創建一個待處理事務 t。
[6] 創建一個待完成工作項 tcomplete。
[7] 初始化發起事務的線程t->from
為 MediaPlayerService 的當前線程。
[8] 在 servicemanager 進程中分配一塊大小為tr->data_size
和tr->offsets_size
之和的內存,用於保存從 MediaPlayerService 用戶空間傳入的通信數據。
[9] 計算出待處理事務 t 中偏移數組的地址。
[10] 使用 copy_from_user 函數從 tr 中拷貝數據到待處理事務 t 中。
[11] 使用 copy_from_user 函數從 tr 中拷貝偏移數組到待處理事務 t 中。
[12] 獲取目標進程 servicemanager 中 MediaPlayerService 對應的 Binder 實體對象。
[13] 第一次調用 binder_get_node 函數返回值為 NULL,所以新建一個 MediaPlayerService 的 Binder 實體對象。
[14] 為 MediaPlayerService 創建一個 Binder 引用對象。關於 Binder 實體對象和 Binder 引用對象的相關內容,請參考 Binder之基本概念這篇博文。
[15] 由於 servicemanager 只能通過句柄值來引用 MediaPlayerService,因此修改
fp->type
的值為 BINDER_TYPE_HANDLE。
[16] 修改fp->handle
的值為 MediaPlayerService 對應引用對象的句柄值。
[17] 待處理事務 t 依賴於thread->transaction_stack
。
[18] 將待處理事務 t 添加到事務發起線程(MediaPlayerService 的當前線程)的事務堆棧。
[19] 將待處理事務 t 加入到 servicemanager 進程的待處理工作隊列 target_list 中。
[20] 將待完成工作項 tcomplete 加入到事務發起線程的待處理項隊列中。
[21] 喚醒目標進程 servicemanager 處理剛剛添加到待處理工作隊列 target_list 中的待處理事務 t(下篇文章分析這個過程)。返回到之前的 binder_ioctl 函數,由於
bwr.read_size
大於0,因此進入 binder_thread_read 函數:代碼路徑:linux/drivers/staging/android/binder.c static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed, int non_block) { 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: wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); [1] ...... if (wait_for_proc_work) { ...... } else { if (non_block) { if (!binder_has_thread_work(thread)) ret = -EAGAIN; } else ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); [2] } ...... while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; if (!list_empty(&thread->todo)) w = list_first_entry(&thread->todo, struct binder_work, entry); [3] else if (!list_empty(&proc->todo) && wait_for_proc_work) w = list_first_entry(&proc->todo, struct binder_work, entry); else { if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */ goto retry; break; } if (end - ptr < sizeof(tr) + 4) break; switch (w->type) { ...... case BINDER_WORK_TRANSACTION_COMPLETE: { cmd = BR_TRANSACTION_COMPLETE; if (put_user(cmd, (uint32_t __user *)ptr)) [4] return -EFAULT; ptr += sizeof(uint32_t); list_del(&w->entry); kfree(w); } break; ...... } if (!t) [5] continue; ...... } done: ...... return 0; }
[1] 通過之前的分析可知
thread->transaction_stack
和thread->todo
都不為 NULL,因此 wait_for_proc_work 的值為 False。
[2] 檢查線程等待隊列thread->todo
中是否存在待處理工作項,如果沒有則睡眠等待。由於之前添加了一個待完成工作項 tcomplete,因此線程不會睡眠。
[3] 從等待隊列thread->todo
中獲取待處理工作項 w。
[4] 調用 put_user 系統調用往用戶空間傳入的 read_buffer 中寫入 BR_TRANSACTION_COMPLETE 命令。然後從thread->todo
刪除待處理工作項 w。
[5] 由於沒有事務需要進一步處理,所以繼續 while 循環。3. MediaPlayerService 用戶空間
此時由於等待隊列thread->todo
中沒有更多的待處理工作項,所以 ioctl 系統調用返回,回到 MediaPlayerService 用戶空間,繼續執行 talkWithDriver 函數,如下所示:代碼路徑:frameworks/native/libs/binder/IPCThreadState.cpp ...... if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { if (bwr.write_consumed < (ssize_t)mOut.dataSize()) mOut.remove(0, bwr.write_consumed); else mOut.setDataSize(0); [1] } if (bwr.read_consumed > 0) { mIn.setDataSize(bwr.read_consumed); [2] mIn.setDataPosition(0); } return NO_ERROR; }
[1] 設置 mOut 大小為0,說明沒有更多內容需要寫入 Binder 驅動。
[2] 設置 mIn 大小為bwr.read_consumed
,說明從 Binder 驅動中讀取的內容長度為bwr.read_consumed
。然後返回到 waitForResponse 函數,繼續執行如下代碼:
代碼路徑:frameworks/native/libs/binder/IPCThreadState.cpp while (1) { if ((err=talkWithDriver()) < NO_ERROR) break; err = mIn.errorCheck(); if (err < NO_ERROR) break; if (mIn.dataAvail() == 0) continue; cmd = mIn.readInt32(); [1] ...... switch (cmd) { case BR_TRANSACTION_COMPLETE: [2] if (!reply && !acquireResult) goto finish; break; ...... }
[1] 從 IPCThreadState 類的成員變量 mIn 中讀取之前 binder_thread_read 函數中寫入 read_buffer 的 BR_TRANSACTION_COMPLETE 命令。
[2] 由於參數 reply 不為 NULL,說明 waitForResponse 函數並沒有結束,繼續執行 while 循環,從而第二次調用 talkWithDriver 函數。對 BR_TRANSACTION_COMPLETE 命令的處理分為兩種情況:如果參數 reply 為 NULL,說明是異步請求,那麼從 waitForResponse 函數返回;如果參數 reply 不為 NULL,說明是同步請求,那麼 waitForResponse 函數並沒有結束,繼續執行 while 循環。
4. Binder 驅動
在 talkWithDriver 函數中,第二次調用 ioctl 系統調用,並且bwr.write_size
的值為0。最終再一次進入 binder_ioctl 函數,由於此時bwr.write_size
的值為0,而bwr.read_size
的值不為0,因此直接進入到 binder_thread_read 函數,代碼如下所示:代碼路徑:linux/drivers/staging/android/binder.c static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed, int non_block) { 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: wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo); [1] ...... if (wait_for_proc_work) { ...... } else { if (non_block) { if (!binder_has_thread_work(thread)) ret = -EAGAIN; } else ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); [2] }
[1] 此時
thread->transaction_stack
不為 NULL, 而線程等待隊列thread->todo
為空,wait_for_proc_work 的值為 False。
[2] 由於線程等待隊列thread->todo
為空,MediaPlayerService 線程進入睡眠,等待 servicemanager 的注冊處理結構。至此,Service 如何通過 Binder 驅動發送注冊請求到 servicemanager 已經分析完畢,下篇文章繼續從 servicemanager 的角度來分析如何處理注冊請求。
細說MySQL 之MEM_ROOT這篇文章會詳細解說MySQL中使用非常廣泛的MEM_ROOT的結構體,同時省去debug部分的信息,僅分析正常情況下,mysql中使用M
Linux內核系列—5.操作系統開發之特權級及特權級的轉移,linux特權CPL——當前執行的程序或任務的特權級,它被存儲在cs和ss的第0位和第
Android模擬登錄評論CSDN 有時候作為非官方開發的APP集成了官方的所有信息,但是現在需要實現另一個功能那就是登錄發表評論到官方的網站,而非官方的APP並不知道官
違章查詢源碼分享,違章查詢源碼 使用快遞100查詢接口實現 源碼地址:https://github.com/chenjie200280/weizhang 測試