1.线程与进程
进程
- 一个进程是一个程序的实例
- 进程统一管理和启动线程
- 一个进程包含一个或多个线程
- 浏览器1个页面4个进程
- 进程之间互相独立,通过IPC通讯
线程
- 多个线程可以实现并行处理
- 线程不能独立存在,依付于进程
- 线程共享进程里的数据
- 单个线程异常会导致整个进程挂掉
早期单线程浏览器
- 缺点
- 在页面线程上有长任务会卡死,不稳定
- 插件可以访问系统资源,不安全
- 由于都在一个进程里,如果内存没有正常释放,会越堆越多
- 组成
- 其他线程
- 网络线程
- 页面线程
- js环境
- 页面渲染
- 页面展示
- 插件
2008多进程浏览器时代
- 组成
- 浏览器进程
- 下载资源
- 管理IPC
- 显示渲染进程生产的图片
- 渲染进程
- 解析
- 渲染
- js执行
- 合成网页图片
- 插件进程
- 浏览器进程
- 优点
- 稳定
- 插件独立进程,奔溃不影响页面
- 流畅
- 多个tab页面多个独立进程渲染进程,不互相影响运行
- 内存泄漏不再存在,因为进程关闭,所有进程对应的所有资源都会回收
- 安全
- 渲染进程在独立沙盒,不能访问系统资源
- 稳定
现代的多进程浏览器架构
多个tab页面就有多个渲染进程,网络进程和 GPU进程UI绘制独立出来。
- 浏览器进程
- 显示
- 用户交互
- 子进程管理
- 储存
- 渲染进程
- html css解析处理的排版引擎Blink 线程
- 执行js的 V8引擎,线程
- 在沙盒里
- 一个tab页签一个渲染进程
- 插件进程
- 网络进程
- GPU进程
- UI界面采用GPU绘制
缺点
- 资源占用更多
- 进程的数量多,对于某些移动设备加载了无用的模块
- 系统架构复杂
- 各个模块互相依赖,强耦合
未来浏览器架构
面向微服务 : 构建一个更内聚、松耦合、易于维护和扩展的系统。
- 组成
- 浏览器主进程 :
- 微服务
- UI进程
- 文件进程
- 音频进程
- GPU进程
- 网络进程
- 微服务
- 渲染进程
- 插件进程
- 浏览器主进程 :
当资源不足,如移动设备 按把上面的微服务进程 改成service需合并到浏览器主进程
优点
- 资源占用更小
- 进程的数量更少了,按需加载
- 系统架构根据简单
- 各个模块解构,互相不再强依赖
2. tcp/udp/ip协议
协议是一套众所周知的规则和标准,大家跟根据规则和约定传输数据
IP协议
- Internet Protocol
- IP地址
- 发出方 应用层->网络层 数据+IP头传输 -> 物理层
- 接收方 物理层-> 网络层 数据+解析IP头判断对应的主机->应用层
UDP协议
- User Data Protocol
- 比IP协议多包一个端口,区分是哪个应用
- 发出方 应用层->传输层 UDP头 -> 网络层 数据+IP头传输 -> 物理层
- 接收方. 物理层 -> 网络层 数据+解析IP头判断对应的主机. ->. 传输层.解析UDP头 ->应用层
- 优点
- 传输快
- 缺点
- 容易丢失
- 不完整
TCP协议
transmission control Protocol 传输控制协议,传输逻辑与 UDP一致,UDP头改TCP头。 是一种面向连接的、可靠的、基于字节流的传输层通信协议。
生命周期
- 建立连接
- 3次握手,客户端与服务端确认身份
- 数据传输
- 每一次客户端接收数据,都是发送给服务器,确认已经收到,当规定时间内未收到确认信息,服务器会重新发送该包
- 大文件会拆成多个小文件,会跟句tcp头的序号排列顺序重新组装
- 断开连接
- 4次挥手
总结
- 互联网通过大文件拆成多个包传输,但是包容易丢失
- IP发送数据
- UDP发送给对应应用数据
- TCP通过3个阶段,建立连接,发送数据,断开连接,确保了对应应用数据的接收的可靠与完整性
3.http协议/请求流程
整体流程分为:客户端请求,服务端响应,客户端处理响应
3.1.客户端发起的流程
- 构建请求 - 构建请求行信息,转化为GET www.baidu.com/index.html HTTP1.1
- 查找本地缓存 - 强缓存 - 当缓存数据满足条件时,直接返回,无需再走下面流程
- DNS解析,通过Domain Name System DNS系统-获取IP - 读取本地浏览器DNS缓存 - 访问系统host配置 - 访问互联网DNS服务器
- 等待TCP队列 - 同个域名只能有6个TCP进行中的请求
- 建立TCP连接 - 3次握手
- 发送HTTP内容
- GET情况
- 请求行
- 请求方法
- 请求URI地址
- 请求http版本
GET 192.168.0.xx/index.html HTTP1.1 # 请求方法 请求URI地址 请求http版本
- 请求行
- POST情况
- 请求行
- 请求头:浏览器信息
- 请求体:数据内容
POST 192.168.0.xx/api HTTP1.1 # 请求行 #--------- 请求头 authority: juejin.cn method: GET scheme: https accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9,ru;q=0.8,zh-TW;q=0.7 cache-control: no-cache pragma: no-cache user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 #---------请求头 #--------- 请求体 [a:1,b:2] #--------- 请求体
- GET情况
3.2.服务器回复的流程
- 服务器接收信息
- 服务器返回HTTP内容
- 返回响应行,响应头,响应体
- TCP连接断开
- 如果没有keep-alive标识,4次挥手
3.3.客户端处理响应
- 客户端接受消息
- 包含响应行,响应头,响应体
- 客户端断开连接
- 如果没有keep-alive标识,4次挥手
- 内容处理
- 重定向:当服务端 返回301,并且 响应体 包含location信息,浏览器拿到后会自己重定向
- 如果是下载流,调用网络进程下载
- 如果是html 开始渲染流程
3.3.用户登录状态实现
- 服务返回 响应头 Set-Cookie: UID=abcds
- 客户端后续请求,请求头 自动带上 Cookie: UID=abcds
- 服务器读取 Cookie: UID确认身份
4.导航流程
导航流程主要包含 浏览器进程,网络进程,渲染进程,三块进行。
- 浏览器发起url
- 网络进程处理请求,并读取响应头和体信息
- 网络进程读完响应头信息,就可以开始通知浏览器进程生成渲染进程,同时浏览器也更新当前tab页签状态与内容清空
- 渲染进程开始解析,不断读取响应体信息
- 当所有资源已加载并解析完成后,通知浏览器进程加载完成。
注意:提交的文档指的是 :响应体数据
进程结构与作用
- 浏览器进程
- 用户交互
- 子进程管理,IPC通讯
- 文件处理
- 图形显示
- 网络进程
- 渲染进程
- html css js 转化为 显示和交互的页面
导航流程
1. 地址栏输入url
地址栏会判断输入的关键字是搜索内容,还是请求的 URL。
- 搜索内容转化成带参数的搜索url地址
- url地址则直接进入页面请求过程
注意:当前web页面内容不清除,需要等到提交文档阶段才刷新
2. 网络进程发起请求
浏览器进程通过IPC通知 网络进程发起网络请求
- 判断本地缓存
- DNS解析ip
- 建立TCP(https包含TLS处理)
- 服务器处理并返回数据
- 客户端处理
- 重定向
- 判断数据类型
- 如果是: text/html继续导航流程
- 如果是: application/octet-stream 交给下载管理器
3. 浏览器 实例化渲染进程
共用一个进程的情况
- 满足条件1: 同一站点:根域名+ 协议 ,如http:www.baidu.com, map.baidu.com:8081
- 满足条件2:并且如果从一个页面打开了另一个新页面
4. 提交文档
渲染进程实例化后,向渲染进程提交页面数据
- 统一由浏览器进程 通知网络进程吧数据提交给 渲染进程。两者建立管道
- 传输完后,渲染进程告诉浏览器进程:确认提交
- 浏览器进程收到确认提交后,更新浏览器页面内容,包括地址栏url,前进后退,更新web页面
注意:
- 由于渲染进程解析的内容都是网络获取,所以浏览器默认认为是不安全的。所以把渲染进程放在安全沙箱里。没有权限直接操作浏览器进程相关内容,只能通过IPC调用。
- 提交的文档不是加载完才提交,而是边读取边提交
5. 渲染进程接收完文档
开始解析与加载子资源,完成页面渲染
- 停止小icon转圈
5.渲染流水线
执行过程
- 多个子阶段
- 每个子阶段都有3个过程
-
输入内容
-
处理内容
-
输出内容
-
3种输入来源
- HTML
- 超文本协议
- 标签+文本
- CSS
- 层叠样式
- 选择器+属性
- JS
- JavaScript
- 动态脚本
9个阶段
1. 构建dom树
- 输入html
- 解析html
- 输出AST dom树
2. 样式计算 style(Recalculate style)
把dom + sheetstyles集合 合并成 渲染树(render tree)
注意: sheetstyles 可以等价理解为 cssom
- 把 CSS 转换为浏览器能够理解的结构
- 输入3种
link , <style> </style>, <p style> - 输出document.sheetstyles集合,即cssom
- 输入3种
- 样式属性值标准化,如
- 2em -> 32px,red -> rgb(255,0,0),bold -> 700
- 计算dom中每个节点的具体样式
- 每一个dom节点都会生成 ComputedStyle对象记录样式
- 继承规则
- UserAgent系统默认样式
- 自上而下的继承样式如body的font-szie 会一直往里继承
- 继承关系 Useragent -> html -> body -> div -> p -> span
- 层叠规则
- 通过层叠样式表叠加(css 全称 层叠样式表 也来源)
3. 布局 layout
布局:创建布局的可见元素的几何位置,这个过程会生成布局树,layout tree
- 创建可见布局树
- 忽略dispaly:none
- 布局计算
- 布局节点的相关坐标 过程是否复杂耗时
- 布局运算的结果会重新回写到布局树
- 即是输入也是输出
4. 分层 layer
渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树 layer tree
- 生成图层树
- 不是所有布局树节点都对应一个图层
- 满足独立生成图片的条件,拥有层叠上下文的属性
- postion:fixed
- z-index:1
- filter:blue(1px)
- opacity:0.3
- 要用于裁剪的地方
- overflow 小的div里放了很多内容 出现
- 小DIV内的内容就会单独生成图层
- 滚动条会单独生成图层
- overflow 小的div里放了很多内容 出现
5. 生成绘制指令列表 paint
- 把多个图层生成多个绘制指令列表
- 指令:操作:绘制圆形坐标:xxx样式:xxxx 注意:这里只是生成指令没有真正绘制图片
6. 分块 tiles
由于每个分层画的内容可能远远超过用户视口可以显示的大小。 为了优化减少渲染的面积,会把一张大图切割成多个小图块,最终根据视口区域渲染指定的小图块渲染。
注意:此时依然为开始渲染图片,只是把每一个layer图层指令再细化精简成绘制图块指令。
如上图所示,浏览器只会拿图块 3,4,5,6,7,8 做渲染,其他都不处理。
- 主线程提交 绘制指令列表 给 合成线程
- 合成线程把大的图层分成一各个小块的图块
512*512或者256*256
- 图块是光栅化执行的最小单位
- 概念
- 视口viewport:当前窗口最大的显示区域
- 可见图块 visible tiles
- 图块 tile
- 图层 layer
7. 光栅化 raster
把上面生成的图块指令通过栅格化,生成一张适合视口大小显示的图片。
- 图块栅格:合成线程根据当前视口所包含的图块都统一渲染为位图
- 栅格化:将图块转化为位图
优化特点
- 通过只渲染视口的内容,实现优化渲染的速度
- 栅格化的线程池
- 图块栅格化都在这里执行
- 包含多个栅格的线程
- 使用GPU加速,把线程池的内容提交给 GPU进程处理,生成的图片保存在GPU中
8. 合成 drawquad
- 当所有图块都被光栅化后,合成进程生成绘图指令 DarwQuad 提交给浏览器进程
9.显示 display
- 浏览器进程使用 viz 的组件 将内容绘制到 内存中
- 最终把内存信息显示到屏幕
完整流程
- 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
- 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
- 创建布局树,并计算元素的布局信息。
- 对布局树进行分层,并生成分层树。
- 为每个图层生成绘制列表指令,并将其提交到合成线程。
- 合成线程将图层指令分成图块指令
- 合成线程根据当前视口所包含的图块指令都统一栅格渲染为位图。
- 合成线程把渲染后的位图发送绘制图块命令DrawQuad给浏览器进程。
- 浏览器进程根据 DrawQuad 消息生成页面,加载到内存,并显示到显示器上。
渲染中的主线程与非主线程关系
- 主线程(阻塞)
- dom树
- style样式计算
- 布局
- 分层
- 生成绘制指令
- 非主线程(非阻塞)
- 合成线程
- 分块
- 生成位图
- GPU进程 :光栅,并且通过drawquad通知 浏览器进程
- 浏览器进程 :display显示
- 合成线程
重排,重绘,合成
重排
- 元素几何属性变化
- 触发dom以外的完整的渲染流程(从style样式计算开始)
el.style.width = '30px';
当前宽度变化,发生几何变化,会从样式计算开始重新走一遍渲染流程
重绘
- 绘制属性变化
- 跳过了 布局和分层阶段 (从style样式计算开始)
el.style.backgroundColor = 'red';
当前背景色发生变化,几何信息没有变动会,会从样式计算开始,中间跳过 layout和layer两个阶段。
因为layout为了计算坐标和显示节点,layer是为了特殊节点如 z-index分层。这些重绘时都没有影响到。
直接合成
- 直接通过css中的transform 改变样式,会跳过 布局 ,分层 ,生成绘制指令阶段,性能更优 (从style样式计算开始)
el.style.transform = 'scaleX(0.8)'; //x方向 压缩为0.8
由于transform 使用的是gpu独立的渲染进程处理,从样式计算,后面的布局 ,分层 ,生成绘制指令阶段都无需执行,直接到分块继续。