我觉得,chatgpt的答案好像更适合答题。
为什么zygote使用socket而不是binder
Zygote是Android中的一个重要进程,它是启动应用程序进程的父进程。Zygote使用Socket来与应用程序进程进行通信,而不是使用Android中的IPC机制Binder,这是因为Socket和Binder有不同的优缺点,而在Zygote进程中使用Socket可以更好地满足Zygote进程的需求。
首先,Socket具有良好的跨平台性,能够在不同的操作系统和语言之间进行通信。这对于Zygote进程来说非常重要,因为它需要在不同的设备和架构上运行,并且需要与不同的应用程序进程进行通信。使用Socket可以让Zygote进程更加灵活和可扩展,因为它不需要考虑Binder所带来的特定限制和要求。
其次,Socket具有简单的API和易于使用的特点。Zygote进程需要快速启动并与应用程序进程建立通信,Socket提供了快速、可靠的通信方式,并且使用Socket API也很容易实现。相比之下,Binder需要更多的配置和维护工作,这对于Zygote进程来说可能会增加不必要的复杂性和开销。
最后,Socket在数据传输时具有更低的延迟和更高的吞吐量,这对于Zygote进程来说非常重要。Zygote进程需要在较短的时间内启动应用程序进程,并且需要传输大量的数据和代码,Socket的高性能和低延迟使其成为更好的选择。
总之,Zygote进程使用Socket而不是Binder是基于其优点和需求而做出的选择。虽然Binder在Android中扮演着重要的角色,但在某些情况下,使用Socket可以提供更好的性能和更大的灵活性。
——分割线——
看完《安卓传奇:Android缔造团队回忆录》后,我觉得这个问题的答案书中给出了.这段历史是这样的...
先来看看书中关于Zygote的灵感来源,Emacs.
是Dalvik小组为Android 1.0 开发的另一个东西。Zygote就像你做三明治时用的面包片。当然,你也可以在每次做三明治时从头开始烤面包,但如果是这样的话,每次你想吃三明治都要花大量的时间和精力。很明显,如果有现成的面包,你只需要把它切成片,并更快更容易地做出三明治。Zygote就像做三明治用的面包。
Dan的这个想法来自Emacs(UNIX系统上的一个非常流行的文本编辑器)的一个特性,我们可以随时转储编辑器的状态,稍后可以从这个保存点启动Emacs(这个过程被巧妙地称为反转储(undump))。这意味着Emacs可以更快地启动,因为它只需要从磁盘上恢复状态,
不需要在启动时重新执行一大堆代码逻辑。“我的想法是我们实现一个具有反转储功能的系统,就像Emacs最‘著名’(至少对我来说)的那个特性。Mike Fleming说:‘我们跳过转储和重新加载这些步骤,怎么样?’说完他就开干了。”
Mike 让系统跑了起来,从根本上改变了应用程序的启动方式。原先的每个应用程序在启动时都需要加载必要的代码,并进行初始化。Zygote 创建了一个包含大部分核心平台代码的进程,并几乎预加载和初始化了所有这些代码。每当有应用程序启动时,都会通过分叉
(将其复制到一个新进程中)Zygote 进程,让应用程序立即进入即将就绪的状态。
而binder的来源.
Binder 的概念可以追溯到 Be 公司。George Hoffman 是 Be 公司图形和框架团队的负责人,他需要一种机制让 Be 的互联网设备的 JavaScript UI 层与底层系统服务发生交互,于是就有了 Binder。随着 Be 公司的工程师后来加入 PalmSource 开发 Palm OS,再到后来加入 Android,Binder 也都一直在演化。George 最终没有参与开发 Android,但他与 Be 和 PalmSource 的未来 Android 工程师一起设计了许多概念,这些概念最终都出现在 Android 上,比如 Activity 和 Intent。
Android 设备上始终运行着许多进程,负责处理系统的各种任务。系统进程负责进程的管理、应用的启动、窗口的管理和其他底层的操作系统功能。电话进程负责保持通话正常连接。运行中的前台应用进程为用户提供交互功能。系统 UI 进程负责处理导航按钮、状态栏和通知。还有很多其他进程,它们都需要在某个时刻与其他流程通信。
通常来说,IPC 机制是一种简单而底层的东西,而这也正是 Danger 前工程师们想要的。
黄威说:“Danger 做事喜欢速战速决,但主要还是要简单。”但来自 Be 公司的工程师,包括 Jeff、Joe 和 Dianne,更喜欢他们在 PalmSource 实现的功能更全面(也更复杂)的 Binder。
况且,Binder 是开源的,可以直接用在这个新平台上。
这一分歧造成了团队之间的摩擦。Mike Fleming 站在 Danger 一边:“我对 Binder 持怀疑态度。我认为它没有经过深思熟虑。这确实是他们在 Palm 做的,但并没有被用在真正的产品中。”
“我感觉特别糟糕的是对Binder的阻塞调用会导致另一边也阻塞。我觉得这导致了很多不必要的线程开销,而且没有为我们带来任何价值。另外,初始的Binder Linux内核驱动程序也不是很健壮。为了让它更健壮,我们也是费了很大一番功夫的。”
对Binder持怀疑态度的人并没有赢得这场战斗,Jeff和他的小组奋勇向前,实现了Binder,成为Android框架的一个基础部分。与此同时,Mike在他的电话功能中跳过Binder:“我在Java进程和本地接口进程之间开了一个UNIX域套接字。”
binder当初并不成熟,团队成员对于进程间通讯更倾向于用socket.后面为了做了很多优化,才使得binder通讯变得成熟稳定.
关于Android操作系统,书中推荐阅读《现代操作系统》,并深入阅读介绍 Android 的章节(10.8)。那一章是 Dianne Hackborn 写的,为 Binder和 Linux 扩展等内容提供了详尽的细节。另关于handle设计思想的理解,我推荐阅读《C++沉思录》.
----------分割线---------
要想了解这个问题,首先需要对Linux进程间通讯机制有一定的了解。
1.Linux进程间通讯机制
https://www.zhihu.com/question/39440766/answer/89210950
Linux现有的所有进程间IPC方式:
1.管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
2.消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
3.共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
4.套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
5.信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
6.信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
Binder机制基于开源的OpenBinder。http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderIPCMechanism.html
2. socket 与 binder 对比
先来看看 socket与binder 两种进程间通讯机制 对比。
接下来回顾下 Zygote启动和Android启动。
Zygote启动过程的函数调用类大致流程:
http://gityuan.com/2016/02/13/android-zygote/#jnistartreg
Zygote启动过程:
1.解析init.zygote.rc中的参数,创建AppRuntime并调用AppRuntime.start()方法;
2.调用AndroidRuntime的startVM()方法创建虚拟机,再调用startReg()注册JNI函数;
3.通过JNI方式调用ZygoteInit.main(),第一次进入Java世界;
4.registerZygoteSocket()建立socket通道,zygote作为通信的服务端,用于响应客户端请求;
5.preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率;
6.zygote完毕大部分工作,接下来再通过startSystemServer(),fork得力帮手system_server进程,也是上层framework的运行载体。
7.zygote功成身退,调用runSelectLoop(),随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作。
下面来看一张图,标示了调用startService之后的通讯流程。
图中涉及3种IPC通信方式:
Binder
、Socket
以及Handler
,在图中分别用3种不同的颜色来代表这3种通信方式。一般来说,同一进程内的线程间通信采用的是 Handler消息队列机制,不同进程间的通信采用的是binder机制,另外与Zygote
进程通信采用的Socket
。
Android系统启动流程如下:
现在回到问题,为什么Zygote通信为什么用Socket,而不是Binder?
具体可看这篇:
android中AMS通知Zygote去fork进程为什么使用socket而不使用binder?
可从以下五个方面分析:
1.先后时序问题:
binder驱动是早于init进程加载的。而init进程是安卓系统启动的第一个进程。
安卓中一般使用的binder引用,都是保存在ServiceManager进程中的,而如果想从ServiceManager中获取到对应的binder引用,前提是需要注册。虽然Init进程是先创建ServiceManager,后创建Zygote进程的。虽然Zygote更晚创建,但是也不能保证Zygote进程去注册binder的时候,ServiceManager已经初始化好了。注册时间点无法保证,AMS无法获取到Zygote的binder引用,这是原因之一。
2.多线程问题
Linux中,fork进程是不推荐fork一个多线程的进程的,因为如果存在锁的情况下,会导致锁异常。
而如果自身作为binder机制的接收者,就会创建一个额外的线程来进行处理(发送者进程是无影响的)。
所以,如果使用binder机制,就会导致去fork一个多线程的进程,这是原因之二。
3.效率问题
AMS和Zygote之间使用的LocalSocket,相对于网络Socket,减少了数据验证等环节,所以其实效率相对于正常的网络Socket会大幅的提升。虽然还是要经过两次拷贝,但是由于数据量并不大,所以其实影响并不明显。
所以,LocalSocket效率其实也不低,这是原因之三。
4.安全问题
LocalSocket其实也有权限校验,并不意味着可以被所有进程随意调用,这是原因之四。
5.Binder拷贝问题
如果使用binder通讯机制的话,从Zygote中fork出子进程会拷贝Zygote中binder对象。所以就凭白多占用了一块无用的内存区域。而Binder对象不能释放。Binder的特殊性在于其是成对存在的,其分为Client端对象和Server端对象。假设我们使用binder,如果要释放掉APP的Server端binder引用对象,就必须释放掉AMS中的Client端binder对象,那这样就会导致AMS失去binder从而无法正常向Zygote发送消息。
而使用socket通讯机制的话,fork出APP进程之后,APP进程会去主动的关闭掉这个socket,从而释放这块区域。相关代码在ZygoteConnection的processCommand方法中:
try {
if (pid == 0) {
// in child
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, childPipeFd,
parsedArgs.mStartChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, serverPipeFd);
return null;
}
}
所以,使用binder会造成额外的内存占用,这是原因之五。
另外,binder调用一般是同步阻塞的。如果使用oneway,是非阻塞(像一些系统服务调用应用进程的时候就会使用 oneway,比如 AMS 调用应用进程启动 Activity,这样就算应用进程中做了耗时的任务,也不会阻塞系统服务的运行。)。但是,binder驱动对于oneway的调用是类似于handler sendmessage那样的,挨个处理,所以如果服务端的oneway接口处理太慢而客户端调用太多的话,来不及处理的调用会占满binder驱动的缓存,导致其他调用抛出上面的transaction failed。
oneway 方法的隐患具体参考这篇。
AIDL oneway 方法的隐患
socket也不是单独使用的,I/O模型中的epoll是一种高效的管理socket的模型,epoll机制相对成熟,是同步非阻塞。
socket(套接字)是对 TCP/IP 或者UDP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。
而Binder很负复杂。
简单的设计,减少出问题的几率,会让系统更加稳定。
Zygote通信为什么用Socket,而不是Binder?
说了这么多,其实是通过对比加深对Android进程间通讯Socket和Binder两种机制的理解。
3.Zygote 处理 socket消息代码分析
具体代码流程分析可看这篇:
app_process: zygote处理socket消息请求(5)
参考链接:
为什么 Android 要采用 Binder 作为 IPC 机制?
为什么Android的Zygote与SystemServer通信采用Socket,而不是Binder?
[026]Zygote中Socket通信能否替换成Binder通信?
Android系统启动-zygote篇
Android10.0系统启动之Zygote进程-[Android取经之路]
app_process: zygote处理socket消息请求(5)
Android Framework层学习——为什么SystemServer进程与Zygote进程通讯采用Socket而不是Binder
android中AMS通知Zygote去fork进程为什么使用socket而不使用binder