CPU
CPU是计算机的核心,其负责承担计算机的计算任务。这里我们比喻为一个工厂
进程(process)
- 简单理解,进程就是程序的一次执行。
- 进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
- 我们这里将进程比喻为工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
线程(thread)
线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元。这里把线程比喻一个车间的工人,即一个车间可以允许由多个工人协同完成一个任务。
进程和线程的区别和关系
- 进程是操作系统分配资源的最小单位,线程是程序执行的最小单位。
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
多进程和多线程
- 多进程:多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。多进程带来的好处是明显的,比如你可以听歌的同时,打开编辑器敲代码,编辑器和听歌软件的进程之间丝毫不会相互干扰。
- 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
浏览器多进程架构
跟现在的很多多线程浏览器不一样,Chrome浏览器使用多个进程来隔离不同的网页。因此在Chrome中打开一个网页相当于起了一个进程。
那么Chrome为什么要使用多进程架构?
在浏览器刚被设计出来的时候,那时的网页非常的简单,每个网页的资源占有率是非常低的,因此一个进程处理多个网页时可行的。然后在今天,大量网页变得日益复杂。把所有网页都放进一个进程的浏览器面临在健壮性,响应速度,安全性方面的挑战。因为如果浏览器中的一个tab网页崩溃的话,将会导致其他被打开的网页应用。另外相对于线程,进程之间是不共享资源和地址空间的,所以不会存在太多的安全问题,而由于多个线程共享着相同的地址空间和资源,所以会存在线程之间有可能会恶意修改或者获取非授权数据等复杂的安全问题。
!!!基于以上知识点,说明下什么是浏览器内核。
浏览器内核
简单来说浏览器内核是通过取得页面内容、整理信息(应用CSS)、计算和组合最终输出可视化的图像结果,通常也被称为渲染引擎。从上面我们可以知道,Chrome浏览器为每个tab页面单独启用进程,因此每个tab网页都有由其独立的渲染引擎实例。
浏览器内核是多线程
浏览器内核是多线程,在内核控制下各线程相互配合以保持同步,一个浏览器通常由以下常驻线程组成:
- GUI 渲染线程
- JavaScript引擎线程
- 定时触发器线程
- 事件触发线程
- 异步http请求线程
GUI渲染线程
GUI渲染线程负责渲染浏览器界面HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了.
Javascript引擎线程
Javascript引擎,也可以称为JS内核,主要负责处理Javascript脚本程序,例如V8引擎。Javascript引擎线程理所当然是负责解析Javascript脚本,运行代码。
Javascript是单线程的
Javascript是单线程的, 那么为什么Javascript要是单线程的?
这是因为Javascript这门脚本语言诞生的使命所致:JavaScript为处理页面中用户的交互,以及操作DOM树、CSS样式树来给用户呈现一份动态而丰富的交互体验和服务器逻辑的交互处理。如果JavaScript是多线程的方式来操作这些UI DOM,则可能出现UI操作的冲突; 如果Javascript是多线程的话,在多线程的交互下,处于UI中的DOM节点就可能成为一个临界资源,假设存在两个线程同时操作一个DOM,一个负责修改一个负责删除,那么这个时候就需要浏览器来裁决如何生效哪个线程的执行结果。当然我们可以通过锁来解决上面的问题。但为了避免因为引入了锁而带来更大的复杂性,Javascript在最初就选择了单线程执行。
进一步说明:javascript的单线程
javascript引擎负责解释和执行javascript代码的线程只有一个,称为主线程。js还有其他的线程称为工作线程。
主线程上只执行同步任务。
- 工作进程
浏览器还有还有其他的线程,例如:
处理 ajax 的线程,dom事件线程、定时器线程、读写文件的线程等。这些被称为工作进程
这些线程可能存在于js引擎之中或者之外, 称为工作线程
工作线程的任务完成之后, 会推入到一个任务队列(task queue)
扩展:JavaScrpt 的异步编程
js引擎只执行同步任务, 异步任务会有工作线程来执行。
- 异步过程
当需要进行异步操作(定时器、ajax请求、dom事件注册等), 主线程会发一个异步任务的请求, 相应的工作线程接受请求; 当工作线程完成工作之后, 通知主线程;主线程接收到通知之后, 会执行一定的操作(回调函数)。
GUI 渲染线程 与 JavaScript引擎线程互斥!
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JavaScript线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JavaScript引擎为互斥的关系,当JavaScript引擎执行时GUI线程会被挂起,GUI更新时,则会被保存在一个队列中等到引擎线程空闲时立即被执行。
JS阻塞页面加载
从上面我们可以推理出,由于GUI渲染线程与JavaScript执行线程是互斥的关系,当浏览器在执行JavaScript程序的时候,GUI渲染线程会被保存在一个队列中,直到JS程序执行完成,才会接着执行。因此如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。简单理解,为什么要有js阻塞,因为Javascript代码可能会修改DOM树的结构。
定时触发器线程
浏览器定时计数器并不是由JavaScript引擎计数的, 因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案。
事件触发线程
当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
异步http请求线程
XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 在检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript引擎的处理队列中等待处理。
参考
http://imweb.io/topic/58e3bfa845e5c13468f567d5
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
https://segmentfault.com/a/1190000009975744