Chromium以多进程架构著称,它主要包含四类进程,分别是Browser进程、Render进程、GPU进程和Plugin进程。之所以要将Render进程、GPU进程和Plugin进程独立出来,是为了解决它们的不稳定性问题。也就是说,Render进程、GPU进程和Plugin进程由于不稳定而引发的Crash不会导致整个浏览器崩溃。本文就对Chromium的多进程架构进行简要介绍,以及制定学习计划。
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
一个Chromium实例只有一个Browser进程和一个GPU进程,但是Render进程和Plugin进程可能有若干个。Browser进程负责合成浏览器的UI,包括标题栏、地址栏、工具栏以及各个TAB的网页内容。Render进程负责解析和渲染网页的内容。一般来说,一个TAB就对应有一个Render进程。但是我们也可以设置启动参数,让具有相同的域名的TAB都运行在同一个Render进程中。简单起见,我们就假设一个TAB就对应有一个Render进程。无论是Browser进程,还是Render进程,当启用了硬件加速渲染时,它们都是通过GPU进程来渲染UI的。不过Render进程是将网页内容渲染在一个离屏窗口的,例如渲染在一个Frame Buffer Object上,而Browser进程是直接将UI渲染在Frame Buffer上,也就是屏幕上。正因为如此,Render进程渲染好的网页UI要经过Browser进程合成之后,才能在屏幕上看到。Plugin进程,就是用来运行第三方开发的Plugin,以便可以扩展浏览器的功能。例如,Flash就是一个Plugin,它运行在独立的Plugin进程中。注意,为了避免创建过多的Plugin进程,同一个Plugin的不同实例都是运行在同一个Plugin进程中的。也就是说,不管是在同一个TAB的网页创建的同类Plugin,还是在不同TAB的网页创建的同类Plugin,它们都是运行在同一个Plugin进程中。
从上面的分析就可以知道,虽然每一个进程的职责不同,但是它们不是相互孤立的,而是需要相同协作,这样就需要执行进程间通信(IPC)。例如,Render进程渲染好自己负责解析的网页之后,需要通知GPU进程离屏渲染已经解析好的网页的UI,接着还要通知Browser进程合成已经离屏渲染好的网页UI。同样,Browser进程也需要通过GPU进程合成标题栏、地址栏、工具栏和各个网页的离屏UI。对于Plugin进程,Render进程需要将一些网页的事件发送给它处理,这样Render进程就需要与Plugin进程进行通信。反过来,Plugin进程也需要通过SDK接口向Render进程请求一些网页相关的信息,以便可以扩展网页的内容。更进一步地,如果Plugin进程需要绘制自己的UI,那么它也需要通过Render进程间接地和GPU进程进行通信。
以上分析的Browser进程、Render进程、GPU进程和Plugin进程,以及它们之间的通信方式,可以通过图1描述,如下所示:
图1 Chromium多进程架构
从图1可以看到,每一个进程除了具有一个用来实现各自职责的主线程之外,都具有一个IO线程。这个IO线程不是用来执行读写磁盘文件之类的IO的,而是用来负责执行IPC的。它们之所以称为IO线程,是因为它们操作的对象是一个文件描述符。即然操作的对象是文件描述符,当然也可以称之类IO。当然,这些是特殊的IO,具体来说,就是一个UNIX Socket。UNIX Socket是用来执行本地IPC的,它的概念与管道是类似的。只不过管道的通信是单向的,一端只能读,另一端只能写,而UNIX Socket的通信是双向的,每一端都既可读也可写。
关于IO线程的实现,可以参考前面Chromium多线程模型设计和实现分析一文。简单来说,就是我们创建了一个UNIX Socket之后,就可以获得两个文件描述符。其中一个文件描述符作为Server端,增加到Server端的IO线程的消息循环中去监控,另一个文件描述符作为Client端,增加到Client端的IO线程的消息循环中去监控。对这些文件描述符的读写操作都封装在一个Channel对象。因此,Server端和Client端都有一个对应的Channel对象。
当一个进程的主线程执行某个操作需要与另一个进程进行通信时,它的主线程就会将一个消息发送到IO线程的消息循环去。IO线程在处理这个消息的时候,就会通过前面已经创建好的UNIX Socket转发给目标进程处理。目标进程在其IO线程接收到消息之后,一般也会通过其主线程的消息循环通知主线程执行相应的操作。这就是说,在Chromium里面,线程间通过消息循环进行通信,而进程间通过UNIX Socket进行通信的。
我们先来看Browser进程和Render进程之间的通信。Browser进程每启动一个Render进程,都会创建一个RenderProcessHost对象。Render进程启动之后,会创建一个RenderProcess对象来描述自己。这样,Browser进程和Render进程之间的通信就通过上述的RenderProcessHost对象和RenderProcess对象进行。
我们再来看Browser进程和GPU进程之间的通信。Browser进程会创建一个GpuProcessHost对象来描述它启动的GPU进程,GPU进程启动之后,会创建一个GpuProcess进程。这样,Browser进程和GPU进程之间的通信就通过上述的GpuProcessHost对象和GpuProcess对象进行。注意,这两个对象之间的Channel是用来执行信令类通信的。例如,Browser进程通过上述Channel可以通知GPU进程创建另外一个Channel,专门用来执行OpenGL命令。这个专门用来执行OpenGL命令的Channel称为Gpu Channel。
我们知道,GPU进程需要同时为多个进程执行OpenGL命令,而OpenGL命令又是具有状态的,因此,GPU进程就需要为每一个Client进程创建一个OpenGL上下文,也就是一个GLContext对象。GPU进程在为某一个Client进程执行OpenGL命令之前,需要找到之前为该Client进程创建的GLContext对象,并且将该GLContext对象描述的OpenGL上下文设置为当前的OpenGL上下文。
前面提到,Render进程也需要与GPU进行通信,这意味着它们也像Browser进程一样,需要与GPU进程建立一对Gpu Channel。不过,Render进程不能像Browser进程一样,直接请求GPU进程创建一对Gpu Channel。Render进程首先要向Browser进程发送一个创建Gpu Channel的请求,Browser进程收到这个请求之后,再向GPU进程转发。GPU接收到创建Gpu Channel的请求后,就会创建一个UNIX Socket,并且将Server端的文件描述符封装在一个GpuChannel对象中,而将Client端的文件描述符返回给Browser进程,Browser进程再返回到Render进程,这样Render进程就可以创建一个Client端的Gpu Channel了。除了创建一个Client端的Gpu Channel,Render进程还会创建一个WebGrahpicsContext3DCommandBufferImpl对象,用来描述一个Client端的OpenGL上下文,这个OpenGL上下文与GPU进程里面的GLContext对象描述的OpenGL上下文是对应的。
最后我们再来看Render进程与Plugin进程之间的通信。Chromium支持两种类型的插件,一种是NPAPI插件,另一种是PPAPI插件。NPAPI插件是来自于Mozilla的一种插件机制,它被很多浏览器所支持,Chromium也不例外。不过由于运行在NPAPI插件中的代码不能利用完全利用Chromium的沙箱技术和其他安全防护技术,现在NPAPI插件已经不被支持。因此这里我们就只关注PPAPI插件机制。
Render进程在解析网页的过程中发现需要创建一个PPAPI插件实例时,就会通知Browser进程创建一个Plugin进程。当然,如果对应的Plugin进程已经存在,就会利用它,而不是再启动一个。Browser进程每启动一个Plugin进程,都会创建一个PpapiPluginProcessHost对象描述它。Plugin进程启动完成后,也会创建一个ChildProcess对象描述自己。这样,以后Browser进程和Plugin进程就可以通过PpapiPluginProcessHost对象和ChildProcess对象之间的Channel进行通信。但是Render进程和Plugin之间的通信需要另外一个Channel。因此,Browser进程会进一步请求Plugin进程创建另外一个Channel,用来在Render进程和Plugin进程之间进行通信。有了这个Channel之后,Render进程会创建一个HostDispatcher对象,而Plugin进程会创建一个PluginDispatcher对象,以后Render进程和Plugin进程之间的通信就通过上述两个对象进行。
前面提到,Plugin进程有可能也需要渲染UI,因此,PPAPI插件机制提供了一个Graphics3D接口,PPAPI插件可以通过该接口与GPU进行通信。注意,Plugin进程和GPU进程之间的通信,不同于Render进程和GPU进程之间的通信,前者没有一个专门的Channel用来执行IPC通信。不过,Plugin进程却可以利用之前它已经与Render进程建立好的Channel进行通信。这意味着,Plugin进程和GPU进程之间的通信是要通过Render进程间接进行的。具体来说,就是Plugin进程首先要将OpenGL命令发送给Render进程,然后再由Render进程通过Gpu Channel发送给GPU进程执行。
在Chromium的运行过程中,进程之间需要发送很多IPC消息。不同类型的IPC消息会被不同的模块处理。为了能够快速地对这些IPC消息进行分发处理,Chromium提供了一套灵活的消息发分机制。这套分发机制规定每一个IPC消息都具有一个32位的Routing ID和一个也是32位的Type,其中,Type的高16位描述的是IPC消息类别,低16位没有特殊的意义。
基于IPC消息的类别信息,我们可以在IO线程中注册一系列的MessageFilter,每一个Filter都可以指定自己所支持的IPC消息类别。IO线程接收到一个IPC消息的时候,首先就会根据它的类别查找有没有注册相应的MessageFilter。如果有的话,就快速地在IO线程分发给它处理。
此外,我们还可以IO线程中注册一个Listener。当一个IPC消息没有相应的MessageFilter可以处理时,那么它接下来就会分发给上述注册的Listener处理。注意,这时候Listener处理代码运行在注册时的线程中。这个线程一般就是主线程。Listener首先根据IPC消息的Type进行分发给相应的Handler进行处理。如果没有相应的Handler可以处理,并且Listener支持注册Router,那么就会再根据IPC消息的Routing ID分发相应的Router进行处理。
最后,还有一点需要注意的是,在Android平台中,Chromium的Browser进程就是Android应用程序的主进程,它具有Android应用程序申请的所有权限,Render进程、GPU进程和Plugin进程是Android应用程序的Service进程。这些Service在AndroidManifest文件中被配置为运行在的孤立进程中,也就是它的android:isolatedProcess属性被设置为true。这类进程在启动的时候,将不会被赋予Android应用程序申请的权限,也就是它们运行在一个非常受限的进程中。这一点我们可以通过分析Android应用程序进程启动的过程源代码看到。
从前面Android应用程序进程启动过程的源代码分析一文可以知道,Android应用程序进程是由ActivityManagerService启动的。具体来说,在启动一个Service的时候,如果发现需要为它创建一个单独的进程时,就会调用ActivityManagerService类的以下成员函数startProcessLocked创建一个新的进程,如下所示:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
null /* entryPoint */, null /* entryPointArgs */);
}
......
}
这个函数定义在文件frameworks/base/services/core/java/com/adroid/server/am/ActivityManagerService.java中。
ActivityManagerService类三个参数版本的成员函数startProcessLocked调用了另外一个重载版本的成员函数startProcessLocked,后者的实现如下所示:
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
......
try {
......
int[] gids = null;
......
if (!app.isolated) {
int[] permGids = null;
try {
......
final PackageManager pm = mContext.getPackageManager();
permGids = pm.getPackageGids(app.info.packageName);
......
} catch (PackageManager.NameNotFoundException e) {
......
}
......
if (permGids == null) {
gids = new int[2];
} else {
gids = new int[permGids.length + 2];
System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
......
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
......
} catch (RuntimeException e) {
......
}
}
......
}
这个函数定义在文件frameworks/base/services/core/java/com/adroid/server/am/ActivityManagerService.java中。
从这里可以看到,只有当参数app描述的一个ProcessRecord对象的成员变量isolated等于false的时候,ActivityManagerService类的成员函数startProcessLocked才会请求PackageManagerService返回要启动的Service所属的Android应用程序申请的权限,也就是一系列GID。而当一个Service在AndroidManifest.xml中将属性android:isolatedProcess属性设置为true的时候,这里的参数app描述的ProcessRecord对象的成员变量isolated也是等于true,这时候就相当于是不给该Service所运行在的进程赋予任何权限,因此它就运行在一个沙箱中。
关于Chromium的多进程架构,我们就介绍到这里。在接下来的一系列文章,我们再结合源代码详细分析它的实现细节。具体来说,包括以下几个情景分析:
1. Render进程的启动过程分析;
2. IPC消息分发机制分析;
3. GPU进程的启动过程分析;
4. Plugin进程的启动过程分析;
这里我们有意略过Browser进程的启动过程分析,这是因为Browser进程实际上就是Android应用程序进程,但是会涉及到一些Chromium相关库的加载过程,而且有些Chromium相关库会由Zygote进程执行预加载处理,等到后面我们分析WebView的启动过程时,再详细分析Browser进程的启动过程。
同时,我们在分析Render进程的启动过程之后,并没有马上连贯分析GPU进程的启动过程,而是先分析一下前面描述过的IPC消息分发机制的具体实现,这是因为理解了Render进程的启动过程之后,我们就可以以它与Browser进程间的通信过程为情景,更好地理解Chromium的IPC消息分发机制。
当分析完成上述四个情景之后,我们还有一个重要任务,那就是分析Render进程和Plugin进程是如何通过GPU进程进行硬件加速渲染UI的。硬件加速渲染是智能设备获得流畅UI的必要条件。可以说,没有硬件加速渲染的支持,设备UI动画要达到60fps,是非常困难的。Chromium使用硬件加速渲染的方式非常独特,具有以下特点:
1. Render进程和Plugin进程虽然调用了OpenGL的API,但是这些API仅仅是一个代理接口,也就是这些API调用仅仅是将对应的命令发送给GPU进程处理而已。
3. GPU进程只有一个用来处理Client端发送过来的OpenGL命令的线程,但是该线程却要同时服务多个Client端,也就是要同时为Render进程、Plugin进程以及Browser进程服务,每一个Client端都相当于有一个OpenGL上下文,这将会涉及到如何为每一个OpenGL上下文进行调度的问题。
4. GPU进程同时服务的多个Client端,它们并不是相互孤立的,它们有时候存在一定的关联性。例如,Render进程渲染好的离屏UI,需要交给Browser进程合成,以便可以最终显示在屏幕中。也就是说,Browser进程需要等待Render进程渲染完成之后,才可以进行合成,否则就会得到不完整的UI。对于GPU进程来说,涉及到的问题就是如何在两个不同的OpengGL上下文中进行同步。
对于上面提到的这些特点,在完成了Chromium的多进程架构分析之后,我们再通过另外一个系列的文章进行详细分析,敬请关注!更多的信息,也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。