两个与Android IPC有关的问题

 

 

1.文件描述符是如何在进程之间传递的?

我们知道文件描述符,就像虚拟内存的地址一样,是进程私有的资源。在一个进程中文件描述符,在另外一个进程中,可能是无效的,也可能是对应另外一个文件。 Android却可以把文件描述符从一个进程传到另外一个进程。第一次发现这种情况时,让我感到很惊奇,所以花了点时间去研究。看明白之后,发现其实现也很简单:

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);
}

在对文件描述符打包时,把对象的类型设置为BINDER_TYPE_FD。

在binder的内核模块binder_transaction函数中,我们可以看:

       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的工作原理应该比较熟悉,先通过服务名称从ServiceManager获取一个Binder,然后通过Binder去调用服务相应的函数。由客户端主动发起请求,这是典型是C/S模型。而Receiver则是服务端反过来调用客户端函数,这就看起来有点让人感到迷惑了。

其实Receiver更简单,所有Broadcast都是从ActivityManagerService发出的,所以只需要让 ActivityManagerService知道你的Receiver就行了,这是通过 ActivityManagerNative.registerReceiver完成的。实现自己的Receiver时都是实现 BroadcastReceiver接口,BroadcastReceiver本身并不是可以跨进程调用的,这是由 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的动作执行放到了mActivityThread线程里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值