存档

作者存档

深入理解现代浏览器 1: 多进程架构

2022/06/21 1,647

译注:本文翻译自 ChromeDeveloper 博客, 原文发表于2018年底, 部分特性可能与目前 Chrome 有所不同。建议有条件的读者直接阅读原文

 

本系列有4章, 我们将从高层架构到管道渲染的细节来深入理解 Chrome 浏览器。如果你想知道浏览器如何将你的代码变成功能性的网站, 或者你不确实为何被建议使用某种技术来提高网页性能, 那么本系列会适合你。
在本章中, 我们将了解核心计算的术语及Chrome 的多进程架构。

计算机的核心是 CPU 和 GPU

为了理解浏览器运行的环境, 我们需要理解一些计算机部分及它们的作用。

CPU

首先是 中央处理单元(CPU) , CPU可以比作你计算机的大脑。一个CPU核心,就如图中的一个办公室职员, 可以一一处理很多不同的任务。它可以处理从数学到文艺的所有的事件, 甚至知道如何答复客户。过去大部分CPU是单芯的。而核就像生活在同一个CPU中的另一个芯片。在现代硬件中,你通常拥有多核CPU,它为你的手机和笔记本提供更多算力。

GPU

图像处理单元(GPU) 是计算机的另一个部件。和CPU不同的是,GPU擅长处理简单任务, 不过它可以同时跨越多核。顾名思义, 它最初是为处理图像而开发。这就是为何在图形上下文中, "使用GPU" 或 "GPU支持" 往往与快速渲染和平滑交互相关联。现如今, 随着GPU加速运算的发展,越来越多仅在GPU上完成的运算成为可能。

当你在电脑或手机上启动程序时, CPU和GPU都为程序提供算力。通常程序使用操作系统提供的机制在CPU和GPU中运行。

在进程和线程中执行程序

在深入研究浏览器架构之前要掌握的概念是进程和线程。进程可以看作是应用程序的执行程序。而线程存在于进程内部执行其进程程序任意部分。

继续阅读

C++ 内存对齐

2021/01/08 2,565

如下两个结构体 A B ,他们的实例的大小相同吗?

两个 char 共占两个字节, 一个 int 占4个字节, 所以两个结构体都是 6个字节。但事实并非如此,在大部分计算机中,他们都不会只占 6 个字节。这涉及到 内存对齐
需知,计算机访问内存的方式,并不是一个字节一个字节访问的,而是以字长(word size)为单位来进行访问的。32位计算机的字长为 4 字节, 64位计算机的字节为 8 字节。而所谓内存对齐, 就是将变量调整至字长的倍数的位置存放,以方便计算机访问
上面两个结构体在内存中的布局最有可能是这个样子的:(这里说 最有可能 是因为具体的布局要视计算机而定)


我们使用一段代码来验证:

结果如下:

A 中,c_1 i 共占一个字,c_2 占一个字。这种将多个变量放入一个字中的动作,叫作 packing 。 而组成一个字时,补充不足的部分,叫 padding
如何知道某种类型的变量要对齐到哪个位置呢?它取决于一个类型的 alignment 如 int 类型的alignment 为4, 那么int 类型变量的地址必定为 4 的倍数。

继续阅读

字符乱码之特征

2020/12/14 2,476

字符乱码之特征

从单例模式谈起(二):volatile 关键字

2020/01/21 3,085

前一部分(从单例模式谈起(一))我们讨论单例模式时,谈到了 Double-Checked Locking 问题。我们讲, volatile 关键字并不能解决Double-Checked Locking 问题。这一节我们讨论与此相关的问题。
现代编译器为了提高程序的效率而对代码做了很多优化。这些优化大部分是有益的,但是也为多线程编程带来问题。

寄存器缓存

我们来看如下代码:

