进程和线程
一、进程
定义:进程是正在运行的程序的实例,它代表CPU所能处理的单个任务,是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)。
组成:每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
- 文本区域存储处理器执行的代码;
- 数据区域存储变量和进程执行期间使用的动态分配的内存;
- 堆栈区域存储着活动过程调用的指令和本地变量。
二、线程
定义: 线程是进程中的一个实体,是被系统独立调度和分派的基本单位。
组成:一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
三、进程和线程的关系
- 任何时刻,单个CPU总是运行一个进程,其他进程处于非运行状态。
- 一个进程可以包括多个线程。
- 线程不拥有自己的系统资源,但是可以和同属一个进程的其它线程共享该进程所拥有的全部资源。同时,由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
- 一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程也有就绪、阻塞和运行三种基本状态。
- 进程中的某一块内存区域,可能只能供给固定数目的线程使用。当某一内存区域只能由一个线程使用时,这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
- “互斥锁”(Mutual exclusion,缩写 Mutex):防止多个线程同时读写某一块内存区域。
- “信号量”(Semaphore):用来保证多个线程不会互相冲突。
浏览器是多进程的
一、浏览器主要包含哪些进程?
- Browser进程:浏览器的主进程(负责协调、主控),只有一个。
作用是:
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
-
第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
-
GPU进程:最多一个,用于3D绘制等
-
浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的):
- 默认每个Tab页面一个进程,互不影响
- 渲染器进程中包含用于操作HTML,JavaScript,CSS,图片和其他内容的复杂的逻辑。
- 每个渲染器进程都运行在沙箱内,这意味着它对磁盘、网络和显示器没有直接的访问权限。
- 所有跟网络应用的交互,包括用户输入事件和屏幕绘制都必须通过Browser进程。这可以让浏览器进程监视渲染器的可疑行为,一旦发现其从事破坏活动就将其终止。
一般来说,在浏览器中打开一个网页相当于新起了一个浏览器渲染进程。
浏览器也由自己的优化策略:允许两个相关的且属于同一站点的网页共享同一个渲染器进程。
二、浏览器多进程的优势
- 避免单个页面崩溃影响整个浏览器
- 避免第三方插件崩溃影响整个浏览器
- 多进程充分利用多核优势
- 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性
三、浏览器渲染进程(浏览器内核)
- GUI渲染线程
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
- 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
- JS引擎线程(JS内核)
- 负责处理Javascript脚本程序,运行代码。
- JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序。
- 注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
- 事件触发线程
- 归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)。
- 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中。
- 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。
- 注意,由于JS的单线程关系,这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
- 定时触发器线程
- 传说中的setInterval与setTimeout所在线程。
- 浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确)。
- 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)。
- 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
- 异步http请求线程
- 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求。
- 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JavaScript引擎执行。
四、主进程(Browser进程)对渲染进程(Render进程)的控制过程
- Browser进程收到用户请求,获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程。此过程可大致分为一下几个步骤:
- URL 解析
- 首先判断你输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
- 由于安全隐患,会使用 HSTS 强制客户端使用 HTTPS 访问页面。
- 浏览器还会进行一些额外的操作,比如安全检查、访问限制。
- DNS 查询
- 浏览器缓存----浏览器会缓存DNS记录一段时间。有趣的是操作系统没有告诉浏览器存储DNS记录的时间,这样不同浏览器会记录他们各自固定的一个时间(2分钟到30分钟不等)
- 系统缓存----如果在浏览器缓存中没有找到需要的记录,浏览器会做一个系统调用(gethostbyname)。这样便可获得系统缓存中的记录。
- 路由器缓存----接着前面的查询请求发向路由器,他一般会有自己的DNS缓存
- ISP DNs缓存----接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。
- 递归搜索----你的ISP的DNS服务器从域名服务器开始进行递归搜索,从com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。
- TCP 连接
- 应用层:发送 HTTP 请求。浏览器只能发送 GET、POST 方法,而打开网页使用的是 GET 方法。
- 传输层:TCP 传输报文。在建立连接前,会先进行 TCP 三次握手。
- 网络层:IP协议查询Mac地址。
以太网规定了连入网络的所有设备都必须具备“网卡”接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。- 链路层:以太网协议。
广播发送数据。直接把数据通过 ARP 协议,向本网络的所有机器发送,接收方根据标头信息与自身 Mac 地址比较,一致就接受,否则丢弃。
- 处理请求
- 接受响应
浏览器接收到来自服务器的响应资源后,会对资源进行分析。
首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
然后,对响应资源做缓存。
接下来,根据响应资源里的 MIME[3] 类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。
- Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染
- 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
- 当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)
- 最后Render进程将结果传递给Browser进程
- Browser进程接收到结果并将结果绘制出来。
- HTML 解析
浏览器需要根据文件指定编码(例如UTF-8)将接收到的响应内容转换成字符串,也就是HTML 代码。
预解析:提前加载资源,减少处理时间,它会识别一些会请求资源的属性,比如img标签的src属性,并将这个请求加到请求队列中。
符号化是词法分析的过程,将输入解析成符号,HTML 符号包括,开始标签、结束标签、属性名和属性值。它通过一个状态机去识别符号的状态,比如遇到<,>状态都会产生变化。
生成Dom树。- CSS 解析
浏览器下载css文件,将css文件解析为样式表对象,并来用渲染dom tree。该对象包含css规则,该规则包含选择器和声明对象。
css元素遍历的顺序,是从树的低端向上遍历。- 渲染树
浏览器结合 DOM 树和 CSSOM 树构建 Render 树,并计算布局属性- 布局与绘制
确定渲染树种所有节点的几何属性,比如:位置、大小等等,最后输入一个盒子模型,它能精准地捕获到每个元素在屏幕内的准确位置与大小。
然后遍历渲染树,调用渲染器的 paint() 方法在屏幕上显示其内容。
浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。
五、JS进程编译执行
- 词法分析
JS 脚本加载完毕后,会首先进入语法分析阶段,它首先会分析代码块的语法是否正确,不正确则抛出“语法错误”,停止执行。
- 预编译
JS有三种运行环境:全局环境、函数环境、eval 。
每进入一个不同的运行环境都会创建一个对应的执行上下文,根据不同的上下文环境,形成一个函数调用栈,栈底永远是全局执行上下文,栈顶则永远是当前执行上下文。
创建执行上下文:创建变量对象,建立作用域链,确定 This 指向
- 执行
参考资料:
1.http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
2. https://www.cnblogs.com/cangqinglang/p/8963557.html
3. https://www.cnblogs.com/tutuj/p/11025042.html
4. http://lynnelv.github.io/js-event-loop-browser
5. https://www.cnblogs.com/jin-zhe/p/11586327.html
6. https://zhuanlan.zhihu.com/p/43369093