Author published on included in 汇编 8086 寄存器分类 所有寄存器均为 16 位,其中 AX、BX、CX、DX 分别可以分成两个 8 位的寄存器使用,比如 AH, AL 分别表示 AX 的高 8 位和低 8 位。
通用:
AX, BX, CX, DX SP, BP SI, DI 专用:
IP PSW CS, DS, SS, FS 8086 指令分类 运算类 算数运算: add, sub… 逻辑运算: and, or… 移位运算: shr, shl… 传送类 单传送: mov, push… 串传送: movsb I/O 传送:int, out 跳转类 直接跳转:jmp 间接跳转:call、ret、iret、loop, int… 修改标志位类:sti, cli… 标记寄存器标记位意义 高四位和低六位中的三个奇数位未用上。
Author published on included in 汇编 虚拟地址 地址总线 20 根,决定了处理器的寻址能力为 1MB,但是 16 位的寄存器(对应数据总线也是 16 根)决定了只凭 1 个寄存器是无法穷尽 1MB 的地址空间的,所以至少需要 2 个寄存器才能表示完整的地址空间。比如,用一个寄存器 A 存储物理地址的高 16 位,剩下的低 4 位地址用另一个 16 位寄存器表示绰绰有余,这样一来将寄存器 A 的内容左移 4 位再加上寄存器 B 的内容就可得到实际的物理地址,其中寄存器 A 的地址称为段地址,寄存器 B 的内容称为偏移地址,即实际物理地址时=段地址 * 16 + 偏移地址。「段地址:偏移地址」所表示的地址对被称为虚拟地址。由于两个 16 位寄存器表示一个 20 位的物理地址是绰绰有余,于是一个物理地址便可能有多个虚拟地址相对应,比如 0xfffff=0xffff * 16 + 0x000f=0xfff0 * 16 + 0x00ff = 0xff00 * 16 + 0x0fff。
1MB 物理地址空间分配:
0x00000~0x9FFFF: 10 * 2^16 = 640kB 由 RAM 提供,编程人员可自由使用 0xA0000~0xEFFFF: 5 * 2^16 = 320kB 对应特定的硬件设备的存储器空间,比如显存的 0xB8000~0xBFFFF:2^15 = 32KB 就在里面 0xF0000~0xFFFFF:1 * 2^16 = 64KB 对应 BIOS-BOM 8086 开机流程 BIOS 自检 开机流程:处理器有个名为 RESET 的引脚,用于接收复位信号,每当处理器加电或者 RESET 引脚由低电平变到高电平时,处理器都会执行硬件初始化的操作,以及一个可选的内部自测试,然后会讲所有的寄存器内容都恢复到初始状态,其中除了 CS 寄存器的初始状态为 0xFFFF,其余所有的寄存器初始状态都为 0x0000。然后处理器便从逻辑地址 0xffff:0000 即对应 0xffff0 的物理地址处开始执行它开机后的第一条指令,这个指令位于 BIOS-ROM 中,通常是一个跳转指令,跳转的地方对应 BIOS-ROM 的较低的地址,然后正式执行 BIOS 自检程序,自检程序的指令是固化在 BIOS-ROM 特定地址的,所以 0xffff0 处跳转指令对应的地址是固定,每次开机都是这样的流程。
Author published on included in 笔记 基本概念 对称加密 加密和解密用的是同一个密钥。
非对称加密 加密和解密需要用到一对不同的密钥 A 和 B,其中可以被公开的称为公钥,不公开的称为私钥,用 A 加密的则必须由 B 来解密,反之亦然。
中间人攻击 客户端和服务端之间传输的消息被一个「中间人」截获,中间人冒充二者与对方交换信息而二者毫不知情。
散列 通过某种算法将,将任何一组任意长度的数据都转换成一个固定( 多半如此)长度的散列值。可用于签名认证,来检查某段信息是否被修改。比如已知信息 A,按某种散列算法 M 散列后得到 α,即 M(A)=α,现得到信息 B 和 α,如果 M(B)=α 也成立,那么依赖这种散列算法 M 自身的特性,理论上我们就认为 A=B,即得到的信息与原始信息一致,在传输过程中没有被篡改。
HTTP 传输的不足之处 明文传输,容易被攻击。
HTTP 避免明文传输的解决方法 对传输的信息进行对称加密 首先客户端先向服务端请求密钥,服务端将密钥明文传输给客户端,以后二者发送信息前均用密钥加密,收到信息后均用密钥解密。看起来像是解决了明文传输的问题,但密钥本身就是用明文传输的,无法防止泄露,只要密钥泄露,加密等于没加密。
对传输的信息进行单向非对称加密 服务端生成一对密钥,将公钥明文传给客户端,客户端向服务端发送、接受信息时,使用公钥加、解密,服务端像客户端发送、接受消息时,使用私钥加、解密。这样一来,即便公钥泄露,也只有服务端发送的消息加密失效。因为私钥始终在服务端,得不到私钥,客户端发送给服务端的消息无法被解密。
对传输的信息进行双向非对称加密 客户端也生成一对公、私钥,将公钥发送给服务端,服务端用客户端的公钥加密,客户端用服务端的公钥加密,然后用各自的私钥解密,这样一来,即便传输过程中两个公钥都泄露,只要本地的私钥不泄露,双方交流的信息就不会泄露。
单向非对称加密 + 对称加密 虽然上面双向非对称加密的方法可以解决明文传输的问题,但是需要两对非对称密钥,未免有点繁琐,而且非对称加密所花费的时间也不可小觑。这样的话,我们是不是可以利用单向非对称加密「起码可以保证客户端使用服务端公钥加密之后发送给服务端的信息无法被解密」的这个特点做点什么?是的,可以通过公钥加密一个对称密钥发送给服务端,后续彼此的信息交流都用这个密钥进行加、解密,这样一来只需要服务端的一对非对称密钥和客户端生成一个对称秘钥即可解决明文传输的问题,显然会比双向非对称加密来的有效率。
但是「单向非对称加密 + 对称加密」也只是解决了避免明文传输的问题,都还没有考虑如果客户端收到的公钥压根儿就不是服务端的公钥要怎么办?或者如果避免那种情况发生?设想一下如果在客户端、服务端之间存在一个可以截获双方来往所有信息的「中间人」,这个时候这个中间人会好到只满足于「偷窥」以外什么都不做么?
只加密传输信息无法防止中间人攻击 拿上面最繁琐的双向非对称举例,
客户端、服务端像彼此发送各自的公钥 A、B,但都被中间人截获,中间人以自己的公钥 C 顶替 A、B 客户端、服务端都收到中间人的公钥 C 客户端通过公钥 C 发送加密消息给服务端,但被中间人截获,(中间人使用自己的私钥解密,拿到信息后)通过服务端的公钥 B 冒充客户端发送加密消息给服务端 客户端收到消息后使用自己的私钥解密,然后用公钥 C 返回加密消息给客户端,依然被中间人截获,(中间人使用自己的私钥解密,拿到信息后)通过客户端的公钥 A 冒充服务端发送加密消息给客户端 这样客户端和服务端之间的通信完全被中间人截获,从而导致它们收到的信息完全有可能不是来自对方(假如这个中间人够坏的话),而双方毫不自知。所以,只要存在中间人,那么仅仅止步于加密是完全不够的。
Author published on included in 笔记 Q:GET 方法和 POST 方法的区别是什么? A:除了 POST 方法可以有 body,GET 没有 body 之外其他的基本都是语义上的区别。比如 GET 方法是「安全的」、「幂等的」、GET 方法对应的 response 默认是应该被 cache 的,而 POST 方法的 response 只有在 headers 中包含 cache-control 或 Expires header 且为合适的值时才能被 cache。
Q:HTTP cache 是什么?如何起作用的? A:cache 指的是 HTTP 中定义的用来减少不必要的 message 发送和不必要的 entity 传输的一套机制,也指专门实现这种机制的服务器,也可以指与原始服务器 response 对应的 copy。cache 的机制分为两部分:expiration 和 validation,前者用于减少不必要的 message 发送,后者用于不必要的 entity 传输。expiration 的机制表现为:当客户端发送一个 request 到 cache server 时,cache server 会检查对应的 cache(假设有这样的 cache)是否是 fresh,如果是的话,直接从那个 cache 构造出对应的 response 发送给客户端。每个 cache 都有它对应的 freshness_lifetime 和 current_age,每次检验是否 fresh 时,只有当计算出来的 current_age 小于 freshness_lifetime 时,这个 cache 才会被认为是 fresh 的。其中 current_age 的算法与 origin server 的 response headers 里的 Date,Age 的值有关。freshness_lifetime 的算法则与 cache-control header 的 max-age 声明的值和 Expires header 的值有关。validation 的机制表现为:当一条 cache 过期被判定为不是 fresh 的之后,cache server 会像 origin server 重新发一条带有 if-none-match (其值来源于被 cache 的 response 的 etag header 值) 或/和 if-modified-since (其值来源于被 cache 的 response 的 last-modified header 值) 请求头的 request,origin server 会根据这两个请求头的值来决定 validation 是否通过,如果通过会给 cache 返回一个状态码为 304 的 response,收到这个 response 和 cache server 根据本地的 cache 构造出对应 response 返回给客户端;如果未通过,origin server 会返回一个新的状态码为 200 的 response 给 cache server,cache server 将这个 response 原本返回给客户端,并据此更新对应的已经不再 fresh 的 cache。
前置条件 存在一对公、私钥,公钥在远程服务器用户 A(具体用户名随意)的 ~/.ssh/authrized_key 中,本地主机可通过私钥 ssh 登录用户 A 用户 A 对 /var/www/任意目录名(nginx 服务启动的根目录)有读写权限 私钥保存到项目的 secret 中(最好通过文本编辑器打开,然后粘贴复制,直接 cat 粘贴复制可能会导致 “invalid format”) github action 操作步骤 将保存在 secret 中的私钥存到环境变量(感觉好像不存应该也可以?) 通过 actions/checkout 拉取代码和切换到项目根目录 通过 actions/setup-node 安装 node 和 npm 安装依赖,打包 将存在当前环境变量的私钥通过 echo 命令写到 github runner 的 ~/.ssh/id_rsa 文件中(其他目录或者其文件名也可以,只不过那样的话,scp 的时候就要显示指定完整路径名),并且将远程服务器的 host 添加到 ~/.ssh/known_hosts 文件中(此举好像为了是避免在第一次 ssh 陌生服务器时,系统会询问「是否确认要与远程服务器连接」而使得后续 action 中断) 通过 scp 命令将打包好的文件复制到远程服务器指定位置 完整 yml 文件 这里 SSH_KEY、SSH_PORT、HOST、SSH_PORT、USERNAME、TARGET_DIR 为提前在项目 secret 下配置的变量,如有需要像打包后的文件生成目录(此处是 ./docs/.vuepress/dist)也可以写进去。
Author published on included in 笔记 什么是 Unicode:Unicode 是一个将现今世界大部分用于交流信息的字符(为组成文本的最小组成单元)映射成从 0 到 16 * 2^16 -1 的连续自然数集合的方案,为每个字符分配的自然数即成为该字符的 code point,通常是通过以十六进制表示的数字值(带有 “U +” 前缀)来引用它们。所有的字符被分为 17 个组(一共 1114112 个,每组 65536 个),大部分常用字符基本都被分到第一组(第一组被称为 Basic Multiple Plane),该组前 128 个字符与 ASCII 码对应的字符集一一对应。所以本质上讲,Unicode 与 ASCII 码将 128 个字符映射成 0 到 127 的自然数没有什么区别,只不过较 ASCII 码而言,Unicode 的编码方案要复杂得多得多。
按照基本用途,1114112 个 code point 分为 7 个基本类型:
Graphic:字母,标记,数字,标点,符号和空格 Format:不可见,但会影响邻近的字符;包括行、段落分隔符 Control:共 65 个(U+0000 到 U+001F 和 U+007F 到 U+009F 之间的 code point) Private-use:用于代表私用字符的 code point,这些字符的解释未由本标准规定,并且其使用可能由合作用户之间的专用协议确定,共 6400 个(U+E000 到 U+F8FF) Surrogate:用于 UTF-16 编码的代理,共 2048 个(U+D800 到 U+DBFF 和 U+DC00 到 U+DFFF 之间的 code point,前 1024 个称为前导代理,后 1024 个称为后尾代理) Noncharacter:Unicode 标准中永久保留供内部使用的 code point,共 66 个(U+FDD0 到 U+FDEF 中的 32 个加上 17 个组中每组的最后两个) Reserved:保留以供将来分配,但被限制用于信息交换 并非所有的 code point 都被分配给了某个字符的,只有 Graphic、Format、Control 和 Private-use 几个类型的 code point 有与之对应的字符;Surrogate 和 Noncharacter 两个类型的 code point 已经被分配,但分配的对象并不是字符;Reserved 保留以供将来分配,但被限制用于信息交换。所有未分配给特定字符的 code point 均被限制用于信息交换。
Author published on included in 前端 前言 在开始本篇之前:
本篇的主要目的在于理解 mounted 的大致原理(mounted 之后的事不在本篇讨论之内)
所用的 Vue 版本是 2.6.11,借以帮助理解的例子是 vue-cli 创建的简单 demo
暂不理会包括但不仅限于 slot、函数式组件、服务端渲染以及其他一些不影响理解的特性
贴出的代码并不完全与源码一模一样,在没有错误的前提下会有一些改动和删减(笔者认为这样做有助于理解)
也不会逐行去解释代码,未提到的部分可以认为暂时不提也无大碍(如果真的不是因为粗心的话)
然后,本篇所用的实例代码如下:
main.js
import Vue from 'vue' import App from './App' new Vue({ render: h => h(App) }).mount('#app') App.vue
<template> <div> <div>hello, {{ msg }}</div> <home></home> </div> </template> <script> import home from './home' export default { name: 'App', components: { home }, data() { return { msg: 'app' } } } </script> home.
Author published on included in 前端 注:此处 Vue App 指的是由 Vue-CLI 3 创建的单页面应用,部署应用的 Web 服务器为 Nginx。
正文 谈到部署前端应用,大概所有有过相关经历的开发者都不会对这个流程感到陌生:npm run build -> dist 目录整个 copy (手动、脚本或 devops 流水线)到对应 Web 服务器的指定目录。
然后在浏览器上输入服务器的地址(不需要任何路径)就可以访问到部署的那个应用了,在应用里面,你也只是按照后台开发给你 API 路径(大概率是个不包括服务器地址的绝对路径)去请求对应的数据,好像也没有做什么特殊的事情,应用完全运行正常。
这一切看起来十分的自然和理所应当,后面你又以相同的方式开发、部署了多个类似的应用,这种在根路径上的部署,对你来说轻车熟路,一切都很顺利。直到有一天,因为防火墙、或者资金紧张或者其他随便的什么原因,你们所有那些应用在公网上全都只能通过某一台服务器(假设叫 outer)访问了,也就是说原先可以分别通过 http://addressN (N = 1, 2, 3…)被 访问的各个应用,现在只能通过 http://address/appN/ 来访问了。这个时候,你想了想好像跟之前没什么区别,(拿 app1 举例)只不过原先部署到 http://address1 的 app1 只能换个内部的服务器(假设叫 inner1)部署了,然后在你重新部署了之后,负责配置 http://address 服务器 Nginx 的人告诉你:我把所有 /app1/ 开头的请求都转到 inner1 上了,你现在看下通过 http://address/app1/ 是不是能访问到 app1。
你内心感觉应该没什么问题地在浏览器上访问了那个地址:浏览器 Tab 条上 app1 的页面 title 出来了,但是整个页面一片空白。然后你很熟练地打开了控制台的 Network 发现,那些 js 和 css 资源都是 404,你很快的察觉到原因是由于这些资源的路径有问题,它们的格式分别是 /js/xx.