IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Blink学习第一天:How Blink works -> 正文阅读

[移动开发]Blink学习第一天:How Blink works

Blink

Blink是Chromium的浏览器引擎
详见Blink官网
How Blink works是官网对它的介绍文档。本帖是对该文档的中文总结。
?
?

Blink的主要功能

  • 实现web平台的各种特性(比如:HTML标准),包括DOM,CSS和Web IDL
  • 嵌入V8引擎(一种Javascript引擎)并运行JS代码
  • 向底层的网络栈请求资源
  • 建立DOM树
  • 推断出样式和布局的信息
  • 嵌入Chrome Compositor

Chromium、Opera、Android的WebView都在使用Blink提供的服务。它们的层级关系如下图所示:
Blink与其它层级的关系
另外,Blink的代码处于third_party/blink文件夹之中。
?
?

进程

众所周知,Chromium属于多进程架构,它含有一个browser进程和多个renderer进程,而且由于安全原因,这些renderer进程都置于一个sanbox之中。而Blink运行在一个renderer进程之中。

严格地说,一个标签页应当对应一个renderer进程。但若用户打开的标签页过多,进程之间的资源分配不得不被慎重考虑。因此,一个renderer进程有时可以对应多个frames或多个标签页。原文在这部分的最后总结得十分清楚:

There is no 1:1 mapping between renderer processes, iframes and tabs.

通信方面,当Blink使用mojo与browser进程进行通信。通信的内容主要包括系统调用(比如访问本地文件、播放视频)以及获取cookies和用户密码。现今,由于browser进程的代码正在转向面向服务的设计方法,所以Blink有时也会与这些broswer进程提供的Service进行通信。
Blink通信
?
?

线程

Blink由一个主线程和多个内在线程组成。所有的JS代码(除了worker)、DOM、CSS、样式布局推断都在主线程进程。放心,主线程已被最大程度地优化,它可以胜任这一切。

Blink还会创建一些新的线程比如Web Worker、Service Worker和Worklets。而当它与V8协同工作时,它还可以创建线程去播放视频(webaudio),管理数据库以及进行垃圾回收。

Blink使用PostTaskAPI进行跨线程通信。Blink极少使用共享内存进行线程通信。

线程通信
?
?

Blink启动和终止

启动时通过BlinkInitializer::Initialize()进行初始化。而由于对性能方面的考虑,它从不进行清理工作而是直接退出。
?
?

文件结构

文件依赖图

首先是两个概念:
Content public API: 便于需要嵌入浏览器引擎的使用者进行嵌入工作的API
Blink public API: 历史遗留的API,开发者正在逐步减少这一API,这个工作的命名很有意思,叫洋葱汤(Onion Soup)。

接着是各个模块的大致介绍:
platform/: 底层实现
core/ , modules/: web平台特性的实现,比如webaudio,indexeddb
bingdings/core/ , bindings/modules/: V8 API的嵌入
controller/: 一些更高水平的模块,比如F12
层次依赖
顺带一提,比platform更底层的模块有//base,//v8,//cc
?
?

WTF

一个blink专用的数据结构。出场最多的莫过于WTF::Vector, WTF::HashSet, WTF::HashMap, WTF::String以及WTF::AtomicString。之所以使用专用的数据结构,是因为blink为这些数据结构设置了专门的垃圾回收机制。
?
?

内存管理

Blink有且仅有两种内存管理方式,没有malloc/free以及new/delete。
第一种:PartitionAlloc

class SomeObject {
	USING_FAST_MALLOC(SomeObject);
}

它的底层是C++11的独占指针std::unique_ptr以及chromium的scoped_refptr<>。

第二种:Oilpan

class SomeObject : public GarbageCollected<SomeObject> {}

它使用Oilpan堆进行垃圾回收管理。

需要注意的是,Blink默认选择第二种进行内存管理。当对象生命周期十分清楚,且使用Oilpan过于复杂、回收工作的压力大时,才可以选择第一种。
?
?

任务调度

为了提高响应能力,blink的任务被尽量设计为异步执行,尽管有些任务不可避免是同步执行的(比如Javascript的执行)。
所有任务都将提交给Blink Scheduler进行统一调度。提交过程如下:

// Post a task to frame's scheduler with a task type of kNetworking
frame->GetTaskRunner(TaskType::kNetworking)->PostTask(?, WTF::Bind(&Function));

而Blink Scheduler则通过管理多个任务队列来完成调度任务。
?
?

Page, Frame, Document, ExecutionContext and DOMWindow

  • Page相当于标签页,一个renderer进程可能管理多个标签页
  • Frame相当于主frame或iframe,Page和Frame的关系通过树(一种数据结构)来体现
  • DOMWindow相当于Javascript的window对象,一个Frame拥有一个DOMWindow
  • Document相当于Javascript的window.document对象,因此,一个Frame也拥有一个Document对象
  • ExecutionContext是主线程的Document和worker线程的WorkerGlobalScope两者的抽象体现。

关于它们的关系,原文写的十分清楚:

Renderer process : Page = 1 : N.
Page : Frame = 1 : M.
Frame : DOMWindow : Document (or ExecutionContext) = 1 : 1 : 1

有时,上面一行的关系会发生变化,比如下面这行代码:

iframe.contentWindow.location.href = "https://example.com";

此时,Frame会被重用,而DOMWindow和Document会被重新创建。
?
?

OOPIF (Out-of-process Frame)

基于浏览器的安全机制,如果一个Page里面存在两个Frame不同域,那么Blink可能就会创建两个renderer进程,也就是说,可能存在两个renderer进程管理同一个Page的情况。

<!-- https://example.com -->
<body>
<iframe src="https://example2.com"></iframe>
</body>

如上例所示,对于主frame而言,https://example.com视为LocalFrame,而https://example2.com则视为RemoteFrame。而对于iframe而言,情况相反。但无论如何,LocalFrame和RemoteFrame的通信都是通过browser进程实现的,因为它们属于不同的renderer进程。
?
?

分离的Frame和Document

doc = iframe.contentDocument;
// The iframe is detached from the DOM tree.
iframe.remove();  
// But you still can run scripts on the detached frame.
doc.createElement("div");  

你可以如上这样做,若如此做了,大部分DOM操作函数会报错,但仍有不完善的地方。因为DOM操作函数会如下面代码那样检查:

void someDOMOperation(...) {
  if (!script_state_->ContextIsValid()) { // The frame is already detached;  // Set an exception etc
    return;
  }
}

但此时,ContextIsValid的判断条件的设置将变得十分困难,因为垃圾回收工作十分庞大。下面代码是介绍如何进行这种情况的垃圾回收:

class SomeObject 
	: public GarbageCollected<SomeObject>, 
	  public ContextLifecycleObserver 
{
  void ContextDestroyed() override {
    // Do clean-up operations here.
  }
  ~SomeObject() {
    // It's not a good idea to do clean-up operations here 
    // because it's too late to do them. 
    // Also a destructor is not allowed 
    // to touch any other objects on Oilpan's heap.
  }
};

?
?

Web IDL bindings

IDL实际上是建立了Javascript与C++的一座桥梁。
原文是以javascript的node.fristChild和node.h的Node::firstChild为例。

首先,在node.idl文件中定义一个映射。

// node.idl
interface Node : EventTarget {
  [...] readonly attribute Node? firstChild;
};

然后,在C++文件node.h中定义映射结果。

// 所有暴露给javascript的接口都需要继承ScriptWrappable
class EventTarget : public ScriptWrappable {  
  ...;
};

class Node : public EventTarget {
  // 所有需要进行映射的类都需要下面这行宏
  DEFINE_WRAPPERTYPEINFO();  
  Node* firstChild() const { return first_child_; }
};

需要注意的是,C++文件和IDL文件需同名。

此时,IDL编译器会在//src/out/{Debug,Release}/gen/third_party/ blink/renderer/bindings/core/v8/v8_node.h自动生成绑定。最后的流程是:
链接过程
?
?

Isolate, Context, World

这些是V8的一些概念。
Isolate相当于物理线程。主线程有自己的Isolate,worker线程有自己的Isolate。
Context相当于一个全局对象。对于主线程而言,Context就是Page的一个window对象。而Page可以有多个window对象,所以使用v8的API时需要注意是否访问到正确的对象。
World是一种视角,是专门针对插件的安全性而定义的概念。每一个插件都可以访问DOM树,但为了安全,主线程会对每一个插件安排一个世界,每一个插件都在自己的世界里访问DOM树。在代码中,每一个世界就是一个V8Wrapper对象。
V8与Blink
总的来说,对于主线程而言,一个Page有许多Frame,因此有很多window对象,而一个window对象可以有多个世界访问它。所以我们可以有M*N个Context(M个Frame和N个world)。当然,Context是懒加载的,且M和N的数字一般很小。
?
?

V8的APIs

V8的API存放在//v8/include/v8.h里,但Chromium在platform/bindings/中提供了一些封装v8的类以便于更准确地进行使用。

V8使用两种方式操作V8对象:

  • 当对象处于machine stack中时,先创建HandleScope对象,再使用v8::Local<>操作v8对象。(下面代码是这种情况的一个例子)
  • 当对象不处于machine stack时,应当使用wrapper track,这种方式十分容易导致引用循环。
void function() {
  v8::HandleScope scope;
  v8::Local<v8::Object> object = ...;  // This is correct.
}

class SomeObject : public GarbageCollected<SomeObject> {
  v8::Local<v8::Object> object_;  // This is wrong.
};

?
?

V8的Wrapper

上文提到,每一个DOM对象在每一个世界都存在一个V8Wrapper。但值得注意的是,V8Wrapper有DOM对象的强引用,但DOM对象只有V8Wrapper的弱引用。这有什么问题呢?我们来看看下面的代码。

div = document.getElementbyId("div");
child = div.firstChild;
child.foo = "bar";
child = null;
// 如果啥也不做,child就会被GC
gc();  
 // 然后下面这行就失效了
assert(div.firstChild.foo === "bar"); 

因为child被置为null了,所以child很容易被GC,而此时child是V8Wrapper,它指向的是DOM对象,即某一节点的firstChild,由于child是强引用,GC之后div.firstChild也没了,所以div.firstChild,foo也就没了。
若希望V8Wrapper不被GC的话,有两种方法:ActiveScriptWrappable和wrapper tracing。
?
?

渲染过程

渲染过程

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-03 16:13:34  更:2022-01-03 16:15:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:00:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码