x++ 的值有锁保护,所以它是独占的,x++ 的行为不会被并发破坏。那么 x 的值必然为 2。 然而现实中并非一定如此:编译器有可能为了提高 x 的访问速度而将 x 放入线程的某个寄存器中,而线程的寄存器是线程私有的。 如果 Thread1 先获得了锁,那么程序的执行过程有可能是这样的:

  1. [Thread1] 读取 x 到寄存器 R1. (R1 = x = 0)
  2. [Thread1] R1++. unlock. 由于之后可能还会访问 x, 所以 Thread1 不将 R1 写回 x . (x = 0)
  3. [Thread2] 读取 x 到寄存器 R2 (R2 = x = 0)
  4. [Thread2] R2++
  5. [Thread2] 将 R2 写回 x (x = R2 =1)
  6. [Thread1] 在某个时候将 R1 写回 x (x = R1 = 1)

由此可见,即使正确的加了锁,也不能保证多线程安全。

继续阅读

Linux Coredump 核心转储

2019/10/14 5,022

什么是 Coredump

coredump 核心转储 ,也称为 核心文件(core file) 是操作系统在进程收到某些 信号 而终止运行时,将此进程的地址空间以内容以及有关进程状态的其他信息写出的一个文件。这种信息往往用于调试。
程序员可以通过工具来分析程序运行过程中哪里出错了:Windows 平台用 userdump 和 WinDBG ,Linux 平台使用gdb, elfdump, objdump 等

Windows WinDBG

关于 windbg, 可以参考以下资料

Linux GDB

有些时候进程在crash的时候会产生 core 文件, 但我们却找不到 core 文件,我们需要使用 ulimit 进行一些设置, 这个命令是用来限制系统用户对shell资源的访问的。
ulimit -a 可以查看当前的设置
ulimit -c 可以设置 core 文件的上限,单位为区块(一般 1 block = 512 bytes) .其值为 0 时不写入 core, 为 unlimited 时不限制 core 文件大小。
需要注意, ulimit 只对当前会话有效。若想对所有会话生效, 需要在 /etc/profile 中进行配置。

源文件如下 test_vec.cpp :

编译运行时可能出现如下现象:

使用 gdb 打开来看:

从 gdb显示的栈信息来看,崩溃发生在 main 函数内的 vector::at 函数内,由 _M_range_check raise 。
如果我们在编译时使用了 -g 选项, 会得到更详细的信息

继续阅读

GDB 常用调试技巧

2019/10/12 3,386

1. 调试宏

宏是预编译的,无法 print 宏的定义。但是如果配合 gcc, 我们还是可以有限地调试宏。
在 GCC 编译程序的时候,加上 -g3 参数,就可以调试宏了。

  • info macro mac_name 可查看宏定义,及位置
  • macro expand mac_expr 可查看宏展开的样子

示例:

调试现场如下:

2. 修改变量

两种方法:

  1. print var_name=x
  2. set var var_name=x

示例:

继续阅读

GDB 常用命令一览

2019/10/11 4,585

命令

GDB 是 Linux 下的命令行调试工具。
启动 GDB 有如下几种方式:

  1. gdb <program> 直接启动执行程序
  2. gdb <program> core 用gdb 同时调试一个可执行程序和core文件。core 是程序非法执行后 core dump 产生的文件
  3. gdb <program> <PID> 指定进程, gdb会自动 attach 上去。program 应该在 PATH 环境变量中可以搜索得到。

常用的 gdb 命令如下

信息 info

info 可以简写成 i

  • info args 列出参数
  • info breakpoints info break i b 列出所有断点
  • info break number i b number 列出序号为 number 的的断点
  • info watchpoints i watchpoints 列出所在 watchpoints
  • info threads 列出所有线程
  • inifo registers 列出寄存器的值
  • info set 列出当前 gdb的所的设置
  • i frame
  • i stack
  • i locals
  • i catch

断点和监视 break & watch

继续阅读

memcpy vs memmove

2019/09/14 1,177

memcpymemmove 都可用于复制一段内存。

它们之间的区别很简单:

  • memmove 更安全:当源内存块和目标内存块存在重叠区域时,memcpy 的行为是未定义的。它可能崩溃或产生错误数据。而且在debug 和 release 模式下的表现各不相同。
  • memcpy 更快。

这是两种不同编程思想的实现。一种更注重安全, 一种更注重效率。

下面是两个方法可能的实现方式.

继续阅读