Chromium多进程,多线程剖析

Chromium多进程的原因:

1. 提高浏览器的健壮性:包括单个plugin,单个页面的崩溃导致整个浏览器的崩溃;

2. 提高浏览器的安全性:基于多进程的安全模型(sanbox模型),比如Render进程默认是基于Sandox模型的,限制了该进程除I/O之外的能力。


Chromium中的进程:

Browser进程:负责管理浏览器的界面,页面间的管理,其它进程的管理;

Render进程:负责页面的显示,页面的渲染过程,chromium中使用webkit的地方;

PPAPI插件进程:同NPAPI,都是插件模型,改善了NPAPI存在的可移植性(和浏览器所处的系统和使用的窗口管理系统密切相关)、性能(图形的绘制效率,基于多进程的保留模式进行内存共享来代替网页发送无效通知给插件的的模式)、安全性方面(通过把具有存取底层os的接口运行在sanbox模型下来保证安全)的缺项。PPAPI是一种对本地api的跨平台的C/C++语言的封装,目的在于提供一个介于插件和浏览器之间的抽象层,用来区分浏览器和系统级别的功能实现。

GPU进程:当用户启动硬件加速时会启动该进程,用于硬件加速;负责处理绘图请求并调用OPENGL进行绘制工作

#if defined(ENABLE_PLUGINS)
    { switches::kPluginProcess,      PluginMain },
    { switches::kWorkerProcess,      WorkerMain },
    { switches::kPpapiPluginProcess, PpapiPluginMain },
    { switches::kPpapiBrokerProcess, PpapiBrokerMain },
#endif  // ENABLE_PLUGINS
    { switches::kUtilityProcess,     UtilityMain },
    { switches::kRendererProcess,    RendererMain },
    { switches::kGpuProcess,         GpuMain },



进程之间通过IPC进行通讯,render和browser进程可以和任何进程直接通讯。


chromium的多进程架构见下图

Chromium多进程,多线程剖析_第1张图片




多进程加载网页资源架构,用来补充说明ResourceDispatcherHost和Channel的Filter模块

在chromium中,所有网络访问在Browser进程中进行,这样做的目的一是有利于全局控制,二是能在不同进程间保持cookie等的session状态。架构图如下所示

Chromium多进程,多线程剖析_第2张图片

可以看到资源加载是依赖chromium多进程架构设计的,Renderer进程的ResourceDispatcher与Browser进程的ResourceDispatcherHost通信,通过Browser完成资源加载。chromium通过重新实现webkit中的ResourceHandle来实现这一逻辑。具体参考: Multi-process Resource Loading


chromium的多线程模型

由于每个进程会处理很多的事情,为了在当前进程中不阻塞别的任务和充分利用多核CPU的特性,我们需要根据不同进程的任务来给进程创建一些线程。

Browser进程中的主要线程:由于browser需要管理其它进程,需要负责用户的响应,资源的加载管理,打开和读取各种文件设备,数据库的操作,历史记录的操作等,我们这些项都创建了单独的线程去完成这些任务。

Chromium多进程,多线程剖析_第3张图片


UI线程,该线程是程序运行的主线程,也是程序的入口点,用来监听用户的输入,并及时的给出响应。UI线程不能被阻塞,否则浏览器的界面就会停住,得不到任何响应。如果在该线程中有一些耗时的任务比如文件读写等需要处理,就发送任务给其他相应线程去执行,等到执行完毕后,把得到的结果发送回来即可。这就保证了浏览器每时每刻都能对用户的UI操作给予响应,从我们使用来看,相比其他浏览器,Chrome浏览器的界面响应速度应该是最快的。

IO线程,它的主要作用是负责与其他进程进行进程间通信(IPC),和负责下载Render进程需要的某些资源文件。

File线程,该线程负责各种文件的读写,比如保存下载的文件到磁盘上,从磁盘上读取缓存文件等等。

数据库线程,该线程主要进行数据库操作,Chromium中引进了数据库来保存某些数据,例如Cookies.

其它线程可以具体查看代码;


Render进程中的主要线程:

Chromium多进程,多线程剖析_第4张图片

Main Thread:负责与Browser进程的通信(Chrome_ChildIOThread线程:用来接受来自其它进程的IPC消息和派发自身消息到其它进程。);

Render Thread:负责玉面渲染和交互,负责该进程中所有renderview和webkit代码的运


GPU进程中的主要线程:

Chromium多进程,多线程剖析_第5张图片


线程模型

chromium的实现手段:
    1、线程内消息循环的设计,为线程异步调用打下基础;
    2、Task封装,解决了加锁的问题,同时用command模式统一同步和异步调用。
    也就是说,Task是对一个任务处理的封装,而线程的消息循环都认识Task,这样整个多线程框架其实就是Task的传递和执行的过程,并且锁只在传递Task时发生。多么简单有效的模型!

    消息循环
       Chromium多进程,多线程剖析_第6张图片
      图示将线程的消息循环处理描述的非常清楚,基本思路和windows的消息循环非常相似。当然,不同类型的线程并不会处理所有的事件,但所有的消息循环都会处理Task。

在Chromium里,需要处理三种类型的消息:

chromium自定义的任务:平台无关,例如History线程则只需要处理自定义任务;

Socket或者文件等IO操作:平台相关,例如IO线程则需要处理Socket和自定义任务;

用户界面(UI)的消息平台相关,例如主线程(UI线程)需要处理UI相关的消息和自定义任务。

    线程消息循环分为MessageLoop和MessagePump,其中MessageLoop专门处理Task,而MessagePump处理其他消息,发现Task时交给MessageLoop。不同线程的消息循环的MessageLoop和MessagePump如图:

Chromium多进程,多线程剖析_第7张图片


    Task    
    Task是对行为的封装,或者说是将处理函数封装为一个对象。Task对象有一个Run函数,供目标线程的消息循环调用,执行Task的处理。还是看张图:
Chromium多进程,多线程剖析_第8张图片
    如果A线程想让B线程处理一个事务,A只需创建一个Task,将事务处理代码封装好,找到B线程的MessageLoop对象,调用PostTask方法将Task插入消息队列。PostTask将立即返回,A线程继续处理自己的消息循环。B线程消息循环处理到Task时,调用其Run方法完成事务处理。
    chromium对Task的创建和传递做了封装,并定义了丰富的Task类型,满足多种场景需要。同时支持Task自身的日志和统计,便于调试。


chromium如何保证线程间的通讯和同步呢,答案就是chromium在多数场景下会采用一种事件和任务的传递机制,只有在非用不可的情况下才会用锁和安全对象。每个线程采用MessageLoop的机制进行事件和任务的处理,当有任务被发送到进程的某个线程的MessageLoop队列时MessageLoop会调度执行这些任务。当需要执行某个操作时,可以把该操作封装成一个任务,由任务派发机制传递给相应的进程的MessageLoop。无论是同步还是异步通讯,都是通过派发任务的方式来进行的,对于异步的需要返回值的通信,通过新Task封装原Task的方式来进行,实现原理是由调用者调用被调用者设置的CALLBACK类的RUN方法。这样通过派送Task的方式既可以保证各个任务的优先级,也可以有序的将同步异步操作统一起来。



参考: 

http://www.360doc.com/content/13/0422/15/168576_280135766.shtml

http://blog.csdn.net/milado_nju/article/details/8027625

http://blog.csdn.net/milado_nju/article/details/8539795

http://blog.sina.com.cn/s/blog_48f93b530101kr5c.html



你可能感兴趣的:(Chromium多进程,多线程剖析)