您的位置:首页 > 移动开发 > Android开发

Android-与Android IPC有关的问题

2011-05-03 15:49 204 查看
1.文件描述符是如何在进程之间传递的?

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

1

2

3

4

5

6

7

8

9

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函数中,我们可以看:

1

2

3

4

5

6

7

8

910

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37


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线程里处理的,主要目的可能为了避免不必要的加锁操作吧。

1

2

3

4

5

6

7

8

910

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37


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
;

这里通过消息把Receiver的动作执行放到了mActivityThread
线程里。

文章出处:http://www.limodev.cn/blog

作者联系方式:李先静
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: