浏览器进程与线程
浏览器进程与线程
浏览器进程有哪些
浏览器进程(主进程)
负责控制浏览器除标签页外的界面,包括地址栏、书签、前进后退按钮等,以及负责与其他进程的协调工作,子进程的管理,创建和销毁等操作,同时提供存储功能
渲染进程
负责控制显示 tab 标签页内的所有内容,核心任务是将 HTML、CSS、JS 转为用户可以与之交互的网页,排版引擎 Blink 和 JS 引擎 V8 都是运行在该进程中,默认情况下 Chrome 会为每个 Tab 标签页创建一个渲染进程
GPU 进程
负责整个浏览器界面的渲染。Chrome 刚开始发布的时候是没有 GPU 进程的,而使用 GPU 的初衷是为了实现 3D CSS 效果,只是后面网页、Chrome 的 UI 界面都用 GPU 来绘制,这使 GPU 成为浏览器普遍的需求,最后 Chrome 在多进程架构上也引入了 GPU 进程
网络进程
负责发起和接受网络请求,以前是作为模块运行在浏览器进程里面的,后面才独立出来,成为一个单独的进程
渲染进程中的线程
GUI 渲染线程
负责渲染页面,解析 html 和 CSS、构建 DOM 树、CSSOM 树、渲染树、和绘制页面,重绘重排也是在该线程执行
JS 引擎线程
一个 tab 页中只有一个 JS 引擎线程(单线程),负责解析和执行 JS。它和 GUI 渲染进程不能同时执行,只能一个一个来,如果 JS 执行过长就会导致阻塞掉帧
计时器线程
指 setInterval 和 setTimeout,因为 JS 引擎是单线程的,所以如果处于阻塞状态,那么计时器就会不准了,所以需要单独的线程来负责计时器工作
异步 http 请求线程
XMLHttpRequest 连接后浏览器开的一个线程,比如请求有回调函数,异步线程就会将回调函数加入事件队列,等待 JS 引擎空闲执行
事件触发线程
主要用来控制事件循环,比如 JS 执行遇到计时器,AJAX 异步请求等,就会将对应任务添加到事件触发线程中,在对应事件符合触发条件触发时,就把事件添加到待处理队列的队尾,等 JS 引擎处理
浏览器进程、线程模型
Chrome 为例,有四种进程模型,分别是:
- Process-per-site-instance:默认模式。访问不同站点创建新的进程,在旧页面中打开的新页面,且新页面与旧页面属于同一站点的话会共用一个进程不会创建
- Process-per-site:同一站点使用同一进程
- Process-per-tab:每一个标签页都创建新的进程
- Single Process:单进程模式
线程模型的作用:
- MessagePumpForIO:处理进程间通信的线程,在 Chrome 中,这类线程都叫做 IO 线程
- MessagePumpForUI:处理 UI 的线程用的
- MessagePumpDefault:一般的线程用到的
进程间通信的方式
- 管道通信:就是操作系统在内核中开辟一段缓冲区,进程 1 可以将需要交互的数据拷贝到这个缓冲区里,进程 2 就可以读取了
- 消息队列通信:消息队列就是用户可以添加和读取消息的列表,消息队列里提供了一种从一个进程向另一个进程发送数据块的方法,不过和管道通信一样每个数据块有最大长度限制
- 共享内存通信:就是映射一段能被其他进程访问的内存,由一个进程创建,但多个进程都可以访问,共享进程最快的是 IPC 方式
- 信号量通信:比如信号量初始值是 1,进程 1 来访问一块内存的时候,就把信号量设为 0,然后进程 2 也来访问的时候看到信号量为 0,就知道有其他进程在访问了,就不访问了
- socket:其他的都是同一台主机之间的进程通信,而在不同主机的进程通信就要用到 socket 的通信方式了,比如发起 http 请求,服务器返回数据
多标签之间通信
没有办法直接通信,需要有一个类似中介者进行消息的转发和接收,比如:
- localStorage:在一个标签页监听 localStorage 的变化,然后当另一个标签页修改的时候,可以通过监听获取新数据
- WebSocket:因为 websocket 可以实现实时服务器推送,所以服务器就可以来当这个中介者。标签页通过向服务器发送数据,然后服务器再向其他标签推送转发
- ShareWorker:会在页面的生命周期内创建一个唯一的线程,并开启多个页面也只会使用同一个线程,标签页共享一个线程
- postMessage:
1
2
3
4
5
6// 发送方
window.parent().pastMessage("发送的数据", "http://接收的址");
// 接收方
window.addEventListener("message", (e) => {
let data = e.data;
});
其他进程
- 孤儿进程:故名思义,就是没爹的孩子。父进程退出了,而它的一个或多个子进程还在运行,那么这些子进程都会成为孤儿进程。这些孤儿都将被 init 进程收养,并负责这些孤儿的以后
- 僵尸进程:就是子进程比父进程先结束,而父进程又没有释放子进程占用的资源,那么子进程的描述还留在系统中,这种进程就是僵尸进程