此篇文章简单介绍了chrome的基本框架和libcef代码结构,主要借鉴了以下四篇文章:http://www.cnblogs.com/duguguiyu/archive/2008/10/04/1303695.html
http://blog.csdn.net/zhuhongshu/article/details/70159672
http://blog.csdn.net/luoshengyang/article/details/47364477
https://github.com/fanfeilong/cefutil
chrome是一个基于多进程框架的浏览器,下图是一个经典的chrome进程结构图(图片来源于:http://www.cnblogs.com/duguguiyu/archive/2008/10/04/1303695.html 【1】)。

上图中一共有三个实线框,一个实线框代表一个进程,整体表示有一个browser进程,两个Render进程。其中brower进程是老大,管理着Render进程,两个render进程互不干涉,互不影响。Browser和Render之间通过IPC(inter-process communication)来进行通信,在windows下实质上就是命名管道(Named Pipe)。但实际上browser和render并不只是通过IPC来进行通信,有些时候会配合共享内存一起来使用,比如browser下载的网页数据,或者render渲染好的数据都是放在共享内存中的,ipc传递的只是关键信息,brower或者render根据ipc传过来的信息到共享内存中去存取数据,并执行后续操作。
实线框中的一个虚线框表示一个线程,从图中可以看出,browser进程和render进程都有多个线程,主要线程为UI线程(MainThread),IO线程(其实是指通信线程,包括IPC消息和网络通信),其他的还有File线程,但图中没有画出来。
每一个虚线框中有一些小的实心框框,下面将分别介绍各个实心框框表示的含义。
一个RenderProcessHost对象描述的是它所启动的一个Render进程,一个RenderViewHost对象描述的是运行在Render进程中的一个网页,可以理解为浏览器中的一个TAB页。每一个RenderProcessHost肯定对应一个render进程中RenderProcess,每一个RenderViewHost也肯定对应Render进程中的一个RenderView。按照罗升阳的说法,可以将*Host对象和*对象理解为一对对等体,因为它们是brower进程和render进程进行IPC通信的两个端点。类似于TCP/IP网络堆栈中的层对层通信。RenderViewHost与RenderView之间的通信代表了browser进程请求Render进程加载、更新、渲染一个页面。值得注意的是一个render进程只有一个RenderProcess,但可以有多个RenderView,因此Chrome所宣传的One-Tab-One-Process其实描述并不是十分精准。
灰色框框Channel是命名管道的一个封装,它可以有两种工作模式,一种是Client,一种是Server,分属两个进程,维持一个管道,以完成通信。Channel中两个重要的子类,Message::Sender和MessageListener,其中Sender是消息发送的接口,Listener是消息接收的接口。由于并不是所有的消息都需要进行处理,因此channel中添加了Filter的功能。
图中可以看到RenderView和Channel并不在一个线程中,那么RenderView是如何通过channel来进行通信的呢。Chrome中是通过ChannelProxy来实现的,ChannelProxy将非IO线程想要执行的任务封装成一个Closure并放到IO线程中的消息队列中,等待IO线程的处理。ChannelProxy为Channel的代理类,它们的接口没有多大的区别。
WebKit框框代表网页渲染模块,是一个底层模块。
将上图和官网上的chrome层次结构图一起来看,能够更加清晰的理解chrome的整体框架。

以上讨论中我们大致明白了chrome的进程框架,当然我们也知道Chrome中并不只有browser进程和render进程,也有GPU进程和plugin进程,完整的进程关系图可以参考【3】,这里不在贴出。另外也看到了Chrome中使用了多线程结构,那么Chrome中各线程如IO线程,UI线程,File线程之间是如何进行同步的呢。一般而言,多线程访问数据时都会在可疑的地方进行加锁,频繁的加锁和解锁肯定会影响效率。为了尽量少使用锁,Chrome采用了消息循环的机制,每一个chrome线程里面都启动了一个消息循环,等待并执行任务。不同的线程差别在于处理的任务(事物)类别不同。线程之间的通信通过Closure的传递来进行,Closure封装了将要执行的任务和参数。下面这个图(【3】)很好的描述了chrome线程之间的通信过程。从途中可以明显的看出,线程1将任务分发给线程2时,自己也在执行另外一个子任务,说明线程之间的通信是异步的。
采用消息循环的方式,锁就只需在Task进入线程的任务队列时使用,其他时候并不需要进行锁操作。这样很大程度上避免了锁竞争带来的资源消耗。
这个机制似乎很完美,但其实对Task划分有很高的要求,为了保证不同task不会同时访问相同的数据结构,需要提前知道每一个任务的执行者,并确保这些数据结构仅会被task的执行者访问。这其实是将很多事情放在了问题建模阶段来完成。
下图(【1】)描述了Task的执行模式,任务可以在任何线程中创建,并投递到IO线程的消息队列中(当然也可以投递到其他任何线程中,只要有需求)。在进入队列和取出执行的时候需要锁操作。
在消息循环中根据逻辑的不同,Task可分为及时处理的Task, 延时处理的Task,空闲时处理的Task。通过分类可以满足不同的需求。下图(【1】)描述了线程中消息的循环。

关于chrome多线程模型优劣的讨论,可以参见【1】,这里不再复述。
-------------------------------------------------------------------------------
以上简单的过了一遍Chrome中进程和线程的基本结构,下面将根据【2】和【4】简单整理一下开发过程中所需要用到的类的基本结构,即Cef的接口简单说明。
CefApp类接口
CefApp提供了进程级的可定制回调函数,由上面讨论可知,一个cef应该中一般包含两个进程browser 和 render,与之对应的两个基类是CefBrowerProcessHandler, CefRenderProcessHandler,若一个进程即作为browser,也作为render,则需要同时继承这两个基类。如果分开在不同进程完成,则可在两个进程中分开继承两个基类。当前作为cef进程,都需要继承CefApp。
主要接口:
CefApp::OnBeforeCommandLineProcessing 可以附加传给cef的参数,如设置渲染进程名称,是否开启扩展功能,是否开启GPU加速等。参数设置的格式一般如下:command_line->AppendSwitch("disable-gpu");
CefRenderProcessHandler::OnWebKitInitialized 通过这个接口可以在render进程初始化时注册JS扩展代码,实现C++与JS的交互,但由于容易触发崩溃,因此现在这种方法用的较少
CefRenderProcessHandler::OnContextCreated 在这个接口中可以实现窗体绑定,所谓窗体绑定是允许客户端应用程序把值附上一个框架窗口对象,这是实现C++和JS交互的另外一种方法,一般格式如下,Js中可以通过windows.external.passport来调用C++的代码。

CefRenderProcessHandler::OnProcessMessageReceived 方法用于接收Browser进程发过来的消息。
CefSettings:
CefSettings结构体定义了Cef的全局配置信息,比如指定单进程模式、指定渲染子进程路径、设置localstorage路径、设置日志等级、Cef资源文件路径。其中对于项目最重要的字段是single_process、multi_threaded_message_loop、windowless_rendering_enabled,分别用于指定单进程模式、多线程渲染模式、离屏渲染模式

CefClient类接口:
每一个browser对象都对应一个CefClient,CefClient可以理解为browser的实体,用于处理浏览器页面的各种回调消息,包括:鼠标,键盘,焦点,下载,对话框以及browser的生命周期。如果对Cef有功能需求,一般可以先看看Client接口中有没有提供相关功能。
CefClient类来定制的回调接口类中常用的有以下一些:
CefDisplayHandler:与显示相关的事件处理。如 OnTitleChange方法
CefLifeSpanHandler:与browser生命周期相关的事件处理。如OnBeforeClose 方法
CefLoadHandler: 与页面加载相关的事件处理。如OnLoadStart方法,OnLoadEnd方法。
CefRenderHandler: 与渲染相关的事件处理。若要实现离屏渲染,就必须实现这个接口类,并在OnPaint方法中做相应操作。
CefRequesthandler: 与资源请求相关的事件处理。若要实现资源的重定向,如加载本地资源,必须实现这个接口类。
CefKeyboardHandler: 与键盘相关的事件处理。如 OnKeyEvent方法
CefDragHandler:与拖拽相关的事件处理。如OnDragEnter方法
另外CefClient::OnProcessMessageReceived方法用于接收render进程发到的消息,C++与JS交互时会用到

关于消息循环
由于Cef是多线程架构,且每个线程都有自定的消息队列,若强制使用单线程(settings.multi_threaded_message_loop = false;)来运行CEF,则需要主动调用Cef的消息循环,调用接口为CefDoMessageLoopWork。但该函数如果调用太频繁则会很消耗CPU,如果调用频率太低,则会导致cef线程中处理任务不及时,cef界面反映变慢。
如果采用多线程设置,则Cef会为自己创建必要的线程(如UI线程,IO线程等),此时外面就无须主动调用CEF的消息循环了。但需要注意的是,采用多线程后,Browser的UI线程和我们自己的UI线程(如果我们有的话)将不是同一个线程,而cef中大多数对象触发的回调函数都是在CEF的UI线程中,而不是我们自己的UI线程。这点是需要明确的,如果想要在CEF的回调函数(如OnLoadEnd)中操作自己的UI线程,则最好将操作转到到你自己的线程中去。
为了保证CEF消息的正常运行,一般建议采用多线程模式。

libCef基本框架与结构相关推荐

  1. Django框架目录结构

    django project 框架目录结构 |my_project //项目名 后期可修改建议不修改 |--- __init__.py //python项目必带 模块化思想 |--- settings ...

  2. Android系统框架四层结构

    Android系统框架四层结构 安卓系统框架图大致如下: (蓝色:java程序,java代码编写:黄色:运行JAVA程序而实现的虚拟机:绿色:C/C++语言编写的程序库:红色:linux内核+driv ...

  3. 二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

    一.V4L2框架主要结构体分析 V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口. V4L2整体框架如下图: 图中主要包括两层 ...

  4. 【Houdini】框架与结构:常见的各个模块名词缩写指的是什么?

    对于刚接触Houdini的一些萌新,会对Houdini中常说的一些SOP.POP.DOP等一些名词十分疑惑,不清楚其中的含义,在此作个解释. 简写 全称 功能 OBJ Object scene 场景描 ...

  5. 一. 弹幕框架三层结构

    文章目录 摘要 游戏要素分析 行为接口 对象池技术 弹幕框架三层结构 摘要 本文主要讲解用于Unity开发2D弹幕游戏的个人向的简单框架的设计结构及其思想. 在本文中将会讲解到对象池.接口等要素 游戏 ...

  6. android应用框架 平台结构 源代码结构 事件处理流程 Framework层收到事件的处理过程 电话处理流程

    android应用框架 平台结构 第1层: Linux操作系统及驱动 C语言实现 第2层: 本地框架和Java运行环境 C和C++实现 第3层: Java框架(framework) Java实现 第4 ...

  7. java集合框架总体结构

    Java集合框架总体结构 Java.util.List接口及其子类,List提供的是一个有序的集合: Java.util.Set接口及其子类,Set提供的是一个无序的集合: Java.util.Mao ...

  8. PHP CI框架目录结构及运行机制

    CI目录结构 CI主要组成部分为,application(应用文件夹).system(系统文件夹)和index.php入口文件. 应用文件夹中主要是存放控制器.模型和视图等,系统文件夹中主要是存放组成 ...

  9. android插件化-apkplug框架基本结构-01

    2019独角兽企业重金招聘Python工程师标准>>> 由于框架开发更新频繁的原因一直都没有时间写出框架的基本架构让大家云里雾里的,现在框架已基本稳定和完善,我就抽出时间写写关于ap ...

  10. php的框架目录,Laravel 框架目录结构

    Laravel5.2目录结构及composer.json文件解析 目录或文件 说明 |– app 包含Controller.Model.路由等在内的应用目录,大部分业务将在该目录下进行 | |– Co ...

最新文章

  1. Flutter开发之认识Flutter(二)
  2. C++之编码问题(Unicode,ASCII,本地默认)
  3. python实现简单的api接口-python中接口的实现实例
  4. android x86一键安装,安卓
  5. 电力论文:基于牛顿拉尔逊(直角坐标)的电气潮流计算(python实现)
  6. VTK:Points之ExtractSurface
  7. 超级详细AST抽象语法树Javascript
  8. Spring原始注解和新注解(使用注解代替xml配置文件)
  9. yunos5 linux内核,魅蓝5S、魅蓝5对比看差异 选Android还是YunOS?
  10. 免费网络研讨会:调试生产中Java的新方法
  11. 括号配对问题 -- ACM解决方法
  12. OpenShift 4 - 安装 OpenShift 集群后如何删除节点或增加新节点
  13. 关于TFS的文档和报告打叉的解决
  14. python语言网课答案_看我们,智慧树python语言入门教程答案
  15. (ffmpeg3.3.x更新纪要)雷霄骅《最简单的基于FFMPEG+SDL的视频播放器》
  16. 计算理论重点——Theory of Computation
  17. 地理信息系统软件学习:流域提取以及地图绘制(简单版)
  18. Wireshark抓包详细分析
  19. springboot通过date_histogram 按时间统计数据
  20. OpenShift 4 - 用 External Secret 集成 Hashicorp Vault

热门文章

  1. CodeSys之CRC校验
  2. 融云通讯服务器,融云IM即时通讯功能接入和部署方式有哪些_如何收费_企业服务汇...
  3. Kibana 操作 ES+搜索
  4. [转]密码算法揭秘,一文让你成为国际安全算法与国密算法专家
  5. C语言之printf输出各种格式
  6. js function
  7. 【网络】解决校园网Wi-Fi登录页无法自动弹出
  8. cad批量等高线lisp_基于AutoCAD Lisp局部等高线自动内插新方法
  9. 基于WebUploader的文件上传插件
  10. linux dm9000网卡驱动,ARM-Linux驱动--DM9000网卡驱动分析(三)