兩個與Android IPC有關(guān)的問題2,566 views| 2010-12-26| 李先靜| Android| | 5 條評論轉(zhuǎn)載時請注明出處和作者聯(lián)系方式
1.文件描述符是如何在進程之間傳遞的? 我們知道文件描述符,就像虛擬內(nèi)存的地址一樣,是進程私有的資源。在一個進程中文件描述符,在另外一個進程中,可能是無效的,也可能是對應(yīng)另外一個 文件。 Android卻可以把文件描述符從一個進程傳到另外一個進程。第一次發(fā)現(xiàn)這種情況時,讓我感到很驚奇,所以花了點時間去研究??疵靼字螅l(fā)現(xiàn)其實現(xiàn)也 很簡單: status_t Parcel::writeFileDescriptor(int fd) { flat_binder_object obj; obj.type = BINDER_TYPE_FD; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; obj.handle = fd; obj.cookie = (void*)0; return writeObject(obj, true); } 在對文件描述符打包時,把對象的類型設(shè)置為BINDER_TYPE_FD。 在binder的內(nèi)核模塊binder_transaction函數(shù)中,我們可以看: case BINDER_TYPE_FD: { int target_fd; struct file *file; if (reply) { if (!(in_reply_to->flags & TF_ACCEPT_FDS)) { binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_fd_not_allowed; } } else if (!target_node->accept_fds) { binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_fd_not_allowed; } file = fget(fp->handle); if (file == NULL) { binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_fget_failed; } target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); if (target_fd < 0) { fput(file); return_error = BR_FAILED_REPLY; goto err_get_unused_fd_failed; } task_fd_install(target_proc, target_fd, file); binder_debug(BINDER_DEBUG_TRANSACTION, " fd %ld -> %d\n", fp->handle, target_fd); /* TODO: fput? */ fp->handle = target_fd; } break; 這里如果是文件描述符,就在目標進程中重新打開同一個文件了(雖然打開的是同一個文件,但目標進程拿到的文件描述符可能不相同)。 2.Receiver是如何工作的? 大家對Service的工作原理應(yīng)該比較熟悉,先通過服務(wù)名稱從ServiceManager獲取一個Binder,然后通過Binder去調(diào)用服 務(wù)相應(yīng)的函數(shù)。由客戶端主動發(fā)起請求,這是典型是C/S模型。而Receiver則是服務(wù)端反過來調(diào)用客戶端函數(shù),這就看起來有點讓人感到迷惑了。 其實Receiver更簡單,所有Broadcast都是從ActivityManagerService發(fā)出的,所以只需要讓 ActivityManagerService知道你的Receiver就行了,這是通過 ActivityManagerNative.registerReceiver完成的。實現(xiàn)自己的Receiver時都是實現(xiàn) BroadcastReceiver接口,BroadcastReceiver本身并不是可以跨進程調(diào)用的,這是由 ActivityThread.PackageInfo.ReceiverDispatcher來包裝的。 這里值得注意的是Receiver都是在ActivityThread里處理的,而不是在Binder線程里處理的,主要目的可能為了避免不必要的加鎖操作吧。 public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky) { if (DEBUG_BROADCAST) { int seq = intent.getIntExtra("seq", -1); Slog.i(TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq + " to " + mReceiver); } Args args = new Args(); args.mCurIntent = intent; args.mCurCode = resultCode; args.mCurData = data; args.mCurMap = extras; args.mCurOrdered = ordered; args.mCurSticky = sticky; if (!mActivityThread.post(args)) { if (mRegistered && ordered) { IActivityManager mgr = ActivityManagerNative.getDefault(); try { if (DEBUG_BROADCAST) Slog.i(TAG, "Finishing sync broadcast to " + mReceiver); mgr.finishReceiver(mIIntentReceiver, args.mCurCode, args.mCurData, args.mCurMap, false); } catch (RemoteException ex) { } } 這里通過消息把Receiver的動作執(zhí)行放到了mActivityThread線程里。 |
|