android灭屏后调用binder通讯竟然影响了socket的POLL_OUT事件,怪事。

当你的android在灭屏(休眠)时分派(dispatch) Ice调用过程中,如果创建了新的进程,你的响应将不会预期那样工作,尽管你已经调用 ice_response或 ice_exception,并且成功返回了。

先来搞清楚,Ice 所有的底层 IO都发生(或者说执行)在 reactor的 ThreadPool。Ice 会向网络发送数据只有三个函数,在代理端 ice_invoke(发起调用)以及在服务端 ice_response(返回响应)和 ice_exception(返回异常)。这三个函数并没有直接在你所在的控制流线程上去进行底层发送数据,而只是将流化后的数据包放到队列,并选出一个数据注册到 reactor的写事件,很明显发送操作只会react 在 reactor的写事件,也就是执行在 reactor的ThreadPool。所以调用这些函数,实际是要唤醒另外一个线程去执行底层发送操作的,唤醒通过socket的 POLL_OIUT事件。

上面提到的问题与这有什么关系?没想到真扯上关系了。如果android在灭屏后,创建了新的进程,Ice 的 reactor就会长时间不能从 socket的 POLL_OUT事件上唤醒过来,阻塞掉了所有的发送操作。这样就不只阻塞掉了响应,同时也阻塞掉ice_invoke的发送,因为心跳也是在发送请求,所以连接维护也受到影响。

但是仔细调试后,发现只要创建出来的新进程exec了 am或pm命令,就会让父进程的socket的POLL_OUT不正常。众所周知这两个命令都是在使用android service,也就是使用了binder通讯。通过尝试android源代码目录 base/cmds 下的其他命令后,同样发生了POLL_OUT不正常现象。

在 base/cmds 目录下的命令,除了app_process和screencap是用c++写的外,其它的命令都是用java写的。screencap大家也知道是在使用SurfaceFlinger服务。而其它java写的应用程序必须依赖宿主app_process运行。打开app_process的源代码,首先就可以看到包含binder的两个头文件 IPCThreadState.h 以及 ProcessState.h,并且在初始化的hook函数中初始化binder环境。

为了验证,我通过内核调试跟踪命令"requestsync --help",因为打印使用手册并不调用android service,requestsync.java 只是简单在System.err.println出使用手册。但是内核跟踪显示:

requestsync命令运行在 app_process宿主上,所以创建出来的进程exec的是app_process。

app_process调用binder_ioctl,c0 04 62 09 就是 RW size_4 'b' 9, 而40 04 62 05 就是 W size_4 'b' 5,翻译过来就是 BINDER_VERSION 和 BINDER_SET_MAX_THREADS。也就是app_process::onStarted调用的。

 

现在可以将问题缩小到 Binder 和 socket的POLL_OUT之间的有某种影响。

但是范围还不够小,还有下面两种情况。

情况一,sh在亮屏的时候fork出来了,在灭屏后A进程通过pipe写命令到sh,由sh去fork出子进程去使用binder,这种情况下,A进程的socket并不受影响。

情况二,在灭屏后A进程fork出一个sh,而sh去fork出子进程去执行binder,这种情况下,A进程的socket的POLL_OUT事件就会受到不明的影响,POLL_IN事件仍正常工作。

 

查POLL_OUT事件的问题,首先想到就是网络相关的软件中断 net_tx_action 以及 net_rx_action。本想再对内核跟踪一下,net_tx_action,raise_softirq_irqoff,sch_direct_xmit等函数的,无奈android手机厂商的内核都不带kprobe模块。

posted on 2017-12-01 20:30  bbqz007  阅读(841)  评论(0编辑  收藏  举报