[{"content":"1. Overview A distributed system is one in which the failure of a coumputer you didn\u0026rsquo;t even know existed can render your own computer unusable.\nUCB 团队在 2009 年发表的论文 Above the Clouds: A Berkeley View of Cloud Computing，预测了云计算机的价值、演进和普及的进程；而 2019 年的论文 Cloud Programming Simplified: A Berkeley View on Serverless Computing，则提出了一种新的猜测：“无服务会发展成未来云计算的主要形式”，无服务架构下的用户不再需要关心细节，而极大解放生产力。\n阿里巴巴团队所编写的云原生架构白皮书也预言在未来十年，云计算将无处不在，像水电煤一样成为数字经济时代的基础设施，云原生让云计算变得标准、开放、简单高效、触手可及。\n一般来说，现代的分布式计算的服务模型基本分为如下一些类别：\nName Description Example SaaS Web services, multimedia, business apps Web services, multimedia, business apps PaaS Software framework (Java/Go), Database systems MS Azure Google App engine IaaS Computation (VM), storage (block, file) Amazon S3; Amazon EC2 Hardware CPU, memory, disk, bandwidth Datacenters 为了实现这些分层，对用户透明，需要很多的抽象，Andrew Tanenbaum 在图书 Distributed Systems Principles and Paradigms 中，也给出了系统透明性的各个角度的描述。\nTransparency Description Access Hide differences in data representation and how an object is accessed Location Hide where an object is located Relocation Hide that an object may be moved to another location while in use Migration Hide that an object may move to another location Replication Hide that an object is replicated Concurrency Hide that an object may be shared by several independent users Failure Hide the failure and recovery of an object 为了实现一个彻底透明的分布式系统还有非常多的挑战，早在 20 世纪 90 年代，L. Peter Deutsch 等人总结出了分布式计算的谬误（The Fallacies of Distributed Computing）：\nThe network is reliable; 网络是可靠的 Latency is zero; 延迟为零 Bandwidth is infinite; 带宽是无限的 The network is secure; 网络是安全的 Topology doesn\u0026rsquo;t change; 拓扑结构不会改变 There is one administrator; 单一的管理员 Transport cost is zero; 传输成本为 0 The network is homogeneous. 网络是同构的 实际上，在真实的世界中，分布式系统充满了各种挑战，其中主要包括如下三点：\n不可靠的网络：消息可能丢失、拥塞、排队、重传、不一定按序到达。即便有了 TCP 这样的协议，在网络硬件设备上同样是不可靠的。 不可靠的进程：系统可能部分节点正常工作，但是另一部分不能正常运行；由于编程语言的垃圾回收（GC），在虚拟化环境中的挂起（Suspend），操作系统的上下文切换，同步磁盘的访问等等不确定性和失效的可能，使得分布式系统难以调试，尤其是对于需要原子操作的场景，部分失效会带来很大的复杂性与挑战。 不可靠的时钟：由于通信不是即时的，网络延迟永远都在发生，不像单机系统中每个进程都有一个共同的时间。一种解决方案是设置时间服务器来同步时间（依旧存在网络可变延迟问题，而且同步后也未必精准）；使用 Spanner, TrueTime；或者使用 Lamport 的逻辑时钟来确定时序等。 2. The System Models All of which prove to be false in the long run and all of which cause big trouble and painful learning experiences.\n两将军问题（Two General\u0026rsquo;s Problem）：两支军队攻打同一个城堡，然而两个军队通信被山谷隔开，信息传递的信使随时可能被俘虏，而且两个军队需要同时达成进攻或者撤退的一致。这个问题已经被 Lamport 的论文 Solved Problems, Unsolved Problems and Non-Problems in Concurrency 证明无解，不过在工程上的解决方案即是 TCP 的三次握手。进一步说明，在一个分布式系统中，某个节点如果需要确认另一个节点的状态，唯一的方法就是进行可靠的消息通信。\n拜占庭将军问题（The Byzantine Generals Problem）：有 n 个拜占庭将军，他们希望在进攻/撤退上达成一致，将军们只能通过信使进行交流，但是信使可能有叛徒。Lamport 给出了最终证明，至少要 3n+1 个计算机，才能保证其中的 n 个“叛徒”不会阻碍正确操作的计算机达成共识。\n网络链路模型：在分布式系统中，网络永远都有可能发生分区（Network Partition），将网络进行抽象，只考虑点对点的链路，可大致分为\n可靠链路（Reliable Link or Perfect Link）：TCP 的编程模型，可以保证：消息传递一定可靠、没有重复消费、不会无中生有消息。 公平损失链路（Fair-Loss Link）：消息可能丢失、重复或者乱序，但是能保证消息最终一定会到达。 任意链路（Arbitrary Link）：最弱的编程模型，允许任意网络链路执行任意操作。使用 TLS 等加密技术可以转换成公平损失链路 节点故障模型：节点可能出现故障，主要包括以下三种\n崩溃-停止（fail-stop or crash-stop）：一个节点崩溃后永远不会恢复。 崩溃-恢复（fail-recover or crash-recovery）：允许节点重新启动后继续执行剩余的步骤。 拜占庭故障（Byzantine fault）：故障的节点不仅会偏离状态，还会恶意破坏系统。 消息传递模型：各个节点通过传递消息进行通信，常见解决方案如消息丢失-\u0026gt;重传，消息重复消费-\u0026gt;采用幂等方案。\n3. Distributed Clock 格林尼治标准时间（Greenwich Mean Time, GMT）：又称格林尼治平均时间，是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时，因为本初子午线被定义为通过那里的经线。\n世界协调时间（Coordinated Universal Time, UTC）：作为一种世界的时间标准，基于国际原子时，并通过不规则添加正负闰秒来抵消地球自转变量的影响，并在时刻上尽量接近格林尼治标准时间（Greenwich Mean Time, GMT）。\nUNIX 时间戳（Timestamp）：计算从 UTC 的 1970 年 1 月 1 日 0 时 0 分 0 秒开始经过的时间，当出现闰秒问题时，通过“降速”问题解决，即出现闰秒后，在未来的十小时内，刻意让时钟变慢来实现“加一秒”。\n夏时令（Daylight Saving Time, DST）：又称日光节约时制和夏令时间，是一种为节约能源而人为规定地方时间的制度；天亮早的夏季人为将时间调快一小时，可以使人早起早睡，减少照明量，以充分利用光照资源，从而节约照明用电；在中国并没有夏令时间。\n由于任何时钟都会出现走不准的问题，分布式系统中，所有服务器都使用本地时间不可避免会出现较多问题。\n3.1. Synchronization 网络时间协议（Network Time Protocol, NTP）：作为 UTC 在互联网中使用的一种标准，是一个典型的 C/S 架构，NTP 通过网络不停的纠正多个客户端的时间，由于网路和 CPU 的延迟，很多时候 NTP 客户端需要计算其时间偏移量和来回通信延迟。\n我们假设，以下 4 个时间节点：\nt0：客户端发起请求的 NTP 包 t1：服务端收到 NTP 包 t2：服务端发送返回 t3：客户端收到返回 其中网络延迟并不固定，为了更精确，只能得到往返延迟 $\\delta = (t_3 - t_0) - (t_2 - t_1)$。\nNTP 认为，RTT 的延迟除以 2 即是一趟消息的时间，这个延迟时间加上 t2 即可得到 NTP 客户端时间，称为客户端的时间偏移，用 $\\theta$ 表示，其中 $\\theta = t_2 + \\frac{\\delta}{2} = \\frac{(t_1 - t_0) + (t_2 - t_3)}{2}$。\n实际上，在具体实践上会更加复杂，往往需要客户端定期轮询 3 台甚至更多的服务器，做统计分析排除异常值，在公共网络下能保证几十毫秒的误差，而在局域网能够小于一毫秒。\n一般来说，一些语言或者 OS 会提供一种单调时钟（Monotonic Clock）来解决，如\n1 2 3 4 long startTime = System.nanoTime(); // Code to be timed goes here long endTime = System.nanoTime(); long elapsedTime = endTime - startTime; 1 2 3 #include \u0026lt;time.h\u0026gt; struct timespec tp; clock_gettime(CLOCK_MONOTONIC, \u0026amp;tp); // ignored error check 如上方法保证获取到的时钟一定是单调的，但是在分布式系统中，有着很大的局限性。 特别地，Golang 标准库中，time.Time 的结构默认包括了 hasMonotonic 选项，意味着其保存了 CLOCK_REALTIME 和 CLOCK_MONTONIC 两个值，即实际时间（Wall Time）和单调时钟（Monotonic Clock），对于测量和计算的操作，均采用单调时钟，不会受到 Wall Time 重置的影响，而 time.In、time.Local 和 time.UTC 等操作，返回的则是真实时间，会从结果中去除单调的读数。\n3.2. Logical Clock Lamport 发表的论文 Time, Clocks, and the Ordering of Events in a Distributed System 提出了逻辑时钟（Logical Clock）的概念，也称作 Lamport 时间戳（Lamport Timestamp）。\n爱因斯坦在狭义相对论（Special Relativity）给出的时间的定义：时空中没有一个不变的、确定的时间顺序，不同的观察者可能对两个事件的顺序各抒己见。Lamport 这里的逻辑时钟借鉴了相对论的定义，其论文描述为 \u0026ldquo;One of the mysteries of the universe is that it is possible to construct a system of physical clocks which, running quite independently of one another, will satisfy the Strong Clock Condition. We can therefore use physical clocks to eliminate anomalous behavior.\u0026rdquo;\nLamport 提出，我们的分布式系统或许并不一定需要一个绝对物理的时钟，而把时钟定义为“发生在之前”（Happens-Before）的关系，并使用如下定义：\na, b 事件属于一个进程，如果 a 发生先于 b，则 $a \\rightarrow b$ a 是发送消息的事件，b 是接受消息的事件，则 $a \\rightarrow b$ 如果有 $a \\rightarrow b, b \\rightarrow c$，则有 $a \\rightarrow c$ 如果事件 a, b 不能满足 $a \\rightarrow b$ 或是 $a \\rightarrow a$，则称 a, b 两个事件是并发的，记为 $a || b$ 逻辑时钟模型并不能得到完整的时间顺序（这也是相比物理时钟最大的缺点），逻辑时钟下的事件排序方式分为全序关系（Total Ordering）和偏序关系（Partial Ordering）。为了获取到系统的全序关系，我们需要在逻辑时钟上加上进程号，同时定义进程的全序关系，默认使用逻辑时钟排序。\n此外，本篇论文还提出了一个重要的思想：任何一个分布式系统都可以被描述为一个特定顺序的状态机，状态机不依赖于物理时钟，可以用来解决网络延迟，网络分区和容错等等问题。\n实际上，Paxos 的 Ballot、Raft 算法的 Term 都采用了逻辑时钟（原文：Terms acts as a logical clock in Raft）的思想。\n3.3. Vector Clock Colin J. Fidge 的论文 Timestamps in Message-Passing Systems That Preserve the Partial Ordering 和 Friedemann Mattern 的论文 Virtual Time and Global States of Distributed Systems 都提出了向量时钟（Vector Clock）的概念。相对于逻辑时钟，只有每个进程知道自己的时钟，没有其他进程的时钟，从而导致通过逻辑时钟无法计算出某些事件，必须指定另一个进程的优先级。\n向量时钟的每个进程都包含了整个系统的时钟，这样就无需指定其他进程的优先级，可以用于冲突场景的检测。\n在 N 个节点的分布式系统中，每个时钟的数据结构都是一个 N 维向量，表示为 $V_0, V_1, \u0026hellip;, V_{N-1}$，其中第 i 个节点的时钟表示为 $(V_{i}[0], V_{i}[1], \u0026hellip;, V_{i}[N-1])$，其中 $V_{i}[i]$ 代表进程自己的时钟。相比于逻辑时钟，向量时钟需要重新更新本地记录所有的节点的向量的时钟。向量时钟的更新逻辑为：\n所有进程的向量时钟的初始值都设置为 0 当进程 a 发生一个事件，则 $V_{a}[a] = V_{a}[a] + 1$ 进程 a 向 b 发送消息，a 先会 $V_{a}[a] = V_{a}[a] + 1$，再把自己本地的 $V_a$ 发给 b，b 再进行操作 $ V_{b}[i] = Max(V_{a}[i], V_{b}[i]), (i \\in [0, N-1]) $ 对于向量时钟，判断“发生在之前”的充要条件为：\n$(\\forall i \\in [0, N-1], V_{a}[i] \\le V_{b}[i]) \\wedge (\\exist j \\in [0, N-1], V_{a}[j] \\lt V_{b}[j]) \\Leftrightarrow a \\rightarrow b$ 此外，在分布式存储领域，还有一个算法称之为版本向量（Version Vector），其思想与向量时钟非常相似，不过其没有记录每个事件的向量时钟，只关心改变了数据副本的事件。\n每个进程的版本向量的初始值都设置为 0 当进程 a 发生更新事件的时候，$V_{i}[i] = V_{i}[i] + 1$ 当 a, b 两个进程消息通信的时候，$ V_{a}[i] = V_{b}[i] = Max(V_{a}[i], V_{b}[i]), (i \\in [0, N-1]) $ 相对于向量时钟，版本向量只在更新操作会增加时钟，且收发双方都要对向量进行同步。对于存储应用，每个数据项都对应一个版本，网络分区时，不同的版本向量可以帮助客户端识别需要解决的冲突数据。但是其缺点是随着向量维度很大，需要很大的存储空间，比较的时间也很长。\n4. Distributed Data No Silver Bullet: Essence and Accidents of Software Engineering\n4.1. Partitioning 垂直分区（Vertical Partitioning）：对列表进行拆分，将某些列拆到特定的分区，如将 TEXT 或者 BLOB 类型的放在单独的表中，保证完整性并提高性能。 水平分区（Horizontal Partitioning）：也称为分片（Sharding），即对行进行拆分，不同的行放入不同的表中，所有表的定义在每个分区中都能找到。 对于水平分区，主要有以下分区策略：\n范围分区（Range Partitioning）：根据关键字来将数据集拆分为多个范围 优点：实现简单，能用关键字范围查询，方便修改范围实现数据的增加或减少 缺点：无法对分区以外的键进行关键字查询，且查询范围较大且位于多个节点是性能较差，数据分布不均匀，容易造成尾部热点效应 哈希分区（Hash Partitioning）：将哈希值用于分区，使得分区相对均匀；但是无法实现范围查询，且不易增减节点\n一致性哈希（Consistent Hashing）：将整个哈希组织抽象成一个环，数据分区到的节点为按顺时针遇见的第一个节点上。当系统节点太少时，一个节点下线会给下一个顺时针方向上的节点增加大量的数据，可以通过虚拟节点（Virtual Node）方案解决，当一个节点失效或新的节点加入时，只需要重新映射它所对应的虚拟节点，而不需要重新映射所有的数据，从而减少了数据迁移的成本。此外，通过虚拟节点还可以刻意让数据产生偏斜，如让更好的机器承载更多的数据等。\n4.2. Replication 为了高可用，除了分区以外还可以使用复制技术，提高数据的可用性和安全性，通过多地数据中心可以减少往返时间（RTT），增加吞吐量。\n单主复制\n客户端的请求都走到主节点，其余副本都是从节点（Follower or Slave），从节点负责读请求，并与主节点同步最新的数据。其中的同步方式也可分为如下三种\n同步复制（Synchronous Replication）：所有从节点都写完成后才返回客户端成功 异步复制（Asynchronous Replication）：主节点完成请求后，立即告诉客户端成功 半同步复制（Semisynchronous Replication）：只需等待至少一个从节点写入成功后即可返回 优点：简单易于实现，通过拓展从节点可以提高读性能，仅仅需要对主节点进行并发操作，避免了处理从节点之间冲突的问题，适用于分布式事务。 缺点：主节点很可能造成性能瓶颈，主节点宕机时，从节点选主不是实时的，可能造成短暂的停机，可能出现脑裂（Split Brain）问题造成数据损坏。 多主复制\n多个节点都可以接受写请求，这意味着对与请求的顺序可能造成分歧造成数据不一致（数据冲突在单主复制也会出现，但是往往用主节点作为最终数据，处理并不复杂）。一般可以让客户端选择如何解决冲突、最后写入胜利（LWW, Last Write Wins）、因果关系跟踪（Happens Before）等手段解决。\n优点：增加了主节点的容错性，可以分担写负载，可以路由到不同主节点提高速度 缺点：解决数据冲突带来了复杂性，节点增加复杂 一般来说，多主复制数据冲突的复杂性远远大于其优点，一般只用于避免跨数据中心的写请求操作，路由到地理位置更近的中心提升写性能。\n无主复制\n让客户写请求发送到多个节点，一旦得到一部分的成功相应，即可判断这次请求成功了。客户的读请求也发到很多的节点，获取数据和数据的版本号，对比决定使用哪个数据。此外，为了让系统能够修复旧数据的不一致，还需要使用一解决方案，这里以 Dynamo 架构（Dynamo-Style）为例\n读修复（Read Repair）：客户端负责更新数据，检测到旧数据后，顺便发送写请求到旧数据所在的节点 反熵过程（Anti-Entropy Process）：建立一个后台进程来修复数据，进程找出错误的数据，并从存储了最新的数据的节点来将数据复制到错误的节点，不保证写的顺序，只保证最终的一致。如使用 Merkle Tree，也称哈希树（Hash Tree）来验证数据是否一致，将数据按关键字划分范围，每个范围计算出一个哈希值作为叶子节点，然后自底向上合并成整个树，如果两个树的根节点相同，则保证了叶子节点也相同，则不需要再检查，如果根节点不同，再往子节点中查找，直至发现不同。 法定人数（Quorum）机制：用于保证分布式系统中的数据冗余和最终一致性，用于多副本数据的一致性维护。在一个 N 个节点组成的系统中，要求至少 W 个节点写成功，且同时从 R 个节点中读取数据，只要保证 W + R \u0026gt; N \u0026amp;\u0026amp; W \u0026gt; N/2，即可保证 R 个返回值中一定有最新的数据，其中的 W \u0026gt; N/2 保证了数据的串行化修改，不能有两个写请求同时修改一份数据。这个机制在工程上的实践可以在读写性能上调节参数，以适配更好的读写负载。\n4.3. CAP Theorem CAP 定理，也称为布鲁尔定理（Brewer\u0026rsquo;s Theorem），指出在一个异步网络环境中，对于一个分布式读写的存储系统（Read-Write Storage System），只能满足一致性（Consistency, C）、可用性（Availability, A）、分区容错性（Partition Tolerance, P）三者中的两个。\nPACELC 定理，指出在分布式系统存在网络分区（Partition, P）的情况下，必须在可用性（Availability, A）和一致性（Consistency, C）中做出选择；否则（Else, E），系统在没有网络分区且正常运行的情况下，必须在延迟（Latency, L）和一致性（Consistency, C）之间做出选择。\nBASE（Basically Available, Soft State, Eventually Consistent）：Dan Pritchett 在论文 Base: An Acid Alternative 提出，BASE 是基本可用、软状态、最终一致的简写。指出存在网络分区的情况，为了高可用，可以暂时牺牲强一致，选择更弱的最终一致性。如经典的缓存一致性问题解决方案 Cache-Aside Pattern 就是采用的 BASE。\n4.4. Consistency Model 线性一致性（Linearizable Consistency or Linearizability）\n也称为强一致性（Strong Consistency），严格一致性（Strict Consistency），原子一致性（Atomic Consistency），立即一致性（Immediate Consistency）或者外部一致性（External Consistency）。\n严格定义：给定一个执行的历史，执行历史根据并发操作可以拓展为多个顺序历史（Sequential History），只要从中找到一个合法的历史，就说明该执行历史是线性一致的。\n线性一致性指的就是 CAP 定理中的一致性，也是最强的一致性模型，往往需要通过共识算法来实现，包括如何选取领导者，如何处理重复请求等；实现线性一致性需要花费很大的代价，同步原语和原子变量等都会增加系统开销。\n顺序一致性（Sequential Consistency）\n要求同一个客户端的操作在排序后的先后顺序不变，不同客户端之间的先后顺序是可以任意改变的。顺序一致与线性一致的主要区别在于只关注局部的顺序。现代 CPU 同样也不能保证顺序一致性，大多数情况下都会进行指令重排以达到更好的性能。\n因果一致性（Causal Consistency）\n相同的顺序看到因果相关的操作，而没有因果关系的并发操作可以乱序，如微信的取号器设计，使用全局唯一单调递增的用户 ID 来确认评论的顺序，每条评论的 ID 都比已经见过的全局 ID 要大，确保因果关系。\n4.5. Isolation Level ANSI SQL-92 标准定义了 4 种数据隔离的级别：\n脏写（Dirty Write）：一个事务覆盖了另一个还没提交事务的写入的值。会破坏完整性约束，大多数场景下都应该避免脏写。 脏读（Dirty Read）：一个事务读到了另一个还没提交的事务已经写入的值。 不可重复读（Non-Repeatable Read）：查询一个值两次，但是两次查询的返回不同，也称为模糊读（Ruzzy Read）。 幻读（Phantom Read）：一个事务进行条件查询时，另一个事务在中间插入或者删除了某些数据，即读到的数据变多了或者变少了。 此外还有一些异常情况：\n更新丢失（Lost Update）：指两个事务同时更新一个数值，最后两个更新只有一个能生效。 读偏斜（Read Skew）：读到了数据一致性约束被破坏的数据，一般指的应用层面，即 ACID 的 C 不满足。 写偏斜（Write Skew）：两个事务读到了相同的数据集，但是各自修改了不相关的数据集，导致一致性被破坏。 脏写 脏读 不可重复读 幻读 更新丢失 读偏斜 写偏斜 读未提交 ❌ ✅ ✅ ✅ ✅ ✅ ✅ 读已提交 ❌ ❌ ✅ ✅ ✅ ✅ ✅ 可重复读 ❌ ❌ ❌ ✅ ❌ ❌ ❌ 串行化 ❌ ❌ ❌ ❌ ❌ ❌ ❌ 这里的表格强依赖于具体实现，因数据库而异。\n需要说明的是，一致性模型和数据库的隔离模型主要区别在于，一致性模型一般用于单个对象，隔离级别通常设计多个操作的对象（并发场景）。\n5. Distributed Consensus 一个共识问题可以描述为：假设系统有 n 个节点，每个节点相互通信，设计一种逻辑，保证出现故障后仍然能够协商出某个不可以撤销的最终决定值，《Distributed Systems: An Algorithmic Approach》一书提出，共识算法应该满足如下三个性质：\n终止性（Termination）：所有正确的节点都会认同某一个值 协定性（Agreement）：所有争取的进程认同的值都是同一个值 完整性（Integrity）或有效性（Validity）：正确的节点倡议某个值 V，则所有的正确的节点都应该接受这个 V。 在分布式系统中，对于不可靠的网络、进程、时钟等问题，可归结为如下故障模型（Failure Models）表格\nType Description of server’s behavior Crash failure Halts, but is working correctly until it halts Omission failure(Receive omission Send omission ) 1.Fails to respond to incoming requests; 2.Fails to receive incoming messages; 3.Fails to send messages Timing failure Response lies outside a specified time interval Response failure (Value failure State-transition failure) 1. Response is incorrect; 2. The value of the response is wrong; 3. Deviates from the correct flow of control Arbitrary failure May produce arbitrary responses at arbitrary times 为了保证故障恢复，常采用一种复制状态机模型（Replicated State Machines）也称状态机复制（State Machine Replication, SMR）。为了实现复制状态机，通常使用多副本的日志（Replicated Log）系统，即多个进程看到的日志的顺序和内容都相同，那么他们的状态应该也能够相同，得到相同的输出。\n实现复制状态机的共识模型能够解决下述问题：\n网络：尽管在丢包、乱序、重复和延迟的场景下，也能确保返回正确的结果 时钟：状态机不再依赖于时钟 可用性：只要过半节点正常，便能保证集群完全可用，也可以容忍低于半数节点的故障。 此外，还能解决分布式系统的以下问题：\n互斥（Mutual Exclusion）：决定哪个进程优先访问临界资源，实现分布式锁等。 选主（Leader Election）：在故障时能够正常切换，在选出新领导上达成共识。 原子提交（Atomic Commit）：对于跨节点或跨分区的数据库，能够让事务在某些方面提交成功，某些方面失败。 Term Description Example Fault prevention Prevent the occurrence of a fault Don’t hire sloppy programmers Fault tolerance Build a component such that it can mask the occurrence of a fault Build each component by two independent programmers Fault removal Reduce the presence, number, or seriousness of a fault Get rid of sloppy programmers Fault forecasting Estimate current presence, future incidence, and consequences of faults Estimate how a recruiter is doing when it comes to hiring sloppy programmers 5.1. FLP Impossibility MICHAEL J. FISCHER, NANCY A. LYNCH 和 MICHAEL S. PATERSON 三个人发表的论文 Impossibility of Distributed Consensus with One Faulty Process，证明了在一个完全异步的系统中（进程可能很慢响应，无法分辨是速度很慢还是已经崩溃），即便只有一个节点发生了故障，也不存在一个算法使得系统达成共识。换句话说，如果系统的节点在宕机后无法恢复，那么不存在任何一种分布式协议可以达成共识。\n换一种类似于 CAP 定理的说法，即我们不可能在完全异步系统中同时满足安全性（Safety，所有正确的节点都认可同一个值）、活性（Liveness，最终认可一个确定值）和容错性（Fault Tolerance）。实际上，我们必须放宽对异步网络的假设绕过 FLP 不可能定理，找到工程最优解。常见的方案有三种：故障屏蔽（Fault Masking）、故障检测（Failure Detectors）、随机算法（Non-Determinism）。\n故障屏蔽（Fault Masking）\n将 FLP 不可能描述中的异步系统，转变为同步系统，即假设故障的进程最终一定能够恢复，并且找到一种重新加入到系统的方式。这样如果某个节点没有受到消息，便允许长时间的等待，直到收到期望的消息。\n自动重启的进程可以采用持久化的方案，如在 2PC 协议中，添加持久化的存储，让进程能够恢复到原来的状态。\n使用故障检测器（Failure Detectors）\n通过某种故障检测器来判断进程是否发生了异常，其中 Tushar Deepak Chandra、Vassos Hadzilacos 和 Sam Toueg 的论文 The Weakest Failure Detector for Solving Consensus* 提出了故障检测器应该满足的两个属性\n完全性（Completeness）：每个故障的进程都应该被一个正确的进程怀疑。 精确性（Accuracy）：正确的进程不应该被其他进程怀疑。 上述条件不一定要彻底实现，即便使用不完美的故障检测器，在通信可靠的情况下，失效的节点不超过 N/2 ，依然能使用来解决共识问题，即最终弱故障检测器（Eventually Weakly Failure Detector），应该满足如下两个性质。\n最终弱完全性（Eventually Weakly Complete）：每个故障的进程都会被一些正确的进程检测到 最终弱精确性（Eventually Weakly Accurate）：一段时间后，正确的进程不会被其他进程怀疑 随机算法（Non-Determinism）\n通过使用随机算法，使得拜占庭问题中的“叛徒”不能有效阻碍系统达成共识。如区块链应用中，基于最快运算结果节点而达成的共识，如 BitCoin 采用的 PoW（Proof of Work）达成的共识；以及 Ethereum、DASH、NEO 使用的 PoS（Proof of Stake）达成的共识等。当然，对于攻击者，囤积 50% 以上算力也可以发动 PoW 的女巫攻击（Sybil Attack），来击退网络上的大部分节点。\n5.2. Paxos Danny Dolev 和 H. Raymond Strong 发表的论文 Authenticated algorithms for byzantine agreement 提出了 Dolev-Strong 算法：在同步系统中，不超过 F 个进程发生故障，且错误的进程数量 F 小于总进程数 N，则经过 F+1 论消息传递后，一定能达成共识。生产环境中大多数共识算法都是基于同步实现，云服务环境在企业的数据中心也基本无需考虑拜占庭容错。\nPaxos 算法是 Lamport 在论文 The Part-Time Parliament 提出的共识算法。并最后重写了一个更加简短的论文 Paxos Made Simple。\n在 Paxos 岛上，每个议员都是分布式系统的节点，每个议员都会进行提案（Proposal）对应分布式系统的状态，包括提案编号（Proposal Number or Ballot）和提案值（Proposal Value）。\n每个议员都会通过消息传递不断提出提案，超过半数的节点同意，则最终使整个系统接受同一个提案，也称提案批准（Chosen）。一般来说 Paxos 算法有三个状态：\n提议者（Proposer）：提议者受到客户端的请求，则提出提案，试图让接受者接受并且在冲突时刻进行协调 接受者（Acceptor）：也称为投票者（Voter），即接受或拒绝提案，超过半数接受则为批准。 学习者（Learner）：学习者只能学习被批准的提案，不能参与提案的决议，客户端如果收到了接受者的同意，则学习者可以学习到提案值。 Basic Paxos\nBasic Paxos 只决议出一个共识的值，主要分为两个阶段，每个阶段分 ab 两个部分，记作 1a, 1b, 2a, 2b（其中 a, b 分别代表请求和相应两个阶段），下用 TLA+ 对 Paxos 算法进行简单描述。\nPhase1a，也称为 Prepare 阶段，发送一个类型为 1a 的消息，其中 ballot 标记为 b。\n1 2 Phase1a(b) == /\\ Send([type |-\u0026gt; \u0026#34;1a\u0026#34;, bal |-\u0026gt; b]) /\\ UNCHANGED \u0026lt;\u0026lt;maxBal, maxVBal, maxVal\u0026gt;\u0026gt; Phase1b，也称为 Promise 阶段，接受者对受到的 Prepare 消息进行判断\n如果 1a 消息的提案编号大于自己的，则返回 1b 消息，保证不会再接收编号小于当前 ballot 的提案；如果接受者已经接收了更早的提案号，则会响应前一次的 ballot 给提议者。 否则，忽略这个请求（也可以发送一个拒绝的响应）。 1 2 3 4 5 6 7 Phase1b(a) == /\\ \\E m \\in msgs : /\\ m.type = \u0026#34;1a\u0026#34; /\\ m.bal \u0026gt; maxBal[a] /\\ maxBal\u0026#39; = [maxBal EXCEPT ![a] = m.bal] /\\ Send([type |-\u0026gt; \u0026#34;1b\u0026#34;, acc |-\u0026gt; a, bal |-\u0026gt; m.bal, mbal |-\u0026gt; maxVBal[a], mval |-\u0026gt; maxVal[a]]) /\\ UNCHANGED \u0026lt;\u0026lt;maxVBal, maxVal\u0026gt;\u0026gt; Phase2a，也称为 Accept 或者 Propose 阶段，提议者收到过半数的响应后，提议者向多数派接受者发送 2a 请求，并且告诉他们 ballot 和 value。\n1 2 3 4 5 6 7 8 9 10 11 12 13 Phase2a(b, v) == /\\ ~ \\E m \\in msgs : m.type = \u0026#34;2a\u0026#34; /\\ m.bal = b /\\ \\E Q \\in Quorum : LET Q1b == {m \\in msgs : /\\ m.type = \u0026#34;1b\u0026#34; /\\ m.acc \\in Q /\\ m.bal = b} Q1bv == {m \\in Q1b : m.mbal \\geq 0} IN /\\ \\A a \\in Q : \\E m \\in Q1b : m.acc = a /\\ \\/ Q1bv = {} \\/ \\E m \\in Q1bv : /\\ m.mval = v /\\ \\A mm \\in Q1bv : m.mbal \\geq mm.mbal /\\ Send([type |-\u0026gt; \u0026#34;2a\u0026#34;, bal |-\u0026gt; b, val |-\u0026gt; v]) /\\ UNCHANGED \u0026lt;\u0026lt;maxBal, maxVBal, maxVal\u0026gt;\u0026gt; Phase2b，也称为 Accepted 阶段，接受者收到 2a 请求后，如果没有另外承诺比当前 ballot 更大的提案，则接受该提案，更新承诺的提案编号。\n1 2 3 4 5 6 7 Phase2b(a) == \\E m \\in msgs : /\\ m.type = \u0026#34;2a\u0026#34; /\\ m.bal \\geq maxBal[a] /\\ maxBal\u0026#39; = [maxBal EXCEPT ![a] = m.bal] /\\ maxVBal\u0026#39; = [maxVBal EXCEPT ![a] = m.bal] /\\ maxVal\u0026#39; = [maxVal EXCEPT ![a] = m.val] /\\ Send([type |-\u0026gt; \u0026#34;2b\u0026#34;, acc |-\u0026gt; a, bal |-\u0026gt; m.bal, val |-\u0026gt; m.val]) FLP 不可能定理对于 Paxos 也生效，很可能多个节点的 Prepare 消息源源不断，以至于谁都不能接受新的提案（活锁问题）。可以通过引入随机时间解决。\n5.3. Raft 斯坦福大学的 Diego Ongaro 和 John Ousterhout 发表的论文 In Search of an Understandable Consensus Algorithm (Extended Version)，提出的 Raft 算法，也可以理解为（R{eliable|eplicated|edundant} And Fault-Tolerant）。\nRaft 算法所运行的系统模型基于以下假设：\n服务器可能宕机、停止运行，过段时间恢复，但不存在拜占庭故障，节点行为非恶意。 消息可能丢失，乱序，重复；可能有网络分区，并在一段时间后恢复。 Raft 算法的服务器中任意服务器只能存在以下三个状态之一。\n1 2 3 4 5 const ( Follower = iota // 跟随者，完全被动处理请求，不主动发送 RPC Candidate // 候选者，是处于领导者和跟随者的中间状态 Leader // 领导者，负责处理所有客户请求和日志复制 ) 以下是使用 TLA+ 对算法的描述，对应的代码片段来源于 https://github.com/ongardie/raft.tla，遵循 CC BY-NC-SA 4.0 协议。\n当节点在超时时间没有收到任期更大的 RPC 请求，则认为当前 Leader 宕机，该节点会更改自己的任期，开始竞选时，设置当前状态为 Candiate，发起索要投票的请求。 当节点收到了半数以上选票时，则可以当选为 Leader。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 \\* Server i times out and starts a new election. Timeout(i) == /\\ state[i] \\in {Follower, Candidate} /\\ state\u0026#39; = [state EXCEPT ![i] = Candidate] /\\ currentTerm\u0026#39; = [currentTerm EXCEPT ![i] = currentTerm[i] + 1] \\* Most implementations would probably just set the local vote \\* atomically, but messaging localhost for it is weaker. /\\ votedFor\u0026#39; = [votedFor EXCEPT ![i] = Nil] /\\ votesResponded\u0026#39; = [votesResponded EXCEPT ![i] = {}] /\\ votesGranted\u0026#39; = [votesGranted EXCEPT ![i] = {}] /\\ voterLog\u0026#39; = [voterLog EXCEPT ![i] = [j \\in {} |-\u0026gt; \u0026lt;\u0026lt;\u0026gt;\u0026gt;]] /\\ UNCHANGED \u0026lt;\u0026lt;messages, leaderVars, logVars\u0026gt;\u0026gt; \\* Candidate i sends j a RequestVote request. RequestVote(i, j) == /\\ state[i] = Candidate /\\ j \\notin votesResponded[i] /\\ Send([mtype |-\u0026gt; RequestVoteRequest, mterm |-\u0026gt; currentTerm[i], mlastLogTerm |-\u0026gt; LastTerm(log[i]), mlastLogIndex |-\u0026gt; Len(log[i]), msource |-\u0026gt; i, mdest |-\u0026gt; j]) /\\ UNCHANGED \u0026lt;\u0026lt;serverVars, candidateVars, leaderVars, logVars\u0026gt;\u0026gt; \\* Candidate i transitions to leader. BecomeLeader(i) == /\\ state[i] = Candidate /\\ votesGranted[i] \\in Quorum /\\ state\u0026#39; = [state EXCEPT ![i] = Leader] /\\ nextIndex\u0026#39; = [nextIndex EXCEPT ![i] = [j \\in Server |-\u0026gt; Len(log[i]) + 1]] /\\ matchIndex\u0026#39; = [matchIndex EXCEPT ![i] = [j \\in Server |-\u0026gt; 0]] /\\ elections\u0026#39; = elections \\cup {[eterm |-\u0026gt; currentTerm[i], eleader |-\u0026gt; i, elog |-\u0026gt; log[i], evotes |-\u0026gt; votesGranted[i], evoterLog |-\u0026gt; voterLog[i]]} /\\ UNCHANGED \u0026lt;\u0026lt;messages, currentTerm, votedFor, candidateVars, logVars\u0026gt;\u0026gt; Raft 算法通过索引和任期号唯一标识一条日志记录，通过 AppendEntries 来复制日志。\n如果两个节点的日志在相同的索引位置上的任期号相同，则认为是一样的命令；如果当前的日志被提交，则之前的日志都判定为已提交。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 \\* Leader i sends j an AppendEntries request containing up to 1 entry. \\* While implementations may want to send more than 1 at a time, this spec uses \\* just 1 because it minimizes atomic regions without loss of generality. AppendEntries(i, j) == /\\ i /= j /\\ state[i] = Leader /\\ LET prevLogIndex == nextIndex[i][j] - 1 prevLogTerm == IF prevLogIndex \u0026gt; 0 THEN log[i][prevLogIndex].term ELSE 0 \\* Send up to 1 entry, constrained by the end of the log. lastEntry == Min({Len(log[i]), nextIndex[i][j]}) entries == SubSeq(log[i], nextIndex[i][j], lastEntry) IN Send([mtype |-\u0026gt; AppendEntriesRequest, mterm |-\u0026gt; currentTerm[i], mprevLogIndex |-\u0026gt; prevLogIndex, mprevLogTerm |-\u0026gt; prevLogTerm, mentries |-\u0026gt; entries, \\* mlog is used as a history variable for the proof. \\* It would not exist in a real implementation. mlog |-\u0026gt; log[i], mcommitIndex |-\u0026gt; Min({commitIndex[i], lastEntry}), msource |-\u0026gt; i, mdest |-\u0026gt; j]) /\\ UNCHANGED \u0026lt;\u0026lt;serverVars, candidateVars, leaderVars, logVars\u0026gt;\u0026gt; Paxos vs Raft\nHeidi Howard 和 Richard Mortier 发表了论文 Paxos vs Raft: Have we reached consensus on distributed consensus? 比较了两者的区别与联系。当然，Chubby 的作者 Mike Burrows 也表明，There is only one consensus protocol, and that’s Paxos（所有的共识算法本质都是 Paxos）\n首先，两个算法的共同点在于：\n只有一个领导者，接受所有写请求，并且把日志发送给追随者 多数派复制了日志后，则该日志永久生效并作用于所有状态机 如果领导者失败了，多数派会重新选举出一个新的领导者 Raft 相比 Paxos 算法也存在诸多优点。Raft 优势在于：\n表现形式：Raft 更容易学习和理解，并且给出了相关的实现逻辑 简单性：Raft 按顺序提交日志，而 Paxos 不保证按顺序，需要额外的协议来实现。而且 Raft 的所有日志都有相同的索引任期和命令。 高效的选举算法：Raft 使用了更加高效率的选举算法，只需要简单比较 id 即可判断谁胜出。 按照 Heidi Howard 论文中的说法，实际上两者相差非常小，很多场景下的优化都是相通的。上交 IPADS 也发表了论文 On the parallels between Paxos and Raft, and how to port optimizations，表示近年来 Raft 算法已经成为了共识算法的优先选择。\n5.4. PBFT PBFT（Piratical Byzantine Fault Tolerance）是由 Miguel Castro 和 Barbara Liskov 提出的一种实用的拜占庭容错算法，也算是一种 Paxos 算法的一种变体，也称为 Byzantine Paxos 算法。。\n同步环境下，容错的要求是 N \u0026gt;= 3F + 1（其中 F 表示故障节点，N 表示总进程数），虽然存在拜占庭将军存在的解答，但是需要很高的复杂度 $O(N^F)$。而 PBFT 只需要 $O(N^2)$ 的信息交换量。\nPBFT 算法的核心思想是通过一个三阶段的消息交互过程来达成共识。具体来说，每个节点在提出一个新的交易或者区块后，会把这个请求发送给其他节点。然后，节点们会经过预准备、准备、提交三个阶段的消息交互，来达成共识并将这个交易或者区块记录到区块链上。\n由于其每个节点都需要经过许可（Permissioned）才能加入系统，这种区块链也称为许可链（Permissioned Blockchain）。被用于权益证明（Proof of Stake, POS）算法中，避免了节点恶意行为，也不需要挖矿浪费电力。\n6. Distributed Transactions Atomic Commit Protocol, ACP\n分布式事务的原子性通过原子提交协议（Atomic Commit Protocol, ACP）来实现，一般来说需要满足以下三个特性：\n协定性（Agreement）：所有进程都决议出同一个值，即所有进程要么同时提交，要么同时回滚 有效性（Validity）：所有进程都提交且没有异常，则最终整个系统都要提交事务，反之系统将终止事务 终止性（Termination）：又分为弱终止（Weak Termination）条件，即没有故障发生时，则所有进程最终都会作出决定；强终止（Strong Termination）条件也称非阻塞（Non-Blocking）条件，指的是，只有没有发生故障的那些进程最终才会作出决议。 6.1. 2PC 两阶段提交（Two-Phase Commit，2PC）有两个阶段组成，是最经典的 ACP 协议。PingCAP 联合创始人黄东旭在演讲中也表示，所有的分布式事务本质都是 2PC。\n2PC 的第一阶段称为准备阶段（Prepare Phase）或者投票阶段（Vote Phase）。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 第一阶段：协调者向所有参与者发送请求 func (c *CoordinatorNode) Phase1() bool { // 向所有参与者发送请求，这个操作可以并行处理 for _, p := range c.Participants { if !p.Prepare() { // 如果有任何一个参与者返回失败，则放弃本次事务 c.Abort() return false } } // 所有参与者都返回成功，则进入第二阶段 return true } // 参与者节点处理请求 func (p *ParticipantNode) Prepare() (ok bool) { // 处理协调者的请求，检查所需条件以及资源，并返回响应结果 // 如果处理成功，则返回 true，否则返回 false return } 第二阶段称为提交阶段（Commit Phase），要求上一阶段的所有参与者都回复“是”，则可以向所有参与者发送消息，表示本次提交，只要有一个参与者回复了“否”，则向所有参与者发送终止。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 // 第二阶段：协调者根据参与者的响应做出决策 func (c *CoordinatorNode) Phase2() bool { // 统计所有参与者的决策结果 trueCount := 0 falseCount := 0 for _, p := range c.Participants { if p.Decision() { trueCount++ } else { falseCount++ } } // 如果所有参与者都已提交，则本次事务成功 if trueCount == len(c.Participants) { c.Commit() return true } // 如果有任何一个参与者已回滚，则放弃本次事务 if falseCount \u0026gt; 0 { c.Abort() return false } // 如果有参与者尚未提交或回滚，则等待下一次协调 return false } 所有参与者和协调者都应该把事务相关的信息持久化，并且所有修改操作都应该实现 WAL。\n实际上 2PC 协议还存在诸多问题：\n阻塞问题：在第二阶段中，如果协调者节点挂掉或者网络出现故障，参与者节点会一直等待，导致整个系统阻塞。 单点故障问题：在第一阶段中，如果协调者节点挂掉，需要选举新的协调者节点来继续进行事务的处理，但是选举过程本身也可能出现故障，从而导致整个系统不可用。 数据一致性问题：在第二阶段中，如果有参与者节点出现故障，导致它无法提交或回滚，会导致数据出现不一致的情况。 性能问题：由于 2PC 需要进行两轮消息交互，因此它的性能比较低，尤其是在参与者节点较多的情况下，会导致消息传递的延迟和负载增加，进而影响整个系统的性能。 可用性问题：由于 2PC 需要协调者节点和参与者节点之间的密切协作，因此在分布式系统中，如果协调者节点或参与者节点的数量较多，会导致系统的可用性降低。 CockroachDB 提出了一种 Parallel Commits 的优化思路，在第一阶段，Coordinator 已经知道了是否应该提交事务，这个状态可以将第一阶段的返回直接交给客户端，并将第一阶段的节点状态写进全局事务的日志中，问题发生时，便可以查询其他节点处理异常情况。\n6.2. 3PC 3PC 主要用于解决 2PC 的提交协议阻塞性这一缺点而设计的，参与者不知道第一阶段的结果，3PC 则在 2PC 的两个阶段中间添加了一个预提交阶段（Prepare to Commit, Pre-Commit），这一阶段，协调者将第一轮的投票结果发送给所有参与者，这样如果运行时出现了故障，则可以从剩下的参与者中重新选举出协调者，新的协调者可以重新选择是提交还是终止。\n3PC 极易受到网络分区的影响，而且一次事务至少需要 3 次消息往返，增加了事务完成时间。大多数情况下还是选择使用 2PC 协议。\n6.3. Paxos Commit James Gray 和 Lamport 共同发表的论文 Consensus on Transaction Commit 提出了 Paxos 提交算法。该算法主要有三个角色\n1 2 3 4 5 const ( ResourceManager = iota // 资源管理者，集群中有 N 个资源管理者，每个都代表一组 Paxos 实例的提案发起者 Leader // 领导者，只有一个领导者，用于协调整个算法，由 Paxos 算法选举出来 Acceptors // 接受者，与资源管理者共同组成 Paxos 实例，RM 共享所有接受者，2F+1 接受者可以允许 F 个故障 ) 完整的提交流程如下：\n任意一个 RM 提交事务以后，发送 BeginCommit 给 Leader，请求提交该事务 Leader 收到消息后，向所有的 RM 发送 Prepare 消息 RM 收到 Prepare 消息后，如果条件允许事务提交，则向所有接受者发送带有提案编号与值为 Prepared 消息；反之发送 Aborted 消息；整个这个阶段也称为 Phase 2a 消息。 接受者收到 Phase 2a 消息后，如果没有更大提案编号的消息，则接受该消息，并回复提案编号与值的 Phase 2b 消息；如果已经有了提案编号更大的消息，则拒绝这条消息。 对于每一组 Paxos 实例，Leader 如果收到了 F+1 条同样编号的消息，根据 Paxos 算法，管理会选定这个值。 每一个 Paxos 实例都选择完毕后，Leader 最终确认，如果每个值都是 Prepared，代表一致认为这个值能被提交，Leader 向所有 RM 发送提交消息，RM 收到消息后对事务进行提交。 如果 Leader 发现有一个 RM 的值有一个是 Aborted，即有的 RM 希望终止事务，则领导者向所有 RM 发送中止消息，RM 收到消息后中止事务。 6.4. Saga Hector 和 Kenneth 发表的论文 SAGAS 定义了一种长活事务（Long-Lived Transaction, LLT），其本质由一连串子事务 T1、T2、\u0026hellip; Tn 组成，可以与其他事务交错运行，依然能够保证所有事务全部成功或者全部失败，每个子事务 Tx 都对应一个补偿的事务 Cx ，补偿事务在回滚的时候执行。\n6.5. Percolator Google 发表的论文 Large-scale Incremental Processing Using Distributed Transactions and Notifications 介绍了 Percolator 的架构设计。Percolator 依赖于一个单点授时，单时间源的授时服务，称为 TSO（Timestamp Oracle），用于给事务分配时间戳，以下是对论文中代码的解释。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 class Transaction { struct Write { Row row; Column col; string value; }; vector\u0026lt;Write\u0026gt; writes_; int start_ts_; // 1. 分配分布式开始的时间戳，开始的时间戳决定了该事务看到的版本 Transaction(): start_ts_(oracle.GetTimestamp()) {} // 2. 对于写操作，将操作放入 Buffer，直到提交才一并写入 void Set(Write w) { writes_.push_back(w); } // 2. 对于读操作，按照快照隔离的要求，允许 start_ts_ 后的事务拥有行锁 bool Get(Row row, Column c, string *value) { while (true) { bigtable::Txn T = bigtable::StartRowTransaction(row); // Check for locks that signal concurrent writes. if (T.Read(row, c + \u0026#34;lock\u0026#34;, [0, start_ts_])) { // There is a pending lock; try to clean it and wait BackoffAndMaybeCleanupLock(row, c); continue; } // Find the latest write below our start timestamp. latest_write = T.Read(row, c + \u0026#34;write\u0026#34;, [0, start_ts_]); if (!latest_write.found()) return false; // no data int data_ts = latest_write.start_timestamp(); *value = T.Read(row, c + \u0026#34;data\u0026#34;, [data_ts, data_ts]); return true; } } // 3. 预写（PreWrite）阶段，即 2PC 的第一阶段。 // 选择一个写操作作为主（Primary）锁，其他操作作为次（Second）锁 // 理论上，主锁可以随便选，论文中选择了第一个写操作 // Prewrite tries to lock cell w, returning false in case of conflict. bool Prewrite(Write w, Write primary) { Column c = w.col; // 启动一个 Bigtable 的单行事务 bigtable::Txn T = bigtable::StartRowTransaction(w.row); // 检查当前写操作所涉及的所有 write 元数据信息，如果有其他事务在当前事务之后 // 即时间戳为 [start, +∞)，如果有，则立即终止当前事务，反之则可以下一步 // Abort on writes after our start timestamp . . . if (T.Read(w.row, c + \u0026#34;write\u0026#34;, [start_ts_, ∞])) return false; // 检查当前写操作所涉及的 Lock 列，检查是否有其他事务持有当前行的锁 // 如果已经有锁，Percolator 不会等锁，而是直接终止事务 // . . . or locks at any timestamp. if (T.Read(w.row, c + \u0026#34;lock\u0026#34;, [0, ∞])) return false; // 顺利通过检查以后，开始更新数据，以开始的时间戳作为 Bigtable 的时间戳 // 这里的 Write 不会覆盖元数据，而是 Append 操作 T.Write(w.row, c + \u0026#34;data\u0026#34;, start_ts_, w.value); // 数据更新以后，获取对应的行锁，以 primary 的 row 和 col 写入 lock 列 T.Write(w.row, c + \u0026#34;lock\u0026#34;, start_ts_, {primary.row, primary.col}); // The primary’s location. return T.Commit(); } // 4. 提交事务 bool Commit() { Write primary = writes_[0]; vector\u0026lt;Write\u0026gt; secondaries(writes_.begin() + 1, writes_.end()); if (!Prewrite(primary, primary)) return false; for (Write w : secondaries) if (!Prewrite(w, primary)) return false; // 获取提交的时间戳 int commit_ts = oracle.GetTimestamp(); // Commit primary first. Write p = primary; bigtable::Txn T = bigtable::StartRowTransaction(p.row); // 对于主锁启动的单行事务，检查事务是否持有 lock 列的锁，如果检查失败，则终止事务 if (!T.Read(p.row, p.col + \u0026#34;lock\u0026#34;, [start_ts_, start_ts_])) return false; // aborted while working // 如果事务仍然持有锁，以提交时间戳作为 BigTable 的时间戳，让该数据对其他事务可见 T.Write(p.row, p.col + \u0026#34;write\u0026#34;, commit_ts, start_ts_); // Pointer to data written at start ts. // 释放主锁，检查主锁的写操作是否对其他事务可见，如果失败则终止事务 T.Erase(p.row, p.col + \u0026#34;lock\u0026#34;, commit_ts); if (!T.Commit()) return false; // commit point // 主锁的写操作提交以后，Percolator 认为整个事务已经完成，进入第二阶段 // 所有次锁的写操作全都可以更新，这个操作可以异步执行 // Second phase: write out write records for secondary cells. for (Write w : secondaries) { bigtable::Write(w.row, w.col + \u0026#34;write\u0026#34;, commit ts, start ts); bigtable::Erase(w.row, w.col + \u0026#34;lock\u0026#34;, commit ts); } return true; } }; // class Transaction Percolator 优点在于松耦合，但是由于单点授时的服务，并且需要与授时服务通信两次，所以很容易成为性能瓶颈。\n","permalink":"https://chasing1020.github.io/post/distributed-systems/","summary":"\u003ch1 id=\"1-overview\"\u003e1. Overview\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://lamport.azurewebsites.net/pubs/distributed-system.txt\"\u003eA distributed system is one in which the failure of a coumputer you didn\u0026rsquo;t even know existed can render your own computer unusable.\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eUCB 团队在 2009 年发表的论文 \u003ca href=\"https://www2.eecs.berkeley.edu/Pubs/TechRpts/2009/EECS-2009-28.pdf\"\u003eAbove the Clouds: A Berkeley View of Cloud Computing\u003c/a\u003e，预测了云计算机的价值、演进和普及的进程；而 2019 年的论文 \u003ca href=\"https://www2.eecs.berkeley.edu/Pubs/TechRpts/2019/EECS-2019-3.pdf\"\u003eCloud Programming Simplified: A Berkeley View on Serverless Computing\u003c/a\u003e，则提出了一种新的猜测：“无服务会发展成未来云计算的主要形式”，无服务架构下的用户不再需要关心细节，而极大解放生产力。\u003c/p\u003e","title":"Distributed Systems"},{"content":"oh-my-zsh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 # ~/.zshrc #brew install --cask font-jetbrains-mono-nerd-font If you come from bash you might have to change your $PATH. # export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH # Path to your Oh My Zsh installation. export ZSH=\u0026#34;$HOME/.oh-my-zsh\u0026#34; # Set name of the theme to load --- if set to \u0026#34;random\u0026#34;, it will # load a random theme each time Oh My Zsh is loaded, in which case, # to know which specific one was loaded, run: echo $RANDOM_THEME # See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes ZSH_THEME=\u0026#34;robbyrussell\u0026#34; # Set list of themes to pick from when loading at random # Setting this variable when ZSH_THEME=random will cause zsh to load # a theme from this variable instead of looking in $ZSH/themes/ # If set to an empty array, this variable will have no effect. # ZSH_THEME_RANDOM_CANDIDATES=( \u0026#34;robbyrussell\u0026#34; \u0026#34;agnoster\u0026#34; ) # Uncomment the following line to use case-sensitive completion. # CASE_SENSITIVE=\u0026#34;true\u0026#34; # Uncomment the following line to use hyphen-insensitive completion. # Case-sensitive completion must be off. _ and - will be interchangeable. # HYPHEN_INSENSITIVE=\u0026#34;true\u0026#34; # Uncomment one of the following lines to change the auto-update behavior zstyle \u0026#39;:omz:update\u0026#39; mode disabled # disable automatic updates # zstyle \u0026#39;:omz:update\u0026#39; mode auto # update automatically without asking # zstyle \u0026#39;:omz:update\u0026#39; mode reminder # just remind me to update when it\u0026#39;s time # Uncomment the following line to change how often to auto-update (in days). # zstyle \u0026#39;:omz:update\u0026#39; frequency 13 # Uncomment the following line if pasting URLs and other text is messed up. # DISABLE_MAGIC_FUNCTIONS=\u0026#34;true\u0026#34; # Uncomment the following line to disable colors in ls. # DISABLE_LS_COLORS=\u0026#34;true\u0026#34; # Uncomment the following line to disable auto-setting terminal title. DISABLE_AUTO_TITLE=\u0026#34;true\u0026#34; # Uncomment the following line to enable command auto-correction. # ENABLE_CORRECTION=\u0026#34;true\u0026#34; # Uncomment the following line to display red dots whilst waiting for completion. # You can also set it to another string to have that shown instead of the default red dots. # e.g. COMPLETION_WAITING_DOTS=\u0026#34;%F{yellow}waiting...%f\u0026#34; # Caution: this setting can cause issues with multiline prompts in zsh \u0026lt; 5.7.1 (see #5765) # COMPLETION_WAITING_DOTS=\u0026#34;true\u0026#34; # Uncomment the following line if you want to disable marking untracked files # under VCS as dirty. This makes repository status check for large repositories # much, much faster. # DISABLE_UNTRACKED_FILES_DIRTY=\u0026#34;true\u0026#34; # Uncomment the following line if you want to change the command execution time # stamp shown in the history command output. # You can set one of the optional three formats: # \u0026#34;mm/dd/yyyy\u0026#34;|\u0026#34;dd.mm.yyyy\u0026#34;|\u0026#34;yyyy-mm-dd\u0026#34; # or set a custom format using the strftime function format specifications, # see \u0026#39;man strftime\u0026#39; for details. # HIST_STAMPS=\u0026#34;mm/dd/yyyy\u0026#34; # Would you like to use another custom folder than $ZSH/custom? # ZSH_CUSTOM=/path/to/new-custom-folder # Which plugins would you like to load? # Standard plugins can be found in $ZSH/plugins/ # Custom plugins may be added to $ZSH_CUSTOM/plugins/ # Example format: plugins=(rails git textmate ruby lighthouse) # Add wisely, as too many plugins slow down shell startup. plugins=( git zsh-autosuggestions zsh-syntax-highlighting ) source $ZSH/oh-my-zsh.sh # User configuration # export MANPATH=\u0026#34;/usr/local/man:$MANPATH\u0026#34; # You may need to manually set your language environment # export LANG=en_US.UTF-8 # Preferred editor for local and remote sessions # if [[ -n $SSH_CONNECTION ]]; then # export EDITOR=\u0026#39;vim\u0026#39; # else # export EDITOR=\u0026#39;nvim\u0026#39; # fi # Compilation flags # export ARCHFLAGS=\u0026#34;-arch $(uname -m)\u0026#34; # Set personal aliases, overriding those provided by Oh My Zsh libs, # plugins, and themes. Aliases can be placed here, though Oh My Zsh # users are encouraged to define aliases within a top-level file in # the $ZSH_CUSTOM folder, with .zsh extension. Examples: # - $ZSH_CUSTOM/aliases.zsh # - $ZSH_CUSTOM/macos.zsh # For a full list of active aliases, run `alias`. # # Example aliases # alias zshconfig=\u0026#34;mate ~/.zshrc\u0026#34; # alias ohmyzsh=\u0026#34;mate ~/.oh-my-zsh\u0026#34; export LANG=\u0026#34;en_US.UTF-8\u0026#34; export LC_ALL=\u0026#34;en_US.UTF-8\u0026#34; alias l=\u0026#34;eza -Hlahb --git\u0026#34; alias ll=\u0026#34;eza -Hlahb --git\u0026#34; eval \u0026#34;$(/opt/homebrew/bin/brew shellenv)\u0026#34; # Canonical hex dump; some systems have this symlinked command -v hd \u0026gt; /dev/null || alias hd=\u0026#34;hexdump -C\u0026#34; # macOS has no `md5sum`, so use `md5` as a fallback command -v md5sum \u0026gt; /dev/null || alias md5sum=\u0026#34;md5\u0026#34; # macOS has no `sha1sum`, so use `shasum` as a fallback command -v sha1sum \u0026gt; /dev/null || alias sha1sum=\u0026#34;shasum\u0026#34; # Recursively delete `.DS_Store` files alias cleanup=\u0026#34;find . -type f -name \u0026#39;*.DS_Store\u0026#39; -ls -delete\u0026#34; # Print each PATH entry on a separate line alias path=\u0026#39;echo -e ${PATH//:/\\\\n}\u0026#39; # Extract archives -- usage: extract \u0026lt;file\u0026gt; function extract () { if [ -f $1 ] ; then case $1 in *.tar.bz2) tar xjf $1 ;; *.tar.gz) tar xzf $1 ;; *.bz2) bunzip2 $1 ;; *.rar) unrar e $1 ;; *.gz) gunzip $1 ;; *.tar) tar xf $1 ;; *.tbz2) tar xjf $1 ;; *.tgz) tar xzf $1 ;; *.zip) unzip \u0026#34;$1\u0026#34; ;; *.Z) uncompress $1 ;; *.7z) 7z x $1 ;; *) echo \u0026#34;\u0026#39;$1\u0026#39; cannot be extracted via extract()\u0026#34; ;; esac else echo \u0026#34;\u0026#39;$1\u0026#39; is not a valid file\u0026#34; fi } export HOMEBREW_NO_AUTO_UPDATE=1 export ZSH_WAKATIME_PROJECT_DETECTION=true alias uvs=\u0026#34;source .venv/bin/activate\u0026#34; alias uvv=\u0026#34;uv venv --python 3.12\u0026#34; alias uvi=\u0026#34;uv init\u0026#34; alias uva=\u0026#34;uv add\u0026#34; alias uvf=\u0026#34;uvx ruff format\u0026#34; alias uvp=\u0026#34;ux pip\u0026#34; alias python=\u0026#34;python3\u0026#34; alias pip=\u0026#34;pip3\u0026#34; # Make vim the default editor. export EDITOR=\u0026#39;vim\u0026#39; alias vi=\u0026#34;nvim\u0026#34; alias vim=\u0026#34;nvim\u0026#34; # Enable persistent REPL history for `node`. export NODE_REPL_HISTORY=~/.node_history; # Allow 32³ entries; the default is 1000. export NODE_REPL_HISTORY_SIZE=\u0026#39;32768\u0026#39;; # Use sloppy mode by default, matching web browsers. export NODE_REPL_MODE=\u0026#39;sloppy\u0026#39;; # Make Python use UTF-8 encoding for output to stdin, stdout, and stderr. export PYTHONIOENCODING=\u0026#39;UTF-8\u0026#39;; # Highlight section titles in manual pages. export LESS_TERMCAP_md=\u0026#34;${yellow}\u0026#34;; # Don’t clear the screen after quitting a manual page. #export MANPAGER=\u0026#39;less -X\u0026#39;; export MANPAGER=\u0026#34;sh -c \u0026#39;awk \u0026#39;\\\u0026#39;\u0026#39;{ gsub(/\\x1B\\[[0-9;]*m/, \\\u0026#34;\\\u0026#34;, \\$0); gsub(/.\\x08/, \\\u0026#34;\\\u0026#34;, \\$0); print }\u0026#39;\\\u0026#39;\u0026#39; | bat -p -lman\u0026#39;\u0026#34; # Avoid issues with `gpg` as installed via Homebrew. # https://stackoverflow.com/a/42265848/96656 export GPG_TTY=$(tty); # Hide the “default interactive shell is now zsh” warning on macOS. export BASH_SILENCE_DEPRECATION_WARNING=1; export PATH=$PATH:/Users/zjc/.local/bin eval \u0026#34;$(zoxide init --cmd cd zsh)\u0026#34; eval \u0026#34;$(starship init zsh)\u0026#34; eval \u0026#34;$(atuin init zsh --disable-up-arrow)\u0026#34; Alacritty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 # ~/.config/alacritty/alacritty.toml [colors] draw_bold_text_with_bright_colors = true [[keyboard.bindings]] chars = \u0026#34;\\u0002,\u0026#34; key = \u0026#34;Comma\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002s\u0026#34; key = \u0026#34;K\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00021\u0026#34; key = \u0026#34;Key1\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00022\u0026#34; key = \u0026#34;Key2\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00023\u0026#34; key = \u0026#34;Key3\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00024\u0026#34; key = \u0026#34;Key4\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00025\u0026#34; key = \u0026#34;Key5\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00026\u0026#34; key = \u0026#34;Key6\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00027\u0026#34; key = \u0026#34;Key7\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00028\u0026#34; key = \u0026#34;Key8\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00029\u0026#34; key = \u0026#34;Key9\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u00020\u0026#34; key = \u0026#34;Key0\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002L\u0026#34; key = \u0026#34;L\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002[\u0026#34; key = \u0026#34;LBracket\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002p\u0026#34; key = \u0026#34;LBracket\u0026#34; mods = \u0026#34;Command|Shift\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002%\u0026#34; key = \u0026#34;D\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002\\\u0026#34;\u0026#34; key = \u0026#34;D\u0026#34; mods = \u0026#34;Command|Shift\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002d\u0026#34; key = \u0026#34;W\u0026#34; mods = \u0026#34;Command|Shift\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002n\u0026#34; key = \u0026#34;RBracket\u0026#34; mods = \u0026#34;Command|Shift\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\u0026#34;\u0026#34; \\u001B:w \u0026#34;\u0026#34;\u0026#34; key = \u0026#34;S\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002c\u0026#34; key = \u0026#34;T\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002!\u0026#34; key = \u0026#34;T\u0026#34; mods = \u0026#34;Command|Shift\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002x\u0026#34; key = \u0026#34;X\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002z\u0026#34; key = \u0026#34;Z\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002\\u001B[D\u0026#34; key = \u0026#34;Left\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002\\u001B[C\u0026#34; key = \u0026#34;Right\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002\\u001B[B\u0026#34; key = \u0026#34;Down\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] chars = \u0026#34;\\u0002\\u001B[A\u0026#34; key = \u0026#34;Up\u0026#34; mods = \u0026#34;Command\u0026#34; [[keyboard.bindings]] action = \u0026#34;ScrollPageUp\u0026#34; key = \u0026#34;PageUp\u0026#34; mode = \u0026#34;~Alt\u0026#34; mods = \u0026#34;Shift\u0026#34; [[keyboard.bindings]] action = \u0026#34;ScrollPageDown\u0026#34; key = \u0026#34;PageDown\u0026#34; mode = \u0026#34;~Alt\u0026#34; mods = \u0026#34;Shift\u0026#34; [[keyboard.bindings]] action = \u0026#34;ScrollToTop\u0026#34; key = \u0026#34;Home\u0026#34; mode = \u0026#34;~Alt\u0026#34; mods = \u0026#34;Shift\u0026#34; [[keyboard.bindings]] action = \u0026#34;ScrollToBottom\u0026#34; key = \u0026#34;End\u0026#34; mode = \u0026#34;~Alt\u0026#34; mods = \u0026#34;Shift\u0026#34; [general] live_config_reload = true [terminal] [colors.bright] black = \u0026#34;0x676767\u0026#34; blue = \u0026#34;0x6871ff\u0026#34; cyan = \u0026#34;0x5ffdff\u0026#34; green = \u0026#34;0x78e364\u0026#34; magenta = \u0026#34;0xff76ff\u0026#34; red = \u0026#34;0xe4544e\u0026#34; white = \u0026#34;0xd0d0d0\u0026#34; yellow = \u0026#34;0xe5e152\u0026#34; [colors.cursor] cursor = \u0026#34;0xfffefe\u0026#34; text = \u0026#34;0xc7c7c7\u0026#34; [colors.normal] black = \u0026#34;0x000000\u0026#34; blue = \u0026#34;0x0225c7\u0026#34; cyan = \u0026#34;0x00c5c7\u0026#34; green = \u0026#34;0x00c200\u0026#34; magenta = \u0026#34;0xc930c7\u0026#34; red = \u0026#34;0xcf2004\u0026#34; white = \u0026#34;0xc7c7c7\u0026#34; yellow = \u0026#34;0xc7c400\u0026#34; [colors.primary] background = \u0026#34;0x000000\u0026#34; foreground = \u0026#34;0xc7c7c7\u0026#34; [cursor.style] blinking = \u0026#34;On\u0026#34; shape = \u0026#34;Beam\u0026#34; [cursor.vi_mode_style] shape = \u0026#34;Beam\u0026#34; [env] TERM = \u0026#34;xterm-256color\u0026#34; [font] size = 14.0 [font.normal] family = \u0026#34;JetBrainsMono Nerd Font Mono\u0026#34; style = \u0026#34;Regular\u0026#34; [scrolling] history = 100000 multiplier = 3 [selection] save_to_clipboard = false semantic_escape_chars = \u0026#39;\u0026#39;\u0026#39;,│`|:\u0026#34;\u0026#39; ()[]{}\u0026lt;\u0026gt;\t\u0026#39;\u0026#39;\u0026#39; [terminal.shell] args = [\u0026#34;new-session\u0026#34;, \u0026#34;-A\u0026#34;, \u0026#34;-D\u0026#34;, \u0026#34;-s\u0026#34;, \u0026#34;main\u0026#34;] program = \u0026#34;/opt/homebrew/bin/tmux\u0026#34; [window] decorations = \u0026#34;full\u0026#34; decorations_theme_variant = \u0026#34;Dark\u0026#34; dynamic_padding = true dynamic_title = true opacity = 0.97 title = \u0026#34;Alacritty\u0026#34; [window.dimensions] columns = 120 lines = 34 [keyboard] Starship 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 # ~/.config/starship.toml format = \u0026#34;\u0026#34;\u0026#34; [░▒▓](#a3aed2)\\ [  ](bg:#a3aed2 fg:#090c0c)\\ [](bg:#769ff0 fg:#a3aed2)\\ $directory\\ [](fg:#769ff0 bg:#394260)\\ $git_branch\\ $git_status\\ [](fg:#394260 bg:#212736)\\ $kubernetes\\ $c\\ $cmake\\ $golang\\ $python\\ $nodejs\\ $rust\\ $java\\ $conda\\ $direnv\\ $env_var\\ $sudo\\ $jobs\\ $battery\\ $status\\ $cmd_duration\\ [](fg:#212736 bg:#1d2230)\\ $time\\ [ ](fg:#1d2230)\\ \\n$character\u0026#34;\u0026#34;\u0026#34; [directory] style = \u0026#34;fg:#e3e5e5 bg:#769ff0\u0026#34; format = \u0026#34;[ $path ]($style)\u0026#34; truncation_length = 3 #truncation_symbol = \u0026#34;…/\u0026#34; [directory.substitutions] \u0026#34;Documents\u0026#34; = \u0026#34;󰈙 \u0026#34; \u0026#34;Downloads\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Music\u0026#34; = \u0026#34; \u0026#34; \u0026#34;Pictures\u0026#34; = \u0026#34; \u0026#34; [git_branch] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#394260\u0026#34; format = \u0026#39;[[ $symbol $branch ](fg:#769ff0 bg:#394260)]($style)\u0026#39; [git_status] style = \u0026#34;bg:#394260\u0026#34; format = \u0026#39;[[($all_status$ahead_behind )](fg:#769ff0 bg:#394260)]($style)\u0026#39; # format = \u0026#39;([$all_status$ahead_behind]($style))\u0026#39; conflicted = \u0026#39;🏳\u0026#39; ahead = \u0026#39;🏎💨\u0026#39; behind = \u0026#39;😪\u0026#39; diverged = \u0026#39;😵\u0026#39; up_to_date = \u0026#39;✅\u0026#39; untracked = \u0026#39;😞\u0026#39; stashed = \u0026#39;📦\u0026#39; modified = \u0026#39;📝\u0026#39; staged = \u0026#39;++\\($count\\)\u0026#39; renamed = \u0026#39;👅\u0026#39; deleted = \u0026#39;🗑 \u0026#39; [nodejs] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; [rust] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; [golang] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; [php] symbol = \u0026#34;\u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; [time] disabled = false time_format = \u0026#34;%R\u0026#34; # Hour:Minute Format style = \u0026#34;bg:#1d2230\u0026#34; format = \u0026#39;[[  $time ](fg:#a0a9cb bg:#1d2230)]($style)\u0026#39; [line_break] disabled = false [username] show_always = true style_user = \u0026#34;bg:#2e3440\u0026#34; style_root = \u0026#34;bg:#9A348E\u0026#34; format = \u0026#39;[$user]($style)\u0026#39; disabled = false [os] style = \u0026#34;bg:#9A348E\u0026#34; disabled = true # Disabled by default [c] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; [python] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[[ ${symbol}${pyenv_prefix}(${version})(\\($virtualenv\\)) ](fg:#769ff0 bg:#212736)]($style)\u0026#39; # format = \u0026#39;\\[[${symbol}${pyenv_prefix}(${version})(\\($virtualenv\\))]($style)\\]\u0026#39; [docker_context] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#5e81ac\u0026#34; format = \u0026#39;[ $symbol $context ]($style) $path\u0026#39; [elixir] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [elm] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [haskell] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [java] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [julia] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [nim] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [scala] symbol = \u0026#34; \u0026#34; style = \u0026#34;bg:#212736\u0026#34; format = \u0026#39;[ $symbol ($version) ]($style)\u0026#39; [character] # success_symbol = \u0026#39;[ ➜](#6272a4)\u0026#39; # error_symbol = \u0026#39;[ ➜](bold red)\u0026#39; success_symbol = \u0026#39;[ \u0026gt;](#6272a4)\u0026#39; error_symbol = \u0026#39;[ \u0026gt;](bold red)\u0026#39; #style = \u0026#34;bg:#6272a4\u0026#34; [cmd_duration] min_time = 500 style = \u0026#34;bg:#212736\u0026#34; # format = \u0026#39; [$duration](#6272a4)\u0026#39; format = \u0026#39;[ $duration]($style)\u0026#39; tmux 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 set -g mouse on set -g base-index 1 setw -g pane-base-index 1 set -g visual-activity off set -g visual-bell off set -g visual-silence off setw -g monitor-activity off set -g bell-action none # modes setw -g clock-mode-colour colour5 setw -g mode-style \u0026#39;fg=colour1 bg=colour18 bold\u0026#39; # panes set -g pane-border-style \u0026#39;fg=colour19 bg=colour0\u0026#39; set -g pane-active-border-style \u0026#39;bg=colour0 fg=colour9\u0026#39; # statusbar set -g status-position bottom set -g status-justify left set -g status-style \u0026#39;bg=colour18 fg=colour137 dim\u0026#39; set -g status-left \u0026#39;\u0026#39; set -g status-right \u0026#39;#[fg=colour233,bg=colour19] %d/%m #[fg=colour233,bg=colour8] %H:%M:%S \u0026#39; set -g status-right-length 50 set -g status-left-length 20 # window setw -g window-status-current-style \u0026#39;fg=colour1 bg=colour19 bold\u0026#39; setw -g window-status-current-format \u0026#39; #I#[fg=colour249]:#[fg=colour255]#W#[fg=colour249]#F \u0026#39; setw -g window-status-style \u0026#39;fg=colour9 bg=colour18\u0026#39; setw -g window-status-format \u0026#39; #I#[fg=colour237]:#[fg=colour250]#W#[fg=colour244]#F \u0026#39; setw -g window-status-bell-style \u0026#39;fg=colour255 bg=colour1 bold\u0026#39; # messages set -g message-style \u0026#39;fg=colour232 bg=colour16 bold\u0026#39; ","permalink":"https://chasing1020.github.io/post/dotfiles/","summary":"\u003ch2 id=\"oh-my-zsh\"\u003eoh-my-zsh\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e  1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 39\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 40\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 41\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 42\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 43\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 44\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 45\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 46\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 47\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 48\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 49\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 50\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 51\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 52\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 53\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 54\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 55\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 56\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 57\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 58\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 59\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 60\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 61\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 62\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 63\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 64\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 65\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 66\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 67\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 68\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 69\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 70\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 71\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 72\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 73\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 74\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 75\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 76\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 77\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 78\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 79\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 80\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 81\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 82\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 83\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 84\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 85\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 86\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 87\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 88\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 89\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 90\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 91\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 92\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 93\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 94\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 95\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 96\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 97\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 98\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 99\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e100\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e101\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e102\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e103\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e104\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e105\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e106\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e107\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e108\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e109\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e110\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e111\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e112\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e113\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e114\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e115\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e116\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e117\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e118\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e119\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e120\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e121\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e122\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e123\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e124\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e125\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e126\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e127\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e128\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e129\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e130\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e131\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e132\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e133\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e134\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e135\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e136\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e137\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e138\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e139\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e140\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e141\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e142\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e143\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e144\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e145\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e146\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e147\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e148\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e149\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e150\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e151\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e152\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e153\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e154\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e155\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e156\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e157\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e158\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e159\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e160\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e161\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e162\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e163\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e164\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e165\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e166\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e167\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e168\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e169\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e170\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e171\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e172\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e173\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e174\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e175\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e176\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e177\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e178\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e179\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e180\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e181\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e182\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e183\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e184\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e185\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e186\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e187\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e188\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e189\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e190\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e191\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e192\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e193\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e194\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e195\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e196\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e197\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e198\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e199\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e200\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e201\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e202\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e203\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e204\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e205\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e206\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e207\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e208\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ~/.zshrc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#brew install --cask font-jetbrains-mono-nerd-font If you come from bash you might have to change your $PATH.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Path to your Oh My Zsh installation.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eZSH\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$HOME\u003c/span\u003e\u003cspan class=\"s2\"\u003e/.oh-my-zsh\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Set name of the theme to load --- if set to \u0026#34;random\u0026#34;, it will\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# load a random theme each time Oh My Zsh is loaded, in which case,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# to know which specific one was loaded, run: echo $RANDOM_THEME\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eZSH_THEME\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;robbyrussell\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Set list of themes to pick from when loading at random\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Setting this variable when ZSH_THEME=random will cause zsh to load\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# a theme from this variable instead of looking in $ZSH/themes/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# If set to an empty array, this variable will have no effect.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ZSH_THEME_RANDOM_CANDIDATES=( \u0026#34;robbyrussell\u0026#34; \u0026#34;agnoster\u0026#34; )\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to use case-sensitive completion.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# CASE_SENSITIVE=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to use hyphen-insensitive completion.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Case-sensitive completion must be off. _ and - will be interchangeable.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# HYPHEN_INSENSITIVE=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment one of the following lines to change the auto-update behavior\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ezstyle \u003cspan class=\"s1\"\u003e\u0026#39;:omz:update\u0026#39;\u003c/span\u003e mode disabled  \u003cspan class=\"c1\"\u003e# disable automatic updates\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# zstyle \u0026#39;:omz:update\u0026#39; mode auto      # update automatically without asking\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# zstyle \u0026#39;:omz:update\u0026#39; mode reminder  # just remind me to update when it\u0026#39;s time\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to change how often to auto-update (in days).\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# zstyle \u0026#39;:omz:update\u0026#39; frequency 13\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line if pasting URLs and other text is messed up.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# DISABLE_MAGIC_FUNCTIONS=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to disable colors in ls.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# DISABLE_LS_COLORS=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to disable auto-setting terminal title.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eDISABLE_AUTO_TITLE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to enable command auto-correction.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ENABLE_CORRECTION=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line to display red dots whilst waiting for completion.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# You can also set it to another string to have that shown instead of the default red dots.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# e.g. COMPLETION_WAITING_DOTS=\u0026#34;%F{yellow}waiting...%f\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Caution: this setting can cause issues with multiline prompts in zsh \u0026lt; 5.7.1 (see #5765)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# COMPLETION_WAITING_DOTS=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line if you want to disable marking untracked files\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# under VCS as dirty. This makes repository status check for large repositories\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# much, much faster.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# DISABLE_UNTRACKED_FILES_DIRTY=\u0026#34;true\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Uncomment the following line if you want to change the command execution time\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# stamp shown in the history command output.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# You can set one of the optional three formats:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# \u0026#34;mm/dd/yyyy\u0026#34;|\u0026#34;dd.mm.yyyy\u0026#34;|\u0026#34;yyyy-mm-dd\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# or set a custom format using the strftime function format specifications,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# see \u0026#39;man strftime\u0026#39; for details.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# HIST_STAMPS=\u0026#34;mm/dd/yyyy\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Would you like to use another custom folder than $ZSH/custom?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ZSH_CUSTOM=/path/to/new-custom-folder\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Which plugins would you like to load?\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Standard plugins can be found in $ZSH/plugins/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Custom plugins may be added to $ZSH_CUSTOM/plugins/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Example format: plugins=(rails git textmate ruby lighthouse)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Add wisely, as too many plugins slow down shell startup.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eplugins\u003c/span\u003e\u003cspan class=\"o\"\u003e=(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\tgit\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\tzsh-autosuggestions\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\tzsh-syntax-highlighting\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003esource\u003c/span\u003e \u003cspan class=\"nv\"\u003e$ZSH\u003c/span\u003e/oh-my-zsh.sh\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# User configuration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# export MANPATH=\u0026#34;/usr/local/man:$MANPATH\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# You may need to manually set your language environment\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# export LANG=en_US.UTF-8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Preferred editor for local and remote sessions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# if [[ -n $SSH_CONNECTION ]]; then\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   export EDITOR=\u0026#39;vim\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# else\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#   export EDITOR=\u0026#39;nvim\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# fi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Compilation flags\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# export ARCHFLAGS=\u0026#34;-arch $(uname -m)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Set personal aliases, overriding those provided by Oh My Zsh libs,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# plugins, and themes. Aliases can be placed here, though Oh My Zsh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# users are encouraged to define aliases within a top-level file in\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# the $ZSH_CUSTOM folder, with .zsh extension. Examples:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# - $ZSH_CUSTOM/aliases.zsh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# - $ZSH_CUSTOM/macos.zsh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# For a full list of active aliases, run `alias`.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Example aliases\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# alias zshconfig=\u0026#34;mate ~/.zshrc\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# alias ohmyzsh=\u0026#34;mate ~/.oh-my-zsh\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eLANG\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;en_US.UTF-8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eLC_ALL\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;en_US.UTF-8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003el\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eza -Hlahb --git\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003ell\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eza -Hlahb --git\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eeval\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003e/opt/homebrew/bin/brew shellenv\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Canonical hex dump; some systems have this symlinked\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecommand\u003c/span\u003e -v hd \u0026gt; /dev/null \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003ehd\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;hexdump -C\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# macOS has no `md5sum`, so use `md5` as a fallback\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecommand\u003c/span\u003e -v md5sum \u0026gt; /dev/null \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003emd5sum\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;md5\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# macOS has no `sha1sum`, so use `shasum` as a fallback\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ecommand\u003c/span\u003e -v sha1sum \u0026gt; /dev/null \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003esha1sum\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;shasum\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Recursively delete `.DS_Store` files\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003ecleanup\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;find . -type f -name \u0026#39;*.DS_Store\u0026#39; -ls -delete\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Print each PATH entry on a separate line\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;echo -e ${PATH//:/\\\\n}\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Extract archives -- usage: extract \u0026lt;file\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efunction\u003c/span\u003e extract \u003cspan class=\"o\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e -f \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003ethen\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e in\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.tar.bz2\u003cspan class=\"o\"\u003e)\u003c/span\u003e tar xjf \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.tar.gz\u003cspan class=\"o\"\u003e)\u003c/span\u003e tar xzf \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.bz2\u003cspan class=\"o\"\u003e)\u003c/span\u003e bunzip2 \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.rar\u003cspan class=\"o\"\u003e)\u003c/span\u003e unrar e \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.gz\u003cspan class=\"o\"\u003e)\u003c/span\u003e gunzip \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.tar\u003cspan class=\"o\"\u003e)\u003c/span\u003e tar xf \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.tbz2\u003cspan class=\"o\"\u003e)\u003c/span\u003e tar xjf \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.tgz\u003cspan class=\"o\"\u003e)\u003c/span\u003e tar xzf \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.zip\u003cspan class=\"o\"\u003e)\u003c/span\u003e unzip \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$1\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.Z\u003cspan class=\"o\"\u003e)\u003c/span\u003e uncompress \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *.7z\u003cspan class=\"o\"\u003e)\u003c/span\u003e 7z x \u003cspan class=\"nv\"\u003e$1\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      *\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#39;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$1\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#39; cannot be extracted via extract()\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eesac\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eecho\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#39;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$1\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#39; is not a valid file\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003efi\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eHOMEBREW_NO_AUTO_UPDATE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eZSH_WAKATIME_PROJECT_DETECTION\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nb\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euvs\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;source .venv/bin/activate\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euvv\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;uv venv --python 3.12\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euvi\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;uv init\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euva\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;uv add\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euvf\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;uvx ruff format\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003euvp\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ux pip\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003epython\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;python3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003epip\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;pip3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Make vim the default editor.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eEDITOR\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;vim\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003evi\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;nvim\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003ealias\u003c/span\u003e \u003cspan class=\"nv\"\u003evim\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;nvim\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Enable persistent REPL history for `node`.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eNODE_REPL_HISTORY\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e~/.node_history\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Allow 32³ entries; the default is 1000.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eNODE_REPL_HISTORY_SIZE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;32768\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Use sloppy mode by default, matching web browsers.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eNODE_REPL_MODE\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;sloppy\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Make Python use UTF-8 encoding for output to stdin, stdout, and stderr.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003ePYTHONIOENCODING\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;UTF-8\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Highlight section titles in manual pages.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eLESS_TERMCAP_md\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003eyellow\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Don’t clear the screen after quitting a manual page.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#export MANPAGER=\u0026#39;less -X\u0026#39;;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eMANPAGER\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;sh -c \u0026#39;awk \u0026#39;\\\u0026#39;\u0026#39;{ gsub(/\\x1B\\[[0-9;]*m/, \\\u0026#34;\\\u0026#34;, \\$0); gsub(/.\\x08/, \\\u0026#34;\\\u0026#34;, \\$0); print }\u0026#39;\\\u0026#39;\u0026#39; | bat -p -lman\u0026#39;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Avoid issues with `gpg` as installed via Homebrew.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# https://stackoverflow.com/a/42265848/96656\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eGPG_TTY\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003etty\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Hide the “default interactive shell is now zsh” warning on macOS.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003eBASH_SILENCE_DEPRECATION_WARNING\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e1\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eexport\u003c/span\u003e \u003cspan class=\"nv\"\u003ePATH\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nv\"\u003e$PATH\u003c/span\u003e:/Users/zjc/.local/bin\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eeval\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003ezoxide init --cmd \u003cspan class=\"nb\"\u003ecd\u003c/span\u003e zsh\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eeval\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003estarship init zsh\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eeval\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eatuin init zsh --disable-up-arrow\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"alacritty\"\u003eAlacritty\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e  1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 39\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 40\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 41\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 42\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 43\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 44\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 45\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 46\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 47\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 48\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 49\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 50\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 51\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 52\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 53\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 54\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 55\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 56\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 57\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 58\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 59\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 60\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 61\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 62\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 63\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 64\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 65\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 66\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 67\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 68\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 69\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 70\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 71\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 72\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 73\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 74\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 75\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 76\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 77\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 78\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 79\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 80\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 81\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 82\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 83\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 84\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 85\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 86\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 87\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 88\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 89\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 90\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 91\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 92\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 93\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 94\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 95\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 96\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 97\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 98\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 99\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e100\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e101\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e102\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e103\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e104\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e105\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e106\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e107\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e108\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e109\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e110\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e111\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e112\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e113\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e114\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e115\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e116\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e117\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e118\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e119\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e120\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e121\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e122\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e123\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e124\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e125\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e126\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e127\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e128\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e129\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e130\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e131\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e132\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e133\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e134\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e135\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e136\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e137\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e138\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e139\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e140\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e141\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e142\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e143\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e144\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e145\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e146\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e147\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e148\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e149\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e150\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e151\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e152\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e153\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e154\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e155\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e156\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e157\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e158\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e159\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e160\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e161\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e162\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e163\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e164\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e165\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e166\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e167\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e168\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e169\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e170\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e171\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e172\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e173\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e174\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e175\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e176\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e177\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e178\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e179\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e180\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e181\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e182\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e183\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e184\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e185\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e186\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e187\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e188\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e189\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e190\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e191\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e192\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e193\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e194\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e195\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e196\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e197\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e198\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e199\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e200\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e201\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e202\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e203\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e204\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e205\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e206\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e207\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e208\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e209\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e210\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e211\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e212\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e213\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e214\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e215\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e216\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e217\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e218\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e219\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e220\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e221\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e222\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e223\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e224\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e225\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e226\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e227\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e228\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e229\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e230\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e231\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e232\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e233\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e234\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e235\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e236\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e237\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e238\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e239\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e240\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e241\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e242\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e243\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e244\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e245\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e246\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ~/.config/alacritty/alacritty.toml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecolors\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edraw_bold_text_with_bright_colors\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002,\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Comma\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002s\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;K\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00021\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00022\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key2\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00023\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key3\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00024\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key4\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00025\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key5\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00026\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key6\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00027\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00028\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00029\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key9\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u00020\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Key0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002L\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;L\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002[\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;LBracket\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002p\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;LBracket\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command|Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002%\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;D\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002\\\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;D\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command|Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002d\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;W\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command|Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002n\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;RBracket\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command|Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\\u001B:w\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;S\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002c\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;T\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;T\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command|Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002x\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;X\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002z\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Z\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002\\u001B[D\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Left\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002\\u001B[C\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Right\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002\\u001B[B\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Down\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003echars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\\u0002\\u001B[A\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Up\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Command\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eaction\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ScrollPageUp\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;PageUp\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emode\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;~Alt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eaction\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ScrollPageDown\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;PageDown\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emode\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;~Alt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eaction\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ScrollToTop\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Home\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emode\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;~Alt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebindings\u003c/span\u003e\u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eaction\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;ScrollToBottom\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;End\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emode\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;~Alt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emods\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Shift\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003egeneral\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003elive_config_reload\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eterminal\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecolors\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebright\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eblack\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x676767\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eblue\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x6871ff\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecyan\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x5ffdff\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003egreen\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x78e364\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emagenta\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xff76ff\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ered\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xe4544e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ewhite\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xd0d0d0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eyellow\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xe5e152\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecolors\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ecursor\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecursor\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xfffefe\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003etext\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xc7c7c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecolors\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eblack\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x000000\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eblue\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x0225c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecyan\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x00c5c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003egreen\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x00c200\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emagenta\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xc930c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ered\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xcf2004\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ewhite\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xc7c7c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eyellow\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xc7c400\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecolors\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eprimary\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ebackground\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x000000\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eforeground\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xc7c7c7\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecursor\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eblinking\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;On\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eshape\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Beam\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecursor\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003evi_mode_style\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eshape\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Beam\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eTERM\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;xterm-256color\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003efont\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esize\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mf\"\u003e14.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003efont\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003enormal\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003efamily\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;JetBrainsMono Nerd Font Mono\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Regular\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003escrolling\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ehistory\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e100000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emultiplier\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eselection\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esave_to_clipboard\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esemantic_escape_chars\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u0026#39;,│`|:\u0026#34;\u0026#39;\u003c/span\u003e \u003cspan class=\"err\"\u003e()\u003c/span\u003e\u003cspan class=\"p\"\u003e[]{}\u003c/span\u003e\u003cspan class=\"err\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e\t\u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"err\"\u003e\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eterminal\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eshell\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;new-session\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;-A\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;-D\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;-s\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;main\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eprogram\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;/opt/homebrew/bin/tmux\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edecorations\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;full\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edecorations_theme_variant\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Dark\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edynamic_padding\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edynamic_title\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eopacity\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mf\"\u003e0.97\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003etitle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Alacritty\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edimensions\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ecolumns\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e120\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003elines\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e34\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeyboard\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"starship\"\u003eStarship\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e  1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e  9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 39\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 40\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 41\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 42\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 43\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 44\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 45\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 46\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 47\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 48\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 49\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 50\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 51\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 52\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 53\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 54\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 55\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 56\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 57\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 58\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 59\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 60\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 61\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 62\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 63\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 64\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 65\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 66\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 67\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 68\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 69\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 70\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 71\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 72\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 73\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 74\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 75\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 76\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 77\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 78\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 79\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 80\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 81\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 82\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 83\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 84\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 85\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 86\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 87\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 88\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 89\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 90\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 91\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 92\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 93\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 94\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 95\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 96\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 97\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 98\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 99\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e100\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e101\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e102\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e103\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e104\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e105\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e106\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e107\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e108\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e109\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e110\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e111\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e112\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e113\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e114\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e115\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e116\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e117\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e118\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e119\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e120\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e121\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e122\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e123\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e124\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e125\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e126\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e127\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e128\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e129\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e130\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e131\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e132\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e133\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e134\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e135\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e136\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e137\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e138\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e139\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e140\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e141\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e142\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e143\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e144\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e145\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e146\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e147\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e148\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e149\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e150\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e151\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e152\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e153\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e154\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e155\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e156\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e157\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e158\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e159\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e160\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e161\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e162\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e163\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e164\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e165\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e166\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e167\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# ~/.config/starship.toml\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[░▒▓](#a3aed2)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[  ](bg:#a3aed2 fg:#090c0c)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[](bg:#769ff0 fg:#a3aed2)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$directory\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[](fg:#769ff0 bg:#394260)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$git_branch\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$git_status\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[](fg:#394260 bg:#212736)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$kubernetes\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$c\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$cmake\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$golang\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$python\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$nodejs\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$rust\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$java\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$conda\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$direnv\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$env_var\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$sudo\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$jobs\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$battery\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$status\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$cmd_duration\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[](fg:#212736 bg:#1d2230)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e$time\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e[ ](fg:#1d2230)\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\\n$character\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;fg:#e3e5e5 bg:#769ff0\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;[ $path ]($style)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003etruncation_length\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e#truncation_symbol = \u0026#34;…/\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edirectory\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003esubstitutions\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Documents\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;󰈙 \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Downloads\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Music\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Pictures\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003egit_branch\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#394260\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol $branch ](fg:#769ff0 bg:#394260)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003egit_status\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#394260\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[($all_status$ahead_behind )](fg:#769ff0 bg:#394260)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# format = \u0026#39;([$all_status$ahead_behind]($style))\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003econflicted\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;🏳\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eahead\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;🏎💨\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ebehind\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;😪\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003ediverged\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;😵\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eup_to_date\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;✅\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003euntracked\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;😞\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estashed\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;📦\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emodified\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;📝\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estaged\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;++\\($count\\)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003erenamed\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;👅\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edeleted\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;🗑 \u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003enodejs\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003erust\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003egolang\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ephp\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edisabled\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003etime_format\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;%R\u0026#34;\u003c/span\u003e                                      \u003cspan class=\"c\"\u003e# Hour:Minute Format\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#1d2230\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[  $time ](fg:#a0a9cb bg:#1d2230)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eline_break\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edisabled\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eusername\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eshow_always\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle_user\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#2e3440\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle_root\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#9A348E\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[$user]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edisabled\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eos\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#9A348E\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003edisabled\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e      \u003cspan class=\"c\"\u003e# Disabled by default\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ec\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ $symbol ($version) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003epython\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[[ ${symbol}${pyenv_prefix}(${version})(\\($virtualenv\\)) ](fg:#769ff0 bg:#212736)]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# format = \u0026#39;\\[[${symbol}${pyenv_prefix}(${version})(\\($virtualenv\\))]($style)\\]\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edocker_context\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#5e81ac\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol $context ]($style) $path\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eelixir\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eelm\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ehaskell\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ejava\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ejulia\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003enim\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003escala\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esymbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34; \u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $symbol ($version) ]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003echaracter\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# success_symbol = \u0026#39;[ ➜](#6272a4)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# error_symbol = \u0026#39;[ ➜](bold red)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003esuccess_symbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ \u0026gt;](#6272a4)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eerror_symbol\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ \u0026gt;](bold red)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e#style = \u0026#34;bg:#6272a4\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ecmd_duration\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003emin_time\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e500\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003estyle\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;bg:#212736\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e# format = \u0026#39; [$duration](#6272a4)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eformat\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;[ $duration]($style)\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"tmux\"\u003etmux\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e36\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g mouse on\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g base-index 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g pane-base-index 1\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g visual-activity off\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g visual-bell off\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g visual-silence off\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g monitor-activity off\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g bell-action none\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e#  modes\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g clock-mode-colour colour5\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g mode-style \u0026#39;fg=colour1 bg=colour18 bold\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e# panes\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g pane-border-style \u0026#39;fg=colour19 bg=colour0\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g pane-active-border-style \u0026#39;bg=colour0 fg=colour9\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e# statusbar\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-position bottom\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-justify left\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-style \u0026#39;bg=colour18 fg=colour137 dim\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-left \u0026#39;\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-right \u0026#39;#[fg=colour233,bg=colour19] %d/%m #[fg=colour233,bg=colour8] %H:%M:%S \u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-right-length 50\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g status-left-length 20\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e# window\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g window-status-current-style \u0026#39;fg=colour1 bg=colour19 bold\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g window-status-current-format \u0026#39; #I#[fg=colour249]:#[fg=colour255]#W#[fg=colour249]#F \u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g window-status-style \u0026#39;fg=colour9 bg=colour18\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g window-status-format \u0026#39; #I#[fg=colour237]:#[fg=colour250]#W#[fg=colour244]#F \u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esetw -g window-status-bell-style \u0026#39;fg=colour255 bg=colour1 bold\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e# messages\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eset -g message-style \u0026#39;fg=colour232 bg=colour16 bold\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e","title":"Dotfiles"},{"content":"本次实现是关于 PingCap Talenet Plan 2022 TinySQL 学习营相关的实验解题思路。本次实验可以作为 CMU 15-445 的补充，主要内容为每周的讲座+通过Lab的所有测评 Case ，总体实现难度并不高，以下是每个 Project 实现的具体思路细节。\nProject 1 相当于是项目的 kick-off ，主要内容为项目的基本介绍，包括 SQL 关系代数入门与研究数据如何映射到 TinyKV 上。 具体来说，对每个表分配一个 TableID，每一行分配一个 RowID，其中 TableID 在整个集群内唯一，RowID 在表内唯一，这些 ID 都是 int64 类型。 本次项目实现非常简单，\nProject 2 本节为 Parser 部分，主要研究 TinySQL 是如何将文本转化为 AST 的。 本节利用 yacc 来进行 SQL 语法的补充，可以看出，ast.XXXStmt 结构体内包含的内容和相对应的语句语法都是一一对应的。 所有产生式也都是根据对应的 SQL 语法来编写的。这个文件最初是用工具从 BNF 转化生成的，此 Project 中补充的是 Join 的语法，针对测试用例中的 join, left join, right join, join on 编写 yacc 文件即可，即完成如下两种模式的匹配。\n1 2 TableRef JoinType OuterOpt \u0026#34;JOIN\u0026#34; TableRef \u0026#34;ON\u0026#34; Expression TableRef CrossOpt TableRef \u0026#34;ON\u0026#34; Expression Project 3 本节实现了 SQL DDL 中删除一列的操作，TinySQL 中的异步 schema 变更是参照了 Google F1 中的 schema 变更的算法。主要内容为参考论文原文 Online, Asynchronous Schema Change in F1。 实验代码主要添加了 public –\u0026gt; write-only –\u0026gt; delete-only –\u0026gt; (reorg) –\u0026gt; absent 一系列转换的逻辑。 需要注意的是在最后检查 job.IsRollingback() ，以此判断调用FinishTableJob时是完成还是需要RollBack。\nProject 4 在优化这一部分，主要研究优化器的两种框架，如何使用统计信息进行数据分布的估算和索引的选择。\nPart 1 本部分主要关于 System R 优化器的实现，主要是实现 PredicatePushDown 这个方法。\n优化器的入口为 plannercore.DoOptimize，在逻辑优化部分，先通过flag判断需要执行的内容，遍历优化的规则列表。\nPredicatePushDown 这个方法为实际的逻辑计划实现谓词下推。这里实现了 LogicalAggregation 的谓词下推，先通过 group by 的列找到可以下推的 condtion，然后保留其中有用的列，最后将选出来的谓词进行下推即可。主要代码逻辑如下：\n1 2 3 4 5 6 7 8 9 10 for _, cond := range predicates { switch cond.(type) { // ... case *expression.ScalarFunction: newFunc := expression.ColumnSubstitute(cond, la.Schema(), exprsOriginal) condsToPush = append(condsToPush, newFunc) // ... } } la.baseLogicalPlan.PredicatePushDown(condsToPush) Part 2 本次Part主要实现和代价选择相关的一些内容\n1. Count-Min Sketch 当结束启发式规则的筛选之后，我们仍然可能剩余多组索引等待筛选我们就需要知道每个索引究竟会过滤多少行数据。 实现 CM Sketch 算法，包括新增 value 以及查询 value。核心 insert 逻辑如下\n1 2 3 4 h1, h2 := murmur3.Sum128(bytes) for i := range c.table { j := (h1 + h2*uint64(i)) % uint64(c.width) // hash } 新增 value 较为简单，算出 Hash 之后再将 table 中对应位置的 count 增加即可。参考文章 TiDB 使用了 Count-Mean-Min Sketch 算法，引入了一个噪声值 (N - CM[i, j]) / (w-1) ，其中 N 为总数，w 为 table 宽度，再取所有行的值减去噪音之后的估计值的中位数作为最后的估计值。\n2. Join Reorder 本节实现了用动态规划算法找出最优 join 顺序。本次实验逻辑参考较多 TiDB 实现。主要实现两个函数 bfsGraph 和 dpGraph。主要步骤如下：\n以邻接表的方式建立 Join 图，记录下 joinGroupEqEdge 与 joinGroupNonEqEdge。 从一个没有访问的点开始进行广度优先遍历，得到一个连通的 Join 节点序列，返回数组 visitID2NodeID。 对于这些节点的 cost，进行动态规划算法得出最少 cost 的 Join 方案，如果还有没访问的节点，则进行循环。 对所有 Join 方案调用 s.makeBushyJoin 得到最终结果。 3. Access Path Selection 本节实现 Skyline Prune 启发式算法来排除效果一定更差的路径，重点在于实现 compareCandidates 函数。根据注释来实现：\n比较两个 candidate 的 col，覆盖范围更大的 candidate 更优。这里的 columnSet 数据结构为 intsets.Sparse，所以只需要判断 s2.Len() \u0026lt; s1.Len() \u0026amp;\u0026amp; s2.SubsetOf(s1) 即可。 比较两个 candidate 是否 match physical property，match 的那个更优，这里 isSingleScan 已经显示给出。 比较两个 candidate 是否只需要扫面一次，只扫描一次的那个更优，同上，只需要比较 isMatchProp 即可。 最后综合以上三种情况，将三者比较的部分进行求和，即 accessResult + scanResult + matchResult 大于0且所有值均大于等于的情况下排除该分支。\nProject 5 Part 1 1. 实现向量化表达式 参考其他函数向量化方法编写，先获取数据，再编写主要逻辑，参考 MySQL String 相关函数，主要逻辑如下：\n1 2 3 4 5 6 7 8 9 result.ResizeInt64(n, false) result.MergeNulls(buf) i64s := result.Int64s() for i := 0; i \u0026lt; n; i++ { if result.IsNull(i) { continue } i64s[i] = int64(len(buf.GetString(i))) } 2. 实现向量化 selection 的 Next 为 SelectionExec 实现 Next 方法，整体逻辑可以参考 unBatchedNext 方法，区别在于每次都是向量化批量处理，也就是说，和传统的 Volcano 模型的一次取一行相比，这里会通过 selected 数组循环判断，一次取一批数据。这个 selected 数组通过调用 VectorizedFilter 即可从 children 中拿到，实验代码数据结构为 SelectionExec.selected。理解 Next 的关键就是当前 Executor 节点的 input 是通过其 children 的 Next 拿到，向量化的作用则是每次是拿一批数据而不单单只是一行。在熏昏最后最后执行 expression.VectorizedFilter(e.ctx, e.filters, e.inputIter, e.selected) 进行 selected 数组的更新即可。\nPart 2 实现并行 Hash Join 算法。\n1. 实现 fetchAndBuildHashTable 首先调用 newHashRowContainer 生成一个新的 hash table，然后通过循环调用 Next 从 inner table 中获取数据（chunk），再将数据放入 hash table，直至没有更多数据即可。主要逻辑如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 e.rowContainer = newHashRowContainer(e.ctx, int(e.innerSideEstCount), hCtx, initList) for { chk := chunk.NewChunkWithCapacity(retTypes(e.innerSideExec), e.maxChunkSize) err := Next(ctx, e.innerSideExec, chk) if err != nil { return err } if chk.NumRows() == 0 { return nil } if err = e.rowContainer.PutChunk(chk); err != nil { return err } } 2. 实现 runJoinWorker 这部分的核心在于分清楚各个 Channel 的功能与收发数据的时机。负责拿取 outer table 的数据并 probe inner table 建立的 hash 表进行 join 操作，并返回交过到 main thread，以下是几个主要需要用的 channel 变量及其用法：\ncloseCh： 用于结束 loop 的通道，select 监测到该信号直接退出循环。 outerResultChs： outer fetcher 通过此通道来向不同的 join worker 分发任务。 outerChkResourceCh：返回当前 worker 接收任务用的通道以及本 worker 用的 chunk，循环利用 chunk，避免重新分配内存。 joinChkResourceCh： outer table 的数据收集完毕后，通过此通道返回 hash join 的结果。 弄清楚各个 Channel 之间的关系以后 只需在循环中反复调用 e.join2Chunk(workerID, probeSideResult, hCtx, joinResult, selected) 根据具体情况发送 Channel 即可。\nPart 3 本节主要实现 Hash Aggregate。有点类似于 MapReduce 的思路，即把整个计算任务分配给多个 PartialWorker，然后按照 Key 预聚合，再Shuffle 给不同的 FinalWorker 进行 Value 的聚合，最后再返回给 Main 组合成最终结果。\n1. consumeIntermData 循环通过 w.getPartialResult(sc, w.groupKeys, w.partialResultMap) 拿到数据，再将数据聚合到对应的 af.MergePartialResult(sctx, prs[j], finalPartialResults[i][j]) 的位置，实现难度不大。\n2. shuffleIntermData 使用 int(murmur3.Sum32([]byte(groupKey))) % finalConcurrency 算出某个 Key 应该发给哪个 final worker，然后通过 outputChs 将这些 groupKeysSlice[i] 与 w.partialResultsMap，发送给对应 worker 即可。\nProject6 Percolator 提交协议的两阶段提交分为 Prewrite 和 Commit：Prewrite 实际写入数据，Commit 让数据对外可见。 在对 Key 进行写操作时，需要将其发送到正确的 Region 上才能够处理。主要代码逻辑如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 for i, k := range keys { if lastLoc == nil || !lastLoc.Contains(k) { var err error lastLoc, err = c.LocateKey(bo, k) if err != nil { return nil, first, err } if filter != nil \u0026amp;\u0026amp; filter(k, lastLoc.StartKey) { continue } } if i == 0 { first = lastLoc.Region } regionVerIDMap[lastLoc.Region] = append(regionVerIDMap[lastLoc.Region], k) } 在 Prewrite 阶段，对于一个 Key 的操作会写入两条记录。1. Default CF 中存储实际的 KV 数据；2.Lock CF 中存储了锁，包括 Key 和时间戳信息，会在 Commit 成功时清理。\nLock Resolver 的职责就是应对一个事务在提交过程中遇到 Lock 的情况。\n这里的 buildPrewriteRequest 需要将请求的 Mutations 进行封装，此外 PrimaryLock 应赋值为 c.primary() 以防止 primaryKey 为 0 的场景。\n1 2 3 for _, key := range batch.keys { req.Mutations = append(req.Mutations, \u0026amp;c.mutations[string(key)].Mutation) } 在 handleSingleBatch 逻辑里与 actionPrewrite 基本类似，需要注意的是在 actionCleanup 执行后将 c.mu.committed 赋值为 false。\n对于 tikvSnapshot.Get 方法，添加如下逻辑：\n如果事务正在提交，等待一段时间并重试； 2. 如果事务已经结束并且带有锁，则进行异常处理 1 2 3 4 5 6 7 8 9 10 msBeforeExpired, _, err := s.store.lockResolver.ResolveLocks(bo, 0, []*Lock{lock}) if err != nil { return nil, err } if msBeforeExpired \u0026gt; 0 { err = bo.BackoffWithMaxSleep(BoTxnLock, int(msBeforeExpired), errors.Errorf(\u0026#34;2PC get lockedKeys: %d\u0026#34;, 1)) if err != nil { return nil, errors.Trace(err) } } Comments and Suggestions 如下为本次实验的一些改进建议，部分内容已提交pr。\n在 Project1-Part2 中， DecodeIndexKeyPrefix 操作可以在以下枚举值中添加一项，方便对含索引的内容部分进行操作，反之则需要在代码中插入常数偏移值，不便维护。 1 2 3 4 5 6 7 8 9 const ( idLen = 8 prefixLen = 1 + idLen /*tableID*/ + 2 // RecordRowKeyLen is public for calculating average row size. RecordRowKeyLen = prefixLen + idLen /*handle*/ tablePrefixLength = 1 recordPrefixSepLength = 2 indexPrefixSepLength = 2 // Add here ) 在 Project2 中，参考 MySQL 的手册，建议添加 join_specification 相关的测试用例，或者修改 parser.y 中如下注释，避免学生产生误解。 1 2 3 4 5 joined_table: { table_reference {[INNER | CROSS] JOIN | STRAIGHT_JOIN} table_factor [join_specification] | table_reference {LEFT|RIGHT} [OUTER] JOIN table_reference join_specification | table_reference NATURAL [INNER | {LEFT|RIGHT} [OUTER]] JOIN table_factor } 在 Project3 中，在 AutoGrading Machine 上的测试结果应该调整与 Project6 无关，避免测试结果与本地存在差异使学生产生疑惑。\nProject6 中，ttlEquals.ttlEquals 的测试代码 c.Assert(int(math.Abs(float64(x-y))), LessEqual, 2) 在新版苹果电脑上运行结果为错误，但是提交测评机结果正常，原因是 arm64 下 math.Abs() 返回值也是 uint（这部分测试用例的判断逻辑写的并不是很好，完全可以手写 abs 函数避免强转为 float64 过程中可能的错误），对于 runtime.GOARCH 的检测需增加 “arm64”。这部分内容已经提交 Pull Request #145。\n","permalink":"https://chasing1020.github.io/post/tinysql-implementation/","summary":"\u003cp\u003e本次实现是关于 PingCap Talenet Plan 2022 TinySQL 学习营相关的实验解题思路。本次实验可以作为 CMU 15-445 的补充，主要内容为每周的讲座+通过Lab的所有测评 Case ，总体实现难度并不高，以下是每个 Project 实现的具体思路细节。\u003c/p\u003e","title":"TinySQL Implementation"},{"content":"By default, the theme even doesn\u0026rsquo;t support the search and copy to the clipboard button which is proposed in issues#289 and issues#399. So I have finished implementing the features.\n1. Search Initially, you should add the output of JSON in your ./config.toml file.\n1 2 [outputs] home = [\u0026#34;HTML\u0026#34;, \u0026#34;RSS\u0026#34;, \u0026#34;JSON\u0026#34;] Then create the file ./themes/even/layouts/_default/index.json and add these lines. Then make sure you can see the output in the localhost:1313/index.json.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 {{- $.Scratch.Add \u0026#34;index\u0026#34; slice -}} {{- range $index, $element := .Site.RegularPages.ByTitle -}} {{if ne .Params.tags nil}}{{if ne .Plain nil}} {{- $.Scratch.Add \u0026#34;index\u0026#34; (dict \u0026#34;id\u0026#34; $index \u0026#34;date\u0026#34; .Date \u0026#34;tags\u0026#34; .Params.tags \u0026#34;categories\u0026#34; .Params.categories \u0026#34;title\u0026#34; .Title \u0026#34;permalink\u0026#34; .Permalink \u0026#34;contents\u0026#34; .Plain ) -}} {{end}}{{end}} {{- end -}} {{- $.Scratch.Get \u0026#34;index\u0026#34; | jsonify -}} And add file ./themes/even/static/js/search.js. I have stored the search object so just need to load the file one time once you enter the search page.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 const summaryInclude = 100; let fuseOptions = { includeMatches: true, threshold: 0.1, tokenize: true, location: 0, distance: 100000, maxPatternLength: 32, minMatchCharLength: 1, keys: [ {name: \u0026#34;title\u0026#34;, weight: 0.9}, {name: \u0026#34;contents\u0026#34;, weight: 0.8}, {name: \u0026#34;tags\u0026#34;, weight: 0.3}, {name: \u0026#34;categories\u0026#34;, weight: 0.3} ] }; let fuse; fetch(\u0026#34;../index.json\u0026#34;) .then(resp =\u0026gt; resp.json()) .then(data =\u0026gt; fuse = new Fuse(data, fuseOptions)); let searchContent = \u0026#34;\u0026#34;; async function executeSearch() { searchContent = $(\u0026#39;#search-content\u0026#39;)[0].value; let results = fuse.search(searchContent); await populateResults(results); } function highlight(str) { return str.replace( new RegExp(searchContent, \u0026#39;gi\u0026#39;), searchContent =\u0026gt; `\u0026lt;strong\u0026gt;\u0026lt;span style=\u0026#34;background-color:yellow;\u0026#34;\u0026gt;${searchContent}\u0026lt;/span\u0026gt;\u0026lt;/strong\u0026gt;` ); } async function populateResults(result){ $(\u0026#39;#search-results\u0026#39;).html(\u0026#39;\u0026#39;); $.each(result, function(key, value) { let contents = value.item.contents; let snippet = \u0026#34;\u0026#34;, start = 0, end = 0; let pos = contents.toLocaleLowerCase().indexOf(searchContent.toLocaleLowerCase()); if (pos != -1) { start = pos - summaryInclude; end = pos + summaryInclude; if (start \u0026lt; 0) start = 0; if (end \u0026gt; contents.length) end = contents.length; snippet = contents.substring(start, end); } $.each(value.matches,function(k, v) { start = v.indices[0][0] - summaryInclude; end = v.indices[0][1] + summaryInclude + 1; if (start \u0026lt; 0) start = 0; if (end \u0026gt; v.value.length) end = v.value.length; if (pos == -1 \u0026amp;\u0026amp; v.key == \u0026#34;contents\u0026#34;) { snippet += v.value.substring(start, end); } }); if(snippet.length \u0026lt; 1){ snippet += contents.substring(0, summaryInclude * 2); } let tags = []; value.item.tags.forEach(t =\u0026gt; tags.push(highlight(t))); let categories = []; value.item.categories.forEach(c =\u0026gt; categories.push(highlight(c))); let template = ` \u0026lt;div id=\u0026#34;summary-${key}\u0026#34;\u0026gt; \u0026lt;article style=\u0026#34;padding: 0px\u0026#34; class=\u0026#34;post\u0026#34;\u0026gt; \u0026lt;header class=\u0026#34;post-header\u0026#34;\u0026gt; \u0026lt;h1 class=\u0026#34;post-title\u0026#34;\u0026gt;\u0026lt;a style=\u0026#34;color:black\u0026#34; class=\u0026#34;post-link\u0026#34; href=\u0026#34;${value.item.permalink}\u0026#34;\u0026gt;${highlight(value.item.title)}\u0026lt;/a\u0026gt;\u0026lt;/h1\u0026gt; \u0026lt;div class=\u0026#34;post-meta\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;post-time\u0026#34;\u0026gt;${value.item.date}\u0026lt;/span\u0026gt; \u0026lt;div class=\u0026#34;post-category\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;/categories/${value.item.categories}/\u0026#34;\u0026gt; ${categories} \u0026lt;/a\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;div class=\u0026#34;post-content\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;post-summary\u0026#34;\u0026gt; ${highlight(snippet)} \u0026lt;a href=\u0026#34;${value.item.permalink}\u0026#34; class=\u0026#34;read-more-link\u0026#34;\u0026gt;Read more...\u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;footer class=\u0026#34;post-footer\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;post-tags\u0026#34;\u0026gt; \u0026lt;a href=\u0026#34;/tags/${value.item.tags}\u0026#34;\u0026gt;${tags}\u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/footer\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/article\u0026gt; \u0026lt;/div\u0026gt;`; $(\u0026#39;#search-results\u0026#39;).append(template); }); } And then just add the search page in your ./content/post/search.md. Although it\u0026rsquo;s not recommended to add the script tag in the middle of your markdown file, it is easy to implement the style that we expected.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 --- title: \u0026#34;Search\u0026#34; layout: \u0026#34;search\u0026#34; priority : 1 menu: \u0026#34;main\u0026#34; weight: 5 --- \u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/fuse.js@6.6.2\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;/js/search.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;section id=\u0026#34;search-input\u0026#34;\u0026gt; \u0026lt;input style=\u0026#34;display: center\u0026#34; id=\u0026#34;search-content\u0026#34; placeholder=\u0026#34;Search text\u0026#34; type=\u0026#34;search\u0026#34; oninput=\u0026#34;executeSearch()\u0026#34;\u0026gt; 🔍 \u0026lt;/section\u0026gt; \u0026lt;hr\u0026gt; \u0026lt;section id=\u0026#34;search-results\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;Please input some keywords to search\u0026lt;/h3\u0026gt; \u0026lt;/section\u0026gt; 2. Copy to clipboard I just add these lines to the end of ./themes/even/layouts/partials/scripts.html.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 \u0026lt;script\u0026gt; function createCopyButton(highlightDiv) { const div = document.createElement(\u0026#34;div\u0026#34;); div.className = \u0026#34;copy-code\u0026#34;; div.innerText = \u0026#34;Copy\u0026#34;; div.addEventListener(\u0026#34;click\u0026#34;, () =\u0026gt; copyCodeToClipboard(div, highlightDiv) ); addCopyButtonToDom(div, highlightDiv); } async function copyCodeToClipboard(button, highlightDiv) { const codeToCopy = highlightDiv.querySelector(\u0026#34;:last-child \u0026gt; .chroma \u0026gt; code\u0026#34;) .innerText; try { result = await navigator.permissions.query({ name: \u0026#34;clipboard-write\u0026#34; }); if (result.state == \u0026#34;granted\u0026#34; || result.state == \u0026#34;prompt\u0026#34;) { await navigator.clipboard.writeText(codeToCopy); } else { copyCodeBlockExecCommand(codeToCopy, highlightDiv); } } catch (_) { copyCodeBlockExecCommand(codeToCopy, highlightDiv); } finally { codeWasCopied(button); } } function codeWasCopied(div) { div.blur(); div.innerText = \u0026#34;Copied!\u0026#34;; setTimeout(function () { div.innerText = \u0026#34;Copy\u0026#34;; }, 2000); } function addCopyButtonToDom(button, highlightDiv) { highlightDiv.insertBefore(button, highlightDiv.firstChild); const wrapper = document.createElement(\u0026#34;div\u0026#34;); wrapper.className = \u0026#34;highlight-wrapper\u0026#34;; highlightDiv.parentNode.insertBefore(wrapper, highlightDiv); wrapper.appendChild(highlightDiv); } document.querySelectorAll(\u0026#34;.highlight\u0026#34;) .forEach((highlightDiv) =\u0026gt; createCopyButton(highlightDiv)); \u0026lt;/script\u0026gt; And modify the ./themes/even/assets/sass/_partial/_post/_code.sass file, add these lines. I think it is a good idea to make the theme style the same as the type on the left of the code block.\n1 2 3 4 5 6 7 8 9 10 11 12 .copy-code { position: absolute; right: 0; z-index: 2; font-size: .9em !important; padding: 0px 1.5rem !important; color: #b1b1b1; font-family: Arial; font-weight: bold; cursor: pointer; user-select: none; } ","permalink":"https://chasing1020.github.io/post/amend-hugo-theme-even/","summary":"\u003cp\u003eBy default, the theme even doesn\u0026rsquo;t support the search and copy to the clipboard button which is proposed in \u003ca href=\"https://github.com/olOwOlo/hugo-theme-even/issues/289\"\u003eissues#289\u003c/a\u003e and \u003ca href=\"https://github.com/olOwOlo/hugo-theme-even/issues/339\"\u003eissues#399\u003c/a\u003e. So I have finished implementing the features.\u003c/p\u003e\n\u003ch1 id=\"1-search\"\u003e1. Search\u003c/h1\u003e\n\u003cp\u003eInitially, you should add the output of JSON in your \u003ccode\u003e./config.toml\u003c/code\u003e file.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eoutputs\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003ehome\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;HTML\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;RSS\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;JSON\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThen create the file \u003ccode\u003e./themes/even/layouts/_default/index.json\u003c/code\u003e and add these lines. Then make sure you can see the output in the \u003ccode\u003elocalhost:1313/index.json\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{-\u003c/span\u003e \u003cspan class=\"err\"\u003e$.Scratch.Add\u003c/span\u003e \u003cspan class=\"nt\"\u003e\u0026#34;index\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003eslice\u003c/span\u003e \u003cspan class=\"err\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{-\u003c/span\u003e \u003cspan class=\"err\"\u003erange\u003c/span\u003e \u003cspan class=\"err\"\u003e$index,\u003c/span\u003e \u003cspan class=\"err\"\u003e$element\u003c/span\u003e \u003cspan class=\"err\"\u003e:=\u003c/span\u003e \u003cspan class=\"err\"\u003e.Site.RegularPages.ByTitle\u003c/span\u003e \u003cspan class=\"err\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{if\u003c/span\u003e \u003cspan class=\"err\"\u003ene\u003c/span\u003e \u003cspan class=\"err\"\u003e.Params.tags\u003c/span\u003e \u003cspan class=\"err\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{if\u003c/span\u003e \u003cspan class=\"err\"\u003ene\u003c/span\u003e \u003cspan class=\"err\"\u003e.Plain\u003c/span\u003e \u003cspan class=\"err\"\u003enil\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{-\u003c/span\u003e \u003cspan class=\"err\"\u003e$.Scratch.Add\u003c/span\u003e \u003cspan class=\"nt\"\u003e\u0026#34;index\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e(dict\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e$index\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Date\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;tags\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Params.tags\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;categories\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Params.categories\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;title\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Title\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;permalink\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Permalink\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"s2\"\u003e\u0026#34;contents\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e.Plain\u003c/span\u003e \u003cspan class=\"err\"\u003e)\u003c/span\u003e \u003cspan class=\"err\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{end\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{end\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{-\u003c/span\u003e \u003cspan class=\"err\"\u003eend\u003c/span\u003e \u003cspan class=\"err\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"err\"\u003e{-\u003c/span\u003e \u003cspan class=\"err\"\u003e$.Scratch.Get\u003c/span\u003e \u003cspan class=\"nt\"\u003e\u0026#34;index\u0026#34;\u003c/span\u003e \u003cspan class=\"err\"\u003e|\u003c/span\u003e \u003cspan class=\"err\"\u003ejsonify\u003c/span\u003e \u003cspan class=\"err\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"err\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eAnd add file \u003ccode\u003e./themes/even/static/js/search.js\u003c/code\u003e. I have stored the search object so just need to load the file one time once you enter the search page.\u003c/p\u003e","title":"Amend Hugo Theme Even"},{"content":"It is a yearly \u0026ldquo;divide major\u0026rdquo; season, and now I am looking back at the series of computer science questions and answers I have browsed in Zhihu during my freshman year, and I feel a lot of emotions. I have been thinking about it for a long time, and this is the first time I have seen such numerous questions and discussions about computer science, hence I write about this post. If you find any bugs or grammar issues, please don\u0026rsquo;t hesitate to contact me to fix them. Anyway, thanks again for accepting my poor English.\n0. Preface What is computer science? Why does our country need students majoring in it? What is the future of this major? Will computer science be popular in the future?\nTwo years later, when I introduced this major to my younger brother and sister who study at our university as a \u0026ldquo;past person\u0026rdquo;, it was too difficult for me to write the answer because I was worried about my lack of ability and short-sightedness, which brought all kinds of misguidance to them; secondly, the choice of major at the undergraduate level will probably affect a person\u0026rsquo;s life and mislead others. The third is that the choice of major at the undergraduate level may affect one\u0026rsquo;s life and mislead the future of others.\nTherefore, in this article, I will try my best to talk about my experience and thoughts in computer science over the past two years, and hope readers can gain something worthwhile. The content I share is only my opinion and does not represent any absolute theory.\n1. Our Environment This is an era of prosperity and crisis: Owing to the COVID-19, our world is experiencing changes never seen in a century.\nThe annually spring and autumn recruitment is the barometer of the development of the computer industry, from last year, Tencent paid 40w high salary in the hot search, and then some time ago, due to the epidemic of the re-emergence of the side of layoffs while also locking the head count. Until today President Liu Kiping of Tencent issued an article: \u0026ldquo;Internal manpower investment will be adjusted from the edge of the business to the core business, in the industry to occur fundamental changes in the industry when actively embracing change\u0026rdquo;.\nIn the general environment, today\u0026rsquo;s Internet is far less than what it was 10 years ago, the so-called \u0026ldquo;Although it is a pig, it can wind and waves.\u0026rdquo;, which Howard\u0026rsquo;s comment is: \u0026ldquo;Now the Internet from the era of incremental into the era of stock. Everyone has no way to make the cake bigger, so they can only grab each other\u0026rsquo;s cake\u0026rdquo;.\nThe Mythical Man-Month which is the bible of software engineering points out that more people can not shorten the software development progress, and it is dangerous to use \u0026ldquo;man-month\u0026rdquo; to measure the size of the work. Because the number of people != efficiency, the way to adjust the organizational structure of the current major manufacturers is also very straightforward - leaving the most valuable programmers. In this environment, with only a small pool of talent to dig, the Internet starting salary also seems very high.\nIf only to high salary or good employment and blindly choose computer science major, it can only be said to have entered the survivorship bias. An objective fact is that the average salary of students graduating from our school is far below the average level of Zhihu or Maimai. The best students can collect offers from all the big companies, but the students who usually waste their time, even though they choose this major, often end up not as good as they expect. The salary distribution of this industry is not normal but power-law distribution, only a small group of the best people can share the most cake, the variance of the future development of the same industry will be very large. If you will choose to enter the industry in the future, please try to become the front group of people.\nIn this regard, I think the reason for choosing this major should be \u0026ldquo;you love computer science enough\u0026rdquo;, not \u0026ldquo;this major is hot\u0026rdquo;. If you like the process of building a whole world of order through a process control (auto-state machine) and have some interest in this major, you may want to read on below.\n2. About CES What is the Computer Engineering and Science?\nThe School of Computer Engineering and Science (CES) offers four majors: Computer Science and Technology, Intelligent Science and Technology, Cyberspace Security, and Artificial Intelligence.\nThe gap between these majors, as reflected in the professional courses taken at the undergraduate level, will not be so big, at least most of the basic courses such as data structures, principles of computer composition, operating systems, computer networks (i.e., the 408 in the Master\u0026rsquo;s degree examination) are treated equally. Corresponding professional courses will focus on the study content, specific details refer to Shanghai University undergraduate professional training program in 2019 and 2020. And all majors\u0026rsquo; rankings are represented by the following table:\nName 2021 Plan 2021 Rank 2020 Plan 2020 Rank 2019 Plan 2019 Rank Computer Science and Technology 110 289/1573 135 560/2029 150 582/2090 Intelligent Science and Technology 26 375/1573 45 486/2029 60 281/2090 Cyberspace Security 20 396/1573 25 495/2029 - - Artificial Intelligence 21 366/1573 - - - - In most cases, there will not be a significant gap in the daily academic communication of interdisciplinary students, because the skills you will use in your future work may not be as closely related to your current chosen major. What teachers teach you will not determine what you will learn about. For example, if you choose computer science and technology, you can still study artificial intelligence, and if you choose AI, you can study cybersecurity-related content. But in detail:\nComputer science and technology: can be understood as the computer industry, the Polytechnic University class, belongs to the computer in the basic disciplines, both soft and hard, optional development direction range is very wide, and the specific direction of research depends entirely on your interest. Intelligent science and technology: need to learn some signal processing, matrix algebra, brain cognition, and operations research-related courses, across a wide range. Cyberspace security: a new major in my year, the scope of the study is not limited to the so-called \u0026ldquo;cyber security\u0026rdquo;, but also distinguishes penetration, reverse, cryptography and so on. Artificial intelligence: the research direction and professional course arrangement are also close to the wisdom of science, including mathematical logic, data mining, brain cognition, etc., it is recommended that students with good mathematics choose, need very good linear algebra and probability theory foundation, but also this is the Department of Mathematics \u0026ldquo;to grab a job\u0026rdquo; one of the directions. There is no absolute answer to the question of whether a specific major is better or worse. From a personal point of view, the teaching program of the new major still needs time to settle down, 10 weeks of short semesters are compressed together, and even if you take grade 4.0 does not mean that you learned the course well.\nRemember what the school teaches != what I need to learn, you need to be clear about where your future competitiveness in the industry lies. Nowadays, more than 90% of the interview contents of large Internet companies are not what the teachers can teach you, but the fact that the teacher did not teach is not a reason why you don\u0026rsquo;t know that.\n3. The future What I will do in the future?\nIn fact, each road is divided and the choice will be different.\nFor work: If you do not have the intention to study what, it is not recommended to go in the direction of algorithms, the main directions include front-end, client, back-end, testing, operations and maintenance, security, etc. Before the junior year, make sure to finish the professional courses, and do not leave too many courses for the senior year. As early as possible to determine the technology stack used in the interview, looking for online classes (preferably open courses in foreign famous universities, books, blogs, and awesome list; job search in the early efforts to do leetcode, the summer of the junior year (or the end of the second year of winter) as early as possible to go to the Internet companies for internship, more accumulated internship experience project experience.\nInternship: it is best to find the schoolmates or online internal push, more understanding of the department\u0026rsquo;s working environment, overtime status, etc. The primary purpose of the internship is to enrich the resume, it is not recommended to care too much about the salary more or less, the key is you will learn what. When you are looking for a job with your internship experience, the name of your internship company only exists for the interviewer to have heard of/not heard of two situations. If the goal is to work for a big company, it is not particularly recommended to go to outsourcing companies and traditional industry companies (i.e. companies whose old business is not the Internet) or if you want to take \u0026ldquo;private jobs\u0026rdquo; from teachers and make sure to identify the good and bad projects, refer to Shanghai Jiao Tong University Survives Guide. Taking part in the civil servant exam: Mainly depends on the province, city, unit, and tier, the specific details are unknown.\nPush avoid: The specific policy will change every year, refer to The Implementation plan for the work of recommending fresh outstanding undergraduate graduates to be exempted from studying for a master\u0026rsquo;s degree in 2022 and 2021 School of Computer Science of Shanghai University. The details of the policy will change every year, but it is important to be clear about where you need to get extra points.\nNote that \u0026ldquo;the scientific research achievements and competition awards of students who cooperate with their immediate family members or those with significantly higher education, titles and positions than themselves are only for reference and are not included in the calculation system of the comprehensive evaluation results.\u0026rdquo;, means that the route of publishing papers in cooperation with supervisors, finding joint competitions for graduate Ph.D. will not work. On the other hand, Shanghai University has a lot of ACM experts == Shanghai University ACM is powerful != you will be powerful definitely when you come to ACM, same for CTF, ASC, etc. Some competitions will cost a lot of time investment if you find it is not suitable also please give up the sunk cost in time, spend time to do leetcode than waste time in the competition. Taking part in the postgraduate entrance exams: The students with low GPAs who want to fulfill their dreams of a famous domestic school is also the choice of most students who do not want to work for the time being. The preparation time for the exam begins in the second semester of the junior year, choice is greater than effort. The process of preparing for the exam, choosing a school, etc., depends on the individual situation, so I won\u0026rsquo;t go into detail here.\nStudy abroad: The first thing you need to do is to make sure that you are ready to go to the country you want to go to, whether you need to take IELTS or TOEFL, and whether you want to take GRE. In addition, striving to participate in internationally renowned competitions or publish some academic results but also trying to accumulate some foreign company internship experience, in the application are very plus points. As for whether to stay or return to your home country at the end of your graduate studies abroad, it ultimately depends on your situation.\n4. Daily life What is life like in the college?\nGrade 2019 and 2020 dormitories are arranged in the New Century, subsequent accommodation is unknown, depending on the school arrangements. Some classes have been deleted from the seminar, but there are still a lot of reports, and some teachers have a lot of requirements. College study materials, textbooks, etc., in the major group chat, can also be seen, you can also refer to CES Materials and you may find it helpful to browse the libshu which includes the material collected by the Open Source Community. Many of the final exams are far less detailed than the freshman year, most of them are less incomplete, and you will need some preparation skills. In class, most of the girls sit in the front row more, and whether or not you can get rid of the single depends on your ability. About class activities, because our courses are self-selected, everyone\u0026rsquo;s daily life trajectory is more scattered or gathered in their small circle, I think the class cohesion is not so strong to some extent. Most of the courses, including laboratory classes, will be on the East Side, or relatively far, winter morning on eight East Side is very painful, so it is recommended to learn a bicycle for convenience. ACM and Cyberspace security labs are located on the 7th and 6th floors of the computer building, which are currently open to specific students. For teamwork competitions, it is important to find like-minded partners who are willing to devote their time and have strong abilities (not reflected in their GPAs). Students who major in AI should try to contact their supervisors as early as possible to try to borrow a GPU server, and those who are strong can also try to contact their supervisors in advance to join their graduate groups. 5. On the way If you have decided to major in the Computer Science and Technology, here are some suggestions for studying in advance.\nEnglish counts! English counts! English counts!\nLearn to use Google and learn to read English materials. Never use Baidu to find answers. (It is highly recommended to add 127.0.0.1 www.baidu.com in your /etc/hosts file, which will promise that you will not able to use it.) Learn more on Github, Stackoverflow and less on CSDN or Cnblogs. Learn to fix bugs by STFW and RTFM, which is the most important skill for a programmer. Learn how to ask questions the smart way. Create a GitHub account, and build a personal blog on it, you can use hugo, hexo and other frameworks, the whole specific process follows Google and official documentation, the process you will learn a lot, including Git, read documents, write configuration files and other operations.\nSpecific learning direction, learning a new language and related technologies, learning process to take notes.\nBack-end: Java, Golang, C++, Rust, etc., and Web frameworks such as Springboot, and Gin, capable students can learn about DBMS, Redis, MQ, ES and other technologies in advance. Front-end/client: JavaScript, TypeScript, Dart, Kotlin, Swift and so on, frameworks including Vue, React, Angular, etc., learning Node.js, as well as agents, caches, gateways, tunnels, etc. AI can start to learn Python, Matlab and so on, as well as the common TensorFlow or PyTorch, efforts to improve the level of English and start trying to improve the ability to find literature/reading literature. Learn Linux including WSL and Docker, it is highly recommended to learn Docker to save the time to build the environment.\nLearn the professional course knowledge in advance, refer to the CS Self-Study Guide, it is suggested to learn computer science by doing the labs.\nWhen you finish the above, you should have a general direction of what to study in the future, you can also read My Computer Study Route.\n6. Ending This article is only expressing my personal opinion, I hope students will not change their opinion towards this major because of the shortcomings of the profession I mentioned, or blindly decide because of the so-called high salary. On the way of choosing a major, often a decision will greatly affect the future trajectory of your life.\nThe fact that you have the grades to get into a computer science college also means that you have the right to choose almost any major, so please choose your major carefully. After all, the final choice of major is still yours, so please make sure to make a decision based on your interests, family environment, future development and all kinds of factors, and remember that at this point, anyone, including your teachers, family and classmates, can only just provide advice at all.\nFour years of college life is neither too long nor too short. While enjoying the comfortable campus life, you have become a frog in warm water without realizing it.\nWe need to break away from the inertia of student thinking under the score measurement system, \u0026ldquo;Please be sure to remember: four years of college life gives you is just a kind of the undergo of your life, when you graduate, the string of pale scores has been invalidated.\u0026rdquo;.\n7. Remarks Here are some links related to this post.\nGuide of Choice SHU CES\nA Guide for newbies in Cyberspace Security\nThank you for reading here, after you have finished your blog, you can comment on this post below to contact me to add your link. Anyway, you are free to ask any questions about this post. If there is anything inappropriate, please comment/contact me and I will change it as soon as possible.\n","permalink":"https://chasing1020.github.io/post/about-computer-science/","summary":"\u003cp\u003eIt is a yearly \u0026ldquo;divide major\u0026rdquo; season, and now I am looking back at the series of computer science \u003ca href=\"https://www.zhihu.com/question/326615493/answer/711051528\"\u003equestions\u003c/a\u003e and answers I have browsed in Zhihu during my freshman year, and I feel a lot of emotions. I have been thinking about it for a long time, and this is the first time I have seen such numerous questions and discussions about computer science, hence I write about this post. If you find any bugs or grammar issues, please don\u0026rsquo;t hesitate to contact me to fix them. Anyway, thanks again for accepting my poor English.\u003c/p\u003e","title":"About Computer Science"},{"content":"1. Overview 2. Language Grammar 字母表（Alphabet）：$\\sum$是一个有穷符号集合，包括字母、数字、标点等。\n乘积（product）：字母表$\\sum_1$和$\\sum_2$的乘积（笛卡儿积）：$\\sum_1\\sum_2 = {ab|a\\in\\sum_1, b\\in\\sum_2}$\nn次幂（power）：字母表$\\sum$的n次幂：1. $\\sum^0={\\varepsilon}$; 2. $\\sum^n =\\sum^{n-1}\\sum, if\\ n \u0026gt; 1$\n正闭包（positive closure）：$\\sum^+ = \\sum \\cup \\sum^2 \\cup\\sum^3 \\cup\u0026hellip;$\n克林闭包（Kleene closure）：即任意符号串（允许长度0）的集合$\\sum^* = \\sum^{0} + \\sum^+ = \\sum^0 \\cup \\sum \\cup \\sum^2 \\cup \\sum^3 \\cup \u0026hellip;$\n串（string）：设$\\sum$是一个字母表，$\\forall x\\in \\sum^*$，x称为$\\sum$上的一个串，串是字母表中的符号的一个有穷序列。\n串的长度（length）：s的长度，记作|s|，即s中符号的个数，长度为0记作空串。\n连接（concatenation）：即y串附加到x串后面形成的串，空串是连接运算的单位元（identity）。\n串的n次幂：将n个串s连接起来：1. $s^0=\\varepsilon$; 2. $s^n = s^{n-1}s, n≥1$，\n文法的形式化定义：$G=(V_T,V_N,P,S)$，其中$V_T$：终结符集合；$V_N$：非终结符集合；P：产生式集合；S：开始符号\n终结符（terminal symbol）：所有文法定义的语言的基本符号，即token。\nExamples:\nIdentifier: strings of letters or digits, starting with a letter Integer: a non-empty string of digits Keyword: “else” or “if” or “begin” or … Whitespace: a non-empty sequence of blanks, newlines, and tabs 非终结符（nonterminal）：表示语法成分的符号，也称为“语法变量”\n产生式（production）：描述了将终结符和非终结符组合成串的方法产生式的一般形式：$\\alpha\\rightarrow\\beta$。其中，$\\alpha\\in(V_T\\cup V_N)^+$，且$\\alpha$至少包含$V_N$中的一个元素：称为产生时的头部（head）或者左部（left side）；$\\beta\\in(V_T\\cup V_N)^*$：称为产生式的体（body）或者右部（right side）。\n开始符号（start symbol）：$S\\in V_N$，表示的是该文法中最大的语法成分。\n产生式的简写：对于一组具有相同左部$\\alpha$的产生式$\\alpha \\rightarrow \\beta_1,\\alpha \\rightarrow \\beta_2,\u0026hellip;,\\alpha \\rightarrow \\beta_n$。\n可以简单计为$\\alpha \\rightarrow \\beta_1|\\beta_2|\u0026hellip;|\\beta_n$。这里的$\\beta_1,\\beta_2,\u0026hellip;,\\beta_n$称为$\\alpha$的候选式（candidate）。\n终结符约定：字母表中前面的小写字母；运算符；标点符号；数字；粗体字符串\n非终结符约定：字母表中前面的大写字母；S通常表示开始符号；小写、斜体的名字；代表程序构造的大写字母。\n文法符号：排在后面的大写字母。\n终结符号串：排在后面的小写字母（包括空串）。\n文法符号串：小写希腊字母（包括空串）。\n语言定义\n给定文法$G=(V_T,V_N,P,S)$，如果$\\alpha\\rightarrow\\beta\\in P$，那么可以将$\\gamma\\alpha\\delta$中的$\\alpha$换成$\\beta$。即重写为$\\gamma\\beta\\delta$，记作$\\gamma\\alpha\\delta\\Rightarrow\\gamma\\beta\\delta$。此时，称文法中的符号串$\\gamma\\alpha\\delta$直接推导（directly derive）$\\gamma\\beta\\delta$，即用产生式的右部替换产生式的左部。\n如果有$\\alpha_0 \\Rightarrow \\alpha_{1} , \\alpha_{1}\\Rightarrow \\alpha_{2} ,\u0026hellip;, \\alpha_{n-1} \\Rightarrow \\alpha_n $ ，则有$\\alpha_0\\Rightarrow\\alpha_1\\Rightarrow\\alpha_2\\Rightarrow\u0026hellip;\\Rightarrow\\alpha_n$，称符号串$\\alpha_0$经过n步推导出$\\alpha_n$，可以简记为$\\alpha_0\\Rightarrow^n\\alpha_n$。\n特别地：$\\alpha \\Rightarrow^0 \\alpha$，$\\Rightarrow^+$ 表示经过正数步推导，$\\Rightarrow^*$ 表示经过若干步（可以为0）推导。\n句子的推导：从生成语言的角度，句子的规约：从识别语言的角度。\n句型（sentential form）：如果$S\\Rightarrow^* \\alpha,\\alpha \\in (V_T\\cup V_N)^* $，则称$\\alpha$是G的一个句型。\n句型可以包含终结符，也可以包含非终结符，也可能是空串。 句子（sentence）：如果$S\\Rightarrow^* w,\\ w\\in V_T^*$，则称w是G的一个句子。\n句子是不包含非终结符的句型。 语言的形式化定义：由文法G开始的符号S推导出的所有句子构成的集合称为文法G生成的语言，记为L(G)。即：$L(G)={w|S\\Rightarrow^* w,\\ w\\in V_T^*}$。\n标识符：令$L={A,B,C,\u0026hellip;,Z,a,b,c,\u0026hellip;,z},D={0,1,2,\u0026hellip;,9}$，则$L(L\\cup D)^*$，表示的语言是标识符。\nChomsky文法分类体系\n0型文法（Type-0 Grammar）：无限制文法（Unrestricted Grammer）/短语结构文法（Phrase Structure Grammer, PSG）：$\\forall\\alpha\\rightarrow\\beta\\in P$，$\\alpha$中至少包含一个非终结符。\n0型语言：由0型文法G生成的语言L(G) 1型文法（Type-1 Grammar）：上下文有关文法（Context-Sensitive Grammar，CSG）：$\\forall\\alpha\\rightarrow\\beta\\in P,\\ |\\alpha|≤|\\beta|$，产生式的一般形式$\\alpha_1A\\alpha_2\\rightarrow\\alpha_1\\beta\\alpha_2(\\beta ≠ \\varepsilon)$\n1型语言（上下文有关语言）：由1型文法G生成的语言L(G)，CSG中不包含$\\varepsilon$产生式。 2型文法（Type-2 Grammar）：上下文无关文法（Context-Free Grammar, CFG）：$\\forall\\alpha\\rightarrow\\beta\\in P, \\alpha\\in V_N$，产生式的一般形式$A\\rightarrow\\beta$\n2型语言（上下文无关语言）：由2型文法G生成的语言L(G) 3型文法（Type-3 Grammar）：正则文法（Regular Grammar, RG），右线性（Right Linear）文法：$A\\rightarrow wB\\ or\\ A\\rightarrow w$，左线性（left Linear）文法：$A\\rightarrow Bw\\ or\\ A\\rightarrow w$，两者都称为正则文法。\n3型语言（正则语言）：由3型文法G生成的语言L(G) 四种语言的关系，逐级限制，逐级包含：\n0型文法：$\\alpha$中至少包含一个非终结符 1型文法：$|\\alpha|≤|\\beta|$ 2型文法：$\\alpha\\in V_N$ 3型文法：$A\\rightarrow wB\\ or\\ A\\rightarrow w$（或$A\\rightarrow Bw\\ or\\ A\\rightarrow w$） CFG 分析树\n根节点标号既是文法开始符号\n内部节点表示对一个产生式$A\\rightarrow\\beta$的应用\n叶结点标号既可以是非终结符，也可以是终结符。从左到右排列叶结点得到的符号串称为是这颗树的产出（yield）或边缘（frontier）。\n如果一个文法可以为某个句子生成多棵分析树，则称该文法存在二义性\n3. Lexical Analysis 正则表达式定义\n$\\varepsilon$是一个RE，$L(\\varepsilon) = {\\varepsilon}$；如果$\\alpha\\in \\sum$，则$\\alpha$是一个RE，$L(\\alpha) = {\\alpha}$。\n假设r, s都是RE，表示的语言分别是L(r), L(s)，则$r|s, rs, r*, (r)$都是RE。\n正则语言（Regular language）或正则集合（Regular language）\n定律 描述 $r s = s $r (s $r(st)=r(st)$ 连接可结合 $r(s t) = rs $\\varepsilon r=r\\varepsilon =r$ $\\varepsilon$是连接的单位元 $r^* = (r｜\\varepsilon )^*$ 闭包中一定含有$\\varepsilon$ $r^{**} = r^*$ *具有幂等性 正则文法和正则表达式等价，任意正则文法G，存在定义统一语言的正则表达式r，反之亦然。\n正则定义（regular definition）：指具有如下形式的定义序列$d_1 \\rightarrow r_1,d_2 \\rightarrow r_2,\u0026hellip;,d_n \\rightarrow r_n$\n其中每个$d_i$都是一个新符号，他们都不在字母表$\\sum$中，而且各不相同。 每个$r_i$都是字母表$\\sum \\cup {d_1, d2_, \u0026hellip;, d_i-1}$上的正则表达式。 Exp: C语言的标识符 $digit\\rightarrow 0|1|2|\u0026hellip;|9$ $letter \\rightarrow A|B|\u0026hellip;|Z|a|b|\u0026hellip;|z|$_ $identification \\rightarrow letter(letter|digit)*$ 单词识别\n有穷自动机（Finite Automata, FA）：系统根据当前状态和输入信息决定系统后继行为。\n转换图（Transition Graph）：结点表示FA状态，初始状态用start表示，终止状态为双圈，有向边带标记表示转换条件。\n确定的有穷自动机（Deterministic finite automata, DFA）：定义$M=(S,\\sum,\\delta,s_0 ,F)$\nS：有穷状态集 $\\sum$：输入字母表，即输入符号集合。$\\varepsilon$不是$\\sum$中的元素 $\\delta$：将$S*\\sum$映射到$S$的转换函数。$\\forall s\\in S, a\\in \\sum, \\delta (s, a)$表示从状态s出发，沿着a边能到达的状态 $s_0$：开始状态，$s_0\\in S$ $F$：接受状态集合，$F\\subsetneqq S$ 非确定的又穷自动机（Nondeterministic finite automata, NFA）：定义$M=(S,\\sum,\\delta,s_0 ,F)$\nS：有穷状态集 $\\sum$：输入字母表，即输入符号集合。$\\varepsilon$不是$\\sum$中的元素 $\\delta$：将$S*\\sum$映射到 $2^S$ 的转换函数。$\\forall s\\in S, a\\in \\sum, \\delta (s, a)$表示从状态s出发，沿着a边能到达的状态的集合 如果是带有空边的NFA，则$\\delta$：将$S*(\\sum \\cup{\\varepsilon})$映射到 $2^S$ 的转换函数。$\\forall s\\in S, a\\in \\sum \\cup {\\varepsilon}, \\delta (s, a)$表示从状态s出发，沿着a边能到达的状态的集合 $s_0$：开始状态，$s_0\\in S$ $F$：接受状态集合，$F\\subsetneqq S$ DFA和NFA唯一确定并且相互等价。且带有和不带有$\\varepsilon$边的NFA等价。DFA每个状态都是NFA状态构成的一个集合。在Go中，采用scanner结构体next方法作为DFA的输入，获取程序Token。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 // next advances the scanner by reading the next token. func (s *scanner) next() { nlsemi := s.nlsemi s.nlsemi = false redo: // skip white space s.stop() startLine, startCol := s.pos() for s.ch == \u0026#39; \u0026#39; || s.ch == \u0026#39;\\t\u0026#39; || s.ch == \u0026#39;\\n\u0026#39; \u0026amp;\u0026amp; !nlsemi || s.ch == \u0026#39;\\r\u0026#39; { s.nextch() } // token start s.line, s.col = s.pos() s.blank = s.line \u0026gt; startLine || startCol == colbase s.start() if isLetter(s.ch) || s.ch \u0026gt;= utf8.RuneSelf \u0026amp;\u0026amp; s.atIdentChar(true) { s.nextch() s.ident() return } switch s.ch { case -1: // omit default: s.errorf(\u0026#34;invalid character %#U\u0026#34;, s.ch) s.nextch() goto redo } return assignop: if s.ch == \u0026#39;=\u0026#39; { s.nextch() s.tok = _AssignOp return } s.tok = _Operator } 4. Sytax Analysis 自顶向下分析\n从分析树的顶部向底部方向构造分析树，可以看成是从文法开始符号S推导出词串w的过程。\n最左推导：每次选择每个句型的最左非终结符替换，$S\\Rightarrow^*_{left most}\\alpha$，则称$\\alpha$是当前文法的最左句型（left- sentential form）。\n最右推导：总是选择每个句型的最右非终结符进行替换。\n预测分析：在输入中向前看固定个数符号来选择正确的A-产生式，不需要回溯，将向前看k个这些文法类可以记$LL(K)$文法类。\n左递归文法：存在一个非终结符$A$，使得对某个串有$A\\Rightarrow ^+A\\alpha$，那么这个文法是左递归的，会使得递归下降分析器进入无限循环。\n消除左递归：引入非终结符和$_\\varepsilon$产生式为代价。伪代码如下\n1 2 3 4 5 6 7 8 A := make(NonTerminal, n) for i := range A { for j := 0; j \u0026lt; i; j++ { // 将A[i]-\u0026gt;A[j]y的产生式替换成产生式组A[i]-\u0026gt;d[1]y|d[2]y|...|d[k]y // A[j]-\u0026gt;d[1]|d[2]|...|d[k]，是所有A[j]产生式 } // 消除A[i]产生式之间的立即左递归 } 提取左公因子算法：改写产生式来推迟决定，如果多个选项有公共最长前缀$\\alpha, \\alpha ≠ \\varepsilon$，即存在非平凡（nontrivial）的公共前缀，则可以将所有A-产生式替换。\nS_文法：不含$\\varepsilon$产生式，候选式唯一，每个产生式右部以终结符开始，同一非终结符的各个候选式的首终结符都不同。\n后继符号集：可能在某个句型中紧跟A后边的终结符a的集合，记为$FOLLOW(A)$，如果A是最右符号，则还要添加结束符\u0026quot;$\u0026quot;。\n$FOLLOW(A) = {a|S\\Rightarrow ^* \\alpha Aa\\beta, a\\in V_T, \\alpha,\\beta \\in (V_T \\cup V_N )^* }$ 产生式的可选集：产生式$A\\rightarrow \\beta$的可选集是指可以用改产生式进行推导的对应输入符号的集合，记为$SELECT(A\\rightarrow \\beta)$。\n$SELECT(A\\rightarrow \\alpha \\beta) = {\\alpha}; SELECT(A\\rightarrow \\varepsilon) = FOLLOW(A)$ q_文法：每个产生式的右部要么是$\\varepsilon$要么是终结符开始，具有相同左部的产生式有不相交的可选集。\n串首终结符集：给定一个文法符号串$\\alpha$，$\\alpha$串首终结符集$FIRST(\\alpha)$被定义为可以从$\\alpha$推导出的所有串终结符构成的集合。如果$\\alpha\\Rightarrow ^* \\varepsilon$，则$\\alpha$也在$FIRST(\\alpha)$中。\n$\\forall \\alpha \\in (V_T \\cup V_N)^+ , FIRST(\\alpha)={a|\\alpha \\Rightarrow^* a\\beta, a\\in V_T, \\beta \\in (V_T \\cup V_N)^*}$ 如果$\\alpha \\Rightarrow ^* \\varepsilon$，那么$\\varepsilon \\in FIRST(\\alpha)$ 如果$\\varepsilon \\notin FIRST(\\alpha)$，那么$SELECT(A\\rightarrow \\alpha) = FIRST(\\alpha)$ 如果$\\varepsilon \\in FIRST(\\alpha)$，那么$SELECT(A\\rightarrow \\alpha)=(FIRST(\\alpha)-{\\varepsilon})\\cup FOLLOW(A)$ $LL(1)$文法：文法G是$LL(1)$当前仅当任意两个相同的左部产生式$A\\rightarrow \\alpha|\\beta$满足以下条件：\n如果$\\alpha$和$\\beta$都不能推导出$\\varepsilon$，则$FIRST(\\alpha)\\cap FRIST(\\beta) = \\Phi$ $\\alpha$和$\\beta$至多有一个能够推导出$\\varepsilon$ 如果$\\beta \\Rightarrow ^* \\varepsilon$，则$FIRST(\\alpha)\\cap FOLLOW(A) = \\Phi$ 如果$\\alpha \\Rightarrow ^* \\varepsilon$，则$FIRST(\\beta)\\cap FOLLOW(A) = \\Phi$ 递归的预测分析法：在递归下降分析中，根据预测分析表进行产生式的选择，为每个终结符编写对应过程\n1 2 3 4 5 6 7 8 9 10 11 12 13 func PredictRecursion() { A := SelectExpression() // A-\u0026gt;X[1]X[2]...X[k] for i := 0; i \u0026lt; k; i++ { switch A.X[i].(type) { case NonTerminal: CallExpression(A.X[i]) case Terminal: ReadNext() default: panic(\u0026#34;type assert error\u0026#34;) } } } 非递归预测分析法：为预测分析表构造一个自动机。\n预测分析法实现步骤：构造文法；改造文法，消除二义性、消除左递归、消除回溯；求每个变量的$FIRST, FOLLOW$集，进而得到$SELECT$集；检查是不是$LL(1)$文法，如果是，构造预测分析表；进行递归/非递归分析。\n自底向上的语法分析\n从分析树的底部向顶部方向构造分析树，可以看成是将输入串w规约为文法开始符号S的过程。\n移入-归约：将输入串从左到右的扫描中，将0个或多个输入符号移入到栈的顶端，知道能进行归约为止。\nLR文法（Knuth，1963）：最大的、可以构造出相应移入-归约语法分析器的文法类，LR(k)表示需要向前查看k个输入符号的LR分析。\n采用“状态”的自动机表示句柄识别进度 LR(0)项目：右部某位置标有圆点的产生式，描述了句柄识别状态。\n圆点后面为非终结符：移进项目 圆点后面为终结符：待约项目 圆点后面为空串：归约项目 增广语法：如果G是S开始符号的文法，则G的增光文法G\u0026rsquo;是G中加上开始符号S\u0026rsquo;和产生式$S\u0026rsquo;\\rightarrow S$的文法，使得文法开始符号仅出现在一个产生式的左边，从而使得分析器只有一个接受状态。\n项目集的闭包：给定项目集I，有\n$CLOSURE(I) = I \\cup {B\\rightarrow ·\\gamma|A \\rightarrow \\alpha·B\\beta \\in CLOSURE(I), B\\rightarrow \\gamma \\in P }$ 后继项目集闭包：给定项目集I对应文法符号X的后继项目集闭包\n$GOTO(I, X) = CLOSURE({A\\rightarrow \\alpha X·\\beta | A\\rightarrow \\alpha·X\\beta \\in I})$ 规范LR(0)项目集族（Canonical LR(0) Collection）：\n$C=\\{ I_0 \\}\\cup \\{I |\\exists J\\in C, X\\in V_N \\cup V_T, I=GOTO(J, X) \\}$ LR(0)自动机：文法$G=(V_N, V_T, P, S)$，则LR(0)自动机定义如下\n$M=(C, V_N\\cup V_T, GOTO, I_0,F)$ $C ={ I_0 }\\cup {I|\\exists J\\in C, X\\in V_N \\cup V_T, I = GOTO(J,X) }$ $I_0=CLOSURE({S\u0026rsquo;\\rightarrow .S})$ $F={CLOSURE({S\u0026rsquo;\\rightarrow S.})}$ SLR分析：已知项目集I有m个移进项目，n个归约项目\n$A_1 \\rightarrow \\alpha_1 · a_1 \\beta_1; A_2 \\rightarrow \\alpha_2 · a_2 \\beta_2;\u0026hellip;;A_m \\rightarrow \\alpha_m · a_m \\beta_m.$\n$B_1\\rightarrow \\gamma_1; B_2\\rightarrow \\gamma_2; \u0026hellip; ;B_n\\rightarrow \\gamma_n;$\n如果集合${a_1,a_2,\u0026hellip;,a_m}$和$FOLLOW(B_1), FOLLOW(B_2), \u0026hellip;, FOLLOW(B_n)$两两不相交，按原则如果a是下一个输入，$if\\ a \\in { a_1,a_2,\u0026hellip;,a_n }$，则移进a，$if\\ a\\in \\cup FOLLOW(B_i)$，则用$B_i\\rightarrow \\gamma_i$归约，否则报错。\nLR(1)项目：$[A\\rightarrow \\alpha ·\\beta]$，的项称为LR(1)项，其中a是一个终结符，表示当前状态下A必须紧跟的终结符，称为该项的展望符。\n$CLOUSURE(I)=I\\cup{[B\\rightarrow ·\\gamma,b]|[A\\rightarrow \\alpha·B\\beta, a]\\in CLOSURE(I), B\\rightarrow \\gamma \\in P, b \\in FIRST(\\beta a) }$\n$GOTO(I,X)=CLOSURE({[A\\rightarrow \\alpha X·\\beta, a]|[A\\rightarrow \\alpha ·X \\beta ]\\in I})$\nLALR(lookahead-LR)分析思想：找具有相同核心的LR(1)项集，合并；最终分析表如果没有冲突，则为LALR(1)文法。\n形式上与LR(1)相同，大小和LR(0)/SLR相当，分析能力SLR\u0026lt;LALR(1)\u0026lt;LR(1) 5. Sytax-Directed Translation 面向文法翻译：以CFG的文法符号设置语义属性，用来表述语法成分对应的语义信息，基于语义规则来进行计算。\n语法制导定义（Syntax-Directed Definitions, SDD）：将每个文法符号和语义属性集合相关联，每个产生式和一组语义规则相关联。\n语法制导翻译方案（Syntax-Directed Translation, SDT）：在产生式右部嵌入程序片段的CFG（位置距决定执行时间），被称之为语义动作，是SDD具体的方案。\n综合属性：只能通过子结点或者本身的属性值来定义，终结符可以具有综合属性（由词法分析器提供的词法值）。\n继承属性：在分析树上继承属性只能由该结点的父亲，兄弟或者自身属性值来定义。\n属性文法：没有副作用SDD。\n属性值计算顺序：将有向图变为拓扑排序，从无依赖的结点出发。\nS-SDD：仅仅使用综合属性的SDD，可以自底向上顺序来计算它各个节点的属性值。将每一个语义动作放在产生式最后即可转换为SDT。\nL-SDD：在各属性之间，依赖图的边可以从左到右但是不能从右到左，不能从右到左，每一个S-SDD都是L-SDD。将继承属性紧靠在出现之前的位置，综合属性放在最右端即可转换为SDT。\n6. Intermediate Code Generation 类型表达式（Type Expressions）：可以为其命名，类型名也是表达式，包括数组构造符、指针、笛卡尔积、函数构造符、记录构造符（结构体内的标识符与类型信息的笛卡尔积）。\n语义分析期间，对每一个标识符都会收集类型信息，确定这个类型的宽度，并分配一个相对地址，保存在符号表的记录中。\n","permalink":"https://chasing1020.github.io/post/compilers/","summary":"\u003ch1 id=\"1-overview\"\u003e1. Overview\u003c/h1\u003e\n\u003ch1 id=\"2-language-grammar\"\u003e2. Language Grammar\u003c/h1\u003e\n\u003cp\u003e字母表（Alphabet）：$\\sum$是一个有穷符号集合，包括字母、数字、标点等。\u003c/p\u003e\n\u003cp\u003e乘积（product）：字母表$\\sum_1$和$\\sum_2$的乘积（笛卡儿积）：$\\sum_1\\sum_2 = {ab|a\\in\\sum_1, b\\in\\sum_2}$\u003c/p\u003e","title":"Compilers"},{"content":"Overview Software is more than just a program code. It is considered to be collection of executable programming code, associated libraries and documentations.\nEngineering on the other hand, is all about developing products, using well-defined, scientific principles and methods.\nIEEE Definition\n(1) The application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software; that is, the application of engineering to software.\n(2) The study of approaches as in the above statement.\nEvolution: Change Request \u0026gt; Impact Analysis \u0026gt; Release Planning \u0026gt; System update \u0026gt; System Release.\nThree categories\nStatic-type (S-type): This is a software, which works strictly according to defined specifications and solutions. For example, calculator program for mathematical computation .\nPractical-type (P-type) : This is a software with a collection of procedures which specifications can be described but the solution is not obviously instant. For example, gaming software.\nEmbedded-type (E-type): This software works closely as the requirement of real-world environment. For example, Online trading software.\nNeeds of Software Engineering: Large, Scalability, Cost, Dynamic Nature, Quality Management.\nDevelopment Life Cycle Software Life Cycle Activities: Communication, Requirement Gathering, Feasibility Study, System Analysis, Software Design, Coding, Testing, Integration, Implementation, Operations \u0026amp; Maintenance, Disposition.\nCommunication: This is the first step where the user initiates the request for a desired software product.\nRequirement Gathering: This step onwards the software development team works to carry on the project.\nFeasibility Study: After requirement gathering, the team comes up with a rough plan of software process.\nSystem Analysis: At this step the developers decide a roadmap of their plan and try to bring up the best software model suitable for the project.\nSoftware Design: Next step is to bring down whole knowledge of requirements and analysis on the desk and design the software product.\nCoding: This step is also known as programming phase.\nTesting: An estimate says that 50% of whole software development process should be tested.\nIntegration: Software may need to be integrated with the libraries, databases, and other program(s).\nImplementation: This means installing the software on user machines.\nOperation and Maintenance: This phase confirms the software operation in terms of more efficiency and less errors.\nWaterfall Model\nWaterfall model is the simplest model of software development paradigm. This model assumes that everything is carried out and taken place perfectly as planned in the previous stage and there is no need to think about the past issues that may arise in the next phase. This model does not work smoothly if there are some issues left at the previous step. The sequential nature of model does not allow us to go back and undo or redo our actions.\nThis model is best suited when developers already have designed and developed similar software in the past and are aware of all its domains.\nInteractive Model\nThis model leads the software development process in iterations. The software is first developed on very small scale and all the steps are followed which are taken into consideration. Then, on every next iteration, more features and modules are designed, coded, tested, and added to the software. Every cycle produces a software, which is complete in itself and has more features and capabilities than that of the previous one. After each iteration, the management team can do work on risk management and prepare for the next iteration. Because a cycle includes small portion of whole software process, it is easier to manage the development process but it consumes more resources.\nSpiral Model\nSpiral model is a combination of both, iterative model and one of the SDLC model.\nThis model considers risk, which often goes un-noticed by most other models. The model starts with determining objectives and constraints of the software at the start of one iteration. Next phase is of prototyping the software. This includes risk analysis. Then one standard SDLC model is used to build the software. In the fourth phase of the plan of next iteration is prepared.\nV Model\nThe major drawback of waterfall model is we move to the next stage only when the previous one is finished and there was no chance to go back if something is found wrong in later stages. V-Model provides means of testing of software at each stage in reverse manner.\nAt every stage, test plans and test cases are created to verify and validate the product according to the requirement of that stage. For example, in requirement gathering stage the test team prepares all the test cases in correspondence to the requirements. Later, when the product is developed and is ready for testing, test cases of this stage verify the software against its validity towards requirements at this stage.\nBig Bang Model\nThis model is the simplest model in its form. It requires little planning, lots of programming and lots of funds. This model is conceptualized around the big bang of universe. As scientists say that after big bang lots of galaxies, planets, and stars evolved just as an event. Likewise, if we put together lots of programming and funds, you may achieve the best software product.\nBig Bang Model\nThis model is the simplest model in its form. It requires little planning, lots of programming and lots of funds. This model is conceptualized around the big bang of universe. As scientists say that after big bang lots of galaxies, planets, and stars evolved just as an event. Likewise, if we put together lots of programming and funds, you may achieve the best software product.\nProject Management A Software Project is the complete procedure of software development from requirement gathering to testing and maintenance, carried out according to the execution methodologies, in a specified period of time to achieve intended software product.\nIt is an essential part of software organization to deliver quality product, keeping the cost within client’s budget constrain and deliver the project as per scheduled.\nSoftware Management Activities\nProject Planning: Software project planning is task, which is performed before the production of software actually starts. It is there for the software production but involves no concrete activity that has any direct connection with the software production; rather it is a set of multiple processes, which facilitates software production. Project planning may include the following:\nScope Management: It defines scope of the project; this includes all the activities, process need to be done in order to make a deliverable software product. Scope management is essential because it creates boundaries of the project by clearly defining what would be done in the project and what would not be done. This makes project to contain limited and quantifiable tasks, which can easily be documented and in turn avoids cost and time overrun. During Project Scope management, it is necessary to: 1. Define the scope; 2. Decide its verification and control; 3. Divide the project into various smaller parts for ease of management.\nProject Estimation: For an effective management, accurate estimation of various measures is a must. With the correct estimation, managers can manage and control the project more efficiently and effectively. Project estimation may involve the following: 1. Software size estimation; 2. Effort estimation; 3. Time estimation; 4. Cost estimation\nDecomposition Technique: 1. Line of Code: Here the estimation is done on behalf of number of line of codes in the software product. 2. Function Points: Here the estimation is done on behalf of number of function points in the software product.\nGantt Chart\nIt is a horizontal bar chart with bars representing activities and time scheduled for the project activities.\nPERT(Program Evaluation \u0026amp; Review Technique) Chart\nEvents are shown as numbered nodes. They are connected by labeled arrows depicting the sequence of tasks in the project.\nSoftware Requirements Requirements Engineering Process takes four steps: Feasibility Study; Requirement Gathering; Software Requirement Specification; Software Requirement Validation.\nRequirement Elicitation Process\nRequirements gathering - The developers discuss with the client and end users and know their expectations from the software.\nOrganizing Requirements - The developers prioritize and arrange the requirements in order of importance, urgency and convenience.\nNegotiation \u0026amp; discussion - If requirements are ambiguous or there are some conflicts in requirements of various stakeholders, it is then negotiated and discussed with the stakeholders. Requirements may then be prioritized and reasonably compromised.\nThe requirements come from various stakeholders. To remove the ambiguity and conflicts, they are discussed for clarity and correctness. Unrealistic requirements are compromised reasonably.\nDocumentation - All formal and informal, functional and non-functional requirements are documented and made available for next phase processing.\nDesign Basics ","permalink":"https://chasing1020.github.io/post/software-engineering/","summary":"\u003ch1 id=\"overview\"\u003eOverview\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003eSoftware\u003c/strong\u003e is more than just a program code. It is considered to be collection of executable programming code, associated libraries and documentations.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eEngineering\u003c/strong\u003e on the other hand, is all about developing products, using well-defined, scientific principles and methods.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIEEE Definition\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e(1) The application of a systematic, disciplined, quantifiable approach to the development, operation, and maintenance of software; that is, the application of engineering to software.\u003c/p\u003e\n\u003cp\u003e(2) The study of approaches as in the above statement.\u003c/p\u003e","title":"Software Engineering"},{"content":"The gets() function cannot be used securely. Because of its lack of bounds checking, and the inability of the calling program to reliably determine the length of the next incoming line, the use of this function enables malicious users to arbitrarily change a running program\u0026rsquo;s functionality through a buffer overflow attack. It is strongly suggested that the fgets() function be used in all cases.\nHow can we find and/or prevent problems like this?\nDynamic analysis: Run the program, watch what it does, and look for problematic behavior. Static analysis: read the source code and try to spot the issues. Write code differently: create habits and frameworks that make it harder to produce these kinds of mistakes. Sandbox: accept that these issues will happen, but try to minimize the consequences. ","permalink":"https://chasing1020.github.io/post/safety-in-systems-programming/","summary":"\u003cp\u003eThe gets() function cannot be used securely.  Because of its lack of bounds checking,\nand the inability of the calling program to reliably determine the length of the\nnext incoming line, the use of this function enables malicious users to arbitrarily\nchange a running program\u0026rsquo;s functionality through a buffer overflow attack.  It is\nstrongly suggested that the fgets() function be used in all cases.\u003c/p\u003e\n\u003cp\u003eHow can we find and/or prevent problems like this?\u003c/p\u003e","title":"Safety in Systems Programming"},{"content":"1. Query Engine 1.1. Relational Model 关系型数据库是建模现实世界的关联数据的有组织的集合。最早期数据库难于构建，逻辑与物理层强耦合。\n查询流程\nSQL（Parser）：词法分析和语法分析。\nAST（Logical Optimizer）：基于关系代数表达式的逻辑计划，基于规则的优化，依据关系代数的等价交换原则做逻辑变换。\nLogical Plan（Physical Optimizer）：基于代价的优化，依据统计信息，对数据读取，表连接方式，连接顺序等进行优化；计算多种可能的执行计划的代价，生成代价最小的物理执行计划\nPhysical Plan（Executor）：采取串行或者是并行的方式执行Operator，自顶向下或者自顶向上返回数据。\n关系代数等价原则\n在满足生成相同结果集的条件下，依据关系代数的等价交换原则做逻辑交换，包括：谓词下推（尽早进行过滤；分解复杂条件；笛卡尔积转Join），投影下推，排序、Limit、聚合下推，投影消除，排序消除等。\n非关联子查询（子查询不包含非子查询的列）：将子查询展开改写，对于包含IN的子查询转为JOIN\n关联子查询（子查询包含非子查询的列）：优化器添加Apply Operator，将整个包含子查询的表达式转移到Apply的右子树上，尽力将Apply转化为等价的JOIN。需要将INNER PLAN包含相关的列的算子提前到Apply中或之上。\n1.2. Execution Engine 1.3. Storage 插入流程：\nClient发起请求，查询处理器使用数据字典和统计信息生成最有执行计划 调用事务管理相关逻辑，如锁、分配事务号时间戳等 调用文件管理器WAL，向缓存区管理器插入数据和索引 检索流程：\nClient发起请求，查询处理器使用数据字典和统计信息生成最有执行计划 调用事务管理相关逻辑，如锁、分配事务号时间戳等 调用缓存区管理器查询索引或者是数据的缓存，向文件管理器查询数据和索引，并且写入缓存 存储形式组织\n列存储：方便数据压缩，适用于OLAP，或者是查询只涉及部分列。\n如果涉及到多个不同的列，就需要多次IO来组合最后的记录\n数据粒度Page：将多行数据聚合Page，用于传统的MySQL，PostgreSQL等。存在读写放大问题，元数据少。\n数据粒度Tuple：适用于新型NVM介质，但行记录就是最小的访问单元，减少Tuple和Page之间的编解码，元数据多。\n索引组织结构\nB-Tree：\n优点：1. 非叶子结点不包含数据，高度小，单次请求设计的磁盘IO次数少； 2. 每次查询都要到叶子结点，路径长度相同，效率稳定； 3. 所有查询都从根节点出发，范围查询效率高。\n缺点：分裂时，逻辑连续的叶子节点在物理上不连续，会产生大量随机的IO，影响性能。\nLSM（Log- Structured Merge Tree）\n通过内存插入与磁盘顺序写一致，大幅提升写操作，适用于HBase，效率比MySQL高一个量级。\n位图索引：\n统计范围速度较快，不适用于范围广、或者频繁更新的列\n2. Log 2.1. Undo Log Undo存储位置，在Undo Tablespace的页面，和正常的表空间一样，修改也由redolog记录，所以事物会滚时会操作两边redolog。落盘时，也和普通的数据页一样，由backgroud thread定期从缓冲区刷入磁盘。\n","permalink":"https://chasing1020.github.io/post/advanced-mysql/","summary":"\u003ch1 id=\"1-query-engine\"\u003e1. Query Engine\u003c/h1\u003e\n\u003ch2 id=\"11-relational-model\"\u003e1.1. Relational Model\u003c/h2\u003e\n\u003cp\u003e关系型数据库是建模现实世界的关联数据的有组织的集合。最早期数据库难于构建，逻辑与物理层强耦合。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e查询流程\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eSQL（Parser）：词法分析和语法分析。\u003c/p\u003e","title":"Advanced MySQL"},{"content":"1. Overview 最初的TCP因为不支持拥塞控制而频繁被丢弃数据包，协议栈被投入使用8年后，Van Jacobson在1988年将 TCP 拥塞控制引入网络。\nIP层并没有提供拥塞控制功能，各个主机不知道什么是合理的速度。理想场景利用负反馈控制窗口，每一个TCP连接，引入变量CongestionWindow与SlowStartThreshold。\n2. Tahoe 定义拥塞发生事件：超时或者是3个冗余ACK。MSS：Maximum Segment Size\nSlowStart（SS）状态：\n每一次RTT，cwnd\u0026gt;\u0026gt;=1，保持SS。 超时或者是3-ACK：重发，cwnd=1MSS，ssthresh=cwnd\u0026gt;\u0026gt;1，保持SS。 如果达到警戒阈值，进入CA。 CongestionAvoidance（CA）状态：\n每一次RTT，cwnd倍增，保持CA。 超时或者是3-ACK：重发，cwnd=1MSS，进入SS。 3. Reno 定义新的状态：FastRecovery，考虑到2-ACK必定乱序造成的，丢包肯定会造成2-ACK。但是超时的情况下必定会进入SS。\nSlowStart（SS）状态：\n每一次RTT，cwnd倍增。 超时：重发，cwnd=1MSS，ssthresh=cwnd\u0026gt;\u0026gt;1，保持SS。 3-ACK：快速重传，ssthresh=cwnd\u0026gt;\u0026gt;1，cwnd=ssthresh+3，进入FR。 如果达到警戒阈值，进入CA。 CongestionAvoidance（CA）状态：\n每一次RTT，cwnd倍增。\n超时：重发，cwnd=1MSS，进入SS。\n3-ACK：ssthresh=cwnd\u0026gt;\u0026gt;1，cwnd=ssthresh+3，重传，进入FR。\nFastRecovery（FR）状态：\ndupACK: cwnd=cwnd+1MSS，保持FR。 newACK：cwnd=ssthresth，进入CA。 超时：ssthtresh=cwnd\u0026gt;\u0026gt;1，cwnd=1，重传，进入SS。 4. New Reno Reno存在的问题：从FR恢复过快，但是实际上在拥塞时分组是成串被丢弃的，后面段的丢失，超时后还是会进入进入SS，使得cwnd又回到1。\n改进：由发送方记住缺少确认的段，当这些缺少的段都被确认后，再走出FR状态。\nFastRecovery（FR）状态：\ndupACK：同上，cwnd=cwnd+1，保持FR\n部分确认（PACK）：收到部分新确认，保持FR\n发送确认后面的段，冗余ACK数量=0， 定时器复位不要超时了， cwnd=cwnd+1\n有新段可以发送，发送新的段\n恢复确认（RACK）：收到所有拥塞时未确认的段确认，cwnd = ssthresh ，定时器复位，进入CA阶段\n存在问题：只能恢复一个段的丢失。\n考虑RFC793，在TCP头部保留有Options\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | | 16 bit | 16 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | | 32 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | | 32 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | |Offset | Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | |4 bits | 6 bits | 6 bits | 16 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | | 16 bits | 16 bits | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | | variable length | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 通过SACK，如接收方给出哪些段收到了，哪些段乱序到达了等信息给发送方。发送端一次发送多个丢失段，每RTT可以重传多个丢失段，提升效率。修改TCP首部的Options如下：\n1 2 3 4 5 6 7 8 9 10 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | nop | nop | SACK(5) | L = 10 | | 8 bits | 8 bits | 8 bits | 8 bits | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Left Edge | | 32 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Right Edge | | 32 bit | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ SACK（Several ACK）：在NewReno的基础上，使用pipe=待确认的段数量（ 在管道中已发送出去的段数） th=cwnd\u0026gt;\u0026gt;1，cwnd=th+3。pipe不能够太满，也不能够太少。\n5. Cubic WIP\n","permalink":"https://chasing1020.github.io/post/tcp-congestion-control/","summary":"\u003ch1 id=\"1-overview\"\u003e1. Overview\u003c/h1\u003e\n\u003cp\u003e最初的TCP因为不支持拥塞控制而频繁被丢弃数据包，协议栈被投入使用8年后，Van Jacobson在1988年将 \u003ca href=\"http://www.cs.binghamton.edu/~nael/cs428-528/deeper/jacobson-congestion.pdf\"\u003eTCP 拥塞控制\u003c/a\u003e引入网络。\u003c/p\u003e\n\u003cp\u003eIP层并没有提供拥塞控制功能，各个主机不知道什么是合理的速度。理想场景利用负反馈控制窗口，每一个TCP连接，引入变量\u003ccode\u003eCongestionWindow\u003c/code\u003e与\u003ccode\u003eSlowStartThreshold\u003c/code\u003e。\u003c/p\u003e","title":"TCP Congestion Control"},{"content":"The Eight Fallacies of Distributed Computing by Peter Deutsch.\nEssentially everyone, when they first build a distributed application, makes the following eight assumptions. All prove to be false in the long run and all cause big trouble and painful learning experiences.\nThe network is reliable Latency is zero Bandwidth is infinite The network is secure Topology doesn\u0026rsquo;t change There is one administrator Transport cost is zero The network is homogeneous 1. 2PC XA规范中定义的分布式事务模型包括四个组成部分：\nRM（Resource Manager，资源管理器），负责管理分布式系统中的部分数据资源，保障该部分数据的一致性，满足规范要求的数据管理系统均可作为RM参与分布式事务，最典型的应用是数据库，如MySQL、Oracle、SQLServer等均支持该规范 TM（Transaction Manager，事务管理器），负责协调跨RM的全局事务的开启、提交和回滚 AP（Application Program，应用程序），通过TM定义事务边界，执行全局事务 CRM（Communication Resource Managers，通信管理器），负责全局事务过程中的跨节点通信 二阶段提交是一种强一致性的设计。设置一个中心的协调者（Coordinator，也称Transaction Manager，TM）与多个被调度的业务节点参与者（Participant，也称Resource Manager，RM）。\n第一阶段（prepare）：\nTM记录事务开始日志。 向所有RM发送Prepare消息，等待响应。 每个参与者都执行事务，记录Undo/Redo日志，向TM返回结果，RM并不提交事务。 TM记录准备完成日志。 第二阶段（if commit）：\n当事务管理者(TM)确认所有参与者(RM)都ready后，TM记录事务提交日志。 TM向所有RM发送commit命令。 RM提交事务，向TM返回执行结果。 TM记录事务结束日志。 第二阶段（if rollback）：\n当事务管理者(TM)确认有任一参与者(RM)失败或超时后，TM记录事务会滚日志。 TM向所有RM发送rollback命令。 RM回滚事务，向TM返回执行结果。 TM记录事务结束日志。 2PC是对业务侵入性较小的强一致性的保证。对于MySQL，XA执行过程中，对对应的资源都要加锁，阻塞其他事务访问。并且TM很容易发生单点故障，此时便会存在数据不一致与不确定性。\n2. Saga Saga理论基础来源于Hector \u0026amp; Kenneth 在1987年发表的论⽂《SAGAS》。它把分布式事务看作一组本地分支事务构成的事务链，业务流程中每个参与者都提交本地事务。在执行链中任何一个失败，则反方向进行补偿操作。\n补偿是子事务的提交，对线上其他事务可见，即：已经产生了影响，只能尽可能补偿。 Saga是满足了BASE，并不支持隔离性，可能会发生脏读脏写。吞吐量较高，一阶段提交本地事务，无锁，高性能。事件驱动架构，参与者可异步执行，而且子事务并不一定都需要是DB相关操作。\n3. TCC TCC（Try-Confirm-Cancel）理论源于 Pat Helland 在2007年发表的论文《Life beyond Distributed Transactions:an Apostate’s Opinion》。其将支持把自定义的分支事务纳入到全局事务的管理中\n全局事务是由若干分支事务组成的，分支事务要满足2PC模型的要求。\n将TM变成多节点，引入超时补偿的概念，并不会锁住所有资源。\nTry 阶段：完成所有业务检查，预留必须业务资源。 Confirm 阶段：确认执行真正执行业务，只使用 Try 阶段预留的业务资源。一旦异常，发现事务提交标记，重试所有Confirm操作（需要保证幂等性）。 Cancel 阶段：取消执行，释放 Try 阶段预留的业务资源。一旦异常，发现事务会滚标记，重试所有Cancel操作（需要保证幂等性）。 TCC满足BASE，相较于2PC，吞吐性、可用性更高。在业务层面保证隔离性。\n4. AT AT（Automatic Transaction）模式不依赖参与者对AX事务的支持。\n在seata的实现中，Automatic (Branch) Transaction Mode对应AT模式，Manual (Branch) Transaction Mode对应TCC模式。\n第一阶段（prepare）：在本地事务中，一并提交业务数据更新和相应回滚日志记录。 第二阶段（commit）：马上成功结束，自动 异步批量清理回滚日志。 第二阶段（rollback）：通过回滚日志，自动 生成补偿操作，完成数据回滚。 隔离级别为RU，未提交的事务数据也会被其他事务读到。\n*. Conclusion XA AT TCC Saga 本地/事务消息方案 一致性 强一致 强一致 最终一致 最终一致 最终一致 隔离性 支持 读未提交 支持（通过业务层面在Try阶段的资源锁定实现隔离） 不支持 不支持 性能 全局锁，性能差 吞吐量差，优于XA Try操作使资源锁定可以尽早释放，系统吞吐量高 吞吐量高 吞吐量高 业务侵入 无侵入 无侵入 较高 较低 较低 优点 强一致性保证 业务无侵入 业务无侵入 适用于短事务 由业务层面来保证隔离性 性能相对较高，吞吐量高 性能相对较高，吞吐量高 对业务侵入较少 对业务侵入较少 通过消息中间件解耦，下游事务异步化 缺点 需要XA规范。存在同步阻塞、单点故障、数据不一致、不确定性等可用性问题 事务隔离级别为脏读 不适用于长事务 业务改造成本较高，业务需分拆为Try/Confirm/Cancel三个操作 引入中间态，业务复杂，不利于迭代维护。 不具备隔离性，易出现脏读，脏写问题，可能造成脏写无法回滚 不具备隔离性 不具备事务回滚，只能重试 适用业务 强一致性 短事务 一般可用性 强一致性 短事务 一般可用性 最终一致性 短事务 强可用性 最终一致性 长事务 强可用性 不要求隔离性 备注 需要注意处理空回滚，重复提交，悬挂等异常情况 需要注意处理空补偿，重复提交，悬挂等异常情况 ","permalink":"https://chasing1020.github.io/post/distributed-transaction/","summary":"\u003cp\u003eThe Eight Fallacies of Distributed Computing by Peter Deutsch.\u003c/p\u003e\n\u003cp\u003eEssentially everyone, when they first build a distributed application, makes the following eight assumptions. All prove to be false in the long run and all cause big trouble and painful learning experiences.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eThe network is reliable\u003c/li\u003e\n\u003cli\u003eLatency is zero\u003c/li\u003e\n\u003cli\u003eBandwidth is infinite\u003c/li\u003e\n\u003cli\u003eThe network is secure\u003c/li\u003e\n\u003cli\u003eTopology doesn\u0026rsquo;t change\u003c/li\u003e\n\u003cli\u003eThere is one administrator\u003c/li\u003e\n\u003cli\u003eTransport cost is zero\u003c/li\u003e\n\u003cli\u003eThe network is homogeneous\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch1 id=\"1-2pc\"\u003e1. 2PC\u003c/h1\u003e\n\u003cp\u003eXA规范中定义的分布式事务模型包括四个组成部分：\u003c/p\u003e","title":"Distributed Transaction"},{"content":"1. Definition of security confidentiality: only sender, intended receiver should “understand” message contents\nsender encrypts message receiver decrypts message authentication: sender, receiver want to confirm identity of each other\nmessage integrity: sender, receiver want to ensure message not altered (in transit, or afterwards) without detection\naccess and availability: services must be accessible and available to users. Access is the basis of availability.\nWithout network security, the intruder can eavesdrop, insert messages, impersonation, hijacking(taking over ongoing connection), denial of service and so on.\n2. Principles of cryptography we can define that:\nm: plaintext message\n$K_A(m)$: ciphertext, encrypted with key $K_A$\n$m = K_B(K_A(m))$\nThere are two kinds of scheme about attacking: 1. cipher-text only attack; 2. known-plaintext attack and chosen-plaintext attack.\nDES: Data Encryption Standard\n56-bit symmetric key, 64-bit plaintext input. Block cipher with cipher block chaining.\n3DES: encrypt 3 times with 3 different keys.\nAES: Advanced Encryption Standard\nprocesses data in 128 bit blocks.\nUsing block chiper, the ith input as $m(i)$, let $c(i) = m(i)\\ xor\\ c(i-1)$.\nRSA: Rivest, Shamir, Adelson algorithm\nCreating public/private key pair: Let m be the plain text message that the originator will encrypt and send to the intended recipient. Let e be the public encryption key, d the private decryption key, c the ciphertext.\nproof: Let $n = pq \\implies \\varphi(n) = (p − 1)(q − 1)$\nEuler\u0026rsquo;s theorem: $m^{\\varphi(n)}\\equiv1\\ (mod\\ n)$\n$\\implies m^{(p-1)(q-1)}\\equiv1 \\mod(pq)$\n$\\implies m^{k\\varphi(n)+1}\\equiv m \\mod(pq)$\nBased on the RSA basic principle, $ed = k\\varphi(n)+1$.\nThis is equivalent to say we need to satisfy: $ed\\equiv1(mod \\varphi(n))$\nIf e is determined, $dmod\\varphi(n)$ could be determined, using the Extend Euclidean algorithm which takes $O(log^2\\varphi(n))$ to run.\n3. Message integrity Cryptographic technique analogous to hand-written signatures.\nDigital signatures: signed message digest\nSuppose Alice receives msg m, with signature: m, $K_B^-(m)$\nAlice verifies $m$ signed by Bob by applying Bob’s public key $K_B^+$ to $K_B^-(m)$ then checks whether $K_B^+(K_B^-(m)) = m$\nIf $K_B^+(K_B^-(m)) = m$, whoever signed m must have used Bob’s private key\nHash function algorithms\nMD5 hash function widely used (RFC 1321) :\ncomputes 128-bit message digest in 4-step process.\narbitrary 128-bit string x, appears difficult to construct msg m whose MD5 hash is equal to x\nSHA-1 is also used:\nUS standard [NIST, FIPS PUB 180-1]\n160-bit message digest\nPublic key Certification Authorities (CA)\ncertification authority (CA): binds public key to particular entity, E.\nentity (person, website, router) registers its public key with CE provides “proof of identity” to CA (bind by OS).\nCA creates certificate binding identity E to E’s public key.\ncertificate containing E’s public key digitally signed by CA: CA says “this is E’s public key”.\n4. Securing TCP connections: TLS TLS is a widely deployed security protocol above the transport layer; supported by almost all browsers, web servers: https (port 443)\nTLS provides: 1.confidentiality: via symmetric encryption; 2. integrity: via cryptographic hashing; 3. authentication: via public key cryptography.\nTLS needed:\nhandshake: Alice, Bob use their certificates, private keys to authenticate each other, exchange or create shared secret.\nkey derivation: Alice, Bob use shared secret to derive set of keys\ndata transfer: stream data transfer: data as a series of records not just one-time transactions\nconnection closure: special messages to securely close connection\nWhich need four keys:\n$K_c$: encryption key for data sent from client to server\n$M_c$: MAC key for data sent from client to server\n$K_s$: encryption key for data sent from server to client\n$M_s$: MAC key for data sent from server to client\nDiffie Hellman Algorithm\nDH algorithm is based on a famous problem called Discrete Logarithm Problem (DLP).\nIt based on a theory that if I define a prime p, and g is a primitive root modulo p. If gives you a random number $a$, it is esay to calculate $g^amodp$. But it is difficult to get the inverse solution $a$ if you only have $p$, $g$, and $g^amodp$.\nThey first agree between them a large prime number p, and a generator (or base) g (where 0 \u0026lt; g \u0026lt; p).\nAlice chooses a secret integer a (her private key) and then calculates $g^a mod p$ (which is her public key). Bob chooses his private key b, and calculates his public key in the same way.\nBob knows $b$ and $g^a$, so he can calculate $(g^a)^b mod p = g^{ab} mod p$. Therefore both Alice and Bob know a shared secret $g^{ab} mod p$. An eavesdropper Eve who was listening in on the communication knows p, g, Alice’s public key $(g^a mod p)$ and Bob’s public key $(g^b mod p)$. She is unable to calculate the shared secret from these values.\n$(g^a mod p)^b mod p = g^{ab} mod p$\n$(g^b mod p)^a mod p = g^{ba} mod p$\nTLS: 1.3 cipher suite\n“cipher suite”: algorithms that can be used for key generation, encryption, MAC, digital signature.\nclient TLS hello message: guesses key agreement protocol (DH key agreement protocol), parameters\nindicates cipher suites it supports\nserver TLS hello msg chooses key agreement protocol (DH key agreement protocol), parameters\nselected cipher suite\nserver-signed certificate\nthen client will: 1. checks server certificate; 2. generates key; 3.can now make application request (e.g.., HTTPS GET)\nRecover connection:\ninitial hello message contains encrypted application data!\n“resuming” earlier connection between client and server\napplication data encrypted using “resumption master secret” from earlier connection\nvulnerable to replay attacks!\nmaybe OK for get HTTP GET or client requests not modifying server state 5. IPSec IPSec provides datagram-level encryption, authentication, integrity, and it has two types:\ntransport mode: only datagram payload is encrypted, authenticated.\ntunnel mode: entire datagram is encrypted, authenticated. Encrypted datagram encapsulated in new datagram with new IP header, tunneled to destination.\n6. Firewall Firewall isolates organization’s internal network from larger Internet, allowing some packets to pass, blocking others.\nStateless packet filtering examples:\nPolicy Firewall Setting no outside Web access drop all outgoing packets to any IP address, port 80 no incoming TCP connections, except those for institution’s public Web server only drop all incoming TCP SYN packets to any IP except 130.207.244.203, port 80 prevent Web-radios from eating up the available bandwidth. drop all incoming UDP packets - except DNS and router broadcasts prevent your network from being used for a smurf DoS attack drop all ICMP packets going to a “broadcast” address (e.g. 130.207.255.255) prevent your network from being tracerouted drop all outgoing ICMP TTL expired traffic ","permalink":"https://chasing1020.github.io/post/network-security/","summary":"\u003ch1 id=\"1-definition-of-security\"\u003e1. Definition of security\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003econfidentiality: only sender, intended receiver should “understand” message contents\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003esender encrypts message\u003c/li\u003e\n\u003cli\u003ereceiver decrypts message\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eauthentication: sender, receiver want to confirm identity of each other\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003emessage integrity: sender, receiver want to ensure message not altered (in transit, or afterwards) without detection\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eaccess and availability: services must be accessible and available to users. Access is the basis of availability.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWithout network security, the intruder can eavesdrop, insert messages, impersonation, hijacking(taking over ongoing connection), denial of service and so on.\u003c/p\u003e","title":"Network Security"},{"content":"AKF Availability Cube 随着业务规模增长，拆解单体应用（monolith），设计成为面向服务架构（Service-oriented architectutre），做成一个个微服务（Micro-service）已经是如今的大趋势。\nScale Cube Model 从拆分的角度来看，也分成多个可以考虑的维度，其中最常用定义分割服务、定义微服务和扩展产品的模型即是比例立方模型。其中保证可用性的解决方案AKF Availability Cube便是最经典的模型。用于指导有关如何实现高可用性的讨论，以及评估现有系统理论上“设计”可用性的工具。它是关于高可用性设计的两部分系列中的第一部分。 Measuring Availability 互联网产品一直在“服务始终可用”的追求上尽可能减少成本，从这个角度出发，服务衡量标准测度为追求可用性改进提供了一个起点。\nClock is not the best measure\n时间对于业务的影响是不相等的，这取决于业务是否处于高峰期；企业的商业术语：收入、成本、利润、投资回报率，都以金钱而非时间作为衡量；基础设施组件的时间作为测度是不准确的，他们并不会捕获程序的异常，尽管服务无法运行。\nTransactional Metics\nRates: 以速度记录，例如登录、添加到购物车、注册、下载、订单等。应用统计过程控制或其他分析方法来建立指示交易速度异常偏差的阈值。 Ratios: 预料外的或失败的结果的比例可用于衡量服务质量。对此类比率的分析将建立异常的偏差水平。 Patterns: 交易模式可以识别预期的活动，没有预期的模式更改可能表示您的产品或服务存在可用性问题。 3-Dimensions of scaling Dimension Description X-Axis Horizontal Duplication and Cloning of services and data Y-Axis Functional Decomposition and Segmentation - Microservices (or micro-services) Z-Axis Service and Data Partitioning along Customer Boundaries - Shards/Pods X-axis scaling X轴的核心思路在于\u0026quot;cloning/replication\u0026quot;，我们将应用复制，以提高可用性。我们假定整体可用率\n$P_{singleton\\ availability}=\\frac{Total\\ Available-Non\\ Available}{Total\\ Available}$\n当添加了多个结点以后，新的可用性概率评估变为\n$P_{overall\\ availability}=1-(1-P_{singleton\\ availability})^n$\n但是仅仅做应用复制，也无法解决如下缺点：\n大量与会话相关的信息，这些信息通常难以分发或需要持久化到服务器\n每个拷贝需要访问所有的数据，对缓存机制要求很高，数据库很可能成为其中的瓶颈。\n不会减少日益增长的开发复杂度。\nY-axis scaling Y轴通过两个角度进行服务切分。分别是动词（操作流程）和名词（对象类型）：\n按动词分割：服务1只负责购买流程，服务2只负责售后流程，服务3只负责广告投放流程\n按名词分割：服务1只负责商品信息；服务2只负责用户信息。\n微服务的设计核心思路便体现在Y轴，其可以将目标应用划分泳道，限制应用的故障域，将应用故障保持在边界内，不会传播影响外界的服务。\n然而，Y轴划分的越细致，越复杂的系统越容易产生“多米诺骨牌”效应，可能导致相邻系统瘫痪，从而导致延迟、停机和/或完全失败。\n比较合理的方式是：将服务集成在一个隔离的堆栈或泳道内（托管云或公共云），以避免跨数据中心调用。并同时在X轴角度，复制服务，以便每个数据中心或云实例都拥有所需的一切。\n综上，Y轴划分的服务需要在增加服务数量提高可用性与减少服务间通信损失做出一个Trade off。\nZ-axis scaling Z轴与X轴很像，区别在于Z轴负责的是数据的子集而不是复制，常见于数据库分片。\n对于每一个Z轴的分片，软件本质上是相同的，区别仅仅在于数据，其定义了“爆炸半径”（blast radius, aka Swimlanes or Bulkhead），边界假设在泳道之间不存在同步调用。提高了解决方案的事务可扩展性，同时能够实现故障的隔离。\n缓存命中率通常随着较小的数据集而上升，优化了缓存利用率，减少内存使用和I/O。\n随着可以使用商品服务器或较小的 IaaS 实例，运营成本通常会下降。\n但是另一个角度，Z轴的分片也带来了如下缺点：\n增加了整体的复杂度。 需要实现分片机制，这个机制难以变更，对新的需求不友好。 ","permalink":"https://chasing1020.github.io/post/akf-availability-cube/","summary":"\u003ch1 id=\"akf-availability-cube\"\u003eAKF Availability Cube\u003c/h1\u003e\n\u003cp\u003e随着业务规模增长，拆解单体应用（monolith），设计成为面向服务架构（Service-oriented architectutre），做成一个个微服务（Micro-service）已经是如今的大趋势。\u003c/p\u003e","title":"AKF Availability Cube"},{"content":"Gorm Source Code 1. sql.DB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // driverConn wraps a driver.Conn with a mutex, to // be held during all calls into the Conn. (including any calls onto // interfaces returned via that Conn, such as calls on Tx, Stmt, // Result, Rows) type driverConn struct { db *DB createdAt time.Time sync.Mutex // guards following ci driver.Conn needReset bool // The connection session should be reset before use if true. closed bool finalClosed bool // ci.Close has been called openStmt map[*driverStmt]bool // guarded by db.mu inUse bool returnedAt time.Time // Time the connection was created or returned. onPut []func() // code (with db.mu held) run when conn is next returned dbmuClosed bool // same as closed, but guarded by db.mu, for removeClosedStmtLocked } sql.DB数据结构对数据库做了一层简单的抽象，但是需要明确的是：sql.DB不是一个连接，也不是映射到任何DataBase或者是Schema的概念。\nsql.DB可以执行一系列重要的任务：包括使用驱动打开和关闭实际底层数据库的连接；通过管理连接池，涉及相关的事务。\n这个数据结构设计为长期存在的，不应该经常性地新建或者是删除。\n在Gorm中，将DB类型和Session类型进行了封装。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // DB GORM DB definition type DB struct { *Config Error error RowsAffected int64 Statement *Statement clone int } // Session session config when create session with Session() method type Session struct { // ... Context context.Context Logger logger.Interface NowFunc func() time.Time CreateBatchSize int } 其中数据库对象放置在Statement中\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Statement statement type Statement struct { *DB TableExpr *clause.Expr Table string Model interface{} Unscoped bool Dest interface{} ReflectValue reflect.Value Clauses map[string]clause.Clause // ... ConnPool ConnPool Schema *schema.Schema Context context.Context // ... } 2. Parse 对于一般数据对象，涉及对表的转换，一般需要这个对象提供信息：\n包括表名到结构体名、字段名和字段类型、额外的约束条件（非空，自增等等）\n在schema/field.go下面，设计了Field结构体，主要部分如下\n1 2 3 4 5 6 7 8 9 10 11 type Field struct { Name string DBName string BindNames []string DataType DataType GORMDataType DataType // ... FieldType reflect.Type Tag reflect.StructTag // ... } 对应reflect包下的Tag操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // A StructTag is the tag string in a struct field. // // By convention, tag strings are a concatenation of // optionally space-separated key:\u0026#34;value\u0026#34; pairs. // Each key is a non-empty string consisting of non-control // characters other than space (U+0020 \u0026#39; \u0026#39;), quote (U+0022 \u0026#39;\u0026#34;\u0026#39;), // and colon (U+003A \u0026#39;:\u0026#39;). Each value is quoted using U+0022 \u0026#39;\u0026#34;\u0026#39; // characters and Go string literal syntax. type StructTag string // Get returns the value associated with key in the tag string. // If there is no such key in the tag, Get returns the empty string. // If the tag does not have the conventional format, the value // returned by Get is unspecified. To determine whether a tag is // explicitly set to the empty string, use Lookup. func (tag StructTag) Get(key string) string { v, _ := tag.Lookup(key) return v } 在对数据类型进行解析时，会执行如下的操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { // ... // default value is function or null or blank (primary keys) field.DefaultValue = strings.TrimSpace(field.DefaultValue) skipParseDefaultValue := strings.Contains(field.DefaultValue, \u0026#34;(\u0026#34;) \u0026amp;\u0026amp; strings.Contains(field.DefaultValue, \u0026#34;)\u0026#34;) || strings.ToLower(field.DefaultValue) == \u0026#34;null\u0026#34; || field.DefaultValue == \u0026#34;\u0026#34; switch reflect.Indirect(fieldValue).Kind() { case reflect.Bool: field.DataType = Bool if field.HasDefaultValue \u0026amp;\u0026amp; !skipParseDefaultValue { if field.DefaultValueInterface, err = strconv.ParseBool(field.DefaultValue); err != nil { schema.err = fmt.Errorf(\u0026#34;failed to parse %s as default value for bool, got error: %v\u0026#34;, field.DefaultValue, err) } } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // case ... } } 3. Cause 对于复杂的SQL语句，一般是由多个子句组合而成，对于每一个子句，Gorm做了简单的抽象，并把子句生成的过程简化为Build方法。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // Interface clause interface type Interface interface { Name() string Build(Builder) MergeClause(*Clause) } // Expression expression interface type Expression interface { Build(builder Builder) } // Builder builder interface type Builder interface { Writer WriteQuoted(field interface{}) AddVar(Writer, ...interface{}) } // Clause type Clause struct { Name string // WHERE BeforeExpression Expression AfterNameExpression Expression AfterExpression Expression Expression Expression Builder ClauseBuilder } 其中，Builder函数返回是Gorm链式操作的核心。\n3.1. Select 对于查询部分，首先定义了查询的部分内容，将默认的查询内容用逗号分隔、去重\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package clause // Select select attrs when querying, updating, creating type Select struct { Distinct bool Columns []Column Expression Expression } func (s Select) Name() string { return \u0026#34;SELECT\u0026#34; } func (s Select) Build(builder Builder) { if len(s.Columns) \u0026gt; 0 { if s.Distinct { builder.WriteString(\u0026#34;DISTINCT \u0026#34;) } for idx, column := range s.Columns { if idx \u0026gt; 0 { builder.WriteByte(\u0026#39;,\u0026#39;) } builder.WriteQuoted(column) } } else { builder.WriteByte(\u0026#39;*\u0026#39;) } } func (s Select) MergeClause(clause *Clause) { if s.Expression != nil { if s.Distinct { if expr, ok := s.Expression.(Expr); ok { expr.SQL = \u0026#34;DISTINCT \u0026#34; + expr.SQL clause.Expression = expr return } } clause.Expression = s.Expression } else { clause.Expression = s } } // CommaExpression represents a group of expressions separated by commas. type CommaExpression struct { Exprs []Expression } func (comma CommaExpression) Build(builder Builder) { for idx, expr := range comma.Exprs { if idx \u0026gt; 0 { _, _ = builder.WriteString(\u0026#34;, \u0026#34;) } expr.Build(builder) } } 3.2. From 采用分类方式，考虑SQL92和SQL99语法上的差异\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package clause // From from clause type From struct { Tables []Table Joins []Join } // Name from clause name func (from From) Name() string { return \u0026#34;FROM\u0026#34; } // Build build from clause func (from From) Build(builder Builder) { if len(from.Tables) \u0026gt; 0 { for idx, table := range from.Tables { if idx \u0026gt; 0 { builder.WriteByte(\u0026#39;,\u0026#39;) } builder.WriteQuoted(table) } } else { builder.WriteQuoted(currentTable) } for _, join := range from.Joins { builder.WriteByte(\u0026#39; \u0026#39;) join.Build(builder) } } // MergeClause merge from clause func (from From) MergeClause(clause *Clause) { clause.Expression = from } 3.3. Where 判定多种数据的结构进行合并操作。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func (not NotConditions) Build(builder Builder) { if len(not.Exprs) \u0026gt; 1 { builder.WriteByte(\u0026#39;(\u0026#39;) } for idx, c := range not.Exprs { if idx \u0026gt; 0 { builder.WriteString(\u0026#34; AND \u0026#34;) } if negationBuilder, ok := c.(NegationExpressionBuilder); ok { negationBuilder.NegationBuild(builder) } else { builder.WriteString(\u0026#34;NOT \u0026#34;) e, wrapInParentheses := c.(Expr) if wrapInParentheses { sql := strings.ToLower(e.SQL) if wrapInParentheses = strings.Contains(sql, \u0026#34;and\u0026#34;) || strings.Contains(sql, \u0026#34;or\u0026#34;); wrapInParentheses { builder.WriteByte(\u0026#39;(\u0026#39;) } } c.Build(builder) if wrapInParentheses { builder.WriteByte(\u0026#39;)\u0026#39;) } } } if len(not.Exprs) \u0026gt; 1 { builder.WriteByte(\u0026#39;)\u0026#39;) } } 后续一系列操作与此类似，在此不再一一列出。\n4. Hooks 利用接口实现，在执行对应操作时，会先进行相关的检查操作，实现较为简单。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func BeforeCreate(db *gorm.DB) { if db.Error == nil \u0026amp;\u0026amp; db.Statement.Schema != nil \u0026amp;\u0026amp; !db.Statement.SkipHooks \u0026amp;\u0026amp; (db.Statement.Schema.BeforeSave || db.Statement.Schema.BeforeCreate) { callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) { if db.Statement.Schema.BeforeSave { if i, ok := value.(BeforeSaveInterface); ok { called = true db.AddError(i.BeforeSave(tx)) } } if db.Statement.Schema.BeforeCreate { if i, ok := value.(BeforeCreateInterface); ok { called = true db.AddError(i.BeforeCreate(tx)) } } return called }) } } 5. Transaction Gorm采用的策略是，在事务执行过程中，判断DB对象的err信息，不为空则返回\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func BeginTransaction(db *gorm.DB) { if !db.Config.SkipDefaultTransaction \u0026amp;\u0026amp; db.Error == nil { if tx := db.Begin(); tx.Error == nil { db.Statement.ConnPool = tx.Statement.ConnPool db.InstanceSet(\u0026#34;gorm:started_transaction\u0026#34;, true) } else if tx.Error == gorm.ErrInvalidTransaction { tx.Error = nil } else { db.Error = tx.Error } } } func CommitOrRollbackTransaction(db *gorm.DB) { if !db.Config.SkipDefaultTransaction { if _, ok := db.InstanceGet(\u0026#34;gorm:started_transaction\u0026#34;); ok { if db.Error != nil { db.Rollback() } else { db.Commit() } db.Statement.ConnPool = db.ConnPool } } } 实际上还可以采用一种比较优雅的设计实现。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func (s Service) DoSomething() (err error) { tx, err := s.db.Begin() if err != nil { return } defer func() { if err != nil { tx.Rollback() return } if r := recover(); r != nil { tx.Rollback() } err = tx.Commit() }() if _, err = tx.Exec(...); err != nil { return } if _, err = tx.Exec(...); err != nil { return } // ... return } 这里可以对比Java的JDBC操作，采用trycatch实现回滚。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Test public void testUpdateWithTx() { Connection conn = null; try { //1.获取连接的操作（ //① 手写的连接：JDBCUtils.getConnection(); //② 使用数据库连接池：C3P0;DBCP;Druid //2.对数据表进行一系列CRUD操作 //① 使用PreparedStatement实现通用的增删改、查询操作（version 1.0 \\ version 2.0) //version2.0的增删改public void update(Connection conn,String sql,Object ... args){} //version2.0的查询 public \u0026lt;T\u0026gt; T getInstance(Connection conn,Class\u0026lt;T\u0026gt; clazz,String sql,Object ... args){} //② 使用dbutils提供的jar包中提供的QueryRunner类 //提交数据 conn.commit(); } catch (Exception e) { e.printStackTrace(); try { //回滚数据 conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally{ //3.关闭连接等操作 //① JDBCUtils.closeResource(); //② 使用dbutils提供的jar包中提供的DbUtils类提供了关闭的相关操作 } } ","permalink":"https://chasing1020.github.io/post/gorm-source-code/","summary":"\u003ch1 id=\"gorm-source-code\"\u003eGorm Source Code\u003c/h1\u003e\n\u003ch1 id=\"1-sqldb\"\u003e1. sql.DB\u003c/h1\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// driverConn wraps a driver.Conn with a mutex, to\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// be held during all calls into the Conn. (including any calls onto\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// interfaces returned via that Conn, such as calls on Tx, Stmt,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Result, Rows)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003edriverConn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003edb\u003c/span\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eDB\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003ecreatedAt\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTime\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003esync\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eMutex\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c1\"\u003e// guards following\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003eci\u003c/span\u003e\u003cspan class=\"w\"\u003e          \u003c/span\u003e\u003cspan class=\"nx\"\u003edriver\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eConn\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003eneedReset\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// The connection session should be reset before use if true.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003eclosed\u003c/span\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003efinalClosed\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// ci.Close has been called\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003eopenStmt\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003edriverStmt\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e// guarded by db.mu\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003einUse\u003c/span\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003ereturnedAt\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTime\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// Time the connection was created or returned.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003eonPut\u003c/span\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c1\"\u003e// code (with db.mu held) run when conn is next returned\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003edbmuClosed\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"c1\"\u003e// same as closed, but guarded by db.mu, for removeClosedStmtLocked\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003esql.DB数据结构对数据库做了一层简单的抽象，但是需要明确的是：\u003ccode\u003esql.DB不是一个连接，也不是映射到任何DataBase或者是Schema的概念\u003c/code\u003e。\u003c/p\u003e","title":"Gorm Source Code"},{"content":"1. Implements volatile：声明后所有线程看到的改变量的值是一样的。写操作时，会添加Lock前缀的汇编。\n保证了1. 保证缓存行的数据写回内存；2. 写回的操作会使其他缓存失效。\nsynchronized：普通的方法，锁是实例；静态同步方法，锁是Class；同步方法块，锁是括号里的对象。\n锁保存在对象头，如果是数组，则用三个字宽保存对象头，非数组则用两个自宽保存对象头。\n长度 内容 说明 32bit/64bit Mark Word 存储对象的hashCode或者是锁信息 32bit/64bit Class Metadata Address 存储到对象类型的指针 32bit/64bit Array Length 数组的长度，（非数组无该字段） 32位下对象头的存储结构\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |----------------------------------------------------------------------------------------|--------------------| | Object Header (64 bits) | State | |-------------------------------------------------------|--------------------------------|--------------------| | Mark Word (32 bits) | Klass Word (32 bits) | | |-------------------------------------------------------|--------------------------------|--------------------| | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Normal | |-------------------------------------------------------|--------------------------------|--------------------| | thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Biased | |-------------------------------------------------------|--------------------------------|--------------------| | ptr_to_lock_record:30 | lock:2 | OOP to metadata object | Lightweight Locked | |-------------------------------------------------------|--------------------------------|--------------------| | ptr_to_heavyweight_monitor:30 | lock:2 | OOP to metadata object | Heavyweight Locked | |-------------------------------------------------------|--------------------------------|--------------------| | | lock:2 | OOP to metadata object | Marked for GC | |-------------------------------------------------------|--------------------------------|--------------------| 锁状态 25bit 4bit 1bit 2bit 状态 对象的hashCode 对象分代年龄 是否是偏向锁 锁标志位 64位下对象头的存储结构\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |------------------------------------------------------------------------------------------------------------|--------------------| | Object Header (128 bits) | State | |------------------------------------------------------------------------------|-----------------------------|--------------------| | Mark Word (64 bits) | Klass Word (64 bits) | | |------------------------------------------------------------------------------|-----------------------------|--------------------| | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Normal | |------------------------------------------------------------------------------|-----------------------------|--------------------| | thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Biased | |------------------------------------------------------------------------------|-----------------------------|--------------------| | ptr_to_lock_record:62 | lock:2 | OOP to metadata object | Lightweight Locked | |------------------------------------------------------------------------------|-----------------------------|--------------------| | ptr_to_heavyweight_monitor:62 | lock:2 | OOP to metadata object | Heavyweight Locked | |------------------------------------------------------------------------------|-----------------------------|--------------------| | | lock:2 | OOP to metadata object | Marked for GC | |------------------------------------------------------------------------------|-----------------------------|--------------------| 锁状态 25bit 31bit 1bit 4bit 1bit 2bit 状态 unused 对象的hashCode cms_free 对象分代年龄 是否偏向锁 锁标志位 可以设置参数-XX:+UseCompressedOops，来进行压缩，参考32bit的PAE实现，可以使JVM的内存超过4G，但不超过32G（超过32G，可以使用-XX:ObjectAlignmentInBytes，来限制压缩的大小，如当对象对齐为 16 字节时，最多可以使用 64 GB 的堆空间和压缩指针），经过压缩后：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |--------------------------------------------------------------------------------------------------------------|--------------------| | Object Header (96 bits) | State | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | Mark Word (64 bits) | Klass Word (32 bits) | | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | unused:25 | identity_hashcode:31 | cms_free:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Normal | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | thread:54 | epoch:2 | cms_free:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | Biased | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | ptr_to_lock_record | lock:2 | OOP to metadata object | Lightweight Locked | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | ptr_to_heavyweight_monitor | lock:2 | OOP to metadata object | Heavyweight Locked | |--------------------------------------------------------------------------------|-----------------------------|--------------------| | | lock:2 | OOP to metadata object | Marked for GC | |--------------------------------------------------------------------------------|-----------------------------|--------------------| 偏向锁：大多数情况下，没有锁竞争，偏向锁在等到竞争时才会释放锁。有竞争时 ，会在全局安全点撤销偏向锁。\n使用-XX:BiasedeLoackingStartupDelay=0，如果大多数锁处于竞争状态，可以使用-XX:UseBiasedLocking=false关闭，这样程序会默认进入轻量级锁的状态。\n轻量级锁：使用CAS操作Mark Word，如果成功，就获得锁；释放时，CAS如果失败，就升级到重量级锁。\n重量级锁：在这个状态下，所有获取锁的操作都会被阻塞，持有锁的线程释放后会唤醒这些线程。\n锁 优点 缺点 场景 偏向锁 加锁不用额外消耗，效率接近非同步方法 如果有锁竞争，会带来撤销消耗 只有一个线程访问的场景 轻量级锁 线程不会阻塞，提高相应速度 得不到锁竞争的线程会自旋消耗CPU 追求响应时间，执行速度快 重量级锁 线程竞争不用自旋，不会消耗CPU 线程阻塞，响应时间缓慢 追求吞吐量，同步块执行速度较长 ABA问题解决：采用AtomicStampedReference来解决，判断是否时等于预期引用和预期标志，两个都成功则设置。\n2. Memory Model 编译器优化重排序：不改变单线程程序语义的情况下，重新安排顺序。\n指令级并行的重排序：指令级并行技术（Instruction-Level Parallelism），如果不存在数据依赖性，可以改变指令执行的顺序。\n内存系统的重排序：读/写缓冲区，使得读写操作可能的乱序。\n屏障类型：LoadLoadBarriers，StoreStoreBarriers，LoadStoreBarriers，StoreLoadBarriers\n其中StoreLoadBarriers拥有其他三个屏障的效果，作为全能型屏障。这个屏障开销昂贵，需要将写缓冲的数据全部刷新到内存中。\nJDK5开始，采用JSR133原则，使用happens-before来解释内存的可见性：\n程序顺序规则：一个线程中的每一个操作，happens-before于后边的操作 监视器锁规则：解锁操作happens-before于上锁操作 volatile变量规则：对volatile修饰变量的写操作，happens-before于读操作 传递性：A happens-before B，B happens-before C，则有A happens-before C 数据依赖性：对同一个变量的写写，写读，读写操作，存在数据依赖，编译器和处理器会遵守数据依赖性。\nas-if-serial：排序结果不能影响本来的执行结果。编译器，runtime，处理器都会遵守。\nJMM不保证对64位的long和double类型的写操作具有原子性，会把64位的写操作拆成两个32位的操作，虽然这违反了顺序一致性模型。\nvolatile的写操作前面加StoreStore屏障，后面加StoreLoad屏障；\nvolatile的写操作后面加LoadLoad屏障，后面加LoadStore屏障。编译器能够通过具体情况省略不必要的屏障。\nJava同步器框架AbstractQueuedSynchronizer。\nfinal域保证构造函数内的final域的写入和引用赋值给另一个引用变量这个操作不可以重排序；第一次读final域对象的引用和读取final域中的操作不能重排序。\n构造函数final域的写操作，在return前，会插入StoreStore屏障，读final域前会加上LoadLoad屏障，禁止final的写操作重排序到构造函数之外。保证了final读取到的值不会发生改变。\n双重锁检查的机制instance = new Instance();实际上会被拆成三行伪代码，在2，3之间会可能发生重排序。\n1 2 3 memory = allocate(); // 1. 分配对象的内存空间 ctorInstance(memory); // 2. 初始化对象，完成后instance != null instance = memory; // 3. 设置instance指向刚分配的内存地址 回顾类加载的时机：加载（通过全限定名获取二进制字节流，转化为方法区运行时数据结构，生成java.lang.Class对象），验证（文件格式，元数据，字节码，符号引用），准备（正式分配内存并且初始化值，对应上述代码的1，2步骤），解析（将常量池的符号引用替换为直接引用；符号引用就是一组符号来描述目标，可以是任何字面量。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。），初始化（初始化阶段是执行初始化方法 \u0026lt;clinit\u0026gt; ()方法的过程，对静态变量/代码块进行初始化，会保证多线程安全性），使用，卸载。\n使用volatile后，可以防止2，3之间的重排序。\n3. Basic suspend(), resume(), stop()标记为deprecated，调用后不一定会释放占有的资源。\n使用wait(), notify(), notifyAll()之前需要对调用对象加锁。\n管道输入输出流，输出与输入进行绑定\n4. Lock AbstractQueuedSynchronizer通过内置的FIFO来实现资源获取线程的排队工作。\n当前线程信息和等待状态构造成一个结点，加入队列，同时阻塞线程。同步状态释放时，首结点中的线程将会被唤醒。在尾部加入时，采用CAS判断插入到“应该插入的位置”。\nacquire(int arg)获取同步状态，对中断不敏感。读acquiredShared获取共享式的同步状态。\n重入锁在获取n次，并释放n次后，其他线程能够获取到该锁，默认非公平，通过组合自定义同步器来实现锁的获取与释放。\n读写锁，写状态位S\u0026amp;0x0000FFFF，读状态为S\u0026raquo;\u0026gt;16，写锁相当于一个支持重进入的排他锁。\n","permalink":"https://chasing1020.github.io/post/concurrency-programming/","summary":"\u003ch1 id=\"1-implements\"\u003e1. Implements\u003c/h1\u003e\n\u003cp\u003evolatile：声明后所有线程看到的改变量的值是一样的。写操作时，会添加Lock前缀的汇编。\u003c/p\u003e\n\u003cp\u003e保证了1. 保证缓存行的数据写回内存；2. 写回的操作会使其他缓存失效。\u003c/p\u003e","title":"Concurrency Programming"},{"content":"As of the time I am writing this post, I have been studying computer science for a year and a half. I have started to organize and share my own computer science related study materials and routes.\nThe courses in this schedule are not a substitute for college courses, but they can help you further your studies in computer science, or build an initial understanding of the field.\nIn some ways, this route can be more of a substitute for college courses outside of class, as a required course to become an industry technician.\nOur industry is different from other industries in that you may simply need a laptop, a good learning environment, or you may also need a good body, and you need more than anything else a solid and unrelenting state of mind to explore. What you also need is how you can stand out among the many people at the level of thinking, which is the key.\nSo with this article, I hope this article can make you have a clearer learning plan and can take less detours, and this article is also a reminiscence of my undergraduate computer learning journey.\n1. The Lesson We\u0026rsquo;re Missing For me personally, this chapter is almost all from the potholes I have stepped in, and the turns I took when I was lost. From another point of view, this is also to become a computer technology personnel must be a mandatory course or a necessary literacy.\nHow To Ask Questions The Smart Way: You don\u0026rsquo;t have to be technically proficient to get our attention, but you must demonstrate the traits that lead you to become proficient: resourceful, thoughtful, observant, and willing to take the initiative to solve problems. Academic Integrity: Honesty is the foundation of good academic work. Whether you are working on a problem set, lab report, project or paper, avoid engaging in plagiarism, unauthorized collaboration, cheating, or facilitating academic dishonesty. The Missing Semester of Your CS Education: Classes teach you all about advanced topics within CS, from operating systems to machine learning, but there’s one critical subject that’s rarely covered, and is instead left to students to figure out on their own: proficiency with their tools. Crash Course Computer Science(Youtube Link): This is one of the few videos I\u0026rsquo;ve watched more than twice, and although it\u0026rsquo;s a documentary on computer science, I guarantee it\u0026rsquo;s more effective than any general education class in school. carrie Anne is an excellent teacher, and the curriculum is designed to cover almost all areas of computing, from history to applications to the future, covering a wide range of knowledge. 2. Computer Systems Course: Intro to Computer Systems: It is known as a course study site for the must-read book 《CSAPP》. NJU-projectn: The computer system fundamentals course lab at Nanjing University is very helpful for personal improvement after completion. Write Great Code: Volume 1: Understanding the Machine: This book explains the underlying mechanics of how a computer works. What Every Programmer Should Know About Memory: A paper by Red Hat, Inc. It explains the structure of memory subsystems in use on modern commodity hardware, illustrating why CPU caches were developed, how they work, and what programs should do to achieve optimal performance by utilizing them. 3. Data Structure 《Data Structures and Algorithms》: As a class note, the books are not very extensive, but they are also all very concise and can be used as a table of contents for study. MIT\u0026rsquo;s Course Introduction To Algorithms 3rd Edition: This course provides an introduction to mathematical modeling of computational problems. It covers the common algorithms, algorithmic paradigms, and data structures used to solve these problems. The course emphasizes the relationship between algorithms and programming, and introduces basic performance measures and analysis techniques for these problems. 《Introduction To Algorithms 3rd Edition》: It can be counted as the bible of algorithm books, which covers very comprehensive knowledge points and is relatively difficult, requiring a lot of time and patience. In contrast, I would recommend reading the following book. 《Algorithhms 4th Edition by Robert Sedgewick, Kevin Wayne.pdf》: A classic book on algorithms written in Java language, with moderate difficulty and rich corresponding illustrations, is more suitable for reading. CS 61B Data structures: A course about data structures and programming methods about Java. 4. Computer Network 《Computer Networking A Top Down Approach 7th》: A classic computer networking book, but the latest version has been updated with more changes. UMASS Computer Networking PowerPoint: Of the book 《Computer Networking A Top Down Approach》 teaching resources courseware, very unfortunately, is the eighth edition, currently the appropriate version on the network is only the seventh edition. USTC\u0026rsquo;s Computer Networking Course: The \u0026ldquo;Computer Networks\u0026rdquo; mooc is the recorded version of the undergraduate course of the Department of Automation of the University of Science and Technology in autumn 2020. On the basis of the introduction of computer network architecture, top-down, the Internet as an example systematically describes the main services, working principles, common technologies and protocols of each level of the network architecture. Mr. Zheng\u0026rsquo;s level is very high and the quality of lecture is excellent. 《TCP/IP Illustrated, Volume 1 The Protocols》: As a top-down, advanced version of the book, it is more difficult, but I think parts of it can be skimmed. 5. Operating System Operating Systems: Three Easy Pieces: One of the best OS textbooks in the world, including the most basic OS content teaching. I highly recommend reading the original English version of this book, because the Chinese translation is really bad. NJU\u0026rsquo;s Operating Systems Course: The companion online course of OSTEP, which was made public by Jiang Yanyan of Nanjing University during the epidemic, is of very high lecture quality and can give a good realistic perspective and a good research direction. Supporting course website jyywiki. 《Modern Operating Systems: Principle and Implementation》: The textbook published by Shanghai Jiao Tong University is a joint effort by a team of teachers from Shanghai Jiao Tong University and one of the few quality textbooks in China. Most of the implementations are based on the Arm platform, and the content is very well developed, with a wide coverage of what is taught, as well as the accompanying Lab. I think the quality of this book is going to be due to Modern Operating Systems. 《Vbird Linux Basic》 : It can be used as reference material, in fact, the main thing is to really install and practice application in project development, do not memorize commands, use more will naturally remember. The main way to learn is to really install a Linux distribution and then use it. 《Linux Inslides》: A book-in-progress about the linux kernel and its insides. It can be done to supplement the above teaching content. 6. Data Base Systems 《Database System Concepts 7th Edition》: Database system concept, can be said to be a very complete and systematic database books, although some of the content is a bit old, but still can be used as a modern database learning textbook. Because the sixth edition of the textbook has been very old, here is more recommended to use the seventh edition of the textbook. This book website: Link CMU\u0026rsquo;s Course Database Systems: I think this is the best quality database online course, the teaching materials provided are very well made. However, the more difficult point is the experimental part, which can be learned on demand according to your level. 7. Compilers 《compilers principles techniques and tools》: Known as the \u0026ldquo;Dragon Book\u0026rdquo; of compilation principles books, it is recommended to patiently watch online classes little by little to break through, and this may be the only book I recommend reading the Chinese version, because the difficulty is too great. 《Writing An Interpreter In Go》: Interpreter written in Go language, which can be used as a supplementary reading material. ","permalink":"https://chasing1020.github.io/post/my-cs-learning-route/","summary":"\u003cp\u003eAs of the time I am writing this post, I have been studying computer science for a year and a half. I have started to organize and share my own computer science related study materials and routes.\u003c/p\u003e\n\u003cp\u003eThe courses in this schedule are not a substitute for college courses, but they can help you further your studies in computer science, or build an initial understanding of the field.\u003c/p\u003e\n\u003cp\u003eIn some ways, this route can be more of a substitute for college courses outside of class, as a required course to become an industry technician.\u003c/p\u003e","title":"My CS Learning Route"},{"content":"SHU计算机学院资料整理 填完埋了很久的坑，补全了计算机学院相关的资料。\n本文仅包含相关电子书，已公开的历年试卷等资源。 本文不含任何教师的个人课件，学长的历史代码，作业，报告等敏感内容。 本文所分享的所有资料，仅供上海大学计算机学院学生学习与交流使用。 本文的资料仅供参考，请自己判断其适用性。 本文一些文件如侵犯了您的权益，请向我邮件，我会及时彻底清除这些文件。 本文许可：CC-BY-NC-SA：署名-非商业性使用-相同方式共享\n文章版权归个人所有，未经作者授权禁止转载（或注明出处），不得将其中的资料用作任何商业用途。\n如有个人补充资料，欢迎前往开源社区项目libshu提交PR，导航: https://github.com/shuosc/libshu\n1. 面向对象程序设计 OOP历年试卷\n链接: https://pan.baidu.com/s/1UPVNIDUHoBK6N3x6w5vnTw\n提取码: 4hd3\n2. 计算机组成原理（1-2） 因为资料太少，就不分了\n链接: https://pan.baidu.com/s/1Lk_a0IezoaA0clFS_kQYJA\n提取码: l76i\n3. 数据结构（1） 课本、试卷\n链接: https://pan.baidu.com/s/1qzMa3rjZtvj04E3je7Ib3g\n提取码: nb4r\n4. 数据结构（2） 试卷\n链接: https://pan.baidu.com/s/1cMfku5w90jgycaN4iGl8VQ\n提取码: agec\n5. 离散数学（1） 课本、试卷\n链接: https://pan.baidu.com/s/1BRzkz8_Mj6SyubMH_wwZLQ\n提取码: ff8j\n6. 离散数学（2） 试卷\n链接: https://pan.baidu.com/s/1TzXBu5PT2f16FNwEclg9Qg\n提取码: kes0\n7. 概率论与数理统计 试卷非常全\n链接: https://pan.baidu.com/s/1OiB_NtoXdcsWuaqWYBK0zQ\n提取码: mmb2\n8. 操作系统 课本\n链接: https://pan.baidu.com/s/1jvjSzjeZKArY7-xxipWA7A\n提取码: ohbe\n9. 编码理论 试卷\n链接: https://pan.baidu.com/s/1A2wrYHBKOC34bBA0twL8Bw\n提取码: apm4\n10. 软件工程 课本、试卷\n链接: https://pan.baidu.com/s/1SyvWYF6S32vai9Zmfd3veg\n提取码: frp1\n11. 编译原理 课本、试卷\n链接: https://pan.baidu.com/s/1BBfrcxm8s9MEczF7Bbqhqw\n提取码: rgl3\n*. 附录 参考链接：\n清华大学计算机系课程攻略: https://github.com/PKUanonym/REKCARC-TSC-UHT\n浙江大学课程攻略共享计划: https://qsctech.github.io/zju-icicles/\n上海交通大学课程资料分享: https://github.com/c-hj/SJTU-Courses\n挖坑: My Computer Learning Route (WIP)\n","permalink":"https://chasing1020.github.io/post/ces-materials/","summary":"\u003ch1 id=\"shu计算机学院资料整理\"\u003eSHU计算机学院资料整理\u003c/h1\u003e\n\u003cp\u003e填完埋了很久的坑，补全了计算机学院相关的资料。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e本文仅包含相关电子书，已公开的历年试卷等资源。\u003c/li\u003e\n\u003cli\u003e本文不含任何教师的个人课件，学长的历史代码，作业，报告等敏感内容。\u003c/li\u003e\n\u003cli\u003e本文所分享的所有资料，仅供上海大学计算机学院学生学习与交流使用。\u003c/li\u003e\n\u003cli\u003e本文的资料仅供参考，请自己判断其适用性。\u003c/li\u003e\n\u003cli\u003e本文一些文件如侵犯了您的权益，请\u003ca href=\"mailto:chasing1020@gmail.com\"\u003e向我邮件\u003c/a\u003e，我会及时彻底清除这些文件。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp\u003e本文许可：\u003ca href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en\"\u003eCC-BY-NC-SA：署名-非商业性使用-相同方式共享\u003c/a\u003e\u003c/p\u003e","title":"CES Materials"},{"content":"Reference Book: Database System Concepts Seventh Edition\n1. Relational Model Readings: Chapters 1-2, 6\nDefinition\nDataBase: Organized collection of inter-related data that models some aspect of the real-world. Databases are the core component of most computer applications.\nA database management system (DBMS) is software that allows applications to store and analyze information in a database. A general-purpose DBMS is designed to allow the definition, creation, querying, update, and administration of databases.\nData Model: is a collection of concepts for describing the data in a database. Schema: is a description of a particular collection of data, using a given data model.\nKinds of DataModels\nType Description Relational Most DBMSs K-V, Graph, Document, Column-family NoSQL Array / Matrix Machine Learning Hierarchical, Network, Multi-Value Obsolete / Legacy / Rare Relational Model\nStructure: The definition of the database\u0026rsquo;s relations and their contents. Integrity: Ensure the database\u0026rsquo;s contents satisfy constraints. Manipulation: Programming interface for accessing and modifying a database\u0026rsquo;s contents.\nA relation is an unordered set that contain the relationship of attributes that represent entities.\nA relation\u0026rsquo;s primary key uniquely identifies a single tuple.\nforeign key specifies that an attribute from one relation has to map to a tuple in another relation.\nData Manipulation Languages\nMethods to store and retrieve information from a database.\nProcedural: (Relational Algebra)\nThe query specifies the (high-level) strategythe DBMS should use to find the desired result. Non-Procedural (Declarative): (Relational Calculus)\nThe query specifies only what data is wanted and not how to find it. Relational Algebra\nSymbol Operation Description σ Select Choose a subset of the tuples from a relation that satisfies a selection predicate. π Projection Generate a relation with tuples that contains only the specified attributes. ⋃ Union Generate a relation that contains all tuples that appear in either only one or both input relations. ⋂ Intersection Generate a relation that contains only the tuples that appear in both of the inputrelations. - Difference Generate a relation that contains only the tuples that appear in the first and not the second of the input relations. × Product Generate a relation that contains all possible combinations of tuples from the input relations. ⋈ Join Generate a relation that contains all tuples that are a combination of two tuples (one from each input relation) with a common value(s) for one or more attributes. Extra Operations:\nRename (ρ), Assignment (R←S), Duplicate Elimination (δ), Aggregation (γ), Sorting (τ), Division (R÷S).\nThe relational model is independent of any query language implementation. SQL is the de facto standard (many dialects).\n2. Advanced SQL Readings: Chapters 3-5\nSQL Standard\nANSI Standard in 1986. ISO in 1987. Structured Query Language\nSQL:2016: JSON, Polymorphic tables\nSQL:2011: Temporal DBs, Pipelined DML\nSQL:2008: Truncation, Fancy Sorting\nSQL:2003: XML, Windows, Sequences, Auto-Gen IDs.\nSQL:1999: Regex, Triggers, OO\nData Manipulation Language (DML): SELECT, INSERT, UPDATE, and DELETE statements.\nData Definition Language (DDL): Schema definitions for tables, indexes, views, and other objects.\nData Control Language (DCL): Security, access controls.\nImportant: SQL is based on bags(duplicates) not sets(no duplicates). Aggregates\nFunctions that return a single value from a bag of tuples. Aggregate functions can (almost) only be used in the SELECT output list.\nAVG(col): Return the average col value.\nMIN(col): Return minimum col value.\nMAX(col): Return maximum col value.\nSUM(col): Return sum of values in col.\nCOUNT(col): Return # of values for col.\nGroup by\nProject tuples into subsets and calculate aggregates againsteach subset.\nNon-aggregated values in SELECT output clause must appear in GROUPBY clause.\nFilters results based on aggregation computation. Like a WHERE clause for a GROUPBY.\nString / Date / Time Operations\nString Case String Quotes SQL-92 Sensitive SingleOnly Postgres Sensitive SingleOnly MySQL Insensitive Single/Double SQLite Sensitive Single/Double DB2 Sensitive SingleOnly Oracle Sensitive Single Only LIKE is used for string matching. String-matching operators.\n\u0026lsquo;%\u0026rsquo; Matches any substring (including empty strings). \u0026lsquo;_\u0026rsquo; Match any one character.\nSQL-92 defines string functions, whereas many DBMSs also have their own unique functions.\nSQL standard says to use || operator to concatenate two or more strings together.\nOperations to manipulate and modify DATE/TIME attributes.\nCan be used in both output and predicates.\nOutput Redirection\nStore query results in another table: 1.Table must not already be defined. 2.Table will have the same # of columns with the same types as the input.\nInsert tuples from query into another table:\nInner SELECT must generate the same columns as the target table.\nDBMSs have different options/syntax on what to do with integrity violations (e.g., invalid duplicates).\nOutput Control\nOrder the output tuples by the values in one or more of their columns.\nLimit the # of tuples returned in output. Can set an offset to return a \u0026ldquo;range\u0026rdquo;\nNested Queries\nQueries containing other queries.\nThey are often difficult to optimize.\nInner queries can appear (almost) anywhere in query.\nALL: Must satisfy expression for all rows in the sub-query.\nANY: Must satisfy expression for at least one row in the sub-query.\nIN: Equivalent to \u0026lsquo;=ANY()\u0026rsquo; .\nEXISTS: At least one row is returned.\nWindow functions\nPerforms a \u0026ldquo;sliding\u0026rdquo; calculation across a set of tuples that are related.\nLike an aggregation but tuples are not grouped into a single output tuples.\n3. DataBase Storage (1) Readings: Chapter 10.1-10.2, 10.5-10.6\nStorage\nVolatile Devices: (CPU Registers, CPU Caches, DRAM)\nVolatile means that if you pull the power from the machine, then the data is lost. Volatile storage supports fast random access with byte-addressable locations. This means that the program can jump to any byte address and get the data that is there. For our purposes, we will always refer to this storage class as \u0026ldquo;memory.\u0026rdquo; Non-Volatile Devices: (SSD, HDD, Network Storage)\nNon-volatile means that the storage device does not require continuous power in order for the device to retain the bits that it is storing. It is also block/page addressable. This means that in order to read a value at a particular offset, the program first has to load the 4 KB page into memory that holds the value the program wants to read. Non-volatile storage is traditionally better at sequential access (reading multiple contiguous chunks of data at the same time). We will refer to this as \u0026ldquo;disk\u0026rdquo;. We will not make a (major) distinction between solid-state storage (SSD) and spinning hard drives (HDD). The DBMS assumes that the primary storage location of the database is on non-volatile disk.\nThe DBMS\u0026rsquo;s components manage the movement of data between non-volatile and volatile storage.\nRandom access on non-volatile storage is usually much slower than sequential access. So DBMS will want to maximize sequential access.\nAlgorithms try to reduce number of writes to random pages so that data is stored in contiguous blocks.\nAllocating multiple pages at the same time is called an extent.\nDBMS vs OS\nIt is possible to use the OS by using mmap. Regarding correctness and performance reasons, mmap hits a page fault, the process will be blocked:\nYou never want to use mmap in your DBMS if you need to write.\nThe DBMS (almost) always wants to control things itself and can do a better job at it since it knows more about the data being accessed and the queries being processed.\nIt is possible to use the OS by using:\nmadvise: Tells the OS know when you are planning on reading certain pages. mlock: Tells the OS to not swap memory ranges out to disk. msync: Tells the OS to flush memory ranges out to disk. Why not use the OS? DBMS (almost) always wants to control things itself and can do a better job than the OS.\nFlushing dirty pages to disk in the correct order.\nSpecialized prefetching.\nBuffer replacement policy.\nThread/process scheduling.\nStorage Manager\nThe DBMS’s storage manager is responsible for managing a database’s files. It represents the files as a collection of pages. It also keeps track of what data has been read and written to pages as well how much free space there is in these pages.\nDataBase Pages\nThe DBMS organizes the database across one or more files in fixed-size blocks of data called pages.\nPages can contain different kinds of data (tuples, indexes, etc).\nMost systems will not mix these types within pages.\nSome systems will require that pages are self-contained, meaning that all the information needed to read each page is on the page itself.\nEach page is given a unique identifier. If the database is a single file, then the page id can just be the file offset. Most DBMSs have an indirection layer that maps a page id to a file path and offset. The upper levels of the system will ask for a specific page number. Then, the storage manager will have to turn that page number into a file and an offset to find the page.\nMost DBMSs uses fixed-size pages to avoid the engineering overhead needed to support variable-sized pages. For example, with variable-size pages, deleting a page could create a hole in files that the DBMS cannot easily fill with new pages. There are three concepts of pages in DBMS:\nHardware page (usually 4 KB). OS page (4 KB). Database page (0.5-16 KB). (4KB: SqLite, DB2, Oracle; 8KB: SQL Server, PostgreSQL; 16KB: MySQL) The storage device guarantees an atomic write of the size of the hardware page.\nA hardware page is the largest block of data that the storage device can guarantee failsafe writes. So if the hardware page is 4 KB and the system tries to write 4 KB to the disk, either all 4 KB will be written, or none of it will.\nThis means that if our database page is larger than our hardware page, the DBMS will have to take extra measures to ensure that the data gets written out safely since the program can get partway through writing a database page to disk when the system crashes.\nExample: MySQL Doublewrite Buffer: The doublewrite buffer is a storage area where InnoDB writes pages flushed from the buffer pool before writing the pages to their proper positions in the InnoDB data files. If there is an operating system, storage subsystem, or unexpected mysqld process exit in the middle of a page write, InnoDB can find a good copy of the page from the doublewrite buffer during crash recovery.\nAlthough data is written twice, the doublewrite buffer does not require twice as much I/O overhead or twice as many I/O operations. Data is written to the doublewrite buffer in a large sequential chunk, with a single fsync() call to the operating system\nDataBase Heap\nA heap file is an unordered collection of pages with tuples that are stored in random order.\nCreate / Get / Write / Delete Page\nMust also support iterating over all pages.\nMaintain a header page at the beginning of the file that stores two pointers: HEAD of the free page list and the data page list.\nEach page keeps track of how many free slots they currently have.\nThe DBMS maintains special pages that tracks the location of data pages in the database files. The directory also records the number of free slots per page. Must make sure that the directory pages are in sync with the data pages.\nPage Layout\nEvery page contains a header of meta-data about the page\u0026rsquo;s contents. Page Size, Checksum, DBMS Version, Transaction Visibility, Compression Information.\nThe most common layout scheme is called slotted pages. The slot array maps \u0026ldquo;slots\u0026rdquo; to the tuples\u0026rsquo; starting position offsets. The header keeps track of: 1. The # of used slots 2. The offset of the starting location of the last slot used.\nEach tuple is assigned a unique record identifier. Most common: page_id+ offset/slot, Can also contain file location info.\nEach tuple is prefixed with a header that contains meta-data about it. Visibility info (concurrency control), Bit Map for NULL values.\nDBMS can physically denormalize (e.g., \u0026ldquo;pre join\u0026rdquo;) related tuples and store them together in the same page.\n4. DataBase Storage (2) Readings: Chapter 10.5-10.8\nData Represtentation\nInstead of storing tuples in pages, the DBMS only stores log records.\nThe system appends log records to the file of how the database was modified:\nInserts store the entire tuple.\nDeletes mark the tuple as deleted.\nUpdates contain the delta of just the attributes that were modified.\nTo read a record, the DBMS scans the log backwards and \u0026ldquo;recreates\u0026rdquo; the tuple to find what it needs. Build indexes to allow it to jump to locations in the log. And periodically compact the log.\nCompaction coalesces larger log files into smaller files by removing unnecessary records.\nNumeric data types with (potentially) arbitrary precision and scale. Used when rounding errors. Many different implementations. Example: NUMERIC, DECIMAL\nExample: Store in an exact, variable-length binary representation with additional meta-data.\nCan be less expensive if you give up arbitrary precision.\nMost DBMSs don\u0026rsquo;t allow a tuple to exceed the size of a single page.\nTo store values that are larger than a page, the DBMS uses separate overflow storage pages.\nPostgres: TOAST (\u0026gt;2KB)\nMySQL: Overflow (\u0026gt;1/2 size of page)\nSQL Server: Overflow (\u0026gt;size of page)\nSome systems allow you to store a really largevalue in an external file.Treated as a BLOB type.\nOracle: BFILE data type\nMicrosoft: FILESTREAM data type\nThe DBMS can not manipulate the contents of an external file. No durability protections. No transaction protections.\nSystem Catalogs\nA DBMS stores meta-data about databases in its internal catalogs.\nTables, columns, indexes, views; Users, permissions; Internal statistics Almost every DBMS stores the database\u0026rsquo;s catalog inside itself (i.e., as tables). You can query the DBMS’s internal INFORMATION_SCHEMA catalog to get info about the database.\nANSI standard set of read-only views that provide info about all the tables, views, columns, and procedures in a database DataBase WorkLoads\nOn-Line Transaction Processing (OLTP):\nFast operations that only read/update a small amount of data each time.\nSimple queries that read/update a small amount of data that is related to a single entity in the database.\nThis is usually the kind of application that people build first.\nOn-Line Analytical Processing (OLAP):\nComplex queries that read a lot of data to compute aggregates.\nComplex queries that read large portions of the database spanning multiple entities.\nHybrid Transaction + Analytical Processing: OLTP + OLAP together on the same database instance\nIdeal for OLTP workloads where queries tend to operate only on an individual entity and insert-heavy workloads.\nThe relational model does not specify that we have to store all of a tuple\u0026rsquo;s attributes together in a single page.\nN-ARY Storage Model\nThe DBMS can store tuples in different ways that are better for either OLTP or OLAP workloads.\nWe have been assuming the n-ary storage model(aka \u0026ldquo;row storage\u0026rdquo;) so far this semester.\nExample: MySQL, PostgreSQL\nAdvantages\nFast inserts, updates, and deletes.\nGood for queries that need the entire tuple.\nDisadvantages\nNot good for scanning large portions of the table and/or a subset of the attributes. Ideal for OLAP workloads where read-only queries perform large scans over a subset of the table’s attributes.\nDecomposition Storage Model\nThe DBMS stores the values of a single attribute for all tuples contiguously in a page(aka \u0026ldquo;column store\u0026rdquo;).\nIdeal for OLAP workloads where read-only queries perform large scans over a subset of the table’s attributes.\nAdvantages\nReduces the amount wasted I/O because the DBMS only reads the data that it needs.\nBetter query processing and data compression (more on this later).\nDisadvantages\nSlow for point queries, inserts, updates, and deletes because of tuple splitting/stitching. Conclusion\nIt is important to choose the right storage model for the target workload:\nOLTP = Row Store\nOLAP = Column Store\n5. Buffer Pools Readings: Chapter 10.5-10.8\nMeta Data\nMemory region organized as an array of fixed-size pages.An array entry is called a frame.\nWhen the DBMS requests a page, an exact copy is placed into one of these frames.\nThe page table keeps track of pages that are currently in memory.\nAlso maintains additional meta-data per page:\nDirty Flag, 2. Pin/Reference Counter Locks and Latches\nLocks:\nProtects the database\u0026rsquo;s logical contents from other transactions.\nHeld for transaction duration.\nNeed to be able to rollback changes.\nLatches:\nProtects the critical sections of the DBMS\u0026rsquo;s internal data structure from other threads.\nHeld for operation duration.\nDo not need to be able to rollback changes.\nPage directory\nThe page directory is the mapping from page ids to page locations in the database files.\nAll changes must be recorded on disk to allow the DBMS to find on restart. The page table is the mapping from page ids to a copy of the page in buffer pool frames.\nThis is an in-memory data structure that does not need to be stored on disk. Global Policies:\nMake decisions for all active transactions. Local Policies:\nAllocate frames to a specific transactions without considering the behavior of concurrent transactions s.\nStill need to support sharing pages.\nBuffer Pool Optimizations\nThe DBMS does not always have a single buffer pool for the entire system.\nMultiple buffer pool instances\nPer-database buffer pool\nPer-page type buffer pool\nHelps reduce latch contention and improve locality.\nEmbed an object identifier in record ids and then maintain a mapping from objects to specific buffer pools.\nHash the page id to select whichbuffer pool to access.\nPrefetching\nThe DBMS can also prefetch pages based on a query plan.\nSequential Scans, 2. Index Scans Scan Sharing\nQueries can reuse data retrieved from storage or operator computations.\nAlso called synchronized scans.\nThis is different from result caching.\nAllow multiple queries to attach to a single cursor that scans a table.\nQueries do not have to be the same.\nCan also share intermediate results.\nIf a query wants to scan a table and another query is already doing this, then the DBMS will attach the second query\u0026rsquo;s cursor to the existing cursor.\nThe sequential scan operator will not store fetched pages in the buffer pool to avoid overhead.\nMemory is local to running query.\nWorks well if operator needs to read a large sequence of pages that are contiguous on disk.\nCan also be used for temporary data (sorting, joins).\nOS Page Caching\nMost disk operations go through the OS API.\nUnless you tell it not to, the OS maintains its own filesystem cache (i.e., the page cache).\nMost DBMSs use direct I/O (O_DIRECT) to bypass the OS’s page cache.\nRedundant copies of pages.\nDifferent eviction policies.\nLoss of control over file I/O.\nBuffer Replacement Policies\nWhen the DBMS needs to free up a frame to make room for a new page, it must decide which page to evictfrom the buffer pool.\nGoals: Correctness, Accuracy, Speed, Meta-data overhead\nMaintain a single timestamp of when each page was last accessed.\nWhen the DBMS needs to evict a page, select the one with the oldest timestamp.\nKeep the pages in sorted order to reduce the search time on eviction. Approximation of LRU that does not need a separate timestamp per page.\nEach page has a reference bit.\nWhen a page is accessed, set to 1.\nOrganize the pages in a circular buffer with a \u0026ldquo;clock hand\u0026rdquo;:\nUpon sweeping, check if a page\u0026rsquo;s bit is set to 1.\nIf yes, set to zero. If no, then evict.\nProblems\nLRU and CLOCK replacement policies are susceptible to sequential flooding.\nA query performs a sequential scan that reads every page.\nThis pollutes the buffer pool with pages that are read once and then never again.\nIn some workloads the most recently used page is the most unneeded page.\nTrack the history of last Kreferences to each page as timestamps and compute the interval between subsequent accesses.\nThe DBMS then uses this history to estimate the next time that page is going to be accessed.\nLocalization\nThe DBMS chooses which pages to evict on a per transactions /query basis. This minimizes the pollution of the buffer pool from each query.\nKeep track of the pages that a query has accessed. Example: Postgres maintains a small ring buffer that is private to the query.\nThe DBMS knows about the context of each page during query execution.\nIt can provide hints to the buffer pool on whether a page is important or not.\nDiary Pages\nFAST: If a page in the buffer pool is notdirty, then the DBMS can simply \u0026ldquo;drop\u0026rdquo; it.\nSLOW: If a page is dirty, then the DBMS must write back to disk to ensure that its changes are persisted.\nTrade-off between fast evictions versus dirty writing pages that will not be read again in the future.\nOther Memory Pools\nThe DBMS needs memory for things other than just tuples and indexes.\nThese other memory pools may not always backed by disk. Depends on implementation.\nSorting + Join Buffers\nQuery Caches\nMaintenance Buffers\nLog Buffers\nDictionary Caches\n6. Hash Tables Readings: Chapter 11.6-11.7\nDefinition\nA hash table implements an unordered associative array that maps keys to values.\nIt uses a hash function to compute an offset into the array for a given key, from which the desired value can be found.\nSpace Complexity: O(n) Time Complexity:\nAverage: O(1)\nWorst: O(n)\nTo find an entry, mod the key by the number of elements to find the offset in the array.\nHash Function\nWe do not want to use a crypto graphic hash function for DBMS hash tables.\nWe want something that is fast and has a low collision rate.\nName Usage CRC-64(1975) Used in networking for error detection. MurmurHash(2008) Designed as a fast, general-purpose hash function. Google CityHash(2011) Designed to be faster for short keys (\u0026lt;64 bytes). Facebook XXHash(2012) From the creator of zstdcompression. Google FarmHash(2014) Newer version of CityHashwith better collision rates. Static Hash Shemes\nLingering Probing Hashing Resolve collisions by linearly searching for the next free slot in the table.\nTo determine whether an element is present, hash to a location in the index and scan for it.\nMust store the key in the index to know when to stop scanning.\nInsertions and deletions are generalizations of lookups.\nRobin Hood Hashing Variant of linear probe hashing that steals slots from \u0026ldquo;rich\u0026rdquo; keys and give them to \u0026ldquo;poor\u0026rdquo; keys.\nEach key tracks the number of positions they are from where its optimal position in the table.\nOn insert, a key takes the slot of another key if the first key is farther away from its optimal position than the second key.\n1 2 3 4 5 6 7 8 9 iterator find(const FindKey\u0026amp; key) { size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); EntryPointer it = entries + ptrdiff_t(index); for (int8_t distance = 0; it-\u0026gt;distance_from_desired \u0026gt;= distance; ++distance, ++it) { if (compares_equal(key, it-\u0026gt;value)) return {it}; } return end(); } Cuckoo Hasing Use multiple hash tables with different hash function seeds.\nOn insert, check every table and pick anyone that has a free slot.\nIf no table has a free slot, evict the element from one of them and then re-hash it find a new location.\nLook-ups and deletions are always O(1) because only one location per hash table is checked.\nDynamic hashing Shemes\nThe previous hash tables require the DBMS to know the number of elements it wants to store.\nOtherwise, it must rebuild the table if it needs to grow/shrink in size. Dynamic hash tables resize themselves on demand.\nChained Hashing Maintain a linked list of buckets for each slot in the hash table.\nResolve collisions by placing all elements with the same hash key into the same bucket.\nTo determine whether an element is present, hash to its bucket and scan for it.\nInsertions and deletions are generalizations of lookups.\nExtendible Hashing Chained-hashing approach where we split buckets instead of letting the linked list grow forever.\nMultiple slot locations can point to the same bucket chain.\nReshuffle bucket entries on split and increase the number of bits to examine.\nData movement is localized to just the split chain. Linear Hashing The hash table maintains a pointer that tracks the next bucket to split.\nWhen any bucket overflows, split the bucket at the pointer location. Use multiple hashes to find the right bucket for a given key.\nCan use different overflow criterion:\nSpace Utilization\nAverage Length of Overflow Chains\n7. Tree Indexes Readings: Chapter 11.1-11.4\nA table index is a replica of a subset of a table\u0026rsquo;s attributes that are organized and/or sorted for efficient access using those attributes.\nThe DBMS ensures that the contents of the table and the index are logically synchronized.\nThere is a trade-off regarding the number of indexes to create per database.\nStorage Overhead\nMaintenance Overhead\nPeople also use the term to generally refer to a class of balanced tree data structures:\nB-Tree(1971), B+Tree(1973), B*Tree(1977?), Blink-Tree(1981) A B+Tree is a self-balancing tree data structure that keeps data sorted and allows searches, sequential access, insertions, and deletions in O(log n).\nGeneralization of a binary search tree, since a node can have more than two children.\nOptimized for systems that read and write large blocks of data.\nB+ Tree Properties\nA B+Tree is an M-way search tree with the following properties:\nIt is perfectly balanced (i.e., every leaf node is at the same depth in the tree)\nEvery node other than the root is at least half-full M/2-1 ≤ #keys ≤ M-1\nEvery inner node with k keys has k+1 non-null children\nNodes\nEvery B+ Treenode is comprised of an array of key/value pairs.\nIt contains level, slots, previous, next, sorted keys and values.\nThe keys are derived from the attribute(s) that the index is based on.\nThe values will differ based on whether the node is classified as an inner node or a leaf node.\nThe arrays are (usually) kept in sorted key order.\nValues Approach:\nRecord IDs(Used: PostgreSQL, SQLServer, DB2, Oracle): A pointer to the location of the tuple to which the index entry corresponds. Tuple Data(Used: SQLite, SQLServer, MySQL, Oracle): The leaf nodes store the actual contents of the tuple. Secondary indexes must store the Record ID as their values. B Tree vs B+Tree\nThe original B-Tree from 1972 stored keys and values in all nodes in the tree.\nMore space-efficient, since each key only appears once in the tree. A B+Tree only stores values in leaf nodes. Inner nodes only guide the search process.\nB+Tree Insert\nFind correct leaf node L.Put data entry into Lin sorted order.\nIf L has enough space, done!\nOtherwise, split L keys into L and a new node L2\nRedistribute entries evenly, copy up middle key.\nInsert index entry pointing to L2 into parent of L.\nTo split inner node, redistribute entries evenly, but push up middle key.\nDuplicate Keys\nApproach #1: Append Record ID\nAdd the tuple\u0026rsquo;s unique Record ID as part of the key to ensure that all keys are unique.\nThe DBMS can still use partial keys to find tuples.\nApproach #2: Overflow Leaf Nodes\nAllow leaf nodes to spill into overflow nodes that contain the duplicate keys.\nThis is more complex to maintain and modify.\nB+Tree Delete\nStart at root, find leaf L where entry belongs. Remove the entry. If Lis at least half-full, done! If L has only M/2-1 entries, then:\nTry to re-distribute, borrowing from sibling (adjacent node with same parent as L).\nIf re-distribution fails, merge L and sibling.\nIf merge occurred, must delete entry (pointing to L or sibling) from parent of L.\nSome DBMSs do not always merge nodes when they are half full.\nDelaying a merge operation may reduce the amount of reorganization.\nIt may also be better to just let smaller nodes exist and then periodically rebuild entire tree.\nClustered Indexs\nThe table is stored in the sort order specified by the primary key.\nCan be either heap-or index-organized storage. Some DBMSs always use a clustered index.\nIf a table does not contain a primary key, the DBMS will automatically make a hidden primary key. Other DBMSs cannot use them at all.\nTraverse to the left-most leaf page and then retrieve tuples from all leaf pages.\nThis will always be better than external sorting.\nRetrieving tuples in the order they appear in a non-clustered index can be very inefficient.\nThe DBMS can first figure out all the tuples that it needs and then sort them based on their Page ID.\nNode Size\nThe slower the storage device, the larger the optimal node size for a B+Tree.\nHDD: ~1MB\nSSD: ~10KB\nIn-Memory: ~512B\nOptimal sizes can vary depending on the workload\nLeaf Node Scans vs. Root-to-Leaf Traversals. Merge Threshold\nSome DBMSs do not always merge nodes when they are half full.\nDelaying a merge operation may reduce the amount of reorganization.\nIt may also be better to just let smaller nodes exist and then periodically rebuild entire tree.\nOptimizations\nPerfix Compression Sorted keys in the same leaf node are likely to have the same prefix.\nInstead of storing the entire key each time, extract common prefix and store only unique suffix for each key.\nDeduplication Non-unique indexes can end up storing multiple copies of the same key in leaf nodes.\nThe leaf node can store the key once and then maintain a list of tuples with that key (similar to what we discussed for hash tables).\nBulk insert The fastest way to build a new B+Treefor an existing table is to first sort the keys and then build the index from the bottom up.\n8. Index Concurrency Control Readings: Chapter 15.10\nA concurrency control protocol is the method that the DBMS uses to ensure \u0026ldquo;correct\u0026rdquo; results for concurrent operations on a shared object.\nLocks Latches Separate User Transactions Threads Protect Database Contents In-Memory Data Structures During Entire Transactions Critical Sections Modes Shared,Exclusive, Update, Intention Read, Write Deadlock Detection \u0026amp; Resolution Avoidance by Waits-for, Timeout, Aborts Coding Discipline Keptin LockManager Protected Data Structure Latch Mode\nRead Mode\nMultiple threads can read the same object at the same time.\nA thread can acquire the read latch if another thread has it in read mode.\nWrite Mode\nOnly one thread can access the object.\nA thread cannot acquire a write latch if another thread has it in any mode.\nApproach\nBlocking OS Mutex Simple to use; Non-scalable (about 25ns per lock/unlock invocation);\nExample: std::mutex.\nReference: https://en.wikipedia.org/wiki/Futex\nTest-and-Set Spin Latch (TAS) Very efficient (single instruction to latch/unlatch); Non-scalable, not cache-friendly, not OS-friendly;\nExample: std::atomic\u0026lt;T\u0026gt;. \u0026ldquo;do not use spinlocks in user space, unless you actually know what you\u0026rsquo;re doing.\u0026rdquo;\nReader-Writer Latches Allows for concurrent readers; Must manage read/write queues to avoid starvation; Can be implemented on top of spin latches.\nHash Table Latch\nEasy to support concurrent access due to the limited ways threads access the data structure.\nAll threads move in the same direction and only access a single page/slot at a time.\nDeadlocks are not possible.\nTo resize the table, take a global write latch on the entire table (e.g., in the header page).\nApproach #1: Page Latches:\nEach page has its own reader-writer latch that protects its entire contents.\nThreads acquire either a read or write latch before they access a page.\nApproach #2: Slot Latches\nEach slot has its own latch.\nCan use a single-mode latch to reduce meta-data and computational overhead.\nAtomic instruction that compares contents of a memory location M to a given value V\nIf values are equal, installs new given value V’ in M\nOtherwise, operation fails\nB+ Tree Concurrency Control\nWe want to allow multiple threads to read and update a B+Treeat the same time.\nWe need to protect against two types of problems:\nThreads trying to modify the contents of a node at the same time.\nOne thread traversing the tree while another thread splits/merges nodes.\nProtocol to allow multiple threads to access/modify B+Treeat the same time.\nBasic Idea:\nGet latch for parent\nGet latch for child\nRelease latch for parent if \u0026ldquo;safe\u0026rdquo;\nA safe node is one that will not split or merge when updated.\nNot full (on insertion)\nMore than half-full (on deletion)\nFind: Start at root and go down; repeatedly,\nAcquire R latch on child\nThen unlatch parent\nInsert/Delete: Start at root and go down, obtaining W latches as needed. Once child is latched, check if it is safe:\nIf child is safe, release all latches on ancestors Better Latching Algorithm\nIt depends on it that most modifications to a B+ Tree will not require a split or merge.\nSearch: Same as before.\nInsert/Delete:\nSet latches as if for search, get to leaf, and set W latch on leaf.\nIf leaf is not safe, release all latches, and restart thread using previous insert/delete protocol with write latches.\nThis approach optimistically assumes that only leaf node will be modified; if not, R latches set on the first pass to leaf are wasteful.\nObservation\nThe threads in all the examples so far have acquired latches in a \u0026ldquo;top-down\u0026rdquo; manner.\nA thread can only acquire a latch from a node that is below its current node.\nIf the desired latch is unavailable, the thread must wait until it becomes available.\nLeaf Node Scans\nLatches do not support deadlock detection or avoidance. The only way we can deal with this problem is through coding discipline.\nThe leaf node sibling latch acquisition protocol must support a \u0026ldquo;no-wait\u0026rdquo; mode.\nThe DBMS\u0026rsquo;s data structures must cope with failed latch acquisitions.\n9. Sorting \u0026amp; Aggregations Readings: Chapter 12.4-12.5\nJust like it cannot assume that a table fits entirely in memory, a disk-oriented DBMS cannot assume that query results fit in memory.\nWe are going to rely on the buffer pool to implement algorithms that need to spill to disk.\nWe are also going to prefer algorithms that maximize the amount of sequential I/O.\nExternal Merge Sort\nDivide-and-conquer algorithm that splits data into separate runs, sorts them individually, and then combines them into longer sorted runs.\nSorting Sort chunks of data that fit in memory and then write back the sorted chunks to a file on disk. Merging Combine sorted runs into larger chunks. The DBMS has a finite number of B buffer pool pages to hold input and output data.\nSteps:\nRead all B pages of the table into memory;\nSort pages into runs and write them back to disk;\nRecursively merge pairs of runs into runs twice as long.\nUses three buffer pages (2 for input pages, 1 for output)\nIn each pass, we read and writeevery page in the file.\n$Number \\ of \\ passes \\ =\\ 1 + ⌈\\log_2N⌉ $\n$Total\\ I/O\\ cost\\ =\\ 2N \\times (Nums \\ of\\ passes)$\nThis algorithm only requires three buffer pool pages to perform the sorting ($B=3$): Two input pages, one output page.\nBut even if we have more buffer space available ($B \u0026gt; 3$), it does not effectively utilize them if the worker must block on disk I/O. Although we can get the equation:\n$Number\\ of\\ passes\\ with\\ N\\ pages\\ and\\ B\\ buffer\\ pools\\ =\\ 1+⌈\\log_B-1⌈N/ B⌉⌉$\nDouble Buffering Optimization\nPrefetch the next run in the background and store it in a second buffer while the system is processing the current run.\nReduces the wait time for I/O requests at each step by continuously utilizing the disk. B+ Trees for Sorting\nIf the table that must be sorted already has a B+Tree index on the sort attribute(s), then we can use that to accelerate sorting.\nRetrieve tuples in desired sort order by simply traversing the leaf pages of the tree.\nClustered B+ Tree Traverse to the left-most leaf page, and then retrieve tuples from all leaf pages.\nThis is always better than external sorting because there is no computational cost, and all disk access is sequential.\nUnclustered B+ Tree Chase each pointer to the page that contains the data.\nThis is almost always a bad idea. In general, one I/O per data record.\nAggregations\nCollapse values for a single attribute from multiple tuples into a single scalar value.\nWhat if we do not need the data to be ordered?\nForming groups in GROUPBY (no ordering)\nRemoving duplicates in DISTINCT (no ordering)\nHashing is a better alternative in this scenario.\nOnly need to remove duplicates, no need for ordering.\nCan be computationally cheaper than sorting.\nPopulate an ephemeral hash table as the DBMS scans the table. For each record, check whether there is already an entry in the hash table:\nDISTINCT: Discard duplicate\nGROUPBY: Perform aggregate computation\nExternal Hashing Aggregate\nPhase #1 Partition\nDivide tuples into buckets based on hash key\nWrite them out to disk when they get full\nPhase #2 ReHash\nBuild in-memory hash table for each partition and compute the aggregation Partition\nUse a hash function $h_1$ to split tuples into partitions on disk.\nA partition is one or more pages that contain the set of keys with the same hash value.\nPartitions are “spilled” to disk via output buffers.\nAssume that we have B buffers.\nWe will use B-1 buffers for the partitions and 1 buffer for the input data.\nReHash\nFor each partition on disk:\nRead it into memory and build an in-memory hash table based on a second hash function $h_2$.\nThen go through each bucket of this hash table to bring together matching tuples.\nThis assumes that each partition fits in memory.\n10. Join Algorithms Readings: Chapter 12.4-12.6\nWe will focus on performing binary joins (two tables) using inner equi join algorithms.\nThese algorithms can be tweaked to support other joins.\nMulti-way joins exist primarily in research literature.\nIn general, we want the smaller table to always be the left table (\u0026ldquo;outer table\u0026rdquo;) in the query plan.\nDecision #1: Output\nWhat data does the join operator emit to its parent operator in the query plan tree? Decision #2: Cost Analysis Criteria\nHow do we determine whether one join algorithm is better than another? Subsequent operators in the query plan never need to go back to the base tables to get more data.\nMaterialization\nEarly Materialization:\nCopy the values for the attributes in outer and inner tuples into a new output tuple. Subsequent operators in the query plan never need to go back to the base tables to get more data.\nLate Materialization:\nOnly copy the joins keys along with the Record IDs of the matching tuples. Ideal for column stores because the DBMS does not copy data that is not needed for the query.\nAnalysis Criteria\nAssume:\nM pages in table R, m tuples in R\nN pages in table S, n tuples in S\nCost Metric: # of IOs to compute join\nWe will ignore output costs since that depends on the data and we cannot compute that yet.\n$R\\ ⨝\\ S$ is the most common operation and thus must be carefully optimized.\n$R\\ \\times\\ S$ followed by a selection is inefficient because the cross-product is large.\nThere are many algorithms for reducing join cost, but no algorithm works well in all scenarios.\nNested Loop Join\nStupid: For every tuple in R, it scans Sonce Cost: $M + (m\\ \\times\\ N)$\nBlock Nested Loop Join: For every block in R, it scans S once. Cost: $M+(M\\ \\times\\ N)$\nIf we have B buffers available:\nUse B-2 buffers for scanning the outer table. Use one buffer for the inner table, one buffer for storing output. This algorithm uses B-2 buffers for scanning R. Cost: $M+ ( ⌈M / (B-2) ⌉\\times N)$, if $B\u0026gt;M+2$, then will be $M+N$\nIndex Nested Join: Assume the cost of each index probe is some constant C per tuple. Cost: $M + (m \\times C)$ Sort Merge Join\nPhase #1: Sort\nSort both tables on the join key(s).\nWe can use the external merge sort algorithm that we talked about last class.\nPhase #2: Merge\nStep through the two sorted tables with cursors and emit matching tuples.\nMay need to backtrack depending on the join type.\nSort Cost (R): $2M \\times (1 + ⌈ logB-1 ⌈M/ B⌉ ⌉)$ Sort Cost (S): $2N \\times (1 + ⌈ logB-1 ⌈N / B⌉ ⌉)$ Merge Cost: $(M+ N)$ Total Cost: Sort + Merge\nThe worst case for the merging phase is when the join attribute of all the tuples in both relations contains the same value.\nThe input relations may be sorted either by an explicit sort operator, or by scanning the relation using an index on the join key.\nHash Join\nPhase #1: Build\nScan the outer relation and populate a hash table using the hash function h1 on the join attributes. Phase #2: Probe\nScan the inner relation and use h1 on each tuple to jump to a location in the hash table and find a matching tuple. Approach #1: Full Tuple\nAvoid having to retrieve the outer relation\u0026rsquo;s tuple contents on a match.\nTakes up more space in memory.\nApproach #2: Tuple Identifier\nCould be to either the base tables or the intermediate output from child operators in the query plan.\nIdeal for column stores because the DBMS does not fetch data from disk that it does not need.\nAlso better if join selectivity is low.\nOptimization: Create a Bloom Filterduring the build phase when the key is likely to not exist in the hash table.\nThreads check the filter before probing the hash table. This will be faster since the filter will fit in CPU caches.\nSometimes called (sideways information passing.)\nGrace Hash Join\nIf the buckets do not fit in memory, then use recursive partitioningto split the tables into chunks that will fit.\nBuild another hash table for $bucket_{R,i}$, using hash function $h_2$ (with $h_2≠h_1$). Then probe it for each tuple of the other table\u0026rsquo;s bucket at that level. Cost of hash join?\nAssume that we have enough buffers. Cost: $3(M+ N)$ Partitioning Phase:\nRead+Write both tables Cost: $2(M+N)$ Probing Phase:\nRead both tables $M+N$ Summary\nAlgorithm IO Cost Example Simple Nested Loop Join $M+(m\\times N)$ 1.3 hours Block Nested Loop Join $M+(M\\times N)$ 50 seconds Index Nested Loop Join $M+(M\\times C)$ Variable Sort-Merge Join $M+N+(sort\\ cost)$ 0.75 seconds Hash Join $3\\times(M+N)$ 0.45 seconds Hashing is almost always better than sorting for operator execution.\nCaveats:\nSorting is better on non-uniform data.\nSorting is better when result needs to be sorted.\n11. Query Execution (1) Readings: Chapter 12.1-12.3, 12.7\nIterator Model\nEach query plan operator implements a Next() function.\nOn each invocation, the operator returns either a single tuple or a null marker if there are no more tuples.\nThe operator implements a loop that calls Next() on its children to retrieve their tuples and then process them.\nAlso called Volcano or Pipeline Model.\nThis is used in almost every DBMS. Allows for tuple pipelining. Some operators must block until their children emit all their tuples.\nJoins, Subqueries, Order By Materialization Model\nEach operator processes its input all at once and then emits its output all at once.\nThe operator \u0026ldquo;materializes\u0026rdquo; its output as a single result.\nThe DBMS can push down hints (e.g., LIMIT) to avoid scanning too many tuples.\nCan send either a materialized row or a single column.\nThe output can be either whole tuples (NSM) or subsets of columns (DSM).\nBetter for OLTP workloads because queries only access a small number of tuples at a time.\nLower execution / coordination overhead.\nFewer function calls.\nNot good for OLAP queries with large intermediate results.\nVectorization Model\nLike the Iterator Model where each operator implements a Next() function, but:\nEach operator emits a batch of tuples instead of a single tuple.\nThe operator\u0026rsquo;s internal loop processes multiple tuples at a time.\nThe size of the batch can vary based on hardware or query properties.\nIdeal for OLAP queries because it greatly reduces the number of invocations per operator.\nAllows for operators to more easily use vectorized (SIMD) instructions to process batches of tuples.\nSequential Scan\nFor each page in the table:\nRetrieve it from the buffer pool.\nIterate over each tuple and check whether to include it.\nThe DBMS maintains an internal cursor that tracks the last page / slot it examined.\nThis is almost always the worst thing that the DBMS can do to execute a query.\nSequential Scan Optimizations: Prefetching, Buffer Pool Bypass, Parallelization, Heap Clustering, Zone Maps, Late Materialization.\nZone Maps: Pre-computed aggregates for the attribute values in a page. DBMS checks the zone map first to decide whether it wants to access the page.\nLate Materialization: DSM DBMSs can delay stitching together tuples until the upper parts of the query plan.\nIndex Scan\nThe DBMS picks an index to find the tuples that the query needs. Which index to use depends on:\nWhat attributes the index contains What attributes the query references The attribute\u0026rsquo;s value domains Predicate composition Whether the index has unique or non-unique keys Multi Index Scan\nSet intersection can be done with bitmaps, hash tables, or Bloom filters.\nOperators that modify the database (INSERT, UPDATE, DELETE) are responsible for checking constraints and updating indexes.\nUPDATE/DELETE:\nChild operators pass Record IDs for target tuples. Must keep track of previously seen tuples. INSERT:\nChoice #1: Materialize tuples inside of the operator.\nChoice #2: Operator inserts any tuple passed in from child operators.\nHalloween Problem: Anomaly where an update operation changes the physical location of a tuple, which causes a scan operator to visit the tuple multiple times.\nCan occur on clustered tables or index scans. First discoveredby IBM researchers while working on System R on Halloween day in 1976.\nExpression Evaluation\nThe DBMS represents a WHERE clause as an expression tree.\nThe nodes in the tree represent different expression types:\nComparisons (=, \u0026lt;, \u0026gt;, !=); Conjunction (AND), Disjunction (OR); Arithmetic Operators (+, -, *****, /, %); Constant Values; Tuple Attribute References.\nEvaluating predicates in this manner is slow.\nThe DBMS traverses the tree and for each node that it visits it must figure out what the operator needs to do.\nA better approach is to just evaluate the expression directly. (Think JIT compilation)\n12. Query Execution (2) Readings: Chapter 12.1-12.3, 12.7\nParallel DBMSs vs Distributed DBMSs\nDatabase is spread out across multiple resources to improve different aspects of the DBMS.\nAppears as a single logical database instance to the application, regardless of physical organization.\nSQL query for a single-resource DBMS should generate same result on a parallel or distributed DBMS. Parallel DBMSs\nResources are physically close to each other.\nResources communicate over high-speed interconnect.\nCommunication is assumed to be cheap and reliable.\nDistributed DBMSs\nResources can be far from each other.\nResources communicate using slow(er) interconnect.\nCommunication cost and problems cannot be ignored.\nProcess Model\nA DBMS’s process model defines how the system is architected to support concurrent requests from a multi-user application.\nA worker is the DBMS component that is responsible for executing tasks on behalf of the client and returning the results.\nProcess per DBMS Worker Each worker is a separate OS process.\nRelies on OS scheduler. Use shared-memory for global data structures. A process crash doesn’t take down entire system. Examples: IBM DB2, Postgres, Oracle Process Pool A worker uses any free process from the pool.\nStill relies on OS scheduler and shared memory. Bad for CPU cache locality. Examples: IBM DB2, Postgres (2015). Thread per DBMS Worker Single process with multiple worker threads.\nDBMS manages its own scheduling. May or may not use a dispatcher thread. Thread crash (may) kill the entire system. Examples: IBM DB2, MSSQL, MySQL, Oracle (2014). Advantages of a multi-threaded architecture:\nLess overhead per context switch. Do not have to manage shared memory. The thread per worker model does not mean that the DBMS supports intra-query parallelism. Inter- vs Intra- Query Parallelism\nInter-Query: Different queries are executed concurrently.\nIncreases throughput \u0026amp; reduces latency. Intra-Query: Execute the operations of a single query in parallel. Decreases latency for long-running queries. Improve overall performance by allowing multiple queries to execute simultaneously. If queries are read-only, then this requires little coordination between queries. If multiple queries are updating the database at the same time, then this is hard to do correctly.\nInter Query Parallelism\nImprove overall performance by allowing multiple queries to execute simultaneously.\nIf queries are read-only, then this requires little coordination between queries.\nIf multiple queries are updating the database at the same time, then this is hard to do correctly…\nIntra Query Parallelism\nImprove the performance of a single query by executing its operators in parallel. Think of organization of operators in terms of a producer/consumer paradigm. There are parallel versions of every operator.\nCan either have multiple threads access centralized data structures or use partitioning to divide work up. Parallel Grace Hash Join: Use a separate worker to perform the join for each level of buckets for R and S after partitioning.\nIntra Operator(Horizontal)\nDecompose operators into independent fragments that perform the same function on different subsets of data.\nThe DBMS inserts an exchange operator into the query plan to coalesce/split results from multiple children/parent operators.\nExchange Operator has three types as follows:\nExchange Type #1 –Gather\nCombine the results from multiple workers into a single output stream. Exchange Type #2 –Distribute\nSplit a single input stream into multiple output streams. Exchange Type #3 –Repartition\nShuffle multiple input streams across multiple output streams. Inter Operator(Vertical)\nOperations are overlapped in order to pipeline data from one stage to the next without materialization.\nWorkers execute operators from different segments of a query plan at the same time.\nAlso called pipeline parallelism.\nBushy Parallelism\nHybrid of intra-and inter-operator parallelism where workers execute multiple operators from different segments of a query plan at the same time.\nStill need exchange operators to combine intermediate results from segments.\nObservation\nUsing additional processes/threads to execute queries in parallel won\u0026rsquo;t help if the disk is always the main bottleneck.\nIn fact, it can make things worse if each worker is working on different segments of the disk. I/O Parallelism\nConfigure OS/hardware to store the DBMS\u0026rsquo;s files across multiple storage devices.\nStorage Appliances, RAID Configuration This is transparent to the DBMS.\nDataBase Partition\nSome DBMSs allow you to specify the disk location of each individual database.\nThe buffer pool manager maps a page to a disk location. This is also easy to do at the filesystem level if the DBMS stores each database in a separate directory.\nThe DBMS recovery log file might still be shared if transactions can update multiple databases. Partitioning\nSplit single logical table into disjoint physical segments that are stored/managed separately.\nPartitioning should (ideally) be transparent to the application.\nThe application should only access logical tables and not have to worry about how things are physically stored. Vertical Partioning Store a table’s attributes in a separate location (e.g., file, disk volume).\nMust store tuple information to reconstruct the original record.\nHorizontal Partioning Divide table into disjoint segments based on some partitioning key. Hash Partitioning\nRange Partitioning\nPredicate Partitioning\n13. Optimization (1) Readings: Chapter 13\nRemember that SQL is declarative. User tells the DBMS what answer they want, not how to get the answer.\nFirst implementation of a query optimizer from the 1970s. People argued that the DBMS could never choose a query plan better than what a human could write.\nHeuristics / Rules\nRewrite the query to remove stupid / inefficient things.\nThese techniques may need to examine catalog, but they do notneed to examine data.\nCost-based Search\nUse a model to estimate the cost of executing a plan.\nEvaluate multiple equivalent plans for a query and pick the one with the lowest cost.\nLogical vs Physical Plans\nThe optimizer generates a mapping of a logical algebra expression to the optimal equivalent physical algebra expression.\nPhysical operators define a specific execution strategy using an access path.\nThey can depend on the physical format of the data that they process (i.e., sorting, compression).\nNot always a 1:1 mapping from logical to physical.\nRelational Algebra Equivalences\nTwo relational algebra expressions are equivalent if they generate the same set of tuples.\nThe DBMS can identify better query plans without a cost model.\nThis is often called query rewriting.\nPredicate Pushdown\nExample:\n1 2 3 4 SELECT s.name, e.cid FROM studnet AS s, enrolled AS e WHERE s.id = e.sid AND e.grade = \u0026#39;A\u0026#39; The algrbra expression is:\n$\\pi_{name,cid}(\\sigma_{grade=\u0026lsquo;A\u0026rsquo;}(student⋈enrolled))$\nwhich is equal to the expression:\n$\\pi_{name,cid}(student⋈\\sigma_{grade={\u0026lsquo;A\u0026rsquo;}}(enrolled))$\nSelections Optimize:\nPerform filters as early as possible. Break a complex predicate, and push down:\n$\\sigma_{p1 \\wedge p2\\wedge \u0026hellip;\\wedge pn}(R)=\\sigma_{p1}(\\sigma_{p2}(\u0026hellip;\\sigma_{pn}(R)))$\nJoins Optimize:\n$R⋈S=S⋈R, \\ (R⋈S⋈T) =R⋈(S⋈T)$\nThe number of different join orderings for an n-way join is a Catalan Number ($≈4^n$)\nExhaustive enumeration will be too slow.\nProjections Optimize (Not important for a column store) :\nPerform them early to create smaller tuples and reduce intermediate results (if duplicates are eliminated) Project out all attributes except the ones requested or required (e.g., joining keys) Let\u0026rsquo;s see the example again:\n1 2 3 4 SELECT s.name, e.cid FROM student AS s, enrolled AS e WHERE s.sid = e.sid AND e.grade = \u0026#39;A\u0026#39; $\\pi_{name,cid}(student⋈\\sigma_{grade={\u0026lsquo;A\u0026rsquo;}}(enrolled))$\n$\\pi_{name,cid}(\\pi_{sid,name}(student)⋈\\pi_{cid,sid}(\\sigma_{grade={\u0026lsquo;A\u0026rsquo;}}(enrolled)))$\nLogical Query Optimization\nTransform a logical plan into an equivalent logical plan using pattern matching rules.\nThe goal is to increase the likelihood of enumerating the optimal plan in the search.\nCannot compare plans because there is no cost model but can \u0026ldquo;direct\u0026rdquo; a transformation to a preferred side.\nSplit Conjunctive predicates\n1 2 3 4 5 SELECT ARTIST.NAME FROM ARTIST, APPEARS, ALBUM WHERE ARTIST.ID=APPEARS.ARTIST_ID AND APPEARS.ALBUM_ID=ALBUM.ID AND ALBUM.NAME=\u0026#34;Andy\u0026#39;s OG Remix\u0026#34; Decompose predicates into their simplest forms to make it easier for the optimizer to move them around.\n$\\pi_{name}(\\sigma_{id=artist_id \\wedge album_id=album.id \\wedge name=\u0026ldquo;Andy\u0026rsquo;s OG Remix\u0026rdquo;}(artist\\times appears\\times album))$\nUsing selections optimization, break a complex predicate, and push down:\n$\\pi_{name}(\\sigma_{id=artist_id }(\\sigma_{album_id=album.id }(\\sigma_{name=\u0026ldquo;Andy\u0026rsquo;s OG Remix\u0026rdquo;}(artist\\times appears\\times album))))$\nMove the predicate to the lowest applicable point in the plan:\n$\\pi_{name}(\\sigma_{album_id=album.id }(\\sigma_{name=\u0026ldquo;Andy\u0026rsquo;s OG Remix\u0026rdquo;}album)\\times(\\sigma_{id=artist_id }(artist\\times appears))))$\nReplace all Cartesian Products with inner joins using the join predicates:\n$\\pi_{name}(\\sigma_{name=\u0026ldquo;Andy\u0026rsquo;s OG Remix\u0026rdquo;}album)⋈{album_id=album.id } (artist ⋈{id=artist_id} appears))$\nEliminate redundant attributes before pipeline breakers to reduce materialization cost:\n$\\pi_{name}(\\pi_{id}(\\sigma_{name=\u0026ldquo;Andy\u0026rsquo;s OG Remix\u0026rdquo;}album)⋈{album_id=album.id } $\\ $(\\pi{id}(\\pi_{id, name}(artist) ⋈{id=artist_{id}} \\pi{artist_{id}, album_{id}}(appears))))$\nNested Sub-Queries\nThe DBMS treats nested sub-queries in the where clause as functions that take parameters and return a single value or set of values. Two Approaches:\nRewrite to de-correlate and/or flatten them Decompose nested query and store result to temporary table 1 2 3 4 5 6 7 8 9 10 11 SELECT name FROM sailors AS S WHERE EXISTS ( SELECT * FROM reserves AS R WHERE S.sid = R.sid AND R.day = \u0026#39;2018-10-15\u0026#39; ) SELECT name FROM sailors AS S, reserves AS R WHERE S.sid = R.sid AND R.day = \u0026#39;2018-10-15\u0026#39; Decompose\n1 2 3 4 5 6 7 8 9 10 11 12 -- For each sailor with the highest rating (over all sailors) and at least -- two reservations for red boats, find the sailor id and the earliest date -- on which the sailor has a reservation for a red boat. SELECT S.sid, MIN(R.day) FROM sailors S, reserves R, boats B WHERE S.sid = R.sid AND R.bid = B.bid AND B.color = \u0026#39;red\u0026#39; AND S.rating = (SELECT MAX(S2.rating) FROM sailors S2) GROUP BY S.sid HAVING COUNT(*) \u0026gt; 1 For harder queries, the optimizer breaks up queries into blocks and then concentrates on one block at a time.\nSub-queries are written to a temporary table that are discarded after the query finishes.\n1 SELECT MAX(rating) FROM sailors -- Nested Block An optimizer transforms a query\u0026rsquo;s expressions (e.g., WHERE clause predicates) into the optimal/minimal set of expressions.\nImplemented using if/then/else clauses or a pattern-matching rule engine.\nSearch for expressions that match a pattern.\nWhen a match is found, rewrite the expression.\nHalt if there are no more rules that match.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 -- Impossible / Unnecessary Predicates SELECT * FROM A WHERE 1 = 0; -- return empty set SELECT * FROM A WHERE 1 = 1; -- remove where predicate -- Join Elimination SELECT A1.* FROM A AS A1 JOIN A AS A2 ON A1.id = A2.id; -- after optimize SELECT * FROM A; SELECT * FROM A AS A1 WHERE EXISTS(SELECT val FROM A AS A2 WHERE A1.id = A2.id); -- after optimize SELECT * FROM A; -- Merging Predicates SELECT * FROM A WHERE val BETWEEN 1 AND 100 OR val BETWEEN 50 AND 150; -- after optimize SELECT * FROM A WHERE val BETWEEN 1 AND 150; Cost-based Query Planning\nGenerate an estimate of the cost of executing a particular query plan for the current state of the database.\nEstimates are only meaningful internally. This is independent of the plan enumeration step that we will talk about next class.\nChoice #1: Physical Costs\nPredict CPU cycles, I/O, cache misses, RAM consumption, pre-fetching, etc…\nDepends heavily on hardware.\nChoice #2: Logical Costs\nEstimate result sizes per operator.\nIndependent of the operator algorithm.\nNeed estimations for operator result sizes.\nChoice #3: Algorithmic Costs\nComplexity of the operator algorithm implementation. Disk based DBMS cost Model\nThe number of disk accesses will always dominate the execution time of a query.\nCPU costs are negligible.\nMust consider sequential vs. random I/O.\nThis is easier to model if the DBMS has full control over buffer management.\nWe will know the replacement strategy, pinning, and assume exclusive access to disk. Postages cost Model\nUses a combination of CPU and I/O costs that are weighted by “magic” constant factors.\nDefault settings are obviously for a disk-resident database without a lot of memory:\nProcessing a tuple in memory is 400x faster than reading a tuple from disk.\nSequential I/O is 4x faster than random I/O.\nExample (IBM DB2 cost Model)\nDatabase characteristics in system catalogs; Hardware environment (microbenchmarks); Storage device characteristics (microbenchmarks); Communications bandwidth (distributed only); Memory resources (buffer pools, sort heaps); Concurrency Environment(Average number of users, Isolation level / blocking, Number of available locks).\n14. Optimization (2) Readings: Chapter 13\nStatistics\nThe DBMS stores internal statistics about tables, attributes, and indexes in its internal catalog.\nDifferent systems update them at different times.\nManual invocations:\nPostgres/SQLite: ANALYZE Oracle/MySQL: ANALYZETABLE SQL Server: UPDATE STATISTICS DB2: RUNSTATS Derivable Statistics\nFor each relation R, the DBMS maintains the following information:\n$N_R$: Number of tuples in R.\n$V(A,R)$: Number of distinct values for attribute A.\nThe selection cardinality $SC(A,R)$ is the average number of records with a value for an attribute A given $N_{R}/ V(A,R)$\nNote that this formula assumes data uniformity where every value has the same frequency as all other values.\nThe selectivity (sel) of a predicate P is the fraction of tuples that qualify.\nEquality Predicate: $sel (A=constant) = SC(P) / N_R$\nRange Predicate: $sel(A\u0026gt;=a) = (A_{max}– a+1) / (A_{max}– A_{min}+1)$\nNegation Query: $sel(not \\ P) = 1 – sel(P)$\nObservation: Selectivity ≈ Probability\nConjunction: $sel(P1 \\wedge P2) = sel(P1) · sel(P2)$\nDisjunction: $sel(P1 \\vee P2)= sel(P1) + sel(P2) – sel(P1 \\wedge P2)= sel(P1) + sel(P2)–sel(P1)·sel(P2)$\nThis assumes that the predicates are independent.\nResult size estimation\nGiven a join of R and S, what is the range of possible result sizes in # of tuples? General case: $R_{cols}\\cap S_{cols}={A}$ where A is not a primary key for either table.\nMatch each R-tuple with S-tuples: $estSize ≈ N_{R} · N_{S} / V(A, S)$\nSymmetrically, for S: $estSize ≈ N_{R} · N_{S} / V(A, R) $\nOverall: $estSize ≈ N_{R} · N_{S} / max({V(A,S), V(A,R)})$\nSelection Cardinality\nAssumption #1: Uniform Data\nThe distribution of values (except for the heavy hitters) is the same. Assumption #2: Independent Predicates\nThe predicates on attributes are independent Assumption #3: Inclusion Principle\nThe domain of join keys overlap such that each key in the inner relation will also exist in the outer table. Sketches\nProbabilistic data structures that generate approximate statistics about a data set.\nCost-model can replace histograms with sketches to improve its selectivity estimate accuracy.\nMost common examples:\nCount-Min Sketch(1988): Approximate frequency count of elements in a set.\nHyperLogLog(2007): Approximate the number of distinct elements in a set.\nSampling\nModern DBMSs also collect samples from tables to estimate selectivities.\nUpdate samples when the underlying tables changes significantly.\nQuery Optimization\nAfter performing rule-based rewriting, the DBMS will enumerate different plans for the query and estimate their costs. Like Single relation, Multiple relations, and Nested sub-queries.\nIt chooses the best plan it has seen for the query after exhausting all plans or some timeout.\nSimple heuristics are often good enough for this. OLTP queries are especially easy.\nQuery planning for OLTP queries is easy because they are sargable (Search Argument Able).\nIt is usually just picking the best index.\nJoins are almost always on foreign key relationships with a small cardinality.\nCan be implemented with simple heuristics.\nMulti-Relation Query Planning\nAs number of joins increases, number of alternative plans grows rapidly\nWe need to restrict search space. Fundamental decision in System R: only left-deep join trees are considered.\nModern DBMSs do not always make this assumption anymore. Use dynamic programming to reduce the number of cost estimations.\nEnumerate the orderings. Example: Left-deep tree #1, Left-deep tree #2…\nEnumerate the plans for each operator. Example: Hash, Sort-Merge, Nested Loop…\nEnumerate the access paths for each table. Example: Index #1, Index #2, SeqScan…\n15. Concurrency Control Theory Readings: Chapter 14\nA transaction is the execution of a sequence of one or more operations (e.g., SQL queries) on a database to perform some higher-level function.\nA transactions may carry out many operations on the data retrieved from the database\nThe DBMS is onlyconcerned about what data is read/written from/to the database.\nStrawman System\nExecute each transactions one-by-one (i.e., serial order) as they arrive at the DBMS.\nOne and only one transactions can be running at the same time in the DBMS. Before a transactions starts, copy the entire database to a new file and make all changes to that file.\nIf the transactions completes successfully, overwrite the original file with the new one.\nIf the transactions fails, just remove the dirty copy.\nCorrectness Criteria ACID\nAtomicity: All actions in the transactions happen, or none happen.(“all or nothing”)\nConsistency: If each transactions is consistent and the DB starts consistent, then it ends up consistent.(“it looks correct to me”)\nIsolation: Execution of one transactions is isolated from that of other transactions s.(“as if alone”)\nDurability: If a transactions commits, its effects persist.(“survive failures”)\nAtomicity\nDBMS guarantees that transactions are atomic.\nFrom user\u0026rsquo;s point of view: transactions always either executes all its actions or executes no actions at all. Mechanism For Ensuring Atomicity\nApproach #1: Logging\nDBMS logs all actions so that it can undo the actions of aborted transactions.\nMaintain undo records both in memory and on disk.\nThink of this like the black box in airplanes…\nApproach #2: Shadow Paging\nDBMS makes copies of pages and transactions smake changes to those copies. Only when the transactions commits is the page made visible to others.\nOriginally from System R.\nDatabase Consistency\nThe \u0026ldquo;world\u0026rdquo; represented by the database is logicallycorrect. All questions asked about the data are given logicallycorrect answers.\nIf the database is consistent before the transaction starts (running alone), it will also be consistent after.\nTransaction consistency is the application\u0026rsquo;s responsibility. DBMS cannot control this.\nIsolation\nDBMS achieves concurrency by interleaving the actions (reads/writes of DB objects) of transactions s.\nA concurrency controlprotocol is how the DBMS decides the proper interleaving of operations from multiple transactions.\nTwo categories of protocols:\nPessimistic: Don\u0026rsquo;t let problems arise in the first place.\nOptimistic: Assume conflicts are rare, deal with them after they happen.\nSerial Schedule\nA schedule that does not interleave the actions of different transactions. Equivalent Schedules\nFor any database state, the effect of executing the first schedule is identical to the effect of executing the second schedule.\nDoesn\u0026rsquo;t matter what the arithmetic operations are!\nSerializable Schedule\nA schedule that is equivalent to some serial execution of the transactions. If each transaction preserves consistency, every serializable schedule preserves consistency.\nThere are different levels of serializability:\nConflict Serializability (Most DBMSs try to support this.)\nView Serializability (No DBMS can do this.)\nDepency Graphs\nOne node per transactions .\nEdge from $T_i$ to $T_j$ if:\nAn operation $O_i$ of $T_i$ conflicts with an operation $O_j$ of $T_j$ and\n$O_i$ appears earlier in the schedule than $O_j$.\nAlternative (weaker) notion of serializability. Schedules $S_1$ and $S_2$ are view equivalent if:\nIf $T_1$ reads initial value of A in $S_1$, then $T_1$ also reads initial value of A in $S_2$.\nIf $T_1$ reads value of A written by $T_2$ in $S_1$, then $T_1$ also reads value of A written by $T_2$ in $S_2$.\nIf $T_1$ writes final value of A in $S_1$, then $T_1$ also writes final value of A in $S_2$.\nAlso known as a precedence graph. A schedule is conflict serializable if fits dependency graph is acyclic.\nView Serializability allows for (slightly) more schedules than Conflict Serializability does.\nBut is difficult to enforce efficiently. Neither definition allows all schedules that you would consider \u0026ldquo;serializable\u0026rdquo;.\nThis is because they don\u0026rsquo;t understand the meanings of the operations or the data (recall example #3) Transaction\nAll the changes of committed transactions should be persistent.\nNo torn updates.\nNo changes from failed transactions.\nThe DBMS can use either logging or shadow paging to ensure that all changes are durable.\n16. Two Phase Locking Readings: Chapter 15.1-15.3, 15.9\nS-LOCK: Shared locks for reads.\nX-LOCK: Exclusive locks for writes.\nCompatibility Matrix\nShared Exclusive Shared ✅ ❌ Exclusive ❌ ❌ Transactions request locks (or upgrades).\nLock manager grants or blocks requests.\nTransactions release locks.\nLock manager updates its internal lock-table.\nIt keeps track of what transactions hold what locks and what transactions are waiting to acquire any locks. Two-phase locking (2PL) is a concurrency control protocol that determines whether a transactions can access an object in the database on the fly.\nThe protocol does not need to know all the queries that a transactions will execute ahead of time.\nPhase #1: Growing\nEach transactions requests the locks that it needs from the DBMS’s lock manager.\nThe lock manager grants/denies lock requests.\nPhase #2: Shrinking\nThe transactions is allowed to only release locks that it previously acquired. It cannot acquire new locks. 2PL on its own is sufficient to guarantee conflict serializability.\nIt generates schedules whose precedence graph is acyclic. But it is subject to cascading aborts\nProblems\nThere are potential schedules that are serializable but would not be allowed by 2PL.\nLocking limits concurrency. Dirty reads Solution: Strong Strict 2PL (aka Rigorous 2PL)\nA schedule is strict if a value written by a transactions is not read or overwritten by other transactions suntil that transactions finishes.\nAdvantages:\nDoes not incur cascading aborts.\nAborted transactions scan be undone by just restoring original values of modified tuples.\nDead Lock Solution\nDeadlocks Solution: Detection or Prevention\nThe DBMS creates a waits-for graph to keep track of what locks each transactions is waiting to acquire:\nNodes are transactions\nEdge from $T_i$ to $T_j$ if $T_i$ is waiting for $T_j$ to release a lock.\nThe system periodically checks for cycles in waits-for graph and then decides how to break it.\nWhen the DBMS detects a deadlock, it will select a \u0026ldquo;victim\u0026rdquo; transactions to rollback to break the cycle.\nThe victim transactions will either restart or abort(more common) depending on how it was invoked.\nThere is a trade-off between the frequency of checking for deadlocks and how long transactions have to wait before deadlocks are broken.\nWhen a transactions tries to acquire a lock that is held by another transactions , the DBMS kills one of them to prevent a deadlock.\nThis approach does not require a waits-for graph or detection algorithm.\nIntension Locks\nTrade-off between parallelism versus overhead.\nFewer Locks, Larger Granularity vs. More Locks, Smaller Granularity. An intention lock allows a higher-level node to be locked in shared or exclusive mode without having to check all descendent nodes.\nIf a node is locked in an intention mode, then some transactions is doing explicit locking at a lower level in the tree.\nIntention-Shared (IS): Indicates explicit locking at lower level with shared locks.\nIntention-Exclusive (IX): Indicates explicit locking at lower level with exclusive locks.\nShared+Intention-Exclusive (SIX): The subtree rooted by that node is locked explicitly in shared mode and explicit locking is being done at a lower level with exclusive-mode locks.\nCompatibility Matrix\nIS IX S SIX X IS ✅ ✅ ✅ ✅ ❌ IX ✅ ✅ ❌ ❌ ❌ S ✅ ❌ ✅ ❌ ❌ SIX ✅ ❌ ❌ ❌ ❌ X ❌ ❌ ❌ ❌ ❌ Hierarchical locks are useful in practice as each transactions only needs a few locks. Intention locks help improve concurrency:\nIntention-Shared (IS): Intent to get lock(s) at finer granularity.\nIntention-Exclusive (IX): Intent to get X lock(s) at finer granularity.\nShared+Intention-Exclusive (SIX): Like and IX at the same time.\nLock escalation dynamically asks for coarser-grained locks when too many low-level locks acquired.\nThis reduces the number of requests that the lock manager must process.\nLock in Practice\nYou typically don\u0026rsquo;t set locks manually in transactions s.\nSometimes you will need to provide the DBMS with hints to help it to improve concurrency. Explicit locks are also useful when doing major changes to the database.\nExplicitly locks a table. Not part of the SQL standard.\nPostgres/DB2/Oracle Modes: SHARE, EXCLUSIVE\nMySQL Modes: READ, WRITE\nPerform a select and then sets an exclusive lock on the matching tuples. Can also set shared locks:\nPostgres: FOR SHARE\nMySQL: LOCK IN SHARE MODE\n17. Timestamp Ordering Readings: Chapter 15.4-15.5\nEvery object Xis tagged with timestamp of the last transactions that successfully did read/write:\nW-TS(X)–Write timestamp on X\nR-TS(X)–Read timestamp on X\nCheck timestamps for every operation:\nIf transactions tries to access an object \u0026ldquo;from the future\u0026rdquo;, it aborts and restarts. Read\nIf $TS(T_{i}) \u0026lt; W-TS(X)$, this violates timestamp order of $T_{i}$ with regard to the writer of $X$.\nAbort $T_{i}$ and restart it with a new TS. Else:\nAllow $T_{i}$ to read $X$. Update $R-TS(X)$ to $max(R-TS(X), TS(T_{i}))$ Make a local copy of $X$ to ensure repeatable reads for $T_{i}$. write\nIf $TS(T_{i})\u0026lt; R-TS(X)$ or $TS(T_{i})\u0026lt; W-TS(X)$\nAbort and restart $T_{i}$. Else:\nAllow Tito write Xand update $W-TS(X)$ Thomas Write Rule: Ignore the write to allow the transactions to continue executing without aborting. Also make a local copy of Xto ensure repeatable reads. Thomas Write Rule\nGenerates a schedule that is conflict serializable if you do not use the Thomas Write Rule.\nNo deadlocks because no transactions ever waits. Possibility of starvation for long transactions if short transactions keep causing conflicts. Recoverable Schedules\nA schedule is recoverable if transactions scommit only after all transactions swhose changes they read, commit.\nOtherwise, the DBMS cannot guarantee that transactions read data that will be restored after recovering from a crash.\nHigh overhead from copying data to transactions\u0026rsquo;s workspace and from updating timestamps.\nLong running transactions can get starved.\nThe likelihood that a transactions will read something from a newer transactions increases. Optimistic Concurrency Control\n#1 –Read Phase:\nTrack the read/write sets of transactions and store their writes in a private workspace. The DBMS copies every tuple that the transactions accesses from the shared database to its workspace ensure repeatable reads. #2 –Validation Phase:\nWhen a transactions commits, check whether it conflicts with other transactions s.\nWhen transactions $T_i$ invokes COMMIT, the DBMS checks if it conflicts with other transactions s.\nThe DBMS needs to guarantee only serializable schedules are permitted.\nChecks other transactions sfor RW and WW conflicts and ensure that conflicts are in one direction (e.g., older- younger).\nTwo methods for this phase:\nBackward Validation\nForward Validation\nBackword: Check whether the committing transactions intersects its read/write sets with those of any transactions sthat have already committed.\nForward: Check whether the committing transactions intersects its read/write sets with any active transactions sthat have not yet committed.\n#3 –Write Phase:\nIf validation succeeds, apply private changes to database. Otherwise abort and restart the transactions . 18. Multi-Version Concurrency Control Readings: Chapter 15.6-15.7\nThe DBMS maintains multiple physical versions of a single logical object in the database:\nWhen a transactions writes to an object, the DBMS creates a new version of that object.\nWhen a transactions reads an object, it reads the newest version that existed when the transactions started.\nWriters do not block readers. Readers do not block writers.\nRead-only transactions scan read a consistent snapshotwithout acquiring locks.\nUse timestamps to determine visibility. Easily support time-travelqueries.\nMVCC is more than just a concurrency control protocol. It completely affects how the DBMS manages transactions and the database.\nConcurrency Control\nApproach #1: Timestamp Ordering\nAssign transactions stimestamps that determine serial order. Approach #2: Optimistic Concurrency Control\nThree-phase protocol from last class.\nUse private workspace for new versions.\nApproach #3: Two-Phase Locking\nVersion Storage\ntransactions acquire appropriate lock on physical version before they can read/write a logical tuple. The DBMS uses the tuples\u0026rsquo; pointer field to create a version chain per logical tuple.\nThis allows the DBMS to find the version that is visible to a particular transactions at runtime.\nIndexes always point to the \u0026ldquo;head\u0026rdquo; of the chain.\nApproach #1: Append-Only Storage\nNew versions are appended to the same table space. Approach #2: Time-Travel Storage\nOld versions are copied to separate table space. Approach #3: Delta Storage\nThe original values of the modified attributes are copied into a separate delta record space. Different storage schemes determine where/what to store for each version.\nGC\nTuple Level:\nBackground Vacuuming: Separate thread(s) periodically scan the table and look for reclaimable versions. Works with any storage.\nCooperative Cleaning: Worker threads identify reclaimable versions as they traverse version chain. Only works with O2N.\nTransaction Level:\nEach transactions keeps track of its read/write set.\nThe DBMS determines when all versions created by a finished transactions are no longer visible.\nMay still require multiple threads to reclaim the memory fast enough for the workload.\nMVCC Indexes\nEach index\u0026rsquo;s underlying data structure must support the storage of non-unique keys.\nUse additional execution logic to perform conditional inserts for pkey/ unique indexes.\nAtomically check whether the key exists and then insert. Workers may get back multiple entries for a single fetch. They then must follow the pointers to find the proper physical version.\nImplementations\nProtocol VersionStorage GarbageCollection Indexes Oracle MV2PL Delta Vacuum Logical Postgres MV-2PL/MV-TO Append-Only Vacuum Physical MySQL-InnoDB MV-2PL Delta Vacuum Logical HYRISE MV-OCC Append-Only – Physical Hekaton MV-OCC Append-Only Cooperative Physical MemSQL MV-OCC Append-Only Vacuum Physical SAP HANA MV-2PL Time-travel Hybrid Logical NuoDB MV-2PL Append-Only Vacuum Logical HyPer MV-OCC Delta transactions -level Logical NoisePage MV-OCC Delta transactions -level Logical 19. Logging Protocols + Schemes Readings: Chapter 16.1-16.7\nRecovery algorithms are techniques to ensure database consistency, transaction atomicity, and durability despite failures.\nRecovery algorithms have two parts:\nActions during normal transactions processing to ensure that the DBMS can recover from a failure.\nActions after a failure to recover the database to a state that ensures atomicity, consistency, and durability.\nStorage\nVolatile Storage:\nData does notpersist after power loss or program exit.\nExamples: DRAM, SRAM\nNon-volatile Storage:\nData persists after power loss and program exit.\nExamples: HDD, SDD\nStable Storage:\nA non-existentform of non-volatile storage that survives all possible failures scenarios. Redo vs Undo\nUndo: The process of removing the effects of an incomplete or aborted transactions .\nRedo: The process of re-instating the effects of a committed transactions for durability.\nHow the DBMS supports this functionality depends on how it manages the buffer pool.\nWhether the DBMS allows an uncommitted transactions to overwrite the most recent committed value of an object in non-volatile storage.\nSTEAL: Is allowed.\nNO-STEAL: Is notallowed.\nWhether the DBMS requires that all updates made by a transactions are reflected on non-volatile storage before the transactions can commit.\nFORCE: Is required.\nNO-FORCE: Is notrequired.\nThis approach is the easiest to implement:\nNever have to undochanges of an aborted transactions because the changes were not written to disk.\nNever have to redo changes of a committed transactions because all the changes are guaranteed to be written to disk at commit time (assuming atomic hardware writes).\nPrevious example cannot support write sets that exceed the amount of physical memory available.\nShaow pages\nTo install the updates, overwrite the root so it points to the shadow, thereby swapping the master and shadow:\nBefore overwriting the root, none of the transactions \u0026lsquo;supdates are part of the disk-resident database\nAfter overwriting the root, all the transactions \u0026lsquo;supdates are part of the disk-resident database.\nSupporting rollbacks and recovery is easy.\nUndo: Remove the shadow pages. Leave the master and the DB root pointer alone.\nRedo: Not needed at all.\nDisadvantages\nCopying the entire page table is expensive:\nUse a page table structured like a B+tree.\nNo need to copy entire tree, only need to copy paths in the tree that lead to updated leaf nodes.\nCommit overhead is high:\nFlush every updated page, page table, and root.\nData gets fragmented.\nNeed garbage collection.\nOnly supports one writer transactions at a time or transactions sin a batch.\nWrite Ahead Log\nMaintain a log file separate from data files that contains the changes that transactions smake to database.\nAssume that the log is on stable storage.\nLog contains enough information to perform the necessary undo and redo actions to restore the database.\nDBMS must write to disk the log file records that correspond to changes made to a database object beforeit can flush that object to disk.\nBuffer Pool Policy: STEAL+ NO-FORCE\nThe DBMS stages all a transactions \u0026lsquo;slog records in volatile storage (usually backed by buffer pool).\nAll log records pertaining to an updated page are written to non-volatile storage beforethe page itself is over-written in non-volatile storage.\nA transactions is not considered committed until allits log records have been written to stable storage.\nWhen should the DBMS write log entries to disk?\nWhen the transaction commits.\nCan use group committo batch multiple log flushes together to amortize overhead.\nWhen should the DBMS write dirty records to disk?\nEvery time the transactions executes an update?\nOnce when the transactions commits?\nLogging Schemes\nLogical logging requires less data written in each log record than physical logging.\nDifficult to implement recovery with logical logging if you have concurrent transactions s.\nHard to determine which parts of the database may have been modified by a query before crash.\nAlso takes longer to recover because you must re-execute every transactions all over again.\nHybrid approach where log records target a single page but do not specify organization of the page.\nIdentify tuples based on their slot number.\nAllows DBMS to reorganize pages after a log record has been written to disk.\nThis is the most popular approach.\nCheck Points\nThe WAL will grow forever.\nAfter a crash, the DBMS must replay the entire log, which will take a long time.\nThe DBMS periodically takes a checkpoint where it flushes all buffers out to disk.\nOutput onto stable storage all log records currently residing in main memory.\nOutput to the disk all modified blocks.\nWrite a \u0026lt;CHECKPOINT\u0026gt; entry to the log and flush to stable storage.\nCheckpointing too often causes the runtime performance to degrade.\nSystem spends too much time flushing buffers. But waiting a long time is just as bad:\nThe checkpoint will be large and slow.\nMakes recovery time much longer.\n20. Crash Recovery Readings: Chapter 16.1-16.8\nAlgorithms for Recovery and Isolation Exploiting Semantics\nWrite-Ahead Logging:\nAny change is recorded in log on stable storage before the database change is written to disk.\nMust use STEAL+ NO-FORCE buffer pool policies.\nRepeating History During Redo:\nOn restart, retrace actions and restore database to exact state before crash. Logging Changes During Undo:\nRecord undo actions to log to ensure action is not repeated in the event of repeated failures. WAL Records\nEvery log record now includes a globally unique log sequence number(LSN).\nName Where Definition flushedLSN Memory Last LSN in log on disk pageLSN $page_x$ Newest update to $page_x$ recLSN $page_x$ Oldest update to $page_x$ since it was last flushed lastLSN $T_i$ Latest record of transactions $T_i$ MasterRecord Disk LSN of latest checkpoint Each data page contains a pageLSN.\nThe LSN of the most recent update to that page. System keeps track of flushedLSN.\nThe max LSN flushed so far. Before page xcan be written to disk, we must flush log at least to the point where:\n$pageLSN_x ≤ flushedLSN$ Transaction Commit\nWrite COMMITrecord to log.\nAll log records up to transactions\u0026rsquo; COMMIT record are flushed to disk.\nLog flushes are sequential, synchronous writes to disk.\nMany log records per log page.\nWhen the commit succeeds, write a special transactions\u0026rsquo; END record to log.\nThis does notneed to be flushed immediately. A CLR describes the actions taken to undo the actions of a previous update record.\nIt has all the fields of an update log record plus the undoNextpointer (the next-to-be-undone LSN).\nCLRs are added to log records but the DBMS does not wait for them to be flushed before notifying the application that the transactions aborted.\nNON-FUZZY CHECKPOINTS\nThe DBMS halts everything when it takes a checkpoint to ensure a consistent snapshot:\nHalt the start of any new transactions s.\nWait until all active transactions sfinish executing.\nFlushes dirty pages on disk.\nThis is bad for runtime performance but makes recovery easy.\nSLIGHTLY BETTER CHECKPOINTS\nPause modifying transactions swhile the DBMS takes the checkpoint.\nPrevent queries from acquiring write latch on table/index pages.\nDon\u0026rsquo;t have to wait until all transactions sfinish before taking the checkpoint.\nWe must record internal state as of the beginning of the checkpoint.\nActive Transaction Table (ATT)\nDirty Page Table (DPT)\nACTIVE TRANSACTION TABLE\nOne entry per currently active transactions .\ntransactions Id: Unique transactions identifier.\nstatus: The current \u0026ldquo;mode\u0026rdquo; of the transactions .\nlastLSN: Most recent LSN created by transactions .\nEntry removed after the transactions -END message.\ntransactions Status Codes:\nR: Running\nC: Committing\nU: Candidate for Undo\nA fuzzy checkpoint is where the DBMS allows active transactions sto continue the run while the system writes the log records for checkpoint.\nNo attempt to force dirty pages to disk. New log records to track checkpoint boundaries:\nCHECKPOINT-BEGIN: Indicates start of checkpoint\nCHECKPOINT-END: Contains ATT+ DPT.\nARIES\nStart from last BEGIN-CHECKPOINTfound via MasterRecord.\nAnalysis: Figure out which transactions scommitted or failed since checkpoint.\nRedo: Repeat allactions.\nUndo: Reverse effects of failed transactions s.\n21. Distributed Databases Approach #1: Homogenous Nodes\nEvery node in the cluster can perform the same set of tasks (albeit on potentially different partitions of data). Makes provisioning and failover \u0026ldquo;easier\u0026rdquo;. Approach #2: Heterogenous Nodes\nNodes are assigned specific tasks.\nCan allow a single physical node to host multiple \u0026ldquo;virtual\u0026rdquo; node types for dedicated tasks.\nFor User, it is Transparent. Users should not be required to know where data is physically located, how tables are partitioned or replicated.\nSplit a table\u0026rsquo;s tuples into disjoint subsets.\nChoose column(s) that divides the database equally in terms of size, load, or usage.\nHash Partitioning, Range Partitioning\nThe DBMS can partition a database physically(shared nothing) or logically (shared disk).\nTP Monitors\nA TP Monitor is an example of a centralized coordinator for distributed DBMSs.\nOriginally developed in the 1970-80s to provide transactions between terminals and mainframe databases.\nExamples: ATMs, Airline Reservations. Many DBMSs now support the same functionality internally.\n22. Distributed OLTP If you do nottrust the other nodes in a distributed DBMS, then you need to use a Byzantine Fault Tolerantprotocol for transactions(blockchain).\nWhen a multi-node transaction finishes, the DBMS needs to ask all the nodes involved whether it is safe to commit.\n2PC Optimizations\nEarly Prepare Voting\nIf you send a query to a remote node that you know will be the last one you execute there, then that node will also return their vote for the prepare phase with the query result. Early Acknowledgement After Prepare\nIf all nodes vote to commit a transaction, the coordinator can send the client an acknowledgement that their transaction was successful before the commit phase finishes. Blocks if coordinator fails after the prepare message is sent, until coordinator recovers.\nPAXOS\nIf the system elects a single leader that oversees proposing changes for some period, then it can skip the Propose phase.\nFall back to full Paxoswhenever there is a failure. The system periodically renews who the leader is using another Paxosround.\nNodes must exchange log entries during leader election to make sure that everyone is up-to-date. Non-blocking if a majority participants are alive, provided there is a sufficiently long period without further failures.\n23. Distributed OLAP Most shared-nothing distributed OLAP DBMSs are designed to assume that nodes do not fail during query execution.\nIf one node fails during query execution, then the whole query fails. The DBMS could take a snapshot of the intermediate results for a query during execution to allow it to recover if nodes fail.\nAll the optimizations that we talked about before are still applicable in a distributed environment.\nPredicate Pushdown\nEarly Projections\nOptimal Join Orderings\nDistributed query optimization is even harder because it must consider the physical location of data and network transfer costs.\n","permalink":"https://chasing1020.github.io/post/database-systems/","summary":"\u003cp\u003eReference Book: \u003ca href=\"https://www.db-book.com/\"\u003e\u003cstrong\u003eDatabase System Concepts Seventh Edition\u003c/strong\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"1-relational-model\"\u003e1. Relational Model\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003eReadings:\u003c/strong\u003e Chapters 1-2, 6\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eDefinition\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eDataBase: Organized collection of inter-related data that models some aspect of the real-world.\nDatabases are the core component of most computer applications.\u003c/p\u003e\n\u003cp\u003eA database management system (DBMS) is software that allows applications to store and analyze information in a database.\nA general-purpose DBMS is designed to allow the definition, creation, querying, update, and administration of databases.\u003c/p\u003e\n\u003cp\u003eData Model: is a collection of concepts for describing the data in a database.\nSchema: is a description of a particular collection of data, using a given data model.\u003c/p\u003e","title":"Database Systems"},{"content":"Mathematic Fomulars The mathematical typesetting is based on LaTeX, so if you need to search for the way to make a particular symbol, include latex in your search. But note: Not all LaTeX macros are available without using additional packages, and those packages likely will only work if you are creating a PDF. On the plus side, if you are working in PDF, you can use additional packages that give much better control and/or easier syntax.\nThis article is about the basics about math symbols in Latex.\nGreek Letters αA $\\alpha A$ νN $\\nu N$ βB $\\beta B$ ξΞ $\\xi\\Xi$ γΓ $\\gamma \\Gamma$ oO $o O$ (omicron) δΔ $\\delta \\Delta$ πΠ $\\pi \\Pi$ ϵεE $\\epsilon \\varepsilon E$ ρϱP $\\rho\\varrho P$ ζZ $\\zeta Z \\sigma \\,\\!$ ΣΣ $\\sigma \\Sigma$ ηH $\\eta H$ τT $\\tau T$ θϑ $\\theta \\vartheta \\Theta$ υΥ $\\upsilon \\Upsilon$ ιI $\\iota I$ ϕφΦ $\\phi \\varphi \\Phi$ κK $\\kappa K$ χX $\\chi X$ λΛ $\\lambda \\Lambda$ ψΨ $\\psi \\Psi$ μM $\\mu M$ ωΩ $\\omega \\Omega$ Operators Symbol Script $\\cos$ \\cos $\\sin$ \\sin $\\lim$ \\lim $\\exp$ \\exp $\\to$ \\to $\\infty$ \\infty $\\equiv$ \\equiv $\\bmod$ \\bmod $\\times$ \\times Power and Indices Symbol Script $k_{n+1}$ k_{n+1} $n^2$ n^2 $k_n^2$ k_n^2 Fractions and Binomials Symbol Script $\\frac{n!}{k!(n-k)!}$ \\frac{n!}{k!(n-k)!} $\\binom{n}{k}$ \\binom{n}{k} $\\frac{\\frac{x}{1}}{x - y}$ \\frac{\\frac{x}{1}}{x - y} $^3/_7$ ^3/_7 Roots Symbol Script $\\sqrt{k}$ \\sqrt{k} $\\sqrt[n]{k}$ \\sqrt[n]{k} Sums and Integrals $\\int a^4 tan^2t sec^3t dt$\nSymbol Script $\\sum_{i=1}^{10} t_i$ \\sum_{i=1}^{10} t_i $\\int_0^\\infty \\mathrm{e}^{-x}\\mathrm{d}x$ \\int_0^\\infty \\mathrm{e}^{-x}\\mathrm{d}x $\\int_{-\\infty}^{\\infty} f(x) ; dx$ \\int_{-\\infty}^{\\infty} f(x) ; dx $\\left. F(x) \\right _{a}^{b}$ $\\sum$ \\sum $\\prod$ \\prod $\\coprod$ \\coprod $\\implies$ \\implies $\\bigoplus$ \\bigoplus $\\bigotimes$ \\bigotimes $\\bigodot$ \\bigodot $\\bigcup$ \\bigcup $\\bigcap$ \\bigcap $\\biguplus$ \\biguplus $\\bigsqcup$ \\bigsqcup $\\bigvee$ \\bigvee $\\vee$ \\vee $\\bigwedge$ \\bigwedge $\\wedge$ \\wedge $\\int$ \\int $\\oint$ \\oint $\\iint$ \\iint $\\iiint$ \\iiint $\\idotsint$ \\idotsint $\\sum_{\\substack{0\u0026lt;i\u0026lt;m,0\u0026lt;j\u0026lt;n}} P(i, j)$ \\sum_{\\substack{0\u0026lt;i\u0026lt;m,0\u0026lt;j\u0026lt;n}} P(i, j) $\\int\\limits_a^b$ \\int\\limits_a^b Marks Symbol Script $a’$ $a^{\\prime}$ a` a^{\\prime} $a’’$ a’’ $\\hat{a}$ hat{a} $\\bar{a}$ \\bar{a} $\\grave{a}$ \\grave{a} $\\acute{a}$ \\acute{a} $\\stackrel{·}{\\sim}$ \\dot{a} $\\ddot{a}$ \\ddot{a} $\\not{a}$ \\not{a} $\\mathring{a}$ \\mathring{a} $\\overrightarrow{AB}$ \\overrightarrow{AB} $\\overleftarrow{AB}$ \\overleftarrow{AB} $a’’’$ a’’’ $\\overline{aaa}$ \\overline{aaa} $\\check{a}$ \\check{a} $\\vec{a}$ \\vec{a} $\\underline{a}$ \\underline{a} $\\color{red}x$ \\color{red}x $\\pm$ \\pm $\\mp$ \\mp $\\int y \\mathrm{d}x$ \\int y \\mathrm{d}x $,$ , $:$ : $;$ ; $!$ ! $\\int y, \\mathrm{d}x$ \\int y, \\mathrm{d}x $\\dots$ \\dots $\\ldots$ \\ldots $\\cdots$ \\cdots $\\vdots$ \\vdots $\\ddots$ \\ddots $\\stackrel{\\bf{}}{\\sim}$\nBrackets etc Symbol Script $(a)$ (a) $[a]$ [a] ${a}$ {a} $\\langle f \\rangle$ \\langle f \\rangle $\\lfloor f \\rfloor$ \\lfloor f \\rfloor $\\lceil f \\rceil$ \\lceil f \\rceil $\\ulcorner f \\urcorner$ \\ulcorner f \\urcorner Aligning equations use \\begin{align*} \u0026hellip; \\end{align*}. Separate lines with \\\\ and use \u0026amp; to mark where things should line up.\n1 2 3 4 5 $\\begin{align*} a \u0026amp; = b \\\\ X \u0026amp;\\sim {\\sf Norm}(10, 3) \\\\ 5 \u0026amp; \\le 10 \\end{align*}$ $\\begin{align*} a \u0026amp; = b \\ X \u0026amp;\\sim {\\sf Norm}(10, 3) \\ 5 \u0026amp; \\le 10 \\end{align*}$\n","permalink":"https://chasing1020.github.io/post/mathematic-fomulars/","summary":"\u003ch1 id=\"mathematic-fomulars\"\u003eMathematic Fomulars\u003c/h1\u003e\n\u003cp\u003eThe mathematical typesetting is based on LaTeX, so if you need to search for the way to make a particular symbol, include \u003ccode\u003elatex\u003c/code\u003e in your search. But note: Not all LaTeX macros are available without using additional packages, and those packages likely will only work if you are creating a PDF. On the plus side, if you are working in PDF, you can use additional packages that give much better control and/or easier syntax.\u003c/p\u003e","title":"Mathematic Fomulars"},{"content":"1. Overview 什么是操作系统？\n硬件角度：\n管理硬件：将复杂的，具备不同功能的硬件资源纳入统一管理。 对硬件进行抽象：抽象成不依赖具体硬件特性的资源。 将有限的，离散的资源高效地抽象成无限的、连续的资源，并提交接口给上层系统调用。\n应用角度：\n服务于应用：提供不同层次，不同功能的接口以满足应用需求 管理应用：负责应用生命周期管理，包括加载，启动，切换，调度等。 2. Hardware Infrastructure 冯诺依曼架构：中央处理单元，存储器，输入输出。\n缓存结构：CPU缓存是由若干个缓存行决定的，每个缓存行包括一个有效位（valid bit），一个标记地址（tag address）。物理地址在逻辑上分为三段。Tag、Index(Set)、Way。物理地址能表示Index(Set)的最大数目为组，支持Tag最大的数目为路（Way）。\n物理地址结构：Tag（物理地址）+ Set（组号）+ Offset（组内偏移）\n直接映射：内存与CPU的Cache Line相对位置固定，导致淘汰换出频繁。\n全相联映射：灵活，电路设计困难，只适合小容量Cache。\n组相联映射：CPU和主存都分组，同组的缓存行数量称为路，组内采用直接映射，组间采用全相联映射。\nCPU访问设备的方式：内存映射输入输出（Memory-Mapped I/O），将IO设备和物理内存放到同一个存储空间。\n如果CPU通过不断轮询，查看MMIO是否有输入效率会很低，所以更高效的做法是利用中断，主动通知CPU。\n3. Operating System Architecture 设计上，策略（Policy，“要做什么”）和机制（Mechanism，“怎么做”）隔离。\n内核架构\n简要结构：以DOS典型，没有现代意义的内存管理单元（Memory Management Unit，MMU）\n宏内核架构（Monolithic Kernel）：又称单内核，现代Unix，Linux，Windows都采用宏内核。\n模块化（modularity）：使用可加载内核模块（Loadable Kernel Module）机制，使内核与其他模块（如驱动）解耦。 抽象（abstraction）：采用“Everything is a file”思想，为上层应用提供统一接口，降低复杂性，增强维护性。 分层（layering）：Dijkstra提出的“THE”操作系统，分为6层，更好地组织各种功能。 层级（hierarchy）：应用于资源管理，如调度算法优先级分类，控制组（cgroup）分类。 微内核架构：以Mach为代表，将单个功能或模块从内核中拆分出来，提高安全性。由于Mach对IPC设计过于通用，以至于自身资源占用过大，效率并不及同期的宏内核。但是微内核具有弹性拓展的能力，硬件异构，并且具有功能安全与信息安全，并且调用具有确定的时延。\n外核架构：库操作系统LibOS，可以按照应用领域的特点与需求，安装最适合的LibOS，最小化非必要代码，多个LibOS之间的隔离很强，具有很好的安全性。还有云计算的容器，基于Unikernel将虚拟机监控器作为支撑Unikernel/LibOS运行的内核。\n其他框架结构：以Android为例，其包含硬件抽象层，Android库，Android运行环境，Android运行框架。\n4. Memory Management 现代操作系统采用虚拟内存，以下作为设计目标\n高效性：虚拟内存没有明显性能开销。 安全性：使应用之间的内存相互隔离。 透明性：虚拟内存抽象对程序员透明。 CPU的内存管理单元（Memory Management Unit，MMU）负责虚拟地址到物理地址的转换，此外，还有地址旁路缓存（Translation Lookaside Buffer，TLB）\n分段机制：如将应用分成代码段，数据段。每个段表，作为段内地址与段内偏移来确定位置。很容易产生外部碎片。\n分页机制：用虚拟页号与页内偏移量，按固定大小管理，减少外部碎片。\n访存次数（一般访问TLB不算访存）\n页式存储，2次：第一次、访问内存中的页表，利用逻辑地址中的页号查找到页帧号，与逻辑地址中的页内偏移拼接形成物理地址；第二次：得到物理地址后，再一次访问内存，存取指令或者数据。\n段式存储，2次（同上）\n段页式存储，3次：\n第一次：访问内存中的段表查到页表的起始地址\n第二次：访问内存中的页表找到页帧号，形成物理地址\n第三次：得到物理地址后，再一次访问内存，存取指令或者数据\n多级页表，若页表划分为N级，则需要访问内存N+1次。TLB命中，只需访问1次内存即可。\n4.1. Paging 内核把物理页作为内存管理的基本单位。物理页的基本数据结构如下：\n1 2 3 4 5 6 7 8 9 10 struct page { unsigned long flags; // 页是不是脏的，是否锁定在内存中 atomic_t _count; // 页的引用计数 atomic_t _mapcount; unsigned long private; struct address_space *mapping; pgoff_t index; struct list_head lru; void *virtual; // 页的虚拟地址 } 对于Arm64位系统下的4级页表：每个页4KB，最低12位($2^{12}$=4KB)来表示页内偏移。每个页表页也占用物理内存的一个物理页(4KB)，每个页表项8字节，所以一个页表页可以对应512页表项(4KB/8B=512=$2^9$)。所以，48-63位全部置为0或者是1。剩下36+12分别对应四级页表，和页表内偏移量。\n对于Sv39 RISC-V，其中前25位为空，由27位index映射到$2^{27}$个（Page Table Entries， PTEs），每个PTE对应物理页44bit的PPN和一些flags，包括记录页表是否有效、可读、可写、User权限是否可以访问等。其中27位index实际上会有3级页表，如果其中任意一级页表不存在，则会抛出一个page-fault exception。Sv48, Sv57同理，Page Table添加一级Level。\n32位系统下的2级页表：每个页4KB，最低12位($2^{12}$=4KB)来表示页内偏移。每个页表页也占用物理内存的一个物理页(4KB)，每个页表项4字节，所以一个页表页可以对应1024页表项(4KB/4B=1024=$2^{10}$)。所以，20+12分别对应二级页表(2*10)，和页表内偏移量。\n对于32bit操作系统，通过物理地址扩展（Physical Address Extension，PAE），CPU执行单元发出的地址将被MMU截获，进行一次地址转换后才传给内存。这样允许32位系统下多个进程的使用内存加起来超过4GB。\nTLB硬件一般采用分层结构，L1部分分为指令TLB与数据TLB，L2不区分指令与数据，作为CPU内部硬件。\n在切换应用刷新时，为每个TLB打上标签（在AArch称为Address Space IDentifier，ASID；x86-64中称为Process Context IDentifier），操作系统为每一个应用分配不同的标签，这个标签记录在页表基址寄存器，这样TLB的缓存项就被应用区分开，切换应用时不用清空TLB。\n在虚拟页使用后，不一定会在页表中。当物理内存不够的时候，应该将物理页的内容写入到磁盘中，同时记录物理页的位置，这个流程称之为换出。\n当访问未映射到内存的虚拟页时，发生缺页异常，将所有磁盘的数据加到物理页，再填写物理页的映射，这个流程称为换入。\n预取（Prefetching）：当换入时，猜测还有哪些页会被访问，提前换入内存，减少缺页此数。\n按需分配（Demand Paging）：将分配的虚拟页标记为“已经分配，但是没有映射到物理内存”的状态，有效提高资源利用率。\n4.2. Strategy MIN/OPT（Minimum，Optimal）：优先选择未来不会访问的页，理论最优，实践困难。\nFIFO（First-In First-Out）：时间开销低，但是实际表现不佳，先后顺序和使用频繁关系与否不大。\nSecond Chance：给队列每一个访问到的设置标志位，按FIFO的“逻辑”，每次将换出时，标志清零，并移到队尾；如果没有标志，且在队头，则会被换出。当所有标志失效时，弱化为FIFO。此外，还有可能发生（Belady异常）\nLRU（Least Recently Used）：被换出时，优先选择最久未被访问到的，链表尾放最常用的，首端放最不常用的，每次淘汰首端。\nMRU（Most Recently Used）：用LRU相反的策略，基于假设“程序不会反复访问相同的地址”。\nClock Algorithm：类似于Second Chance，但是不需要移动链表中的页号，效率更高。\n4.3. Virtual Memory COW（Copy On Write）：用于动态链接库，fork等。用只读的方式共享内存。一旦发生写操作，产生缺页异常，触发COW机制。\nKSM（Kernel Same-page Merging）：基于COW，定期扫描相同的物理页，进行合并，减少内存占用。\nzswap：在换出内存时，使用压缩算法，将数据压缩后，存放至缓冲区，而不直接存磁盘，可以有效避免磁盘IO。\nbuddy system：需求m页时，分裂($2^{n-1}\u0026lt; m ≤ 2^n$)至合适的物理块，释放时合并空闲块，这两个操作是级联的，减少外部碎片。\nSLAB：一般为SLUB，SLOB，SLAB的统称。为高效分配较小的内存块提高速度，SLAB只会分配固定大小的内存块($2^n$, $3≤ n \u0026lt;12$)，实际还会有特殊值避免内部碎片，这些块称为slab，每个slab会被分成小块，用链表链接，其中current指向当前的slab，partial指向所有空闲的slab，分配时，先去current取，current空了再取一个partial的slab交给current。一般来说，Linux采用slab分配器分配task_struct结构，能够更好实现对象复用和缓存着色。\nImplicit Free List：每个内存块的头部，用链表维护是否空闲，块大小的信息，分配请求时，依次合并。\nExplicit Free List：仅仅把空闲的链接起来，所以每个空闲块只用维护两个指针和块大小，分配速度比隐式快。\nSegregated Free List：维护多条显示链表，找到块大小最合适的空闲块，有空余则插入空闲链表中。\nCache coloring：不同位置的物理页标上不同颜色，连续分配内存物理页时，优先选择不同物理页颜色的进行分配。\nZero Copy\n传统的内存访问方式，如read/write，每次操作都需要进行内核态与用户态之间的切换，read() 把数据从存储器 (磁盘、网卡等) 读取到用户缓冲区，write() 则是把数据从用户缓冲区写出到存储器：\n1 2 3 4 5 6 #include \u0026lt;unistd.h\u0026gt; // 其中void *buf指向的内存空间要求合法判断，为了安全考虑， // 内核会发起从用户内存到内核内存的拷贝的调用 ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); 一般Linux零拷贝有三种实现方式：1.减少拷贝次数，2.绕过内核直接IO，3.COW技术（只读则不用拷贝）\n方法一：采用mmap+write，可以减少一次CPU拷贝，节省50%内存，适用于大文件传输；小文件反而会造成更多的内存碎片浪费。\n1 2 3 4 #include \u0026lt;sys/mman.h\u0026gt; // mmap creates a new mapping in the virtual address space of the calling process. // The starting address for the new mapping is specified in addr. void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 方法二：采用Linux内核版本2.1引入的sendfile系统调用，因为这种拷贝发生在内核态，相比于read/write节省了将数据拷贝至用户态的时间。\n1 2 3 4 #include \u0026lt;sys/sendfile.h\u0026gt; // sendfile copies data between one file descriptor and another. ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count); 方法三：采用Linux 内核版本2.6.17引入的splice系统调用，解决了sendfile只在网络协议专用的弊端，在数据传输过程中，仅传递内存页的指针而不是真实的数据。\n1 2 3 4 5 6 7 #define _GNU_SOURCE /* See feature_test_macros(7) */ #include \u0026lt;fcntl.h\u0026gt; // splice moves data between two file descriptors // without copying between kernel address space and user address space. ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags); 5. Process and Thread 5.1. Process 进程的状态：新生（new）、就绪（ready）、运行（running）、阻塞（blocked）、终止（terminated）。在进程结构体中的state域做了标记\n标记 状态 TASK_RUNNING 正在执行或者是运行队列中等待执行 TASK_INTERRUPTIBLE 进程正在阻塞，等待某一事件的达成。 TASK_UNINTERRUPTIBLE 收到信号也不会被唤醒或者准备投入运行。 __TASK_TRACED 被其他跟踪的进程（如ptrace） __TASK_STOPPED 停止执行，收到SIGSTOP，SIGTSP，SIGTTIN，SIGTTOU信号后 进程的内存空间布局：（在/proc/\u0026lt;PID\u0026gt;/maps查看布局）\n内核部分：空间最顶端的部分，只有进入内核态才可见。\n用户栈：自顶向下，保存各种临时变量的值。\n代码库：保存依赖共享的代码库，如libc等，这段标记为只读。\n数据与代码段：自底向上，这个段用于保存二进制文件，加载时会被载入虚拟空间中。\n1 2 3 4 5 6 #inclue\u0026lt;unistd.h\u0026gt; #inclue\u0026lt;sys/types.h\u0026gt; pid_t fork(void); #inclue\u0026lt;unistd.h\u0026gt; int execve(const char *pathname, char *const argv[], char *const envp[]); exevce会根据pathname的路径，将数据段和代码段加载到当前进程地址空间中，再重新初始化堆栈再将PC指向代码段定义的入口\n进程监控：wait\n1 2 3 4 #include\u0026lt;sys/types.h\u0026gt; #include\u0026lt;sys/wait.h\u0026gt; pid_t waitpid(pid_t pid, int *wstatus, int options); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include\u0026lt;stdio.h\u0026gt; #include\u0026lt;stdlib.h\u0026gt; #include\u0026lt;sys/wait.h\u0026gt; #include\u0026lt;sys/types.h\u0026gt; #include\u0026lt;unistd.h\u0026gt; int main(int argc, char *argv[]) { int rc = fork(); if (rc \u0026lt; 0) { fprintf(stderr, \u0026#34;Fork failed\\n\u0026#34;); } else if (rc == 0) { printf(\u0026#34;ChildProcess: existing\\n\u0026#34;); } else { int status = 0; if (waitpid(rc, \u0026amp;status, 0) \u0026lt; 0) { fprintf(stderr, \u0026#34;Parent process: waitpid failed\u0026#34;); exit(-1); } if (WIFEXITED(status)) { printf(\u0026#34;Parent process: my child has exited\\n\u0026#34;); } else { fprintf(stderr, \u0026#34;Parent process: waitpid returns for unkonwn reasons\u0026#34;); } } return 0; } 如果父进程没有wait或者没运行到到wait，子进程中止了，父进程未进行适当处理SIGCHLD信号，占用的资源也不会释放，被称为僵尸进程。内核会为僵尸进程保留Pid以及终止时的status。如果父进程退出了，那么子进程的信息也不会被父进程占用，所有的僵尸进程都会被init用wait回收。（如果父进程不退出，一直死循环，那僵尸进程会越来越多）\n进程组（Process Group）：默认父进程和子进程是一个进程组，子进程可以setpgid()创建新进程组或者移入已有进程组。应用程序可以killpg发送信号，通知进程组的每个进程。\n会话（Session）：进程组的集合，根据执行状态，分为前台进程组（foreground thread group），和后台进程组（background group）。控制终端是会话与外界交互的一个入口。\n1 2 #include\u0026lt;sched.h\u0026gt; int clone(int (fn*)(void *), void *stack, int flags, void *arg, ...); Linux支持对clone进行更精密的控制，允许指定进程栈的位置、禁止复制内存等操作。包括隔离namespace等。\n5.2. Thread Linux操作系统把所有的线程都当作进程来实现，内核并没有准备特别的调度算法或者定义特别的数据结构来表征线程。仅仅被视为与其他进程做了资源共享的进程。\n线程分为用户态线程和内核态线程，一般来说，有三种调度模型：\n用户对内核：多对一，一对一，多对多。没有一对多（即一个用户线程对应多个内核线程）。\n1 2 3 4 5 6 7 8 #include\u0026lt;pthread.h\u0026gt; int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); int pthread_exit(void *retval); int pthread_yield(void); int pthread_join(pthread_t thread, void **retval); 线程创建函数最后还是会调用clone，其中clone_flag为CLONE_THREAD。\n线程退出函数不是必要的，退出时自动会退出；yield用于让出，会发起sched_yield系统调用。\n合并操作允许等待一个线程结束，并保留返回值。\n1 2 3 4 #include\u0026lt;unistd.h\u0026gt; unsigned int sleep(unsigned int seconds); int pthread_cond_wait(pthread_t *restrict cond, pthread_mutex_t *restrict mutex); sleep会让线程挂起数秒，或者是使用cond_wait在条件变量为同步方式等待。\n6. Operating System Scheduling Long-term Scheduling：决定当前真正可以被调度的进程数量。\nShort-term Scheduling：负责进程在就绪、运行、阻塞三个状态之间的转换。\nMidium-term Scheduling：负责管理内存（如将进程挂起并且替换入磁盘），换页机制等。\n经典调度策略：\nFIFO/FCFS（First In First Out / First Come First Serve）：在长短任务混合的情况下对短任务不友好。\nSJF（Shortest Job First）：表现严重依赖于任务到达的时间点。\nSTCF（Shortest Time-to-Completion First）：相比FIFO和SJF，引入preemptive机制，但是可能会导致长任务饥饿。\nRR（Round Robin）：在任务运行时间相似的情况下周转时间很高。\n优先级调度策略：\nMLQ（Multi-Level Queue）：按多个等级排列，下一级队列为空后，再调度新的任务，在每级队列可以使用不同的调度策略。但是可能造成低优先级饥饿，同时，在切换任务时，发生锁竞争，可能会产生优先级反转。解决方法：priority inheritance，将自己的优先级交给低的，等待放锁后立即执行。\nMLFQ（Multi-Level Feedback Queue）：相同优先级的采用RR策略，同时短任务有更高的优先级。如果某一任务超过队列允许运行的最大的时间，则优先级降低。同时，将低优先级的任务采用更长的时间片，定时将所有任务的优先级提至最高。采用这种动态提升与降低（Boost and Penalty）被应用于早期的Linux。\n公平共享调度策略：\nFair-share Scheduling：以份额量化CPU时间\nLottery Scheduling：以抽彩票数量决定被调度的概率；份额大的任务可以彩票转让（ticket transfer）给小的任务；同时允许给自己的子任务不同的彩票货币（ticket currency）避免频繁修改；任务可以根据CPU需求，通过彩票通胀（ticket inflation）决定自己的调度份额。\nStride Scheduling：引入虚拟时间（virtual runtime）以步幅来决定调度，任务份额决定步幅的倒数。也可以使用借用虚拟时间（Borrowed Virtual Runtime），保证公平的同时提升实时性。\n多核调度策略：\nLoad Sharing：从全局队列中取任务。后续添加了（Two-level Scheduling）策略，引入了本地队列。 Energy Aware Scheduling：对每个CPU的容量，功率权衡，划分性能域，找到最合适的核来运行。 Linux Scheduling：\n2.6.23开始采用Complete Fair Scheduling策略。将调度器实体封装成如下数据结构\n1 2 3 4 5 6 7 8 9 10 struct sched_entity { struct load_weight load; struct rb_node; struct list_head; unsigned int on_rq; u64 exec_start; u64 sum_exec_time; u64 vruntime; // ... }; Linux操作系统将vruntime作为红黑树的value，在选择下一个运行进程时，只需要找到最左叶子结点的缓存，即只需要O(1)复杂度，而不是O(logn)。\n7. Inter-Process Communication 管道：在内存中以FIFO的形式缓冲数据，本质上属于一个文件的两个fd，收发端对fd进行操作，有匿名管道（fork后子进程继承文件描述符）和命名管道（mkfifo）两种实现方式。属于单向IPC，收发端需要对字节流进行解析。\n1 2 3 #include \u0026lt;unistd.h\u0026gt; int pipe(int pipefd[2]); // pipefd[0] -\u0026gt; read; pipefd[1] -\u0026gt; write int pipe2(int pipefd[2], int flags); 消息队列：四个基本操作：msgget：获取已有的消息队列连接或者新创建一个，msgsnd往消息队列上发消息，msgrcv：接收消息（收发过程如果消息队列已满或为空，则阻塞），msgctl。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include \u0026lt;sys/msg.h\u0026gt; // get System V message queue identifier // associated with the value of the key argument. int msgget(key_t key, int msgflg); struct msgbuf { long mtype; /* message type, must be \u0026gt; 0 */ char mtext[1]; /* message data */ }; // appends a copy of the message pointed to by msgp // to the message queue whose identifier is specified by msqid. int msgsnd(int msqid, const void msgp[.msgsz], size_t msgsz, int msgflg); // removes a message from the queue specified // by msqid and places it in the buffer pointed to by msgp. ssize_t msgrcv(int msqid, void msgp[.msgsz], size_t msgsz, long msgtyp, int msgflg); // performs the control operation specified by cmd // on the System V message queue with identifier msqid. int msgctl(int msqid, int cmd, struct msqid_ds *buf); 信号量：P（Probeer，尝试）计数器减一，操作失败会进入阻塞，V（Verhoog，增加）计数器加一，唤醒P。\n共享内存：为需要通信的进程建立共享区域，一旦建立成功，就不再需要内核参与进程间通信。\n信号：使用kill向进程、进程组或者是tgkill向线程发送信号，其中1～31为常规信号，32～64为实时信号。处理时机通常在执行完异常、中断、系统调用返回时。\n1 2 3 4 5 6 7 #include \u0026lt;signal.h\u0026gt; // send any signal to any process group or process. int kill(pid_t pid, int sig); // be delivered to an arbitrary thread within that process. int tgkill(pid_t tgid, pid_t tid, int sig); 套接字：使用特定“地址”来找到要调用的服务端进程。\n1 2 3 4 #include \u0026lt;sys/socket.h\u0026gt; // create an endpoint for communication int socket(int domain, int type, int protocol); 8. Synchronization Primitives 皮特森算法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 while(true) { flag[0] = true; turn = 1; while(flag[1] == true \u0026amp;\u0026amp; turn == 1); do_critical_section(); flag[0] = false; } while(true) { flag[1] = true; turn = 0; while(flag[0] == true \u0026amp;\u0026amp; turn == 0); do_critical_section(); flag[1] = false; } 要求访存操作严格顺利执行。\n原子操作：CAS（Compare And Swap），以及FAA（Fetch-And-Add）。\nIntel平台采用内联汇编来保证操作的原子性，比较expected（这里+a当作寄存器%eax）和addr的值，如果相等就存入地址addr中。\n1 2 3 4 5 6 7 int atomic_CAS(int *addr, int expected, int new_value) { asm volatile(\u0026#34;lock empxchg %[new], %[ptr]\u0026#34; :\u0026#34;+a\u0026#34;(expected), [ptr] \u0026#34;+m\u0026#34;(*addr) :[new] \u0026#34;r\u0026#34;(new_value) :\u0026#34;memory\u0026#34;); return expected; } ARM平台（与RISC-V类似）采用Load-Link和/Store-Conditional的指令组合，通过CPU监视器来实现\n1 2 3 4 5 6 7 8 9 10 11 12 13 int atomic_CAS(int *addr, int expected, int new_value) { int oldval, ret; asm volatile( \u0026#34;1: ldxr %w0, %2\\n\u0026#34; \u0026#34; cmp %w0, %3\\n\u0026#34; \u0026#34; b.ne %2f\\n\u0026#34; \u0026#34; stxr %w1, %w4, %2\\n\u0026#34; \u0026#34;2:\u0026#34; : \u0026#34;=\u0026amp;r\u0026#34; (oldval), \u0026#34;=\u0026amp;r\u0026#34; (ret), \u0026#34;+Q\u0026#34; (*addr) : \u0026#34;r\u0026#34; (expected), \u0026#34;r\u0026#34; (new_value) : \u0026#34;memory\u0026#34;); return oldval; } 自旋锁\n1 2 3 4 5 6 7 8 9 10 11 12 void lock_init(int *lock) { *lock = 0; } void lock(int *lock) { while (atomic_CAS(lock, 0, 1) != 0) ; } void unlock(int *lock) { *lock = 0; } 并不保证有限等待，即不具有公平性，会陷入循环等待。\n排号锁\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct lock { volatile int owner; volatile int next; } void lock_init(struct lock *lock) { lock-\u0026gt;owner = 0; lock-\u0026gt;next = 0; } void lock(struct lock *lock) { volatile int my_ticket = atomic_FAA(\u0026amp;lock-\u0026gt;next, 1); while (lock-\u0026gt;owner != my_ticket) ; } void unlock(struct lock *lock) { lock-\u0026gt;owner++; } 条件变量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct cond { struct thread *wait_list; } void cond_wait(struct cond *cond, struct lock *mutex) { list_append(cond-\u0026gt;wait_list, thread_self()); atomic_block_unlock(mutex); // 原子挂起并释放锁 lock(mutex); // 重新获得互斥锁 } void cond_signal(struct cond *cond) { if (!list_empty(cond-\u0026gt;wait_list)) { wakeup(list_remove(cond-\u0026gt;wait_list)); } } void cond_broadcast(struct cond *cond) { while (!list_empty(cond-\u0026gt;wait_list)) { wakeup(list_remove(cond-\u0026gt;wait_list)); } } 生产者消费者问题，采用条件变量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 int empty_slot = 5; int filled_slot = 0; struct cond empty_cond; struct lock empty_cnt_lock; struct cond filled_cond; struct lock filled_cnt_lock; void producer(void) { int new_msg; while (true) { new_msg = produce_new(); lock(\u0026amp;empty_cnt_lock); while (empty_slot == 0) { cond_wait(\u0026amp;empty_cond, \u0026amp;empty_cnt_lock); } empty_slot--; unlock(\u0026amp;empty_cnt_lock); buffer_add_safe(new_msg); lock(\u0026amp;filled_cnt_lock); filled_slot++; cond_signal(\u0026amp;filled_cond); unlock(\u0026amp;filled_cnt_lock); } } void consumer(void) { int cur_msg; while (true) { lock(\u0026amp;filled_cnt_lock); while (\u0026amp;filled_slot == 0) { cond_wait(\u0026amp;filled_cond, \u0026amp;filled_cnt_lock); } filled_slot--; unlock(\u0026amp;filled_cnt_lock); cur_msg = buffer_remove_safe(); lock(\u0026amp;empty_cnt_lock); empty_slot++; cond_signal(\u0026amp;empty_cond); unlock(\u0026amp;empty_cnt_lock); comsume_msg(cur_msg); } } 信号量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 struct sem { int value; int wakeup; struct lock sem_lock; struct cond sem_cond; } void wait(struct sem *S) { lock(\u0026amp;S-\u0026gt;sem_lock); S-\u0026gt;value--; if (S-\u0026gt;value \u0026lt; 0) { do { cond_wait(\u0026amp;S-\u0026gt;sem_cond, \u0026amp;S-\u0026gt;sem_lock); } while(S-\u0026gt;wakeup == 0); S-\u0026gt;wakeup--; } unlock(\u0026amp;S-\u0026gt;sem_lock); } void signal(struct sem *S) { lock(\u0026amp;S-\u0026gt;sem_lock); S-\u0026gt;value++; if (S-\u0026gt;value \u0026lt;= 0) { S-\u0026gt;wakeup++; cond_signal(\u0026amp;S-\u0026gt;sem_cond); } unlock(\u0026amp;S-\u0026gt;sem_lock); } 生产者消费者问题，采用信号量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 sem_t empty_slot; sem_t empty_slot; void producer(void) { int new_msg; while(true) { new_msg = produce_new(); wait(\u0026amp;empty_slot); // P buffer_add_safe(new_msg); signal(\u0026amp;filled_slot); // V } } void consumer(void) { int cur_msg; while(true) { wait(\u0026amp;filled_slot); // P cur_msg = buffer_remove_safe(); signal(\u0026amp;empty_slot); // V consume_msg(cur_msg); } } 死锁产生的原因：\n互斥访问、持有并等待、资源非抢占、循环等待 银行家算法：假设系统有M个资源，N个线程。\n全局可利用资源Available[M]，每个线程最大需求Max[N][M]，已分配资源Allocation[N][M]，还需要分配的资源Need[N][M]。同时，保证供给关系固定。线程的需求不能超过总量，任意线程分配的资源加上还需要的资源小雨该线程的最大需求。线程获得资源后，有限时间能完成，并且最后会释放这些资源。\n死锁检测与恢复：事后恢复；死锁预防：解决死锁产生的四个必要条件之一；死锁避免：找到安全序列，如银行家算法。\n优先级反转避免：优先级继承协议（Priority Inheritance protocol）：高优先级等待锁时，会使持有者继承其优先级，避免临界区被低优先级打断。\n9. File System Index Node：记录文件索引节点（即存储块），inode保存三块指针，一种直接指向数据块，一种是间接指向一个一级索引块，一种是二级指针指向一级索引。\ninode也保存着文件模式、链接数、拥有者用户组，大小、访问时间等信息。\nSymbol File type Description - Ordinary or regular files Contain data of various content types such as text, script, image, videos, etc. d Directory files Contain the name and address of other files. b \u0026amp; c Block or character special files Represent device files such as hard drives, monitors, etc. l Link files Point or mirror other files s Socket files Provide inter-process communication p Named pipe files Allow processes to send data to other processes or receive data from other processes. 目录大小即记录的文件数量以及文件名长度的大小。\n每个inode可以被多个目录项指向，该链接数为0时，inode资源和数据可以被销毁。\nVirtual File System：通过基于inode设计了一系列数据结构，包括超级快，inode，目录项等，VFS隐蔽了实现细节。\nmount：文件访问到挂载点，就会跳转到挂载点的根目录访问。\nPseudo File System（Synthetic file system）：常见的伪文件系统如/proc，/sys，/sys/kernel/config等。\n10. Device Management Peripheral Component Interconnect：PCI标准，满足用于PCI插槽与CPU高效通信\nData Memory Access，DMA：设备与内存之间高效数据传输形式。一般有三个流程\n处理器向DMA发送至缓冲区的位置和长度，以及数据方向。 DMA获得总线控制权，可以直接和内存与设备进行通信。 DMA控制器将根据处理器获得的命令，将设备数据拷贝至内存，这期间处理器可以执行其他任务 完成后，DMA控制器向处理器发送中断，此时处理器重新获得总线的控制权。 Generic Interrupt Controller：ARM下的通用中断控制器，负责对设备的中断信号处理。每个中断有四种状态：\nInactive：无效，此时中断未到来。 Pending：有效状态，中断已发生，CPU未响应中断。 Active：CPU处于响应并处理中断的过程中。 Active \u0026amp; Pending：处理中断时，有相同的中断号发生。 Linux操作系统中，中断分为上半部和下半部。上半部处理时，关闭中断，做一些严格有时限的工作，如应答并且复位硬件。完成后向中断控制器声明中断处理器并开中断，设备驱动通过以下函数来注册/释放相应设备硬中断。\n1 2 3 4 5 6 7 8 9 typedef irqreturn_t (*irq_headler_t)(int, void *); int request_irq (unsigned int irq, // 不同设备的中断号 irq_handler_t handler, // 指向这个中断处理程序的指针 unsigned long flags, // 标志掩码，如关中断等 const char *name, // 中断设备名称的的ASCII表示 void* dev) // 共享中断总线，标志中断处理程序 void free_irq (unsigned int irq, // 注销中断处理程序，释放中断线 void *dev) // 如果中断总线共享，则仅删除dev对应的处理程序 软中断：处理函数要求是可重入的，要求软中断的执行过程中可以被硬中断抢占。所以，软中断时应该避免全局变量，或者加锁保护关键数据结构。Linux共有10种中断信号，从TASKLET_HI往下包括定时器，网卡收发，块设备中断等。内核会选择恰当时机来处理软中断（大多数时间在硬中断得到处理后，除非CPU还持有未执行的软中断）。\n1 2 3 4 5 struct softirq_action { void (*action)(struct softirq_action *); } // 软中断在编译阶段静态分配，最多只能有32个 static struct softirq_action softirq_vec[NR_SOFTIRQS]; 通常标记了软中断后执行的场景有：1.从硬件中断代码返回时；2.在ksoftirqd内核线程中；3.在那些显示检查和执行待处理的软中断代码中，如网络子系统。\ntasklet：软中断只能编译时静态分配，为了动态分配设计了tasklet，每个tasklet只有SCHED（已被调度未执行）和RUN（正在执行），将所有tasklet用链表串起来。此外tasklet不允许多CPU并发执行，同时保证原子性，不允许被其他下半部机制抢占，满足可重入性。\n1 2 3 4 5 6 7 struct tasklet_struct { struct tasklet_strcut *next; // 链表中的下一个tasklet unsigned long state; // 三个状态：0，准备运行，正在运行（仅限多核作为优化使用） atomic_t count; // 引用计数器 void (*func)(unsigned long); // tasklet处理函数 unsigned long data; // 给tasklet处理用的参数 } Work Queue：把需要推迟的函数放在下半部，用内核线程来执行。Work Queue由驱动开发者自己定义，并且由于其是串行的，可能产生阻塞，也会浪费PID资源。\nConcurrency Managed WorkQueue：将工作队列管理还给内核，同一个队列的work不遵守串行，可以并发执行，如果有任务长时间足额，那么CMWQ会自动创建一个新的工作线程去处理该任务后续的工作。\n11. System Virtualization CPU虚拟化：通过vCPU抽象执行指令，直接运行在物理机上，使用物理ISA。虚拟机模拟一个安全的下陷的过程。\n内存虚拟化：使用客户物理地址与主机物理地址，影子页表机制\nIO虚拟化：主引导记录，全局唯一标识分区表。\n12. Multi-core processor 最小的操作粒度（Cache Line）：一般是64字节，通常L1 Cache 进一步划分为单独的数据缓存（Data Cache）与指令缓存（Instruction Cache）。所有核心共享最末级缓存（Last Level Cache，LLC）。\n直写策略（Write Through）：在写时，立刻将修改的值刷回内存（该值会同时保留在高速缓存中）。\n写回策略（Write Back）：将值暂时存在高速缓存中。只有在出现高速缓存逐出（Cache Eviction），或是CPU 核心调用写回指令时，修改才会被更新至物理内存。\n非一致缓存访问（Non-Uniform Cache Access，NUCA）：不同核心访问时延会依据缓存行所在位置有所差别。\n1 2 3 4 5 6 7 8 9 10 11 12 13 var a, b int func main() { go func() { a++ fmt.Println(\u0026#34;b = \u0026#34;, b) }() go func() { b++ fmt.Println(\u0026#34;a = \u0026#34;, a) }() time.Sleep(time.Second) } 内存一致性模型（Memory Consistency Model，简称为内存模型）明确定义了不同核心对于共享内存操作需要遵循的顺序。以上述代码为例，不同情况下(a, b)可能的取值范围(0, 0), (1, 0), (0, 1), (1, 1)也会发生改变。\n严格一致性模型（Strict Consistency）：所有访存操作都是严格按程序顺序。\n顺序一致性模型（Sequential Consistency）：不同核心看到的访存操作顺序完全一致，这个顺序称为全局顺序。在这种模型下，不可能出现(a, b)=(0, 0)的情况。\nTSO一致性模型（Total Store Ordering）：保证对不同地址且无依赖的读读、读写、写写操作之间的全局可见顺序，只有写读的全局可见顺序不能得到保证。通过加入写缓冲来实现这个操作，这允许了(0, 0)可能的出现。\nPSO一致性模型（Partial Store Ordering）：Is a more relaxed memory consistency model compare to the Total Store Ordering (TSO). PSO is essentially TSO with one additional relaxation to the consistency: PSO only guarantees writes to the same location is in order whereas writes to different memory location may not be in order at all. The processor may rearrange writes so that a sequence of write to memory system may not be in their original order.\n弱序一致性模型（Weak-ordering Consistency）：不保证任何不同地址且无依赖的访存操作之间的顺序，也即读读，读写，写读与写写操作之间都可以乱序全局可见。\n读读 读写 写读 写写 严格一致性模型 ✅ ✅ ✅ ✅ 顺序一致性模型 ✅ ✅ ✅ ✅ TSO一致性模型 ✅ ✅ ❌ ✅ PSO一致性模型 ✅ ✅ ❌ ✅（同位置）/❌（不同） 弱序一致性模型 ❌ ❌ ❌ ❌ 硬件内存屏障（Barrier/Fence，简称内存屏障）:\n1 2 3 4 5 6 7 8 // Any of these GNU inline assembler statements forbids the GCC compiler // to reorder read and write commands around it: asm volatile (\u0026#34;\u0026#34;:::\u0026#34;memory\u0026#34;); __asm__ __volatile__ (\u0026#34;\u0026#34; ::: \u0026#34;memory\u0026#34;); // This C11/C++11 function forbids the compiler // to reorder read and write commands around it: atomic_signal_fence(memory_order_acq_rel); 弱顺序一致性 TSO一致性模型 顺序一致性 体系结构 ARM，PowerPC x86 Dual386，MIPS R10000 使用场景 嵌入式，手机/平板，高性能服务器 桌面电脑，高性能服务器 已被淘汰 依赖关系：数据依赖（要写入的某值，依赖另一个运行结果，Java的编译器和处理器会满足这个依赖性）；地址依赖（如写操作需要写的地址依赖于读操作读出来的值）、控制依赖（如写操作只有在读操作结束分支满足后才能执行）。\n重排序缓冲区（Re-Order Buffer, ROB），让指令按照程序顺序退役（Retire）对应顺序执行中的执行结束，其意味着该条指令对系统的影响终将全局可见。\n存取单元（Load/Store Unit，LSU）中预留了读缓冲区与写缓冲区。\n阿姆达尔定律（Amdahl’s Law）用以描述并行计算的加速比：$S = \\frac{1}{(1 − p) + \\frac{p}{s}}$\n其中 S 描述加速比，p 为程序中可以并行的部分所占比例（$0≤p≤1$），而 s 为可以并行部分的加速比。在理想情况下，如我们如果有N 个核，此时的并行部分加速比为$s = N$。\n*. Docker 容器本身作为宿主机上的一个进程，通过namespace实现资源隔离，cgroups实现资源限制，通过copy-on-write实现了高效的文件操作。\n*.1. namespace namespace 隔离机制：\nnamespace 系统调用参数 隔离内容 UTS CLONE_NEWUTS 主机名和域名 IPC CLONE_NEWIPC 信号量，消息队列，共享内存 PID CLONE_NEWPID 进程编号 NetWork CLONE_NEWNET 网络设备，网络栈，网络端口等 Mount CLONE_NEWNS 挂载点（文件系统） User CLONE_NEWUSER 用户和用户组 系统调用方式：clone(), setns(), unshare(), 以及/proc下的虚拟文件。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #inclue\u0026lt;unistd.h\u0026gt; #inclue\u0026lt;sys/types.h\u0026gt; // fork - create a child process // 传统创建进程的方式 pid_t fork(void); #include\u0026lt;sched.h\u0026gt; // clone - create a child process // 是fork更加通用的实现方式，可以使用flag来实现功能。 int clone(int (fn*)(void *), void *stack, int flags, void *arg, ...); // setns - reassociate thread with a namespace // 加入现有的namespace中 int setns(int fd, int nstype); // setns - reassociate thread with a namespace // 在原进程上进行namespace隔离 int unshare(int flags); // Docker并未采用 UTS(Unix Time-haring System) namespace: 提供了主机和域名的隔离，这样每个容器便可以视作独立节点而非宿主机上的一个进程。\nIPC(Inter-Process Communication) namespace: 主要包括常见的信号量，消息队列和共享内存。申请IPC其实就是申请了全局唯一的32位ID。IPC namespace实际上就包含了系统的IPC标识符以及实现了POSIX消息队列的文件系统。不同的IPC namespace下的进程之间不可见。\nPID(Process ID) namespace: 对进程的PID进行重写，不同namespace下的进程可以拥有相同的PID，所有PID namespace维持一个树状结构。其中init进程具有屏蔽权限，一旦init被销毁，那么同一个namespace下的PID都会收到SIGKILL而被杀死\nmount namespace: 是历史上第一个namespace，所以标志位比较特殊，就是CLONE_NEWNS。隔离以后，不同namespace下的文件将不受影响。\n在2006年引入了挂载传播(mount propagation)机制，使得挂载可以定义挂载对象之间的关系，决定了事件如何传播到其他挂载的对象。包括：共享(share)，从属(slave)，共享/从属(share and slave)，私有(private)，不可绑定(unbinable)，共五种。\nnetwork namespace: 提供了网络资源的隔离，包括网络设备、IPv4/IPv6协议栈、IP路由表、防火墙，/proc/net目录，/sys/class/net目录以及socket等。一个物理的网络设备只能处于一个network namespace中，通过创建veth pair创建通道以达到通信目的。\n在建立网桥之前，新旧namespace通过建立pipe，init在pipe一段循环等待，直到管道另一边传来veth的信息，init才结束等待。\nuser namespace: 直到Linux内核3.8开始都未完全实现，还有部分文件系统未支持。设计主要隔离了安全相关的标识符(identifier)和属性(attribute)，包括用户ID，用户组ID，root目录，key（密钥）以及特殊权限。Docker在1.10开始才支持user namespace，启动daemon时指定userns-remap，则容器内的root将不再等于宿主机内的root。\nnamespace 的核心实际上是通过层次关联起来，每个namespace都源自于root namespace的映射。\n*.2. cgroups cgroups：限制被namespace隔离起来的资源，还可以为资源设置权重、计算使用量、操控任务（进程或线程）启停等。\ncgroups是Linux内核提供的一种机制，这种机制可以根据需求把一系列系统任务及其子任务整合（或分隔）到按资源划分等级的不同组内，从而为系统资源管理提供一个统一的框架。cgroups的实现本质上是给任务挂上钩子，当任务运行的过程中涉及某种资源时，就会触发钩子上所附带的子系统进行检测\ncgroups为了不同用户层面的管理资源，并提供统一化的接口，包括如下四个功能：\n资源限制：cgroups可以对资源的总和进行限制，当超过上限即触发OOM。 优先级分配：分配CPU分得的时间片以及磁盘I/O带宽，决定其优限级。 资源统计：统计内存使用时长，CPU使用量等。 任务控制：实现任务挂起，恢复等操作。 镜像分层：每个镜像都由一系列镜像层构成，采用COW机制，并根据文件内容索引镜像和镜像层。联合挂载技术可以在一个挂载点同时挂载多个文件系统，将挂载点的原目录与被挂载内容进行整合，使得最终可见的文件系统将会包含整合之后的各层的文件和目录。\nOverlayFS是一种新型联合文件系统（union filesystem），Linux内核版本3.18开始支持，它允许用户将一个文件系统与另一个文件系统重叠（overlay），在上层的文件系统中记录更改，而下层的文件系统保持不变。\nlibnetwork：在Docker的桥接网络模式中，docker0的IP地址作为连于之上的容器的默认网关地址存在。\nBridge驱动：默认方式，Container会接到Docker网桥上，与外界通信采用NAT，增加了通信复杂性。 host驱动：不创建默认的namespace，使用宿主机的网卡、IP和端口，避免了地址转换问题。 overlay驱动：采用IETF的VXLAN方式，适合大规模的云计算虚拟化SDN controller模式，但是需要额外配置服务，如Consul，etcd和ZooKeeper等，在启动时添加参数 remote驱动：没有使用真正的网络服务发现，调用了用户自己的网络驱动插件。 null驱动：Container有自己的namespace，但是不会做任何网络相关的配置。 ","permalink":"https://chasing1020.github.io/post/operating-system/","summary":"\u003ch1 id=\"1-overview\"\u003e1. Overview\u003c/h1\u003e\n\u003cp\u003e什么是操作系统？\u003c/p\u003e\n\u003cp\u003e硬件角度：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003col\u003e\n\u003cli\u003e管理硬件：将复杂的，具备不同功能的硬件资源纳入统一管理。\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e对硬件进行抽象：抽象成不依赖具体硬件特性的资源。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e将有限的，离散的资源高效地抽象成无限的、连续的资源，并提交接口给上层系统调用。\u003c/p\u003e","title":"Operating System"},{"content":"Kubernetes 场景：管理容器化的工作负载和服务，可促进声明式配置和自动化\n功能：服务发现和负载均衡、存储编排、自动部署和回滚、自动完成装箱计算、自我修复、密钥与配置管理\n1. Component 一个 Kubernetes 集群由一组被称作节点的机器组成。这些节点上运行 Kubernetes 所管理的容器化应用。集群具有至少一个工作节点。\n工作节点托管作为应用负载的组件的 Pod 。控制平面管理集群中的工作节点和 Pod 。 为集群提供故障转移和高可用性，这些控制平面一般跨多主机运行，集群跨多个节点运行。\n1.1. Control Plane Components 控制面板用于控制整个集群的工作，其包含多个组件，可以运行在单个主机上或者通过副本分别不是在多个主节点用以确保高可用\nkube-apiserver API 服务器是 Kubernetes 控制面的组件， 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。\netcd etcd 是兼具一致性和高可用性的键值数据库，可以作为保存 Kubernetes 所有集群数据的后台数据库。\nkube-scheduler 控制平面组件，负责监视新创建的、未指定运行节点（node）的 Pods，选择节点让 Pod 在上面运行。\nkube-controller-manager 从逻辑上讲，每个控制器都是一个单独的进程， 但是为了降低复杂性，它们都被编译到同一个可执行文件，并在一个进程中运行。\n节点控制器（Node Controller）: 负责在节点出现故障时进行通知和响应 任务控制器（Job controller）: 监测代表一次性任务的 Job 对象，然后创建 Pods 来运行这些任务直至完成 端点控制器（Endpoints Controller）: 填充端点(Endpoints)对象(即加入 Service 与 Pod) 服务帐户和令牌控制器（Service Account \u0026amp; Token Controllers）: 为新的命名空间创建默认帐户和 API 访问令牌 cloud-controller-manager 云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器使得你可以将你的集群连接到云提供商的 API 之上， 并将与该云平台交互的组件同与你的集群交互的组件分离开来。 cloud-controller-manager 仅运行特定于云平台的控制回路。 如果你在自己的环境中运行 Kubernetes，或者在本地计算机中运行学习环境， 所部署的环境中不需要云控制器管理器。\n1.2. Node Component kubelet 一个在集群中每个节点（node）上运行的代理。 它保证容器（containers）都 运行在 Pod 中。\nkubelet 接收一组通过各类机制提供给它的 PodSpecs，确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。\nkube-proxy kube-proxy 是集群中每个节点上运行的网络代理， 实现 Kubernetes 服务（Service） 概念的一部分，负责组件之间的负载均衡网络流量。\nkube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。\nContainer Runtime 容器运行环境是负责运行容器的软件。\nKubernetes 支持多个容器运行环境: Docker、 containerd、CRI-O 以及任何实现 Kubernetes CRI (容器运行环境接口)。\n1.3. Objects 在 Kubernetes 系统中，Kubernetes 对象 是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。特别地，它们描述了如下信息：\n哪些容器化应用在运行（以及在哪些节点上） 可以被应用使用的资源 关于应用运行时表现的策略，比如重启策略、升级策略，以及容错策略 2. Minikube Quick Start 1 2 3 4 5 6 curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube minikube start minikube dashboard 在浏览器出现minikube仪表盘即安装成功。\n使用 kubectl create 命令创建管理 Pod 的 Deployment。该 Pod 根据提供的 Docker 镜像运行 Container。\n1 kubectl create deployment hello-node --image=k8s.gcr.io/echoserver:1.4 查看 Deployment：\n1 kubectl get deployments 查看 Pod：\n1 kubectl get pods 输出结果类似于这样：\n1 2 NAME READY STATUS RESTARTS AGE hello-node-5f76cf6ccf-br9b5 1/1 Running 0 1m 每当运行kubectl时，即像RESTAPI服务器发送请求，创建Pod并且调度到工作节点，kubetlet收到通知，再去告诉Docker运行镜像\n3. Pod 3.1. Basic Pod作为一组并置的容器，代表了k8s中的基本构建模型，一个Pod的所有容器只能运行在同一个节点上。\n无论是IPC还是本地文件通信，在一个独立机器上显得十分合理。\n同一个Pod下的所有容器共享Linux的namespace与network interface，因此可以通过IPC进行通信。同一个Pod下的端口号会发生冲突，但是一个Pod中的所有容器也有相同的loopback网络接口，即可以使用localhost与其他容器进行通信。在Pod之间都在一个共享的网络地址空间中，即不需要经过NAT转换。\nPod常用的命令\n1 2 3 4 5 6 7 8 kubectl explain pods kubetcl create -f xx.yaml kubectl get po xx -o json kubectl delete po xx kubectl logs xx kubectl port-forward xx 8888:8080 kubetcl get ns kubectl create namespace mynamespace 通过yaml定义的Pod，包含metadata：名称命名空间，标签；spec：Pod实际说明，包括容器，数据卷等；status：Pod当前信息，所处条件，容器状态。\n此外，还可以手动给Pod添加标签，每一个Pod有两种标签，在yaml的metadata处为Pod附加标签\napp：指定app属于哪个应用、组建或者微服务\nrel：显示Pod运行应用程序版本stable、beta、canary\n3.2. Workloads Controller 3.2.1 Liveness Probe 通过存活探针liveness probe检测容器是否正在运行，常用方式有：HTTP GET、TCP socket、Exec command。\n在spec.containers下面定义livenessProbe并设置path\n1 kubectl describe po kubia-liveness 3.2.2. ReplicatoinController ReplicationController用于确保pod始终是可以运行状态，如果Pod以任何原因消失，那么ReplicationController会注意到缺少，并创建一个Pod替代。运行时，Replication会持续监控正在运行的Pod列表，保证相对应的类型的Pod与预期相符，就算有多余Pod也会删除。\n可以确保一个（或多个）Pod持续运行，方法是在现有Pod丢失时启动新的Pod。\n通过创建kind: ReplicationController来定义，spec.replicas: x决定目标数目，template下定义创建新的Pod所用的模版。\n1 2 3 4 5 6 7 kubectl get pods kubectl get rc kubectl describe rc kubia kubectl label pod xx app=foo --overwrite # 修改后将不再由该RC管理 kubectl edit rc kubia # 当然，可以使用export KUBE_EDITOR=\u0026#34;/usr/bin/vim\u0026#34;来决定编辑器 kubectl scale rc kubia --replicas=10 # 扩容 其主要由三个部分构成：\nlabel selector：确定当前ReplicationController作用域下有多少Pod\nreplica count：副本个数，指定运行的数量\npod template：用于创建新的pod副本\n3.2.3. ReplicaSet 相较于ReplicationController，ReplicaSet具有类似的行为，但是可以更好地表达Pod选择器，可以允许匹配缺少某个标签的Pod，或者包含特定标签名的Pod。\n其包含四个特定字段：\nIn：label的值必须与其中一个指定的values匹配\nNotIn：Label的值与任何一个values不匹配。\nExists：Pod必须包含一个指定名称的标签，使用此运算符不应该包括values字段。\nDosNotExist：Pod不得包含有指定名称的标签，同样不包括values字段。\n3.2.4. DaemonSet DaemonSet 确保全部（或者某些）节点上运行一个 Pod 的副本。 当有节点加入集群时， 也会为他们新增一个 Pod 。 当有节点从集群移除时，这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。\nDaemonSet 的一些典型用法：\n在每个节点上运行集群守护进程 在每个节点上运行日志收集守护进程 在每个节点上运行监控守护进程 通过给Node打上标签\n1 kubectl label node xx disk=ssd 即可以对该节点保证运行设定好的校验ssd的Pod。\n3.2.5. Jobs Job 会创建一个或者多个 Pods，并将继续重试 Pods 的执行，直到指定数量的 Pods 成功终止。 随着 Pods 成功结束，Job 跟踪记录成功完成的 Pods 个数。 当数量达到指定的成功个数阈值时，任务（即 Job）结束。 删除 Job 的操作会清除所创建的全部 Pods。 挂起 Job 的操作会删除 Job 的所有活跃 Pod，直到 Job 被再次恢复执行。\n一种简单的使用场景下，你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除（比如因为节点硬件失效或者重启）时，Job 对象会启动一个新的 Pod。\n你也可以使用 Job 以并行的方式运行多个 Pod。\n在Job中可以定义运行多个Pod示例，包括顺序，并行，CronJob等\n4. Service 由于Pod的可变动性，导致其随时会被启动或者关闭，并且在Pod启动钱，会给已经调度到节点上的Pod分配IP地址，且由于水平伸缩的特性，多个Pod可能会提供相同的服务。\n通过创建yml文件定义service\n1 kubectl get svc 可以通过创建服务，来让一个单一稳定的IP访问到Pod，在服务的整个生命周期内，这个地址会保持不变，在服务后的Pod可以删除重建，但是对外的IP不会变化。\n为了外部连接k8s的服务，在服务和pod之间不会直接相连的，而是存在一个endpoint资源，用以暴露服务ip和端口的列表。\n将服务暴露给客户端，有如下几种方式：\nNodePort：每个集群节点都会打开一个端口，并将在该端口上接受到的流量重定向到基础服务。 LoadBalance：使得服务可以通过一个专用的负载均衡器进行访问， Ingress：通过一个IP地址公开多个服务 ","permalink":"https://chasing1020.github.io/post/kubernetes/","summary":"\u003ch1 id=\"kubernetes\"\u003eKubernetes\u003c/h1\u003e\n\u003cp\u003e场景：管理容器化的工作负载和服务，可促进声明式配置和自动化\u003c/p\u003e\n\u003cp\u003e功能：\u003cstrong\u003e服务发现和负载均衡\u003c/strong\u003e、\u003cstrong\u003e存储编排\u003c/strong\u003e、\u003cstrong\u003e自动部署和回滚\u003c/strong\u003e、\u003cstrong\u003e自动完成装箱计算\u003c/strong\u003e、\u003cstrong\u003e自我修复\u003c/strong\u003e、\u003cstrong\u003e密钥与配置管理\u003c/strong\u003e\u003c/p\u003e","title":"Kubernetes"},{"content":"Redis Data Structure 0. Redis Object 1 2 3 4 5 6 7 struct RedisObject { int4 type; // 4bits \\ int4 enconding; // 4bits = 4bytes int24 lru; // 24bits / int32 refcount; // 4bytes void* ptr; // 8bytes (64bit-system) } robj; RedisObject对于不通对象都是相同的，对于这样的结构，每个都需要16byte的空间。\nRedis可以说是当代内存抠门学的设计典范之一。\n下文的int4，int8代表位域，即type [member_name] : width ;来限制数据类型的宽度；\n特定的结构体采用了__attribute__ ((__packed__))来取消优化对齐；\n其中Generic (即\u0026lt;T\u0026gt;) 借由TYPE_MASK的\u0026amp;运算来实现（当然对于C而言可以用宏，与C11中的_Generic），这里当作伪代码使用。\n1. SDS Redis字符串简称SDS（Simple Dynamic String）。 C语言传统以NULL，即\u0026rsquo;\\0\u0026rsquo;作为字符串结束符，这样使用strlen获取长度的复杂度是O(n)，所以Redis没有使用C的传统结束符，而是使用了SDS这样的数据结构。\n1 2 3 4 5 6 struct SDS\u0026lt;T\u0026gt; { T capacity; T len; byte flags; byte[] content; } 这里的len和capacity类比于Go的[]byte切片，为了支持append内容，必然会涉及到分配新数组与复制的过程，将会带来不少的开销。\n1 2 3 4 5 6 7 8 9 sds sdscatlen(sds s, const void *t, size_len) { size_t curlen = sdslen(s); s = sdsMakeRoomFor(s, len); if (s == NULL) return NULL; // OOM mencpy(s+curlen, t, len); sdssetlen(s, curlen+len); s[curlen+len] = \u0026#39;\\0\u0026#39;; return s; } 选择使用范型为了追求极限的性能优化，可以在较短时使用byte和short。\nRedis设定String不能超过512MB，创建时的len和capacity一样长，不会分配多余空间。\n2. Dict 1 2 3 4 5 struct RedisDB{ dict* dict; dict* expires; // ... } 除了hash结构以外，Redis也有全局字典记录键值对。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct dictEntry { void* key; void* val; dictEntry* next; } struct dictht { dictEntry** table; long size; long used; // ... } struct dict { // ... dicht ht[2]; } Redis的字典与Java的HashMap接近，采用分桶解决哈希冲突，即一维数组+二维链表的方式。\n当hash表中元素等于一维数组的长度时，开始扩容，如果Redis在做bgsave，为了减少内存页的过多分离(COW)，就不会扩容，但是如果元素个数达到了5倍，还是会发生强制扩容。\n扩容时，如果全部重新申请数组，并将链表元素挂在新的数组下，耗时O(n)，对于单线程而言很难接受，所以Redis采用渐进式扩容策略\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing){ long index; dictEntry *entry; dictht *ht; if (dictisRehashing(d)) _dictRehashing(d); if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1) return NULL; ht = dictIsRehashing(d) ? \u0026amp;d-\u0026gt;ht[1] : \u0026amp;d-\u0026gt;ht[0]; entry = zmalloc(sizeof(*entry))； entry-\u0026gt;next = ht-\u0026gt;table[index]; ht-\u0026gt;table[index] = entry; ht-\u0026gt;used++; dictSetKey(d, entry, key); return entry; } 此外，在客户端空闲时，Redis也设计了定时任务(databaseCron)，对字典进行主动搬迁。\nRedis的Set采用的与字典相同的数据结构，不过是Value都为NULL(和Go一样)。\n3. Zip List 当zset和hash对象在元素个数较少时，都在用压缩列表存储，能保证数据没有冗余。\n1 2 3 4 5 6 7 8 9 10 11 12 struct ziplist\u0026lt;T\u0026gt; { int32 zlbytes; int32 zltail_offset; int16 zllength; T[] entries; int8 zlend; } struct entry { int\u0026lt;var\u0026gt; prevlen; int\u0026lt;var\u0026gt; encoding; optional byte[] content; } zltial_offset用于记录最后一个元素的偏移量，prevlen用于记录前一个entry的长度，倒序遍历时可以快速定位到下一个元素位置。\nencoding字段为记录编码信息，做了相当复杂的设计，类似于UTF8编码，采用了前缀位类区分内容。\noptional代表该字段是可选的，对于很小的整数而言，可能内容已经inline到encoding的尾部了。\n每次插入ziplist都需要使用realloc拓展内存，将先前的内容进行拷贝，或者在原地址拓展。这种操作对于大内存的效率不高，所以ziplist并不适合存储大元素。\n4. Quick List 1 2 3 4 5 6 7 8 9 10 struct listNode\u0026lt;T\u0026gt; { listNode *prev; listNode *next; T value; } struct list { listNode *head; listNoed *tail; long length; } 早期的Redis在元素少时使用ziplist，元素多时使用linkedlist，这样前后指针就需要16字节，浪费内存空间。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 struct ziplist\u0026lt;T\u0026gt; { int32 zlbytes; int32 zltail_offset; int16 zllength; T[] entries; int8 zlend; } struct ziplist_compressed { int32 size; byte[] compressed_data; } struct quicklistNode { quicklistNode* prev; quicklistNode* next; ziplist* zl; int32 size; int16 count; int2 encoding; // ... } struct quicklist { quicklistNode* head; quicklistNode* tail; long count; int nodes; int compressDepth; // ... } 每个ziplist长度为8kb，超过这个值会新建一个ziplist。一般而言，Redis默认的压缩深度为0，可以采用LZF算法对内存进行压缩，可以选择压缩深度。\n5. Skip List 对于zset这样的复合结构，需要hash存储kv，也需要对score排序，即需要SkipList数据结构\n1 2 3 4 5 6 7 8 9 10 11 struct zslnode { string value; double score; zslnode*[] forwards; zslnode* backward; } struct zsl { zslnode* header; int maxLevel; map\u0026lt;string, zslnode*\u0026gt; ht; } 每次查询时，从header最高层开始，遍历找到最后一个比当前层要小的元素，之后发生降层。\n插入时，设置分到第n层的概率为$(1/2)^n$，即每一层晋升率为50%，(官方的源码中晋升率为25%，相对扁平)。\n更新时，Redis采用的策略是：先删除再插入，能够较好调整位置。\n如果score相同，Redis会将排序指标设计为Value(strcmp)，来防止性能退化至O(n)。\n6. List Pack Redis5.0更新了listpack来对ziplist进行优化\n1 2 3 4 5 6 7 8 9 10 11 struct listpack\u0026lt;T\u0026gt; { int32 total_bytes; int16 size; T[] entries; int8 end; } struct lpentry { int\u0026lt;var\u0026gt; encoding; optional byte[] content; int\u0026lt;var\u0026gt; length; } listpack采用varint进行编码，不同的长度编码可以是1-5中任意一个。解决了ziplist级联更新的行为，元素间独立，不会对后续有影响。不过由于ziplist使用过广泛，现在只有Stream采用了listpack。\n7. Rax Radix Tree类比HttpRouter中的TireTree，被用于存储消息队列，其中消息前缀即是时间戳+序号。\n1 2 3 4 5 6 7 struct raxNode { int1 isKey; int1 isNull; int1 isCompressed; int29 size; byte[] data; } rax在结构上并不是严格的RadixTree，如果中间节点有多个子节点，路由就是一个字符。如果只有一个叶子节点，那么路由键就是字符串。\n1 2 3 4 5 6 7 8 9 10 11 12 struct data { optional struct { byte[] childKey; raxNode* childNode; } child; optional string value; } struct data { byte[] childKeys; raxNode*[] childNodes; optional string value; } 如果叶子节点只有一个，就是压缩结构。反之则是存在多个路由键，一个键对应一个字符。\n","permalink":"https://chasing1020.github.io/post/redis-data-structure/","summary":"\u003ch1 id=\"redis-data-structure\"\u003eRedis Data Structure\u003c/h1\u003e\n\u003ch2 id=\"0-redis-object\"\u003e0. Redis Object\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e7\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-C\" data-lang=\"C\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003estruct\u003c/span\u003e \u003cspan class=\"n\"\u003eRedisObject\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eint4\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e// 4bits   \\ \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eint4\u003c/span\u003e \u003cspan class=\"n\"\u003eenconding\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 4bits   = 4bytes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eint24\u003c/span\u003e \u003cspan class=\"n\"\u003elru\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e// 24bits /\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eint32\u003c/span\u003e \u003cspan class=\"n\"\u003erefcount\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 4bytes\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"kt\"\u003evoid\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003eptr\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e      \u003cspan class=\"c1\"\u003e// 8bytes (64bit-system)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"n\"\u003erobj\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eRedisObject对于不通对象都是相同的，对于这样的结构，每个都需要16byte的空间。\u003c/p\u003e","title":"Redis Data Structure"},{"content":"Part6. Network 1. IO/Polling select 操作的不足之处：\n监听能力有限 — 最多只能监听 1024 个文件描述符； 内存拷贝开销大 — 需要维护一个较大的数据结构存储文件描述符，该结构需要拷贝到内核中； 时间复杂度 O(n)O(n) — 返回准备就绪的事件个数后，需要遍历所有的文件描述符； 为了提高 I/O 多路复用的性能，不同的操作系统也都实现了自己的 I/O 多路复用函数，例如：epoll、kqueue 和 evport 等。Go 语言为了提高在不同操作系统上的 I/O 操作性能，使用平台的特定的函数实现了多个版本的网络轮询模块：\n1 2 3 4 5 func netpollinit() func netpollopen(fd uintptr, pd *pollDesc) int32 func netpoll(delta int64) gList func netpollBreak() func netpollIsPollDescriptor(fd uintptr) bool runtime.netpollinit — 初始化网络轮询器，通过 sync.Once 和 netpollInited 变量保证函数只会调用一次； runtime.netpollopen — 监听文件描述符上的边缘触发事件，创建事件并加入监听； runtime.netpoll— 轮询网络并返回一组已经准备就绪的 Goroutine，传入的参数会决定它的行为 如果参数小于 0，无限期等待文件描述符就绪； 如果参数等于 0，非阻塞地轮询网络； 如果参数大于 0，阻塞特定时间轮询网络； runtime.netpollBreak — 唤醒网络轮询器，例如：计时器向前修改时间时会通过该函数中断网络轮询器4； runtime.netpollIsPollDescriptor — 判断文件描述符是否被轮询器使用； 当前\n2. Json Marshaler 序列化和反序列化的开销完全不同，JSON 反序列化的开销是序列化开销的好几倍，相信这背后的原因也非常好理解。Go 语言中的 JSON 序列化过程不需要被序列化的对象预先实现任何接口，它会通过反射获取结构体或者数组中的值并以树形的结构递归地进行编码，标准库也会根据 encoding/json.Unmarshal 中传入的值对 JSON 进行解码。\n在创建结构体时，可以添加tag来实现基本的解码方式，其中omitempty表示不存在时就丢弃，加入-代表永远丢弃该字段\n1 2 3 4 type Author struct { Name string `json:\u0026#34;name,omitempty\u0026#34;` Age int32 `json:\u0026#34;age,string,omitempty\u0026#34;` } 其中最重要的两个方法分别是\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // Marshal returns the JSON encoding of v. func Marshal(v interface{}) ([]byte, error) { e := newEncodeState() err := e.marshal(v, encOpts{escapeHTML: true}) if err != nil { return nil, err } buf := append([]byte(nil), e.Bytes()...) encodeStatePool.Put(e) return buf, nil } 与反序列化\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Unmarshal parses the JSON-encoded data and stores the result // in the value pointed to by v. If v is nil or not a pointer, // Unmarshal returns an InvalidUnmarshalError. func Unmarshal(data []byte, v interface{}) error { // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. var d decodeState err := checkValid(data, \u0026amp;d.scan) if err != nil { return err } d.init(data) return d.unmarshal(v) } 首先校验是否为Valid数据\n3. DataBase 结构化查询语言（Structured Query Language、SQL）是在关系型数据库系统中使用的领域特定语言（Domain-Specific Language、DSL），它主要用于处理结构化的数据1。作为一门领域特定语言，它有更加强大的表达能力，与传统的命令式 API 相比，它能够提供两个优点：\n可以使用单个命令在数据库中访问多条数据； 不需要在查询中指定获取数据的方法； Go 语言的database/sql 就建立在上述前提下，我们可以使用相同的 SQL 语言查询关系型数据库，所有关系型数据库的客户端都需要实现如下所示的驱动接口：\n1 2 3 4 5 6 7 8 9 type Driver interface { Open(name string) (Conn, error) } type Conn interface { Prepare(query string) (Stmt, error) Close() error Begin() (Tx, error) } database/sql/driver.Driver接口中只包含一个 Open 方法，该方法接收一个数据库连接串作为输入参数并返回一个特定数据库的连接，作为参数的数据库连接串是数据库特定的格式，这个返回的连接仍然是一个接口。 database/sql中提供的 database/sql.Register 方法可以注册自定义的数据库驱动，这个 package 的内部包含两个变量，分别是 drivers 哈希以及 driversMu 互斥锁，所有的数据库驱动都会存储在这个哈希中：\n1 2 3 4 5 6 7 8 9 10 11 func Register(name string, driver driver.Driver) { driversMu.Lock() defer driversMu.Unlock() if driver == nil { panic(\u0026#34;sql: Register driver is nil\u0026#34;) } if _, dup := drivers[name]; dup { panic(\u0026#34;sql: Register called twice for driver \u0026#34; + name) } drivers[name] = driver } MySQL 驱动会在 go-sql-driver/mysql/mysql.init中调用上述方法将实现 database/sql/driver.Driver接口的结构体注册到全局的驱动列表中：\n1 2 3 func init() { sql.Register(\u0026#34;mysql\u0026#34;, \u0026amp;MySQLDriver{}) } 当我们在全局变量中注册了驱动之后，就可以使用 database/sql.Open方法获取特定数据库的连接。在如下所示的方法中，我们通过传入的驱动名获取 database/sql/driver.Driver组成 database/sql.dsnConnector结构体后调用 database/sql.OpenDB：\n1 2 3 4 5 6 7 8 9 10 func Open(driverName, dataSourceName string) (*DB, error) { driversMu.RLock() driveri, ok := drivers[driverName] driversMu.RUnlock() if !ok { return nil, fmt.Errorf(\u0026#34;sql: unknown driver %q (forgotten import?)\u0026#34;, driverName) } ... return OpenDB(dsnConnector{dsn: dataSourceName, driver: driveri}), nil } database/sql.OpenDB 会返回一个 database/sql.DB 结构，这是标准库包为我们提供的关键结构体，无论是我们直接使用标准库查询数据库，还是使用 GORM 等 ORM 框架都会用到它：\n1 2 3 4 5 6 7 8 9 10 11 12 func OpenDB(c driver.Connector) *DB { ctx, cancel := context.WithCancel(context.Background()) db := \u0026amp;DB{ connector: c, openerCh: make(chan struct{}, connectionRequestQueueSize), lastPut: make(map[*driverConn]string), connRequests: make(map[uint64]chan connRequest), stop: cancel, } go db.connectionOpener(ctx) return db } 结构体 database/sql.DB 在刚刚初始化时不会包含任何的数据库连接，它持有的数据库连接池会在真正应用程序申请连接时在单独的 Goroutine 中获取。database/sql.DB.connectionOpener方法中包含一个不会退出的循环，每当该 Goroutine 收到了请求时都会调用 database/sql.DB.openNewConnection\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func (db *DB) openNewConnection(ctx context.Context) { ci, _ := db.connector.Connect(ctx) ... dc := \u0026amp;driverConn{ db: db, createdAt: nowFunc(), returnedAt: nowFunc(), ci: ci, } if db.putConnDBLocked(dc, err) { db.addDepLocked(dc, dc) } else { db.numOpen-- ci.Close() } } 数据库结构体 database/sql.DB 中的链接器是实现了 database/sql/driver.Connector](https://draveness.me/golang/tree/database/sql/driver.Connector) 类型的接口，我们可以使用该接口创建任意数量完全等价的连接，创建的所有连接都会被加入连接池中，MySQL 的驱动在 go-sql-driver/mysql/mysql.connector.Connect方法实现了连接数据库的逻辑。\n无论是使用 ORM 框架还是直接使用标准库，当我们在查询数据库时都会调用 database/sql.DB.Query方法，该方法的入参就是 SQL 语句和 SQL 语句中的参数，它会初始化新的上下文并调用 database/sql.DB.QueryContext\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) { var rows *Rows var err error for i := 0; i \u0026lt; maxBadConnRetries; i++ { rows, err = db.query(ctx, query, args, cachedOrNewConn) if err != driver.ErrBadConn { break } } if err == driver.ErrBadConn { return db.query(ctx, query, args, alwaysNewConn) } return rows, err } database/sql.DB.query 的执行过程可以分成两个部分，首先调用私有方法 database/sql.DB.conn 获取底层数据库的连接，数据库连接既可能是刚刚通过连接器创建的，也可能是之前缓存的连接；获取连接之后调用 database/sql.DB.queryDC在特定的数据库连接上执行查询：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func (db *DB) queryDC(ctx, txctx context.Context, dc *driverConn, releaseConn func(error), query string, args []interface{}) (*Rows, error) { queryerCtx, ok := dc.ci.(driver.QueryerContext) var queryer driver.Queryer if !ok { queryer, ok = dc.ci.(driver.Queryer) } if ok { var nvdargs []driver.NamedValue var rowsi driver.Rows var err error withLock(dc, func() { nvdargs, err = driverArgsConnLocked(dc.ci, nil, args) if err != nil { return } rowsi, err = ctxDriverQuery(ctx, queryerCtx, queryer, query, nvdargs) }) if err != driver.ErrSkip { if err != nil { releaseConn(err) return nil, err } rows := \u0026amp;Rows{ dc: dc, releaseConn: releaseConn, rowsi: rowsi, } rows.initContextClose(ctx, txctx) return rows, nil } } ... } 上述方法在准备了 SQL 查询所需的参数之后，会调用 database/sql.ctxDriverQuery完成 SQL 查询，我们会判断当前的查询上下文究竟实现了哪个接口，然后调用对应接口的 Query 或者 QueryContext：\n1 2 3 4 5 6 7 8 9 10 11 func ctxDriverQuery(ctx context.Context, queryerCtx driver.QueryerContext, queryer driver.Queryer, query string, nvdargs []driver.NamedValue) (driver.Rows, error) { if queryerCtx != nil { return queryerCtx.QueryContext(ctx, query, nvdargs) } dargs, err := namedValueToValue(nvdargs) if err != nil { return nil, err } ... return queryer.Query(query, dargs) } 对应的数据库驱动会真正负责执行调用方输入的 SQL 查询，作为中间层的标准库可以不在乎具体的实现，抹平不同关系型数据库的差异，为用户程序提供统一的接口。\n","permalink":"https://chasing1020.github.io/post/go6-network/","summary":"\u003ch1 id=\"part6-network\"\u003ePart6. Network\u003c/h1\u003e\n\u003ch2 id=\"1-iopolling\"\u003e1. IO/Polling\u003c/h2\u003e\n\u003cp\u003eselect 操作的不足之处：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e监听能力有限 — 最多只能监听 1024 个文件描述符；\u003c/li\u003e\n\u003cli\u003e内存拷贝开销大 — 需要维护一个较大的数据结构存储文件描述符，该结构需要拷贝到内核中；\u003c/li\u003e\n\u003cli\u003e时间复杂度 O(n)O(n) — 返回准备就绪的事件个数后，需要遍历所有的文件描述符；\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e为了提高 I/O 多路复用的性能，不同的操作系统也都实现了自己的 I/O 多路复用函数，例如：\u003ccode\u003eepoll\u003c/code\u003e、\u003ccode\u003ekqueue\u003c/code\u003e 和 \u003ccode\u003eevport\u003c/code\u003e 等。Go 语言为了提高在不同操作系统上的 I/O 操作性能，使用平台的特定的函数实现了多个版本的网络轮询模块：\u003c/p\u003e","title":"Go(6) Network"},{"content":"Part5. Concurrency 1. Channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G\u0026#39;s status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex } type waitq struct { first *sudog last *sudog } // sudog represents a g in a wait list, such as for sending/receiving // on a channel. // sudog is necessary because the g ↔ synchronization object relation // is many-to-many. A g can be on many wait lists, so there may be // many sudogs for one g; and many gs may be waiting on the same // synchronization object, so there may be many sudogs for one object. // 如果目标 Channel 没有被关闭并且已经有处于读等待的 Goroutine，那么 runtime.chansend 会从接收队列 recvq 中取出最先陷入等待的 Goroutine 并直接向它发送数据：\n1 2 3 4 if sg := c.recvq.dequeue(); sg != nil { send(c, sg, ep, func() { unlock(\u0026amp;c.lock) }, 3) return true } 调用 runtime.sendDirect 将发送的数据直接拷贝到 x = \u0026lt;-c 表达式中变量 x 所在的内存地址上； 调用 runtime.goready 将等待接收数据的 Goroutine 标记成可运行状态 Grunnable 并把该 Goroutine 放到发送方所在的处理器的 runnext 上等待执行，该处理器在下一次调度时会立刻唤醒数据的接收方； 1 2 3 4 5 6 7 8 9 10 func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) { if sg.elem != nil { sendDirect(c.elemtype, sg, ep) sg.elem = nil } gp := sg.g unlockf() gp.param = unsafe.Pointer(sg) goready(gp, skip+1) } 如果创建的 Channel 包含缓冲区并且 Channel 中的数据没有装满，会执行下面这段代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { ... if c.qcount \u0026lt; c.dataqsiz { qp := chanbuf(c, c.sendx) typedmemmove(c.elemtype, qp, ep) c.sendx++ if c.sendx == c.dataqsiz { c.sendx = 0 } c.qcount++ unlock(\u0026amp;c.lock) return true } ... } 在这里我们首先会使用 runtime.chanbuf 计算出下一个可以存储数据的位置，然后通过 runtime.typedmemmove将发送的数据拷贝到缓冲区中并增加 sendx 索引和 qcount 计数器。\n当 Channel 没有接收者能够处理数据时，向 Channel 发送数据会被下游阻塞，当然使用 select 关键字可以向 Channel 非阻塞地发送消息。向 Channel 阻塞地发送数据会执行下面的代码，我们可以简单梳理一下这段代码的逻辑：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { ... if !block { unlock(\u0026amp;c.lock) return false } gp := getg() mysg := acquireSudog() mysg.elem = ep mysg.g = gp mysg.c = c gp.waiting = mysg c.sendq.enqueue(mysg) goparkunlock(\u0026amp;c.lock, waitReasonChanSend, traceEvGoBlockSend, 3) gp.waiting = nil gp.param = nil mysg.c = nil releaseSudog(mysg) return true } 当我们从一个空 Channel 接收数据时会直接调用 runtime.gopark 让出处理器的使用权。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { if c == nil { if !block { return } gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) throw(\u0026#34;unreachable\u0026#34;) } lock(\u0026amp;c.lock) if c.closed != 0 \u0026amp;\u0026amp; c.qcount == 0 { unlock(\u0026amp;c.lock) if ep != nil { typedmemclr(c.elemtype, ep) } return true, false } 如果当前 Channel 已经被关闭并且缓冲区中不存在任何数据，那么会清除 ep 指针中的数据并立刻返回。\n除了上述两种特殊情况，使用 runtime.chanrecv 从 Channel 接收数据时还包含以下三种不同情况：\n当存在等待的发送者时，通过 runtime.recv 从阻塞的发送者或者缓冲区中获取数据； 当缓冲区存在数据时，从 Channel 的缓冲区中接收数据； 当缓冲区中不存在数据时，等待其他 Goroutine 向 Channel 发送数据； 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func(), skip int) { if c.dataqsiz == 0 { if ep != nil { recvDirect(c.elemtype, sg, ep) } } else { qp := chanbuf(c, c.recvx) if ep != nil { typedmemmove(c.elemtype, ep, qp) } typedmemmove(c.elemtype, qp, sg.elem) c.recvx++ c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz } gp := sg.g gp.param = unsafe.Pointer(sg) goready(gp, skip+1) } 如果 Channel 不存在缓冲区； 调用 runtime.recvDirect 将 Channel 发送队列中 Goroutine 存储的 elem 数据拷贝到目标内存地址中； 如果 Channel 存在缓冲区； 将队列中的数据拷贝到接收方的内存地址； 将发送队列头的数据拷贝到缓冲区中，释放一个阻塞的发送方； 当 Channel 的发送队列中不存在等待的 Goroutine 并且缓冲区中也不存在任何数据时，从管道中接收数据的操作会变成阻塞的，然而不是所有的接收操作都是阻塞的，与 select 语句结合使用时就可能会使用到非阻塞的接收操作：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { ... if !block { unlock(\u0026amp;c.lock) return false, false } gp := getg() mysg := acquireSudog() mysg.elem = ep gp.waiting = mysg mysg.g = gp mysg.c = c c.recvq.enqueue(mysg) goparkunlock(\u0026amp;c.lock, waitReasonChanReceive, traceEvGoBlockRecv, 3) gp.waiting = nil closed := gp.param == nil gp.param = nil releaseSudog(mysg) return true, !closed } 在正常的接收场景中，我们会使用 runtime.sudog 将当前 Goroutine 包装成一个处于等待状态的 Goroutine 并将其加入到接收队列中。\n完成入队之后，上述代码还会调用 runtime.goparkunlock 立刻触发 Goroutine 的调度，让出处理器的使用权并等待调度器的调度。\nch \u0026lt;- i 向 Channel 发送数据时遇到的几种情况：\n如果当前 Channel 的 recvq 上存在已经被阻塞的 Goroutine，那么会直接将数据发送给当前 Goroutine 并将其设置成下一个运行的 Goroutine； 如果 Channel 存在缓冲区并且其中还有空闲的容量，我们会直接将数据存储到缓冲区 sendx 所在的位置上； 如果不满足上面的两种情况，会创建一个 runtime.sudog 结构并将其加入 Channel 的 sendq 队列中，当前 Goroutine 也会陷入阻塞等待其他的协程从 Channel 接收数据； 发送数据的过程中包含几个会触发 Goroutine 调度的时机：\n发送数据时发现 Channel 上存在等待接收数据的 Goroutine，立刻设置处理器的 runnext 属性，但是并不会立刻触发调度； 发送数据时并没有找到接收方并且缓冲区已经满了，这时会将自己加入 Channel 的 sendq 队列并调用 runtime.goparkunlock 触发 Goroutine 的调度让出处理器的使用权； 从 Channel 中接收数据时可能会发生的五种情况：\n如果 Channel 为空，那么会直接调用 runtime.gopark 挂起当前 Goroutine； 如果 Channel 已经关闭并且缓冲区没有任何数据，runtime.chanrecv 会直接返回； 如果 Channel 的 sendq 队列中存在挂起的 Goroutine，会将 recvx 索引所在的数据拷贝到接收变量所在的内存空间上并将 sendq 队列中 Goroutine 的数据拷贝到缓冲区； 如果 Channel 的缓冲区中包含数据，那么直接读取 recvx 索引对应的数据； 在默认情况下会挂起当前的 Goroutine，将 runtime.sudog 结构加入 recvq 队列并陷入休眠等待调度器的唤醒； 我们总结一下从 Channel 接收数据时，会触发 Goroutine 调度的两个时机：\n当 Channel 为空时； 当缓冲区中不存在数据并且也不存在数据的发送者时； 2. Select Linux的select 是操作系统中的系统调用，我们经常会使用 select、poll 和 epoll 等函数构建 I/O 多路复用模型提升程序的性能（synchronous I/O multiplexing）。Go 语言的 select 与操作系统中的 select 比较相似，C 语言的 select 系统调用可以同时监听多个文件描述符的可读或者可写的状态，Go 语言中的 select 也能够让 Goroutine 同时等待多个 Channel 可读或者可写，在多个文件或者 Channel状态改变之前，select 会一直阻塞当前线程或者 Goroutine。\n如果是带有chan的语句，则会直接执行如下操作：\n当存在可以收发的 Channel 时，直接处理该 Channel 对应的 case； 当不存在可以收发的 Channel 时，执行 default 中的语句； 空的select会直接转换成调用 runtime.block 函数，阻塞这个Goroutine并且永远无法唤醒。\n如果当前的 select 条件只包含一个 case，那么编译器会将 select 改写成 if 条件语句。下面对比了改写前后的代码：\n1 2 3 4 5 6 7 8 9 10 11 12 // 改写前 select { case v, ok \u0026lt;-ch: // case ch \u0026lt;- v ... } // 改写后 if ch == nil { block() } v, ok := \u0026lt;-ch // case ch \u0026lt;- v ... 当 select 中仅包含两个 case，并且其中一个是 default 时，Go 语言的编译器就会认为这是一次非阻塞的收发操作。cmd/compile/internal/gc.walkselectcases 会对这种情况单独处理。不过在正式优化之前，该函数会将 case 中的所有 Channel 都转换成指向 Channel 的地址，我们会分别介绍非阻塞发送和非阻塞接收时，编译器进行的不同优化。\n编译器会使用如下的流程处理 select 语句\n将所有的 case 转换成包含 Channel 以及类型等信息的 runtime.scase 结构体；\n调用运行时函数 runtime.selectgo 从多个准备就绪的 Channel 中选择一个可执行的 runtime.scase 结构体；\n通过 for 循环生成一组 if 语句，在语句中判断自己是不是被选中的 case；\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) { cas1 := (*[1 \u0026lt;\u0026lt; 16]scase)(unsafe.Pointer(cas0)) order1 := (*[1 \u0026lt;\u0026lt; 17]uint16)(unsafe.Pointer(order0)) ncases := nsends + nrecvs scases := cas1[:ncases:ncases] pollorder := order1[:ncases:ncases] lockorder := order1[ncases:][:ncases:ncases] norder := 0 for i := range scases { cas := \u0026amp;scases[i] } for i := 1; i \u0026lt; ncases; i++ { j := fastrandn(uint32(i + 1)) pollorder[norder] = pollorder[j] pollorder[j] = uint16(i) norder++ } pollorder = pollorder[:norder] lockorder = lockorder[:norder] // 根据 Channel 的地址排序确定加锁顺序 ... sellock(scases, lockorder) ... } Go 语言会对 select 语句进行优化，它会根据 select 中 case 的不同选择不同的优化路径：\n空的 select 语句会被转换成调用 runtime.block 直接挂起当前 Goroutine； 如果 select 语句中只包含一个 case，编译器会将其转换成 if ch == nil { block }; n; 表达式； 首先判断操作的 Channel 是不是空的；然后执行 case 结构中的内容；如果 select 语句中只包含两个 case 并且其中一个是 default，那么会使用 runtime.selectnbrecv 和 runtime.selectnbsend 非阻塞地执行收发操作； 在默认情况下会通过 runtime.selectgo 获取执行 case 的索引，并通过多个 if 语句执行对应 case 中的代码； 在编译器已经对 select 语句进行优化之后，Go 语言会在运行时执行编译期间展开的 runtime.selectgo 函数，该函数会按照以下的流程执行：\n随机生成一个遍历的轮询顺序 pollOrder 并根据 Channel 地址生成锁定顺序 lockOrder； 根据 pollOrder 遍历所有的 case 查看是否有可以立刻处理的 Channel； 1.如果存在，直接获取 case 对应的索引并返回； 2.如果不存在，创建 runtime.sudog 结构体，将当前 Goroutine 加入到所有相关 Channel 的收发队列，并调用 runtime.gopark 挂起当前 Goroutine 等待调度器的唤醒； 当调度器唤醒当前 Goroutine 时，会再次按照 lockOrder 遍历所有的 case，从中查找需要被处理的 runtime.sudog 对应的索引； 3. Locker 1 2 3 4 5 // A Locker represents an object that can be locked and unlocked. type Locker interface { Lock() Unlock() } 4. Mutex 1 2 3 4 5 6 7 8 9 type Mutex struct { state int32 sema uint32 } //在默认情况下，互斥锁的所有状态位都是 0，int32 中的不同位分别表示了不同的状态： //waitersCount — 当前互斥锁上等待的 Goroutine 个数； //mutexStarving — 当前的互斥锁进入饥饿状态； //mutexWoken — 表示从正常模式被从唤醒； //mutexLocked — 表示互斥锁的锁定状态； 在正常模式下，锁的等待者会按照先进先出的顺序获取锁。但是刚被唤起的 Goroutine 与新创建的 Goroutine 竞争时，大概率会获取不到锁，为了减少这种情况的出现，一旦 Goroutine 超过 1ms 没有获取到锁，它就会将当前互斥锁切换饥饿模式，\n在cas状态不成功时，会进入如下的一个循环，判断当前 Goroutine 能否进入自旋：\n互斥锁只有在普通模式才能进入自旋； runtime.sync_runtime_canSpin 需要返回 true： 运行在多 CPU 的机器上，并且GOMAXPROCS \u0026gt; 1； 当前 Goroutine 为了获取该锁进入自旋的次数小于四次； 当前机器上至少存在一个正在运行的处理器 P 并且处理的运行队列为空 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var waitStartTime int64 starving := false awoke := false iter := 0 old := m.state for { if old\u0026amp;(mutexLocked|mutexStarving) == mutexLocked \u0026amp;\u0026amp; runtime_canSpin(iter) { if !awoke \u0026amp;\u0026amp; old\u0026amp;mutexWoken == 0 \u0026amp;\u0026amp; old\u0026gt;\u0026gt;mutexWaiterShift != 0 \u0026amp;\u0026amp; atomic.CompareAndSwapInt32(\u0026amp;m.state, old, old|mutexWoken) { awoke = true } runtime_doSpin() iter++ old = m.state continue } 在处理自旋逻辑以后，互斥锁会再次根据上下文来计算互斥锁最新的状态\n1 2 3 4 5 6 7 8 9 10 11 12 13 new := old if old\u0026amp;mutexStarving == 0 { new |= mutexLocked } if old\u0026amp;(mutexLocked|mutexStarving) != 0 { new += 1 \u0026lt;\u0026lt; mutexWaiterShift } if starving \u0026amp;\u0026amp; old\u0026amp;mutexLocked != 0 { new |= mutexStarving } if awoke { new \u0026amp;^= mutexWoken } 在互斥锁状态更新结束以后，会让CAS再次更新\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 if atomic.CompareAndSwapInt32(\u0026amp;m.state, old, new) { if old\u0026amp;(mutexLocked|mutexStarving) == 0 { break // 通过 CAS 函数获取了锁 } ... runtime_SemacquireMutex(\u0026amp;m.sema, queueLifo, 1) starving = starving || runtime_nanotime()-waitStartTime \u0026gt; starvationThresholdNs old = m.state if old\u0026amp;mutexStarving != 0 { delta := int32(mutexLocked - 1\u0026lt;\u0026lt;mutexWaiterShift) if !starving || old\u0026gt;\u0026gt;mutexWaiterShift == 1 { delta -= mutexStarving } atomic.AddInt32(\u0026amp;m.state, delta) break } awoke = true iter = 0 } else { old = m.state } 在正常模式下，这段代码会设置唤醒和饥饿标记、重置迭代次数并重新执行获取锁的循环； 在饥饿模式下，当前 Goroutine 会获得互斥锁，如果等待队列中只存在当前 Goroutine，互斥锁还会从饥饿模式中退出；\n通过自旋等待互斥锁的释放； 计算互斥锁的最新状态； 更新互斥锁的状态并获取锁； 如果该函数返回的新状态等于 0，当前 Goroutine 就成功解锁了互斥锁； 如果该函数返回的新状态不等于 0，这段代码会调用 sync.Mutex.unlockSlow 开始慢速解锁： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func (m *Mutex) unlockSlow(new int32) { if (new+mutexLocked)\u0026amp;mutexLocked == 0 { throw(\u0026#34;sync: unlock of unlocked mutex\u0026#34;) } if new\u0026amp;mutexStarving == 0 { // 正常模式 old := new for { if old\u0026gt;\u0026gt;mutexWaiterShift == 0 || old\u0026amp;(mutexLocked|mutexWoken|mutexStarving) != 0 { return } new = (old - 1\u0026lt;\u0026lt;mutexWaiterShift) | mutexWoken if atomic.CompareAndSwapInt32(\u0026amp;m.state, old, new) { runtime_Semrelease(\u0026amp;m.sema, false, 1) return } old = m.state } } else { // 饥饿模式 runtime_Semrelease(\u0026amp;m.sema, true, 1) } } 5. RWMutex 1 2 3 4 5 6 7 type RWMutex struct { w Mutex // held if there are pending writers writerSem uint32 // semaphore for writers to wait for completing readers readerSem uint32 // semaphore for readers to wait for completing writers readerCount int32 // number of pending readers readerWait int32 // number of departing readers } writerSem和readerSem分别用于写等待读和读等待写\n当资源的使用者想要获取写锁时，需要调用 sync.RWMutex.Lock 方法：\n1 2 3 4 5 6 7 func (rw *RWMutex) Lock() { rw.w.Lock() r := atomic.AddInt32(\u0026amp;rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders if r != 0 \u0026amp;\u0026amp; atomic.AddInt32(\u0026amp;rw.readerWait, r) != 0 { runtime_SemacquireMutex(\u0026amp;rw.writerSem, false, 0) } } 调用结构体持有的 sync.Mutex 结构体的 sync.Mutex.Lock 阻塞后续的写操作； 因为互斥锁已经被获取，其他 Goroutine 在获取写锁时会进入自旋或者休眠； 调用 sync/atomic.AddInt32 函数阻塞后续的读操作： 如果仍然有其他 Goroutine 持有互斥锁的读锁，该 Goroutine 会调用 runtime.sync_runtime_SemacquireMutex 进入休眠状态等待所有读锁所有者执行结束后释放 writerSem 信号量将当前协程唤醒；\n1 2 3 4 5 6 7 8 9 10 func (rw *RWMutex) Unlock() { r := atomic.AddInt32(\u0026amp;rw.readerCount, rwmutexMaxReaders) if r \u0026gt;= rwmutexMaxReaders { throw(\u0026#34;sync: Unlock of unlocked RWMutex\u0026#34;) } for i := 0; i \u0026lt; int(r); i++ { runtime_Semrelease(\u0026amp;rw.readerSem, false, 0) } rw.w.Unlock() } 调用 sync/atomic.AddInt32 函数将 readerCount 变回正数，释放读锁； 通过 for 循环释放所有因为获取读锁而陷入等待的 Goroutine： 调用 sync.Mutex.Unlock 释放写锁； 读锁的加锁方法 sync.RWMutex.RLock 很简单，该方法会通过 sync/atomic.AddInt32 将 readerCount 加一：\n1 2 3 4 5 func (rw *RWMutex) RLock() { if atomic.AddInt32(\u0026amp;rw.readerCount, 1) \u0026lt; 0 { runtime_SemacquireMutex(\u0026amp;rw.readerSem, false, 0) } } 如果该方法返回负数 — 其他 Goroutine 获得了写锁，当前 Goroutine 就会调用 runtime.sync_runtime_SemacquireMutex 陷入休眠等待锁的释放； 如果该方法的结果为非负数 — 没有 Goroutine 获得写锁，当前方法会成功返回； 当 Goroutine 想要释放读锁时，会调用如下所示的 sync.RWMutex.RUnlock 方法：\n1 2 3 4 5 func (rw *RWMutex) RUnlock() { if r := atomic.AddInt32(\u0026amp;rw.readerCount, -1); r \u0026lt; 0 { rw.rUnlockSlow(r) } } 该方法会先减少正在读资源的 readerCount 整数，根据 sync/atomic.AddInt32 的返回值不同会分别进行处理：\n如果返回值大于等于零 — 读锁直接解锁成功； 如果返回值小于零 — 有一个正在执行的写操作，在这时会调用sync.RWMutex.rUnlockSlow 方法；\n1 2 3 4 5 6 7 8 func (rw *RWMutex) rUnlockSlow(r int32) { if r+1 == 0 || r+1 == -rwmutexMaxReaders { throw(\u0026#34;sync: RUnlock of unlocked RWMutex\u0026#34;) } if atomic.AddInt32(\u0026amp;rw.readerWait, -1) == 0 { runtime_Semrelease(\u0026amp;rw.writerSem, false, 1) } } sync.RWMutex.rUnlockSlow 会减少获取锁的写操作等待的读操作数 readerWait 并在所有读操作都被释放之后触发写操作的信号量 writerSem，该信号量被触发时，调度器就会唤醒尝试获取写锁的 Goroutine。\n6. WaitGroup 1 2 3 4 5 6 7 8 9 10 type WaitGroup struct { noCopy noCopy // 64-bit value: high 32 bits are counter, low 32 bits are waiter count. // 64-bit atomic operations require 64-bit alignment, but 32-bit // compilers do not ensure it. So we allocate 12 bytes and then use // the aligned 8 bytes in them as state, and the other 4 as storage // for the sema. state1 [3]uint32 } 其中noCopy确保不会被赋值的方式拷贝，state1记录状态量，其提供了三个方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func (wg *WaitGroup) Add(delta int) { statep, semap := wg.state() state := atomic.AddUint64(statep, uint64(delta)\u0026lt;\u0026lt;32) v := int32(state \u0026gt;\u0026gt; 32) w := uint32(state) if v \u0026lt; 0 { panic(\u0026#34;sync: negative WaitGroup counter\u0026#34;) } if v \u0026gt; 0 || w == 0 { return } *statep = 0 for ; w != 0; w-- { runtime_Semrelease(semap, false, 0) } } // Done decrements the WaitGroup counter by one. func (wg *WaitGroup) Done() { wg.Add(-1) } 通过对 sync.WaitGroup 的分析和研究，我们能够得出以下结论：\nsync.WaitGroup 必须在 sync.WaitGroup.Wait 方法返回之后才能被重新使用； sync.WaitGroup.Done 只是对 sync.WaitGroup.Add 方法的简单封装，我们可以向 sync.WaitGroup.Add 方法传入任意负数（需要保证计数器非负）快速将计数器归零以唤醒等待的 Goroutine； 可以同时有多个 Goroutine 等待当前 sync.WaitGroup 计数器的归零，这些 Goroutine 会被同时唤醒； 7. Once 1 2 3 4 type Once struct { done uint32 m Mutex } 为当前 Goroutine 获取互斥锁； 执行传入的无入参函数； 运行延迟函数调用，将成员变量 done 更新成 1；\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (o *Once) Do(f func()) { if atomic.LoadUint32(\u0026amp;o.done) == 0 { o.doSlow(f) } } func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(\u0026amp;o.done, 1) f() } } 8. Context 1 2 3 4 5 6 type Context interface { Deadline() (deadline time.Time, ok bool) Done() \u0026lt;-chan struct{} Err() error Value(key interface{}) interface{} } 在 Goroutine 构成的树形结构中对信号进行同步以减少计算资源的浪费是 context.Context 的最大作用。Go 服务的每一个请求都是通过单独的 Goroutine 处理的，HTTP/RPC 请求的处理器会启动新的 Goroutine 访问数据库和其他服务。\n都会从最顶层的 Goroutine 一层一层传递到最下层。context.Context 可以在上层 Goroutine 执行出现错误时，将信号及时同步给下层。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() \u0026lt;-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } context.Background 是上下文的默认值，所有其他的上下文都应该从它衍生出来； context.TODO 应该仅在不确定应该使用哪种上下文时使用； 9. Map 1 2 3 4 5 6 7 8 9 10 type Map struct { // 加锁作用，保护 dirty 字段 mu Mutex // 只读的数据，实际数据类型为 readOnly read atomic.Value // 最新写入的数据 dirty map[interface{}]*entry // 计数器，每次需要读 dirty 则 +1 misses int } 使用read，dirty空间换时间。通过引入两个map将读写分离到不同的map，其中read map提供并发读和已存元素原子写，而dirty map则负责读写。 这样read map就可以在不加锁的情况下进行并发读取,当read map中没有读取到值时,再加锁进行后续读取,并累加未命中数。 当未命中数大于等于dirty map长度,将dirty map上升为read map。 从结构体的定义可以发现，虽然引入了两个map，但是底层数据存储的是指针，指向的是同一份值。\n其中设置ReadOnly的字段为：\n1 2 3 4 5 6 type readOnly struct { // 内建 map m map[interface{}]*entry // 表示 dirty 里存在 read 里没有的 key，通过该字段决定是否加锁读 dirty amended bool } 通过 read 和 dirty 两个字段将读写分离，读的数据存在只读字段 read 上，将最新写入的数据则存在 dirty 字段上\n读取时会先查询 read，不存在再查询 dirty，写入时则只写入 dirty\n读取 read 并不需要加锁，而读或写 dirty 都需要加锁\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func (m *Map) Load(key interface{}) (value interface{}, ok bool) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] if !ok \u0026amp;\u0026amp; read.amended { m.mu.Lock() // Avoid reporting a spurious miss if m.dirty got promoted while we were // blocked on m.mu. (If further loads of the same key will not miss, it\u0026#39;s // not worth copying the dirty map for this key.) read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok \u0026amp;\u0026amp; read.amended { e, ok = m.dirty[key] // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. m.missLocked() } m.mu.Unlock() } if !ok { return nil, false } return e.load() } func (e *entry) load() (value interface{}, ok bool) { p := atomic.LoadPointer(\u0026amp;e.p) if p == nil || p == expunged { return nil, false } return *(*interface{})(p), true } 另外有 misses 字段来统计 read 被穿透的次数（被穿透指需要读 dirty 的情况），超过一定次数则将 dirty 数据同步到 read 上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 // Store sets the value for a key. func (m *Map) Store(key, value interface{}) { read, _ := m.read.Load().(readOnly) if e, ok := read.m[key]; ok \u0026amp;\u0026amp; e.tryStore(\u0026amp;value) { return } m.mu.Lock() read, _ = m.read.Load().(readOnly) if e, ok := read.m[key]; ok { if e.unexpungeLocked() { // The entry was previously expunged, which implies that there is a // non-nil dirty map and this entry is not in it. m.dirty[key] = e } e.storeLocked(\u0026amp;value) } else if e, ok := m.dirty[key]; ok { e.storeLocked(\u0026amp;value) } else { if !read.amended { // We\u0026#39;re adding the first new key to the dirty map. // Make sure it is allocated and mark the read-only map as incomplete. m.dirtyLocked() m.read.Store(readOnly{m: read.m, amended: true}) } m.dirty[key] = newEntry(value) } m.mu.Unlock() } // tryStore stores a value if the entry has not been expunged. // // If the entry is expunged, tryStore returns false and leaves the entry // unchanged. func (e *entry) tryStore(i *interface{}) bool { for { p := atomic.LoadPointer(\u0026amp;e.p) if p == expunged { return false } if atomic.CompareAndSwapPointer(\u0026amp;e.p, p, unsafe.Pointer(i)) { return true } } } 对于删除数据则直接通过标记来延迟删除\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { read, _ := m.read.Load().(readOnly) e, ok := read.m[key] if !ok \u0026amp;\u0026amp; read.amended { m.mu.Lock() read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok \u0026amp;\u0026amp; read.amended { e, ok = m.dirty[key] delete(m.dirty, key) // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. m.missLocked() } m.mu.Unlock() } if ok { return e.delete() } return nil, false } // Delete deletes the value for a key. func (m *Map) Delete(key interface{}) { m.LoadAndDelete(key) } ","permalink":"https://chasing1020.github.io/post/go5-concurrency/","summary":"\u003ch1 id=\"part5-concurrency\"\u003ePart5. Concurrency\u003c/h1\u003e\n\u003ch2 id=\"1-channel\"\u003e1. Channel\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ehchan\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003eqcount\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e           \u003c/span\u003e\u003cspan class=\"c1\"\u003e// total data in the queue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003edataqsiz\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e           \u003c/span\u003e\u003cspan class=\"c1\"\u003e// size of the circular queue\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003ebuf\u003c/span\u003e\u003cspan class=\"w\"\u003e      \u003c/span\u003e\u003cspan class=\"nx\"\u003eunsafe\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePointer\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// points to an array of dataqsiz elements\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003eelemsize\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint16\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003eclosed\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"kt\"\u003euint32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003eelemtype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003e_type\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// element type\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003esendx\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// send index\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003erecvx\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// receive index\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003erecvq\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003ewaitq\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c1\"\u003e// list of recv waiters\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003esendq\u003c/span\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003ewaitq\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"c1\"\u003e// list of send waiters\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// lock protects all fields in hchan, as well as several\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// fields in sudogs blocked on this channel.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e   // Do not change another G\u0026#39;s status while holding this lock\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// (in particular, do not ready a G), as this can deadlock\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"c1\"\u003e// with stack shrinking.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e   \u003c/span\u003e\u003cspan class=\"nx\"\u003elock\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003emutex\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ewaitq\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003efirst\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003esudog\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003elast\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003esudog\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// sudog represents a g in a wait list, such as for sending/receiving\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// on a channel.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// sudog is necessary because the g ↔ synchronization object relation\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// is many-to-many. A g can be on many wait lists, so there may be\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// many sudogs for one g; and many gs may be waiting on the same\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// synchronization object, so there may be many sudogs for one object.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e如果目标 Channel 没有被关闭并且已经有处于读等待的 Goroutine，那么 \u003ca href=\"https://draveness.me/golang/tree/runtime.chansend\"\u003e\u003ccode\u003eruntime.chansend\u003c/code\u003e\u003c/a\u003e 会从接收队列 \u003ccode\u003erecvq\u003c/code\u003e 中取出最先陷入等待的 Goroutine 并直接向它发送数据：\u003c/p\u003e","title":"Go(5) Concurrency"},{"content":"Part4. Runtime 1. GMP 1.1. Implement 1.1.1. Data Structure 创建、销毁、调度 G 都需要每个 M 获取锁，这就形成了激烈的锁竞争。 M 转移 G 会造成延迟和额外的系统负载。比如当 G 中包含创建新协程的时候，M 创建了 G’，为了继续执行 G，需要把 G’交给 M’执行，也造成了很差的局部性，因为 G’和 G 是相关的，最好放在 M 上执行，而不是其他 M’。 系统调用 (CPU 在 M 之间的切换) 导致频繁的线程阻塞和取消阻塞操作增加了系统开销。\n全局队列（Global Queue）：存放等待运行的 G。 P 的本地队列：不超过 256 个。新建 G’时，G’优先加入到 P 的本地队列，如果队列满了，则会把本地队列中一半的 G 移动到全局队列。 P 列表：所有的 P 都在程序启动时创建，并保存在数组中，最多有 GOMAXPROCS(可配置) 个。 M：线程想运行任务就得获取 P，从 P 的本地队列获取 G，P 队列为空时，M 也会尝试从全局队列拿一批 G 放到 P 的本地队列，或从其他 P 的本地队列偷一半放到自己 P 的本地队列。M 运行 G，G 执行之后，M 会从 P 获取下一个 G，不断重复下去。\n1.1.1.1. G G：作为最小的运行的调度单元\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 // Stack describes a Go execution stack. // The bounds of the stack are exactly [lo, hi), // with no implicit data structures on either side. type stack struct { lo uintptr hi uintptr } type g struct { // Stack parameters. // stack describes the actual stack memory: [stack.lo, stack.hi). // stackguard0 is the stack pointer compared in the Go stack growth prologue. // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption. // stackguard1 is the stack pointer compared in the C stack growth prologue. // It is stack.lo+StackGuard on g0 and gsignal stacks. // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash). stack stack // offset known to runtime/cgo stackguard0 uintptr // offset known to liblink stackguard1 uintptr // offset known to liblink _panic *_panic // innermost panic - offset known to liblink _defer *_defer // innermost defer m *m // current m; offset known to arm liblink sched gobuf syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc stktopsp uintptr // expected sp at top of stack, to check in traceback param unsafe.Pointer // passed parameter on wakeup atomicstatus uint32 stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus goid int64 schedlink guintptr waitsince int64 // approx time when the g become blocked waitreason waitReason // if status==Gwaiting preempt bool // preemption signal, duplicates stackguard0 = stackpreempt preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule preemptShrink bool // shrink stack at synchronous safe point // asyncSafePoint is set if g is stopped at an asynchronous // safe point. This means there are frames on the stack // without precise pointer information. asyncSafePoint bool paniconfault bool // panic (instead of crash) on unexpected fault address gcscandone bool // g has scanned stack; protected by _Gscan bit in status throwsplit bool // must not split stack // activeStackChans indicates that there are unlocked channels // pointing into this goroutine\u0026#39;s stack. If true, stack // copying needs to acquire channel locks to protect these // areas of the stack. activeStackChans bool // parkingOnChan indicates that the goroutine is about to // park on a chansend or chanrecv. Used to signal an unsafe point // for stack shrinking. It\u0026#39;s a boolean value, but is updated atomically. parkingOnChan uint8 raceignore int8 // ignore race detection events sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine sysexitticks int64 // cputicks when syscall has returned (for tracing) traceseq uint64 // trace event sequencer tracelastp puintptr // last P emitted an event for this goroutine lockedm muintptr sig uint32 writebuf []byte sigcode0 uintptr sigcode1 uintptr sigpc uintptr gopc uintptr // pc of go statement that created this goroutine ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors) startpc uintptr // pc of goroutine function racectx uintptr waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order cgoCtxt []uintptr // cgo traceback context labels unsafe.Pointer // profiler labels timer *timer // cached timer for time.Sleep selectDone uint32 // are we participating in a select and did someone win the race? // Per-G GC state // gcAssistBytes is this G\u0026#39;s GC assist credit in terms of // bytes allocated. If this is positive, then the G has credit // to allocate gcAssistBytes bytes without assisting. If this // is negative, then the G must correct this by performing // scan work. We track this in bytes to make it fast to update // and check for debt in the malloc hot path. The assist ratio // determines how this corresponds to scan work debt. gcAssistBytes int64 } 可以看到，g的stack保存了当前的栈范围，分别存储 defer 和 panic 对应结构体的链表， m — 当前 Goroutine 占用的线程，可能为空； atomicstatus — Goroutine 的状态； sched — 存储 Goroutine 的调度相关的数据； goid — Goroutine 的 ID，该字段对开发者不可见，Go 团队认为引入 ID 会让部分 Goroutine 变得更特殊，从而限制语言的并发能力。\n其中g还内嵌了一个结构体gobuf，用于调度器保存以及恢复上下文中用到\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 type gobuf struct { // The offsets of sp, pc, and g are known to (hard-coded in) libmach. // // ctxt is unusual with respect to GC: it may be a // heap-allocated funcval, so GC needs to track it, but it // needs to be set and cleared from assembly, where it\u0026#39;s // difficult to have write barriers. However, ctxt is really a // saved, live register, and we only ever exchange it between // the real register and the gobuf. Hence, we treat it as a // root during stack scanning, which means assembly that saves // and restores it doesn\u0026#39;t need write barriers. It\u0026#39;s still // typed as a pointer so that any other writes from Go get // write barriers. sp uintptr pc uintptr g guintptr // 持有该gobuf的goroutine ctxt unsafe.Pointer ret uintptr // 系统调用的返回值 lr uintptr bp uintptr // for framepointer-enabled architectures } 此外atomicstatus还记录了goroutine当前的状态\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 const ( // G status // // Beyond indicating the general state of a G, the G status // acts like a lock on the goroutine\u0026#39;s stack (and hence its // ability to execute user code). // _Gidle means this goroutine was just allocated and has not yet been initialized. _Gidle = iota // 0 // _Grunnable means this goroutine is on a run queue. It isnot currently executing user code. The stack is not owned. _Grunnable // 1 // _Grunning means this goroutine may execute user code. The stack is owned by this goroutine. It is not on a run queue. It is assigned an M and a P (g.m and g.m.p are valid). _Grunning // 2 // _Gsyscall means this goroutine is executing a system call. It is not executing user code. The stack is owned by this goroutine. It is not on a run queue. It is assigned an M. _Gsyscall // 3 // _Gwaiting means this goroutine is blocked in the runtime. // It is not executing user code. It is not on a run queue, // but should be recorded somewhere (e.g., a channel wait // queue) so it can be ready()d when necessary. The stack is // not owned *except* that a channel operation may read or // write parts of the stack under the appropriate channel // lock. Otherwise, it is not safe to access the stack after a // goroutine enters _Gwaiting (e.g., it may get moved). _Gwaiting // 4 // _Gmoribund_unused is currently unused, but hardcoded in gdb // scripts. _Gmoribund_unused // 5 // _Gdead means this goroutine is currently unused. It may be // just exited, on a free list, or just being initialized. It // is not executing user code. It may or may not have a stack // allocated. The G and its stack (if any) are owned by the M // that is exiting the G or that obtained the G from the free // list. _Gdead // 6 // _Genqueue_unused is currently unused. _Genqueue_unused // 7 // _Gcopystack means this goroutine\u0026#39;s stack is being moved. It // is not executing user code and is not on a run queue. The // stack is owned by the goroutine that put it in _Gcopystack. _Gcopystack // 8 // _Gpreempted means this goroutine stopped itself for a // suspendG preemption. It is like _Gwaiting, but nothing is // yet responsible for ready()ing it. Some suspendG must CAS // the status to _Gwaiting to take responsibility for // ready()ing this G. _Gpreempted // 9 // _Gscan combined with one of the above states other than // _Grunning indicates that GC is scanning the stack. The // goroutine is not executing user code and the stack is owned // by the goroutine that set the _Gscan bit. // // _Gscanrunning is different: it is used to briefly block // state transitions while GC signals the G to scan its own // stack. This is otherwise like _Grunning. // // atomicstatus\u0026amp;~Gscan gives the state the goroutine will // return to when the scan completes. _Gscan = 0x1000 _Gscanrunnable = _Gscan + _Grunnable // 0x1001 _Gscanrunning = _Gscan + _Grunning // 0x1002 _Gscansyscall = _Gscan + _Gsyscall // 0x1003 _Gscanwaiting = _Gscan + _Gwaiting // 0x1004 _Gscanpreempted = _Gscan + _Gpreempted // 0x1009 ) 主要状态如下：\n状态 描述 _Gidle 刚刚被分配并且还没有被初始化，初始化结束后变为_Gdead _Grunnable 没有执行代码，没有栈的所有权，存储在运行队列中 _Grunning 可以执行代码，拥有栈的所有权，被赋予了内核线程 M 和处理器 P _Gsyscall 正在执行系统调用，拥有栈的所有权，没有执行用户代码，被赋予了内核线程 M 但是不在运行队列上 _Gwaiting 由于运行时而被阻塞，没有执行用户代码并且不在运行队列上，但是可能存在于 Channel 的等待队列上 _Gdead 可以是被销毁的，也可以是刚创建的。没有被使用，没有执行代码，可能有分配的栈 _Gcopystack 栈正在被拷贝，没有执行代码，不在运行队列上 _Gpreempted 由于抢占而被阻塞，没有执行用户代码并且不在运行队列上，等待唤醒 _Gscan GC 正在扫描栈空间，没有执行代码，可以与其他状态同时存在 其中g0作为特殊的调度协程：执行函数的流程相对固定，切为了避免栈溢出，g0的栈会复用\n1.1.1.2. M 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 type m struct { g0 *g // goroutine with scheduling stack morebuf gobuf // gobuf arg to morestack divmod uint32 // div/mod denominator for arm - known to liblink // Fields not known to debuggers. procid uint64 // for debuggers, but offset not hard-coded gsignal *g // signal-handling g goSigStack gsignalStack // Go-allocated signal handling stack sigmask sigset // storage for saved signal mask tls [tlsSlots]uintptr // thread-local storage (for x86 extern register) mstartfn func() curg *g // current running goroutine caughtsig guintptr // goroutine running during fatal signal p puintptr // attached p for executing go code (nil if not executing go code) nextp puintptr oldp puintptr // the p that was attached before executing a syscall id int64 mallocing int32 throwing int32 preemptoff string // if != \u0026#34;\u0026#34;, keep curg running on this m locks int32 dying int32 profilehz int32 spinning bool // m is out of work and is actively looking for work blocked bool // m is blocked on a note newSigstack bool // minit on C thread called sigaltstack printlock int8 incgo bool // m is executing a cgo call freeWait uint32 // if == 0, safe to free g0 and delete m (atomic) fastrand [2]uint32 needextram bool traceback uint8 ncgocall uint64 // number of cgo calls in total ncgo int32 // number of cgo calls currently in progress cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily cgoCallers *cgoCallers // cgo traceback if crashing in cgo call doesPark bool // non-P running threads: sysmon and newmHandoff never use .park park note alllink *m // on allm schedlink muintptr lockedg guintptr createstack [32]uintptr // stack that created this thread. lockedExt uint32 // tracking for external LockOSThread lockedInt uint32 // tracking for internal lockOSThread nextwaitm muintptr // next m waiting for lock waitunlockf func(*g, unsafe.Pointer) bool waitlock unsafe.Pointer waittraceev byte waittraceskip int startingtrace bool syscalltick uint32 freelink *m // on sched.freem // mFixup is used to synchronize OS related m state // (credentials etc) use mutex to access. To avoid deadlocks // an atomic.Load() of used being zero in mDoFixupFn() // guarantees fn is nil. mFixup struct { lock mutex used uint32 fn func(bool) bool } // these are here because they are too large to be on the stack // of low-level NOSPLIT functions. libcall libcall libcallpc uintptr // for cpu profiler libcallsp uintptr libcallg guintptr syscall libcall // stores syscall parameters on windows vdsoSP uintptr // SP for traceback while in VDSO call (0 if not in call) vdsoPC uintptr // PC for traceback while in VDSO call // preemptGen counts the number of completed preemption // signals. This is used to detect when a preemption is // requested, but fails. Accessed atomically. preemptGen uint32 // Whether this is a pending preemption signal on this M. // Accessed atomically. signalPending uint32 dlogPerM mOS // Up to 10 locks held by this m, maintained by the lock ranking code. locksHeldLen int locksHeld [10]heldLockInfo } 其中 g0 是持有调度栈的 Goroutine，curg 是在当前线程上运行的用户 Goroutine，这也是操作系统线程唯一关心的两个 Goroutine。\n1.1.1.3. P 调度器中的处理器 P 是线程和 Goroutine 的中间层，它能提供线程需要的上下文环境，也会负责调度线程上的等待队列，通过处理器 P 的调度，每一个内核线程都能够执行多个 Goroutine，它能在 Goroutine 进行一些 I/O 操作时及时让出计算资源，提高线程的利用率。\n因为调度器在启动时就会创建 GOMAXPROCS 个处理器，所以 Go 语言程序的处理器数量一定会等于 GOMAXPROCS，这些处理器会绑定到不同的内核线程上。\nruntime.p 是处理器的运行时表示，作为调度器的内部实现，它包含的字段也非常多，其中包括与性能追踪、垃圾回收和计时器相关的字段，这些字段也非常重要，但是在这里就不展示了，我们主要关注处理器中的线程和运行队列：\nP：作为中间层，\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 type p struct { id int32 status uint32 // one of pidle/prunning/... link puintptr schedtick uint32 // incremented on every scheduler call syscalltick uint32 // incremented on every system call sysmontick sysmontick // last tick observed by sysmon m muintptr // back-link to associated m (nil if idle) mcache *mcache pcache pageCache raceprocctx uintptr deferpool [5][]*_defer // pool of available defer structs of different sizes (see panic.go) deferpoolbuf [5][32]*_defer // Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen. goidcache uint64 goidcacheend uint64 // Queue of runnable goroutines. Accessed without lock. runqhead uint32 runqtail uint32 runq [256]guintptr // runnext, if non-nil, is a runnable G that was ready\u0026#39;d by // the current G and should be run next instead of what\u0026#39;s in // runq if there\u0026#39;s time remaining in the running G\u0026#39;s time // slice. It will inherit the time left in the current time // slice. If a set of goroutines is locked in a // communicate-and-wait pattern, this schedules that set as a // unit and eliminates the (potentially large) scheduling // latency that otherwise arises from adding the ready\u0026#39;d // goroutines to the end of the run queue. // // Note that while other P\u0026#39;s may atomically CAS this to zero, // only the owner P can CAS it to a valid G. runnext guintptr // Available G\u0026#39;s (status == Gdead) gFree struct { gList n int32 } sudogcache []*sudog sudogbuf [128]*sudog // Cache of mspan objects from the heap. mspancache struct { // We need an explicit length here because this field is used // in allocation codepaths where write barriers are not allowed, // and eliminating the write barrier/keeping it eliminated from // slice updates is tricky, moreso than just managing the length // ourselves. len int buf [128]*mspan } tracebuf traceBufPtr // traceSweep indicates the sweep events should be traced. // This is used to defer the sweep start event until a span // has actually been swept. traceSweep bool // traceSwept and traceReclaimed track the number of bytes // swept and reclaimed by sweeping in the current sweep loop. traceSwept, traceReclaimed uintptr palloc persistentAlloc // per-P to avoid mutex _ uint32 // Alignment for atomic fields below // The when field of the first entry on the timer heap. // This is updated using atomic functions. // This is 0 if the timer heap is empty. timer0When uint64 // The earliest known nextwhen field of a timer with // timerModifiedEarlier status. Because the timer may have been // modified again, there need not be any timer with this value. // This is updated using atomic functions. // This is 0 if there are no timerModifiedEarlier timers. timerModifiedEarliest uint64 // Per-P GC state gcAssistTime int64 // Nanoseconds in assistAlloc gcFractionalMarkTime int64 // Nanoseconds in fractional mark worker (atomic) // gcMarkWorkerMode is the mode for the next mark worker to run in. // That is, this is used to communicate with the worker goroutine // selected for immediate execution by // gcController.findRunnableGCWorker. When scheduling other goroutines, // this field must be set to gcMarkWorkerNotWorker. gcMarkWorkerMode gcMarkWorkerMode // gcMarkWorkerStartTime is the nanotime() at which the most recent // mark worker started. gcMarkWorkerStartTime int64 // gcw is this P\u0026#39;s GC work buffer cache. The work buffer is // filled by write barriers, drained by mutator assists, and // disposed on certain GC state transitions. gcw gcWork // wbBuf is this P\u0026#39;s GC write barrier buffer. // // TODO: Consider caching this in the running G. wbBuf wbBuf runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point // statsSeq is a counter indicating whether this P is currently // writing any stats. Its value is even when not, odd when it is. statsSeq uint32 // Lock for timers. We normally access the timers while running // on this P, but the scheduler can also do it from a different P. timersLock mutex // Actions to take at some time. This is used to implement the // standard library\u0026#39;s time package. // Must hold timersLock to access. timers []*timer // Number of timers in P\u0026#39;s heap. // Modified using atomic instructions. numTimers uint32 // Number of timerDeleted timers in P\u0026#39;s heap. // Modified using atomic instructions. deletedTimers uint32 // Race context used while executing timer functions. timerRaceCtx uintptr // preempt is set to indicate that this P should be enter the // scheduler ASAP (regardless of what G is running on it). preempt bool // Padding is no longer needed. False sharing is now not a worry because p is large enough // that its size class is an integer multiple of the cache line size (for any of our architectures). } 其中 P 也定义了状态字段\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 const ( // P status // _Pidle means a P is not being used to run user code or the // scheduler. Typically, it\u0026#39;s on the idle P list and available // to the scheduler, but it may just be transitioning between // other states. // // The P is owned by the idle list or by whatever is // transitioning its state. Its run queue is empty. _Pidle = iota // _Prunning means a P is owned by an M and is being used to // run user code or the scheduler. Only the M that owns this P // is allowed to change the P\u0026#39;s status from _Prunning. The M // may transition the P to _Pidle (if it has no more work to // do), _Psyscall (when entering a syscall), or _Pgcstop (to // halt for the GC). The M may also hand ownership of the P // off directly to another M (e.g., to schedule a locked G). _Prunning // _Psyscall means a P is not running user code. It has // affinity to an M in a syscall but is not owned by it and // may be stolen by another M. This is similar to _Pidle but // uses lightweight transitions and maintains M affinity. // // Leaving _Psyscall must be done with a CAS, either to steal // or retake the P. Note that there\u0026#39;s an ABA hazard: even if // an M successfully CASes its original P back to _Prunning // after a syscall, it must understand the P may have been // used by another M in the interim. _Psyscall // _Pgcstop means a P is halted for STW and owned by the M // that stopped the world. The M that stopped the world // continues to use its P, even in _Pgcstop. Transitioning // from _Prunning to _Pgcstop causes an M to release its P and // park. // // The P retains its run queue and startTheWorld will restart // the scheduler on Ps with non-empty run queues. _Pgcstop // _Pdead means a P is no longer used (GOMAXPROCS shrank). We // reuse Ps if GOMAXPROCS increases. A dead P is mostly // stripped of its resources, though a few things remain // (e.g., trace buffers). _Pdead ) 状态 描述 _Pidle 处理器没有运行用户代码或者调度器，被空闲队列或者改变其状态的结构持有，运行队列为空 _Prunning 被线程 M 持有，并且正在执行用户代码或者调度器 _Psyscall 没有执行用户代码，当前线程陷入系统调用 _Pgcstop 被线程 M 持有，当前处理器由于垃圾回收被停止 _Pdead 当前处理器已经不被使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct P { Lock; uint32 status; P* link; uint32 tick; M* m; MCache* mcache; G** runq; // goroutine组成的环形数组 int32 runqhead; int32 runqtail; int32 runqsize; G* gfree; int32 gfreecnt; }; G的状态 空闲中(_Gidle): 表示G刚刚新建, 仍未初始化 待运行(_Grunnable): 表示G在运行队列中, 等待M取出并运行 运行中(_Grunning): 表示M正在运行这个G, 这时候M会拥有一个P 系统调用中(_Gsyscall): 表示M正在运行这个G发起的系统调用, 这时候M并不拥有P 等待中(_Gwaiting): 表示G在等待某些条件完成, 这时候G不在运行也不在运行队列中(可能在channel的等待队列中) 已中止(_Gdead): 表示G未被使用, 可能已执行完毕(并在freelist中等待下次复用) 栈复制中(_Gcopystack): 表示G正在获取一个新的栈空间并把原来的内容复制过去(用于防止GC扫描) M的状态 M并没有像G和P一样的状态标记, 但可以认为一个M有以下的状态:\n自旋中(spinning): M正在从运行队列获取G, 这时候M会拥有一个P 执行go代码中: M正在执行go代码, 这时候M会拥有一个P 执行原生代码中: M正在执行原生代码或者阻塞的syscall, 这时M并不拥有P 休眠中: M发现无待运行的G时会进入休眠, 并添加到空闲M链表中, 这时M并不拥有P 自旋中(spinning)这个状态非常重要, 是否需要唤醒或者创建新的M取决于当前自旋中的M的数量.\nP的状态 空闲中(_Pidle): 当M发现无待运行的G时会进入休眠, 这时M拥有的P会变为空闲并加到空闲P链表中 运行中(_Prunning): 当M拥有了一个P后, 这个P的状态就会变为运行中, M运行G会使用这个P中的资源 系统调用中(_Psyscall): 当go调用原生代码, 原生代码又反过来调用go代码时, 使用的P会变为此状态 GC停止中(_Pgcstop): 当gc停止了整个世界(STW)时, P会变为此状态 已中止(_Pdead): 当P的数量在运行时改变, 且数量减少时多余的P会变为此状态 P的数量：设置由启动时环境变量 $GOMAXPROCS 或者是由 runtime 的方法 GOMAXPROCS() 决定。这意味着在程序执行的任意时刻都只有 $GOMAXPROCS 个 goroutine 在同时运行。\nM 的数量：go 语言本身的限制：go 程序启动时，会设置 M 的最大数量，默认 10000. 但是内核很难支持这么多的线程数，所以这个限制可以忽略。runtime/debug 中的 SetMaxThreads 函数，设置 M 的最大数量。一个 M 阻塞了，会创建新的 M。\n在go中有多个运行队列可以保存待运行(_Grunnable)的G, 它们分别是各个P中的本地运行队列和全局运行队列. 入队待运行的G时会优先加到当前P的本地运行队列, M获取待运行的G时也会优先从拥有的P的本地运行队列获取, 本地运行队列入队和出队不需要使用线程锁.\n本地运行队列有数量限制, 当数量达到256个时会入队到全局运行队列. 本地运行队列的数据结构是环形队列, 由一个256长度的数组和两个序号(head, tail)组成.\n当M从P的本地运行队列获取G时, 如果发现本地队列为空会尝试从其他P盗取一半的G过来, 这个机制叫做Work Stealing, 详见后面的代码分析.\n全局运行队列保存在全局变量sched中, 全局运行队列入队和出队需要使用线程锁. 全局运行队列的数据结构是链表, 由两个指针(head, tail)组成.\n1.1.2. Start 在调度器初始函数执行的过程中会将 maxmcount 设置成 10000，这也就是一个 Go 语言程序能够创建的最大线程数，虽然最多可以创建 10000 个线程，但是可以同时运行的线程还是由 GOMAXPROCS 变量控制。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func schedinit() { _g_ := getg() ... sched.maxmcount = 10000 ... sched.lastpoll = uint64(nanotime()) procs := ncpu if n, ok := atoi32(gogetenv(\u0026#34;GOMAXPROCS\u0026#34;)); ok \u0026amp;\u0026amp; n \u0026gt; 0 { procs = n } if procresize(procs) != nil { throw(\u0026#34;unknown runnable goroutine during bootstrap\u0026#34;) } } 当procresize启动后，开始等待用户创建运行新的 Goroutine 并为 Goroutine 调度处理器资源\n1.2. Create Goroutine 当调用go func()时，编译器调用call方法，然后再转换成newproc的调用\n1 2 3 4 5 6 7 8 9 10 11 12 func (s *state) call(n *Node, k callKind) *ssa.Value { if k == callDeferStack { ... } else { switch { case k == callGo: call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, newproc, s.mem()) default: } } ... } runtime.newproc的入参是参数大小和表示函数的指针 funcval，它会获取 Goroutine 以及调用方的程序计数器，然后调用 runtime.newproc1函数获取新的 Goroutine 结构体、将其加入处理器的运行队列并在满足条件时调用 runtime.wakep 唤醒新的处理执行 Goroutine：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(\u0026amp;fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) _p_ := getg().m.p.ptr() runqput(_p_, newg, true) if mainStarted { wakep() } }) } 其中newproc1调用如下，其调用可以分为如下三步：\n获取或者创建新的 Goroutine 结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *g { _g_ := getg() siz := narg siz = (siz + 7) \u0026amp;^ 7 _p_ := _g_.m.p.ptr() newg := gfget(_p_) // 获取新的g if newg == nil { newg = malg(_StackMin) casgstatus(newg, _Gidle, _Gdead) allgadd(newg) } //... 把参数放入栈区：\n接下来，我们会调用 runtime.memmove 将 fn 函数的所有参数拷贝到栈上，argp 和 narg 分别是参数的内存空间和大小，我们在该方法中会将参数对应的内存空间整块拷贝到栈上\n1 2 3 4 5 6 7 8 9 //... totalSize := 4*sys.RegSize + uintptr(siz) + sys.MinFrameSize totalSize += -totalSize \u0026amp; (sys.SpAlign - 1) sp := newg.stack.hi - totalSize spArg := sp if narg \u0026gt; 0 { memmove(unsafe.Pointer(spArg), argp, uintptr(narg)) } //... 更新 Goroutine 调度相关的属性； 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //... memclrNoHeapPointers(unsafe.Pointer(\u0026amp;newg.sched), unsafe.Sizeof(newg.sched)) newg.sched.sp = sp newg.stktopsp = sp newg.sched.pc = funcPC(goexit) + sys.PCQuantum newg.sched.g = guintptr(unsafe.Pointer(newg)) gostartcallfn(\u0026amp;newg.sched, fn) newg.gopc = callerpc newg.startpc = fn.fn casgstatus(newg, _Gdead, _Grunnable) newg.goid = int64(_p_.goidcache) _p_.goidcache++ return newg } 其中gfget方法如下：\n从 Goroutine 所在处理器的 gFree 列表或者调度器的 sched.gFree 列表中获取 runtime.g； 1 2 3 4 5 6 7 8 9 10 // Available G\u0026#39;s (status == Gdead) gFree struct { gList n int32 } // A gList is a list of Gs linked through g.schedlink. A G can only be // on one gQueue or gList at a time. type gList struct { head guintptr } 调用 runtime.malg生成一个新的 runtime.g 并将结构体追加到全局的 Goroutine 列表 allgs 中。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func gfget(_p_ *p) *g { retry: if _p_.gFree.empty() \u0026amp;\u0026amp; (!sched.gFree.stack.empty() || !sched.gFree.noStack.empty()) { for _p_.gFree.n \u0026lt; 32 { gp := sched.gFree.stack.pop() if gp == nil { gp = sched.gFree.noStack.pop() if gp == nil { break } } _p_.gFree.push(gp) } goto retry } gp := _p_.gFree.pop() if gp == nil { return nil } return gp } 当处理器的 Goroutine 列表为空时，会将调度器持有的空闲 Goroutine 转移到当前处理器上，直到 gFree 列表中的 Goroutine 数量达到 32； 当处理器的 Goroutine 数量充足时，会从列表头部返回一个新的 Goroutine； 当调度器的 gFree 和处理器的 gFree 列表都不存在结构体时，运行时会调用 runtime.malg初始化新的 runtime.g结构，如果申请的堆栈大小大于 0，这里会通过 runtime.stackalloc 分配 2KB 的栈空间：\n1 2 3 4 5 6 7 8 9 10 func malg(stacksize int32) *g { newg := new(g) if stacksize \u0026gt;= 0 { stacksize = round2(_StackSystem + stacksize) newg.stack = stackalloc(uint32(stacksize)) newg.stackguard0 = newg.stack.lo + _StackGuard newg.stackguard1 = ^uintptr(0) } return newg } 之后会调用runtime.runqput会将 Goroutine 放到运行队列上，这既可能是全局的运行队列，也可能是处理器本地的运行队列：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func runqput(_p_ *p, gp *g, next bool) { if next { retryNext: oldnext := _p_.runnext if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) { goto retryNext } if oldnext == 0 { return } gp = oldnext.ptr() } retry: h := atomic.LoadAcq(\u0026amp;_p_.runqhead) t := _p_.runqtail if t-h \u0026lt; uint32(len(_p_.runq)) { _p_.runq[t%uint32(len(_p_.runq))].set(gp) atomic.StoreRel(\u0026amp;_p_.runqtail, t+1) return } if runqputslow(_p_, gp, h, t) { return } goto retry } 当 next 为 true 时，将 Goroutine 设置到处理器的 runnext 作为下一个处理器执行的任务； 当 next 为 false 并且本地运行队列还有剩余空间时，将 Goroutine 加入处理器持有的本地运行队列； 当处理器的本地运行队列已经没有剩余空间时就会把本地队列中的一部分 Goroutine 和待加入的 Goroutine 通过 runtime.runqputslow添加到调度器持有的全局运行队列上； 这里取全局队列的Goroutine数目刚好为n = min(len(GQ)/GOMAXPROCS + 1, len(GQ/2))\n1.3. Schedule 1.3.1. M0, G0 go语言的启动流程简单示意见下注释\n1 2 3 4 5 6 7 8 // The bootstrap sequence is: // // call osinit // call schedinit // make \u0026amp; queue new G // call runtime·mstart // // The new G calls runtime·main. 此外，runtime/proc下也定义了初始状态\n1 2 3 4 5 6 var ( m0 m g0 g mcache0 *mcache raceprocctx0 uintptr ) 程序开始时，会先执行schedinit创建第一个G，mstart函数创建第一个M\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 func newm1(mp *m) { if iscgo { var ts cgothreadstart if _cgo_thread_start == nil { throw(\u0026#34;_cgo_thread_start missing\u0026#34;) } ts.g.set(mp.g0) ts.tls = (*uint64)(unsafe.Pointer(\u0026amp;mp.tls[0])) ts.fn = unsafe.Pointer(funcPC(mstart)) if msanenabled { msanwrite(unsafe.Pointer(\u0026amp;ts), unsafe.Sizeof(ts)) } execLock.rlock() // Prevent process clone. asmcgocall(_cgo_thread_start, unsafe.Pointer(\u0026amp;ts)) execLock.runlock() return } execLock.rlock() // Prevent process clone. newosproc(mp) execLock.runlock() } func mstart() { _g_ := getg() // 这里创建的是g0，分配在系统堆栈 osStack := _g_.stack.lo == 0 if osStack { // Initialize stack bounds from system stack. size := _g_.stack.hi if size == 0 { size = 8192 * sys.StackGuardMultiplier } _g_.stack.hi = uintptr(noescape(unsafe.Pointer(\u0026amp;size))) _g_.stack.lo = _g_.stack.hi - size + 1024 } // Initialize stack guard so that we can start calling regular // Go code. _g_.stackguard0 = _g_.stack.lo + _StackGuard // This is the g0, so we can also call go:systemstack // functions, which check stackguard1. _g_.stackguard1 = _g_.stackguard0 mstart1() // Exit this thread. if mStackIsSystemAllocated() { osStack = true } mexit(osStack) } 以上函数函数会调用mstart1来将g0绑定到m0上，并开始执行schedule调度\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 func mstart1() { _g_ := getg() if _g_ != _g_.m.g0 { throw(\u0026#34;bad runtime·mstart\u0026#34;) } // Record the caller for use as the top of stack in mcall and // for terminating the thread. // We\u0026#39;re never coming back to mstart1 after we call schedule, // so other calls can reuse the current frame. save(getcallerpc(), getcallersp()) asminit() minit() // 将M进行初始化，设置线程的备用信号堆栈和信号掩码 // Install signal handlers; after minit so that minit can // prepare the thread to be able to handle the signals. if _g_.m == \u0026amp;m0 { mstartm0() } if fn := _g_.m.mstartfn; fn != nil { fn() } if _g_.m != \u0026amp;m0 { acquirep(_g_.m.nextp.ptr()) _g_.m.nextp = 0 } schedule() } // mstartm0 implements part of mstart1 that only runs on the m0. // // Write barriers are allowed here because we know the GC can\u0026#39;t be // running yet, so they\u0026#39;ll be no-ops. // //go:yeswritebarrierrec func mstartm0() { // Create an extra M for callbacks on threads not created by Go. // An extra M is also needed on Windows for callbacks created by // syscall.NewCallback. See issue #6751 for details. if (iscgo || GOOS == \u0026#34;windows\u0026#34;) \u0026amp;\u0026amp; !cgoHasExtraM { cgoHasExtraM = true newextram() } initsig(false) } mstart1() 在结束阶段，会调用schedule，而schedule在本地队列中第一次会找到main goroutine，而第一个goroutine绑定的方法就是main，而main方法完成以下操作：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 // The main goroutine. func main() { g := getg() // Racectx of m0-\u0026gt;g0 is used only as the parent of the main goroutine. // It must not be used for anything else. g.m.g0.racectx = 0 // Max stack size is 1 GB on 64-bit, 250 MB on 32-bit. // Using decimal instead of binary GB and MB because // they look nicer in the stack overflow failure message. if sys.PtrSize == 8 { maxstacksize = 1000000000 } else { maxstacksize = 250000000 } // An upper limit for max stack size. Used to avoid random crashes // after calling SetMaxStack and trying to allocate a stack that is too big, // since stackalloc works with 32-bit sizes. maxstackceiling = 2 * maxstacksize // Allow newproc to start new Ms. mainStarted = true if GOARCH != \u0026#34;wasm\u0026#34; { // no threads on wasm yet, so no sysmon // For runtime_syscall_doAllThreadsSyscall, we // register sysmon is not ready for the world to be // stopped. atomic.Store(\u0026amp;sched.sysmonStarting, 1) systemstack(func() { newm(sysmon, nil, -1) }) } // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won\u0026#39;t care, but a few // do require certain calls to be made by the main thread. // Those can arrange for main.main to run in the main thread // by calling runtime.LockOSThread during initialization // to preserve the lock. lockOSThread() if g.m != \u0026amp;m0 { throw(\u0026#34;runtime.main not on m0\u0026#34;) } m0.doesPark = true // Record when the world started. // Must be before doInit for tracing init. runtimeInitTime = nanotime() if runtimeInitTime == 0 { throw(\u0026#34;nanotime returning zero\u0026#34;) } if debug.inittrace != 0 { inittrace.id = getg().goid inittrace.active = true } doInit(\u0026amp;runtime_inittask) // Must be before defer. // Defer unlock so that runtime.Goexit during init does the unlock too. needUnlock := true defer func() { if needUnlock { unlockOSThread() } }() gcenable() main_init_done = make(chan bool) if iscgo { if _cgo_thread_start == nil { throw(\u0026#34;_cgo_thread_start missing\u0026#34;) } if GOOS != \u0026#34;windows\u0026#34; { if _cgo_setenv == nil { throw(\u0026#34;_cgo_setenv missing\u0026#34;) } if _cgo_unsetenv == nil { throw(\u0026#34;_cgo_unsetenv missing\u0026#34;) } } if _cgo_notify_runtime_init_done == nil { throw(\u0026#34;_cgo_notify_runtime_init_done missing\u0026#34;) } // Start the template thread in case we enter Go from // a C-created thread and need to create a new thread. startTemplateThread() cgocall(_cgo_notify_runtime_init_done, nil) } doInit(\u0026amp;main_inittask) // Disable init tracing after main init done to avoid overhead // of collecting statistics in malloc and newproc inittrace.active = false close(main_init_done) needUnlock = false unlockOSThread() if isarchive || islibrary { // A program compiled with -buildmode=c-archive or c-shared // has a main, but it is not executed. return } fn := main_main // make an indirect call, as the linker doesn\u0026#39;t know the address of the main package when laying down the runtime fn() if raceenabled { racefini() } // Make racy client program work: if panicking on // another goroutine at the same time as main returns, // let the other goroutine finish printing the panic trace. // Once it does, it will exit. See issues 3934 and 20018. if atomic.Load(\u0026amp;runningPanicDefers) != 0 { // Running deferred functions should not take long. for c := 0; c \u0026lt; 1000; c++ { if atomic.Load(\u0026amp;runningPanicDefers) == 0 { break } Gosched() } } if atomic.Load(\u0026amp;panicking) != 0 { gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1) } exit(0) for { var x *int32 *x = 0 } } 综上所述：Go语言启动流程可以简化如下\n确定当前系统的平台，OSinit\nschedinit：做好初始化一些锁，cpu，gc等，之后调用procresize\nprocresize：将当前的m0和allp[0]进行绑定，再调用\nmstart：分配go的堆栈，将m0和g0绑定\nschedule：调度开始，首先执行m0的g0，绑定的函数即为main\nmain goroutine：开始函数的执行流\n1.3.2. Schedinit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func schedinit() { _g_ := getg() ... sched.maxmcount = 10000 ... sched.lastpoll = uint64(nanotime()) procs := ncpu if n, ok := atoi32(gogetenv(\u0026#34;GOMAXPROCS\u0026#34;)); ok \u0026amp;\u0026amp; n \u0026gt; 0 { procs = n } if procresize(procs) != nil { throw(\u0026#34;unknown runnable goroutine during bootstrap\u0026#34;) } } 如果全局变量 allp 切片中的处理器数量少于期望数量，会对切片进行扩容； 使用 new 创建新的处理器结构体并调用 runtime.p.init 初始化刚刚扩容的处理器； 通过指针将线程 m0 和处理器 allp[0] 绑定到一起； 调用 runtime.p.destroy 释放不再使用的处理器结构； 通过截断改变全局变量 allp 的长度保证与期望处理器数量相等； 将除 allp[0] 之外的处理器 P 全部设置成 _Pidle 并加入到全局的空闲队列中； 1.3.3. Schedule Loop 当程序的调度器启动之后，会执行schedule()函数查找goroutine\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func schedule() { _g_ := getg() top: var gp *g var inheritTime bool if gp == nil { // 如果发生了61次调度，且全局队列不为空，则去全局队列中找goroutine if _g_.m.p.ptr().schedtick%61 == 0 \u0026amp;\u0026amp; sched.runqsize \u0026gt; 0 { lock(\u0026amp;sched.lock) gp = globrunqget(_g_.m.p.ptr(), 1) unlock(\u0026amp;sched.lock) } } if gp == nil { gp, inheritTime = runqget(_g_.m.p.ptr()) } if gp == nil { gp, inheritTime = findrunnable() } // ... if gp.lockedm != 0 { // Hands off own p to the locked m, // then blocks waiting for a new p. startlockedm(gp) goto top } execute(gp, inheritTime) } 为了保证公平，当全局运行队列中有待执行的 Goroutine 时，通过 schedtick 保证有一定几率会从全局的运行队列中查找对应的 Goroutine； 从处理器本地的运行队列中查找待执行的 Goroutine； 如果前两种方法都没有找到 Goroutine，会通过 runtime.findrunnable进行阻塞地查找 Goroutine； findrunnable函数会执行以下操作：\n从本地运行队列、全局运行队列中查找； 从网络轮询器中查找是否有 Goroutine 等待运行； 通过 runtime.runqsteal尝试从其他随机的处理器中窃取待运行的 Goroutine，该函数还可能窃取处理器的计时器； 在获取到Goroutine以后，最终会调用execute函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func execute(gp *g, inheritTime bool) { _g_ := getg() _g_.m.curg = gp gp.m = _g_.m casgstatus(gp, _Grunnable, _Grunning) gp.waitsince = 0 gp.preempt = false gp.stackguard0 = gp.stack.lo + _StackGuard if !inheritTime { _g_.m.p.ptr().schedtick++ } gogo(\u0026amp;gp.sched) } 通过gogo函数，将这个Goroutine调度到当前线程上，gogo函数会从 runtime.gobuf 中取出了 runtime.goexit的程序计数器和待执行函数的程序计数器，其中：\nruntime.goexit 的程序计数器被放到了栈 SP 上； 待执行函数的程序计数器被放到了寄存器 BX 上； 当这个Goroutine结束的时候，会runtime·goexit(SB)，再经过一系列复杂的函数调用，我们最终在当前线程的 g0 的栈上调用 runtime.goexit0函数，该函数会将 Goroutine 转换会 _Gdead 状态、清理其中的字段、移除 Goroutine 和线程的关联并调用 runtime.gfput重新加入处理器的 Goroutine 空闲列表 gFree：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func goexit0(gp *g) { _g_ := getg() casgstatus(gp, _Grunning, _Gdead) gp.m = nil ... gp.param = nil gp.labels = nil gp.timer = nil dropg() gfput(_g_.m.p.ptr(), gp) schedule() } 可以看到，在goexit0调用结束后，会再次出发schedule()重新进行一轮调度。\n1.4. Trigger Scheduling 1.4.0. Create 详情见1.2，在线程启动 runtime.mstart和 Goroutine 执行结束 runtime.goexit0触发调度之外。还有以下方法会执行系统调度策略。\n1.4.1. Park 主动挂起是触发调度最常见的方法，该函数会将当前 Goroutine 暂停，被暂停的任务不会放回运行队列\n1 2 3 4 5 6 7 8 9 10 11 func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) { mp := acquirem() gp := mp.curg mp.waitlock = lock mp.waitunlockf = unlockf gp.waitreason = reason mp.waittraceev = traceEv mp.waittraceskip = traceskip releasem(mp) mcall(park_m) } 其中park_m还会将状态由_Grunning切换为_Gwaiting，并且使用dropg移除当前线程和这个Goroutine的关联。\n1 2 3 4 5 6 7 8 func park_m(gp *g) { _g_ := getg() casgstatus(gp, _Grunning, _Gwaiting) dropg() schedule() } 再需要重新唤醒的时候，会调用以下方法进行唤醒，将状态切换回_Grunnable 并将其加入处理器的运行队列中，等待调度器的调度。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func goready(gp *g, traceskip int) { systemstack(func() { ready(gp, traceskip, true) }) } func ready(gp *g, traceskip int, next bool) { _g_ := getg() casgstatus(gp, _Gwaiting, _Grunnable) runqput(_g_.m.p.ptr(), gp, next) if atomic.Load(\u0026amp;sched.npidle) != 0 \u0026amp;\u0026amp; atomic.Load(\u0026amp;sched.nmspinning) == 0 { wakep() } } 1.4.2. System call Go 语言通过 syscall.Syscall和 syscall.RawSyscall等使用汇编语言编写的方法封装操作系统提供的所有系统调用\n1 2 3 4 5 6 7 8 9 10 11 12 13 #define INVOKE_SYSCALL INT $0x80 TEXT ·Syscall(SB),NOSPLIT,$0-28 CALL runtime·entersyscall(SB) ... INVOKE_SYSCALL ... CALL runtime·exitsyscall(SB) RET ok: ... CALL runtime·exitsyscall(SB) RET Go使用了entrysyscall和exitsyscall完成系统调用时的准备与清理工作，对于不需要运行时参与的系统调用，比如SYS_TIME，SYS_EPOLL_WAIT，Go封装了一层RawSyscall来调用。\n在调用entrysyscall会调用reentersyscall函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 //go:nosplit //go:linkname entersyscall func entersyscall() { reentersyscall(getcallerpc(), getcallersp()) } func reentersyscall(pc, sp uintptr) { _g_ := getg() _g_.m.locks++ _g_.stackguard0 = stackPreempt _g_.throwsplit = true save(pc, sp) _g_.syscallsp = sp _g_.syscallpc = pc casgstatus(_g_, _Grunning, _Gsyscall) _g_.m.syscalltick = _g_.m.p.ptr().syscalltick _g_.m.mcache = nil pp := _g_.m.p.ptr() pp.m = 0 _g_.m.oldp.set(pp) _g_.m.p = 0 atomic.Store(\u0026amp;pp.status, _Psyscall) if sched.gcwaiting != 0 { systemstack(entersyscall_gcwait) save(pc, sp) } _g_.m.locks-- } 禁止线程上发生的抢占，防止出现内存不一致的问题； 保证当前函数不会触发栈分裂或者增长； 保存当前的程序计数器 PC 和栈指针 SP 中的内容； 将 Goroutine 的状态更新至 _Gsyscall； 将 Goroutine 的处理器和线程暂时分离并更新处理器的状态到 _Psyscall； 释放当前线程上的锁； 当前线程会陷入系统调用等待返回，在锁被释放后，会有其他 Goroutine 抢占处理器资源。\n当系统调用结束后，会再次返回\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func exitsyscall() { _g_ := getg() oldp := _g_.m.oldp.ptr() _g_.m.oldp = 0 if exitsyscallfast(oldp) { _g_.m.p.ptr().syscalltick++ casgstatus(_g_, _Gsyscall, _Grunning) ... return } mcall(exitsyscall0) _g_.m.p.ptr().syscalltick++ _g_.throwsplit = false } 其中exitsyscallfast会有两种不同的分支：\n如果 Goroutine 的原处理器处于 _Psyscall 状态，会直接调用 wirep 将 Goroutine 与处理器进行关联； 1 2 3 4 5 6 7 // Try to re-acquire the last P. if oldp != nil \u0026amp;\u0026amp; oldp.status == _Psyscall \u0026amp;\u0026amp; atomic.Cas(\u0026amp;oldp.status, _Psyscall, _Pidle) { // There\u0026#39;s a cpu for us, so we can run. wirep(oldp) exitsyscallfast_reacquired() return true } 如果调度器中存在闲置的处理器，会调用 acquirep使用闲置的处理器处理当前 Goroutine； 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 func exitsyscallfast_pidle() bool { lock(\u0026amp;sched.lock) _p_ := pidleget() if _p_ != nil \u0026amp;\u0026amp; atomic.Load(\u0026amp;sched.sysmonwait) != 0 { atomic.Store(\u0026amp;sched.sysmonwait, 0) notewakeup(\u0026amp;sched.sysmonnote) } unlock(\u0026amp;sched.lock) if _p_ != nil { acquirep(_p_) return true } return false } // Associate p and the current m. // // This function is allowed to have write barriers even if the caller // isn\u0026#39;t because it immediately acquires _p_. // //go:yeswritebarrierrec func acquirep(_p_ *p) { // Do the part that isn\u0026#39;t allowed to have write barriers. wirep(_p_) // Have p; write barriers now allowed. // Perform deferred mcache flush before this P can allocate // from a potentially stale mcache. _p_.mcache.prepareForSweep() if trace.enabled { traceProcStart() } } // wirep is the first step of acquirep, which actually associates the // current M to _p_. This is broken out so we can disallow write // barriers for this part, since we don\u0026#39;t yet have a P. // //go:nowritebarrierrec //go:nosplit func wirep(_p_ *p) { _g_ := getg() if _g_.m.p != 0 { throw(\u0026#34;wirep: already in go\u0026#34;) } if _p_.m != 0 || _p_.status != _Pidle { id := int64(0) if _p_.m != 0 { id = _p_.m.ptr().id } print(\u0026#34;wirep: p-\u0026gt;m=\u0026#34;, _p_.m, \u0026#34;(\u0026#34;, id, \u0026#34;) p-\u0026gt;status=\u0026#34;, _p_.status, \u0026#34;\\n\u0026#34;) throw(\u0026#34;wirep: invalid p state\u0026#34;) } _g_.m.p.set(_p_) _p_.m.set(_g_.m) _p_.status = _Prunning } 另一个相对较慢的路径 runtime.exitsyscall0 会将当前 Goroutine 切换至 _Grunnable 状态，并移除线程 M 和当前 Goroutine 的关联：\n当我们通过 runtime.pidleget 获取到闲置的处理器时就会在该处理器上执行 Goroutine； 在其它情况下，我们会将当前 Goroutine 放到全局的运行队列中，等待调度器的调度； 无论哪种情况，我们在这个函数中都会调用 runtime.schedule 触发调度器的调度。\n1.4.3. Cooperative 该方法不会挂起Goroutine，而是将当前的Goroutine调度到其他线程上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // Gosched yields the processor, allowing other goroutines to run. It does not // suspend the current goroutine, so execution resumes automatically. func Gosched() { checkTimeouts() mcall(gosched_m) } // Gosched continuation on g0. func gosched_m(gp *g) { if trace.enabled { traceGoSched() } goschedImpl(gp) } func goschedImpl(gp *g) { status := readgstatus(gp) if status\u0026amp;^_Gscan != _Grunning { dumpgstatus(gp) throw(\u0026#34;bad g status\u0026#34;) } casgstatus(gp, _Grunning, _Grunnable) dropg() lock(\u0026amp;sched.lock) globrunqput(gp) unlock(\u0026amp;sched.lock) schedule() } 运行时会更新 Goroutine 的状态到 _Grunnable，让出当前的处理器并将 Goroutine 重新放回全局队列，在最后，该函数会调用 runtime.schedule 触发调度。\n1.5. Thread Control Goroutine 应该在调用操作系统服务或者依赖线程状态的非 Go 语言库时调用\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func LockOSThread() { if atomic.Load(\u0026amp;newmHandoff.haveTemplateThread) == 0 \u0026amp;\u0026amp; GOOS != \u0026#34;plan9\u0026#34; { startTemplateThread() } _g_ := getg() _g_.m.lockedExt++ dolockOSThread() } func dolockOSThread() { _g_ := getg() _g_.m.lockedg.set(_g_) _g_.lockedm.set(_g_.m) } 会分别设置线程的 lockedg 字段和 Goroutine 的 lockedm 字段，这两行代码会绑定线程和 Goroutine。\n当Goroutine完成操作之后，会使用如下方法分离Goroutine和线程。\nGo 语言的运行时会通过 runtime.startm 启动线程来执行处理器 P，如果我们在该函数中没能从闲置列表中获取到线程 M 就会调用 runtime.newm 创建新的线程：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func newm(fn func(), _p_ *p, id int64) { mp := allocm(_p_, fn, id) mp.nextp.set(_p_) mp.sigmask = initSigmask ... newm1(mp) } func newm1(mp *m) { if iscgo { ... } newosproc(mp) } 该函数在 Linux 平台上会通过系统调用 clone 创建新的操作系统线程，它也是创建线程链路上距离操作系统最近的 Go 语言函数\n1 2 3 4 5 6 func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) ... ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) ... } 2. System Monitor 在程序启动前，会首先调用runtime.main函数，其中就包括了sysmon的创建\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main() { ... if GOARCH != \u0026#34;wasm\u0026#34; { systemstack(func() { newm(sysmon, nil) }) } ... } func newm(fn func(), _p_ *p) { mp := allocm(_p_, fn) mp.nextp.set(_p_) mp.sigmask = initSigmask ... newm1(mp) } newm1会调用特定平台的 runtime.newosproc 通过系统调用 clone 创建一个新的线程并在新的线程中执行 runtime.mstart\n1 2 3 4 5 6 7 8 func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) var oset sigset sigprocmask(_SIG_SETMASK, \u0026amp;sigset_all, \u0026amp;oset) ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) sigprocmask(_SIG_SETMASK, \u0026amp;oset, nil) ... } 在新建线程时，会执行runtime.m中的sysmon启动监控\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func sysmon() { sched.nmsys++ checkdead() lasttrace := int64(0) idle := 0 delay := uint32(0) for { if idle == 0 { delay = 20 } else if idle \u0026gt; 50 { delay *= 2 } if delay \u0026gt; 10*1000 { delay = 10 * 1000 } usleep(delay) ... } } 当运行时刚刚调用上述函数时，会先通过 runtime.checkdead 检查是否存在死锁，然后进入核心的监控循环；系统监控在每次循环开始时都会通过 usleep 挂起当前线程，该函数的参数是微秒，运行时会遵循以下的规则决定休眠时间：\n初始的休眠时间是 20μs； 最长的休眠时间是 10ms； 当系统监控在 50 个循环中都没有唤醒 Goroutine 时，休眠时间在每个循环都会倍增； 当程序趋于稳定之后，系统监控的触发时间就会稳定在 10ms。它除了会检查死锁之外，还会在循环中完成以下的工作：\n运行计时器 — 获取下一个需要被触发的计时器； 轮询网络 — 获取需要处理的到期文件描述符； 抢占处理器 — 抢占运行时间较长的或者处于系统调用的 Goroutine； 垃圾回收 — 在满足条件时触发垃圾收集回收内存； 3. Stack And Heap 3.1. Traditional Language 栈一般由操作系统来分配和释放，堆由程序员通过编程语言来申请创建与释放。\n栈用来存放函数的参数、返回值、局部变量、函数调用时的临时上下文等，堆用来存放全局变量。我们可以这样理解数据存放的规则：只要是局部的、占用空间确定的数据，一般都存放在stack 里面，否则就放在 heap 里面。\n栈的访问速度相对比堆快。\n一般来说，每个线程分配一个stack，每个进程分配一个heap，也就是说，stack 是线程独占的，heap 是线程共用的。\nstack 创建的时候，大小是确定的，数据超过这个大小，就发生stack overflow 错误，而heap的大小是不确定的，需要的话可以不断增加。\n栈是由高地址向低地址增长的，而堆是由低地址向高地址增长的。\n在Go语言中，官方对堆栈管理做了如下的解释 只要有对变量的引用，变量就会存在，而它存储的位置与语言的语义无关。如果可能，变量会被分配到其函数的栈，但如果编译器无法证明函数返回之后变量是否仍然被引用，就必须在堆上分配该变量，采用垃圾回收机制进行 管理，从而避免指针悬空。此外，局部变量如果非常大，也会存在堆上。\n在编译器中，如果变量具有地址，就作为堆分配的候选，但如果逃逸分析可以确定其生存周期不会超过函数返回，就会分配在栈上。\n3.2. Implement Go 的内存分配器基于 Thread-Cache Malloc (tcmalloc) ，tcmalloc 为每个线程实现了一个本地缓存， 区分了小对象（小于 32kb）和大对象分配两种分配类型，其管理的内存单元称为 span。\nheapArena: 保留整个虚拟地址空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // A heapArena stores metadata for a heap arena. heapArenas are stored // outside of the Go heap and accessed via the mheap_.arenas index. //go:notinheap type heapArena struct { bitmap [heapArenaBitmapBytes]byte spans [pagesPerArena]*mspan // spans maps from virtual address page ID within this arena to *mspan. pageInUse [pagesPerArena / 8]uint8 pageMarks [pagesPerArena / 8]uint8 pageSpecials [pagesPerArena / 8]uint8 checkmarks *checkmarksMap zeroedBase uintptr } //arenaHint is a hint for where to grow the heap arenas. See // mheap_.arenaHints. //go:notinheap type arenaHint struct { addr uintptr down bool next *arenaHint } mheap：分配的堆，在页大小为 8KB 的粒度上进行管理\n然而管理 arena 如此粒度的内存并不符合实践，相反，所有的堆对象都通过 span 按照预先设定好的 大小等级分别分配，小于 32KB 的小对象则分配在固定大小等级的 span 上，否则直接从 mheap 上进行分配。\nmspan：是 mheap 上管理的一连串的页\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //go:notinheap type mspan struct { // 双向链表 next *mspan // 链表中的下一个 span，如果为空则为 nil prev *mspan // 链表中的前一个 span，如果为空则为 nil ... startAddr uintptr // span 的第一个字节的地址，即 s.base() npages uintptr // 一个 span 中的 page 数量 manualFreeList gclinkptr // mSpanManual span 的释放对象链表 ... freeindex uintptr nelems uintptr // span 中对象的数量 allocCache uint64 allocBits *gcBits ... allocCount uint16 // 分配对象的数量 spanclass spanClass // 大小等级与 noscan (uint8) incache bool // 是否被 mcache 使用 state mSpanState // mspaninuse 等待信息 ... } mcentral：收集了给定大小等级的所有 span。当 mcentral 中 nonempty 列表中也没有可分配的 span 时，则会向 mheap 提出请求，从而获得新的 span，并进而交给 mcache。 1 2 3 4 5 6 7 8 //go:notinheap type mcentral struct { lock mutex spanclass spanClass nonempty mSpanList // 带有自由对象的 span 列表，即非空闲列表 empty mSpanList // 没有自由对象的 span 列表（或缓存在 mcache 中） ... } mcache：为 per-P 的缓存。 1 2 3 4 5 6 7 8 9 10 //go:notinheap type mcache struct { ... tiny uintptr tinyoffset uintptr local_tinyallocs uintptr alloc [numSpanClasses]*mspan // 用来分配的 spans，由 spanClass 索引 stackcache [_NumStackOrders]stackfreelist ... } mcache负责小对象和微对象的分配，其持有mspan\n3.3. Allocation 常见的内存管理模块分为如下三种fixalloc、linearAlloc、mcache\nfixalloc 是一个基于自由列表的固定大小的分配器。其核心原理是将若干未分配的内存块连接起来， 将未分配的区域的第一个字为指向下一个未分配区域的指针使用。Go 的主分配堆中 malloc（span、cache、treap、finalizer、profile、arena hint 等） 均 围绕它为实体进行固定分配和回收。\nfixalloc 作为抽象，非常简洁，只包含三个基本操作：初始化、分配、回收\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // fixalloc 是一个简单的固定大小对象的自由表内存分配器。 // Malloc 使用围绕 sysAlloc 的 fixalloc 来管理其 MCache 和 MSpan 对象。 // // fixalloc.alloc 返回的内存默认为零，但调用者可以通过将 zero 标志设置为 false // 来自行负责将分配归零。如果这部分内存永远不包含堆指针，则这样的操作是安全的。 // 调用方负责锁定 fixalloc 调用。调用方可以在对象中保持状态， // 但当释放和重新分配时第一个字会被破坏。 // 考虑使 fixalloc 的类型变为 go:notinheap. type fixalloc struct { size uintptr first func(arg, p unsafe.Pointer) // 首次调用时返回 p arg unsafe.Pointer list *mlink chunk uintptr // 使用 uintptr 而非 unsafe.Pointer 来避免 write barrier nchunk uint32 inuse uintptr // 正在使用的字节 stat *uint64 zero bool // 归零的分配 } Go 语言对于零值有自己的规定，自然也就体现在内存分配器上。而 fixalloc 作为内存分配器内部组件的来源于 操作系统的内存，自然需要自行初始化，因此，fixalloc 的初始化也就不可避免的需要将自身的各个字段归零：\n1 2 3 4 5 6 7 8 9 10 11 func (f *fixalloc) init(size uintptr, first func(arg, p unsafe.Pointer), arg unsafe.Pointer, stat *uint64) { f.size = size f.first = first f.arg = arg f.list = nil f.chunk = 0 f.nchunk = 0 f.inuse = 0 f.stat = stat f.zero = true } fixalloc 基于自由表策略进行实现，分为两种情况：\n存在被释放、可复用的内存\n不存在可复用的内存\n对于第一种情况，也就是在运行时内存被释放，但这部分内存并不会被立即回收给操作系统， 我们直接从自由表中获得即可，但需要注意按需将这部分内存进行清零操作。\n对于第二种情况，直接向操作系统申请固定大小的内存，然后扣除分配的大小即可。 回收阶段\n1 2 3 4 5 6 7 8 func (f *fixalloc) free(p unsafe.Pointer) { // 减少使用的字节数 f.inuse -= f.size // 将要释放的内存地址作为 mlink 指针插入到 f.list 内，完成回收 v := (*mlink)(p) v.next = f.list f.list = v } linearAlloc 是一个基于线性分配策略的分配器，但由于它只作为 mheap_.heapArenaAlloc 和 mheap_.arena 在 32 位系统上使用，这里不做详细分析。\n此外，对于每一个P，都有mcache做缓存，每个线程独立持有，也就不会出现并发问题，也不用上锁\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 //go:notinheap type mcache struct { // 下面的成员在每次 malloc 时都会被访问 // 因此将它们放到一起来利用缓存的局部性原理 next_sample uintptr // 分配这么多字节后触发堆样本 local_scan uintptr // 分配的可扫描堆的字节数 // 没有指针的微小对象的分配器缓存。 // 请参考 malloc.go 中的 \u0026#34;小型分配器\u0026#34; 注释。 // // tiny 指向当前 tiny 块的起始位置，或当没有 tiny 块时候为 nil // tiny 是一个堆指针。由于 mcache 在非 GC 内存中，我们通过在 // mark termination 期间在 releaseAll 中清除它来处理它。 tiny uintptr tinyoffset uintptr local_tinyallocs uintptr // 不计入其他统计的极小分配的数量 // 下面的不在每个 malloc 时被访问 alloc [numSpanClasses]*mspan // 用来分配的 spans，由 spanClass 索引 stackcache [_NumStackOrders]stackfreelist // 本地分配器统计，在 GC 期间被刷新 local_largefree uintptr // bytes freed for large objects (\u0026gt;maxsmallsize) local_nlargefree uintptr // number of frees for large objects (\u0026gt;maxsmallsize) local_nsmallfree [_NumSizeClasses]uintptr // number of frees for small objects (\u0026lt;=maxsmallsize) // flushGen indicates the sweepgen during which this mcache // was last flushed. If flushGen != mheap_.sweepgen, the spans // in this mcache are stale and need to the flushed so they // can be swept. This is done in acquirep. flushGen uint32 } 运行时的 runtime.allocmcache 从 mheap 上分配一个 mcache。 由于 mheap 是全局的，因此在分配期必须对其进行加锁，而分配通过 fixAlloc 组件完成：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 虚拟的MSpan，不包含任何对象。 var emptymspan mspan func allocmcache() *mcache { var c *mcache systemstack(func() { lock(\u0026amp;mheap_.lock) c = (*mcache)(mheap_.cachealloc.alloc()) c.flushGen = mheap_.sweepgen unlock(\u0026amp;mheap_.lock) } for i := range c.alloc { c.alloc[i] = \u0026amp;emptymspan // 暂时指向虚拟的 mspan 中 } // 返回下一个采样点，是服从泊松过程的随机数 c.next_sample = nextSample() return c } 当需要释放时调用freemcache函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func freemcache(c *mcache) { systemstack(func() { // 归还 span c.releaseAll() // 释放 stack stackcache_clear(c) lock(\u0026amp;mheap_.lock) // 记录局部统计 purgecachedstats(c) // 将 mcache 释放 mheap_.cachealloc.free(unsafe.Pointer(c)) unlock(\u0026amp;mheap_.lock) }) } func (c *mcache) releaseAll() { for i := range c.alloc { s := c.alloc[i] if s != \u0026amp;emptymspan { // 将 span 归还 mheap_.central[i].mcentral.uncacheSpan(s) c.alloc[i] = \u0026amp;emptymspan } } // 清空 tinyalloc 池. c.tiny = 0 c.tinyoffset = 0 } mcache 会被 P 持有，当 M 和 P 绑定时，M 同样会保留 mcache 的指针 mcache 直接向操作系统申请内存，且常驻运行时 P 通过 make 命令进行分配，会分配在 Go 堆上 3.4. Escape Go 程序的执行是基于 goroutine 的，goroutine 和传统意义上的程序一样，也有栈和堆的概念。只不过 Go 的运行时帮我们屏蔽掉了这两个概念，只在运行时内部区分并分别对应：goroutine 执行栈以及 Go 堆。\ngoroutine 的执行栈与传统意义上的栈一样，当函数返回时，在栈上就会被回收，栈中的对象都会被回收，从而 无需 GC 的标记；而堆则麻烦一些，由于 Go 支持垃圾回收，只要对象生存在堆上，Go 的运行时 GC 就会在 后台将对应的内存进行标记从而能够在垃圾回收的时候将对应的内存回收，进而增加了开销。\n以下定义了4种情况，使用 -gcflags \u0026quot;-N -l -m\u0026quot;\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package main type smallobj struct { arr [1 \u0026lt;\u0026lt; 10]byte } type largeobj struct { arr [1 \u0026lt;\u0026lt; 26]byte } func f1() int { x := 1 return x // 直接返回，没有逃逸 } func f2() *int { y := 2 return \u0026amp;y // 发生逃逸 } func f3() { large := largeobj{} // large 无法被一个执行栈装下，也会逃逸 println(\u0026amp;large) } func f4() { small := smallobj{} // small 对象能够被一个执行栈装下，变量没有返回到栈外，进而没有发生逃逸。 print(\u0026amp;small) } func main() { x := f1() y := f2() f3() f4() println(x, y) } Go语言中的引用类型有func（函数类型），interface（接口类型），slice（切片类型），map（字典类型），channel（管道类型），*（指针类型）。\n在编译阶段，可以使用go tool compile命令，查看，如果有runtime.newobject，则说明在堆上，反之则在栈上。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // 创建一个新的对象 func newobject(typ *_type) unsafe.Pointer { return mallocgc(typ.size, typ, true) // true 内存清零 } func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { // 创建大小为零的对象，例如空结构体 if size == 0 { return unsafe.Pointer(\u0026amp;zerobase) } mp := acquirem() mp.mallocing = 1 ... // 获取当前 g 所在 M 所绑定 P 的 mcache c := gomcache() var x unsafe.Pointer noscan := typ == nil || typ.kind\u0026amp;kindNoPointers != 0 if size \u0026lt;= maxSmallSize { if noscan \u0026amp;\u0026amp; size \u0026lt; maxTinySize { // 微对象分配 ... } else { // 小对象分配 ... } } else { // 大对象分配 ... } ... mp.mallocing = 0 releasem(mp) ... return x } 小对象分配 当对一个小对象（\u0026lt;32KB）分配内存时，会将该对象所需的内存大小调整到某个能够容纳该对象的大小等级（size class）， 并查看 mcache 中对应等级的 mspan，通过扫描 mspan 的 freeindex 来确定是否能够进行分配。\n当没有可分配的 mspan 时，会从 mcentral 中获取一个所需大小空间的新的 mspan，从 mcentral 中分配会对其进行加锁， 但一次性获取整个 span 的过程均摊了对 mcentral 加锁的成本。\n如果 mcentral 的 mspan 也为空时，则它也会发生增长，从而从 mheap 中获取一连串的页，作为一个新的 mspan 进行提供。 而如果 mheap 仍然为空，或者没有足够大的对象来进行分配时，则会从操作系统中分配一组新的页（至少 1MB）， 从而均摊与操作系统沟通的成本。\n微对象分配 对于过小的微对象（\u0026lt;16B），它们的分配过程与小对象的分配过程基本类似，但是是直接存储在 mcache 上，并由其以 16B 的块大小直接进行管理和释放。\n大对象分配 大对象分配非常粗暴，不与 mcache 和 mcentral 沟通，直接绕过并通过 mheap 进行分配。\n4. Garbage Collection 清理终止阶段； 暂停程序，所有的处理器在这时会进入安全点（Safe point）； 如果当前垃圾收集循环是强制触发的，我们还需要处理还未被清理的内存管理单元； 标记阶段； 将状态切换至 _GCmark、开启写屏障、用户程序协助（Mutator Assists）并将根对象入队； 恢复执行程序，标记进程和用于协助的用户程序会开始并发标记内存中的对象，写屏障会将被覆盖的指针和新指针都标记成灰色，而所有新创建的对象都会被直接标记成黑色； 开始扫描根对象，包括所有 Goroutine 的栈、全局对象以及不在堆中的运行时数据结构，扫描 Goroutine 栈期间会暂停当前处理器； 依次处理灰色队列中的对象，将对象标记成黑色并将它们指向的对象标记成灰色； 使用分布式的终止算法检查剩余的工作，发现标记阶段完成后进入标记终止阶段； 标记终止阶段； 暂停程序、将状态切换至 _GCmarktermination ，确认标记工作已经完成，并关闭辅助标记的用户程序； 清理处理器上的线程缓存； 清理阶段； 将状态切换至 _GCoff 开始清理阶段，在此之前，新建的对象是黑色，切换后的对象是白色，初始化清理状态并关闭写屏障； 后台并发清理所有的内存管理单元，当 Goroutine 申请新的内存管理单元时就会触发清理； ","permalink":"https://chasing1020.github.io/post/go4-runtime/","summary":"\u003ch1 id=\"part4-runtime\"\u003ePart4. Runtime\u003c/h1\u003e\n\u003ch2 id=\"1-gmp\"\u003e1. GMP\u003c/h2\u003e\n\u003ch3 id=\"11-implement\"\u003e1.1. Implement\u003c/h3\u003e\n\u003ch4 id=\"111-data-structure\"\u003e1.1.1. Data Structure\u003c/h4\u003e\n\u003cp\u003e创建、销毁、调度 G 都需要每个 M 获取锁，这就形成了激烈的锁竞争。\nM 转移 G 会造成延迟和额外的系统负载。比如当 G 中包含创建新协程的时候，M 创建了 G’，为了继续执行 G，需要把 G’交给 M’执行，也造成了很差的局部性，因为 G’和 G 是相关的，最好放在 M 上执行，而不是其他 M’。\n系统调用 (CPU 在 M 之间的切换) 导致频繁的线程阻塞和取消阻塞操作增加了系统开销。\u003c/p\u003e","title":"Go(4) Runtime"},{"content":"Part3. Common Keyword 1. For And Range 对于所有的 range 循环，Go 语言都会在编译期将原切片或者数组赋值给一个新变量 ha，在赋值的过程中就发生了拷贝，而我们又通过 len 关键字预先获取了切片的长度，所以在循环中追加新的元素也不会改变循环执行的次数，这也就解释了循环永动机一节提到的现象。\n1 2 3 4 5 6 7 8 9 10 func main() { arr := []int{1, 2, 3} newArr := []*int{} for i, _ := range arr { newArr = append(newArr, \u0026amp;arr[i]) } for _, v := range newArr { fmt.Println(*v) } } 而遇到这种同时遍历索引和元素的 range 循环时，Go 语言会额外创建一个新的 v2 变量存储切片中的元素，循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖，赋值时也会触发拷贝。\n在遍历哈希表时，会发生如下操作：\n1 2 3 4 5 6 7 8 ha := a hit := hiter(n.Type) th := hit.Type mapiterinit(typename(t), ha, \u0026amp;hit) for ; hit.key != nil; mapiternext(\u0026amp;hit) { key := *hit.key val := *hit.val } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func mapiternext(it *hiter) { h := it.h t := it.t bucket := it.bucket b := it.bptr i := it.i alg := t.key.alg next: if b == nil { if bucket == it.startBucket \u0026amp;\u0026amp; it.wrapped { it.key = nil it.value = nil return } b = (*bmap)(add(it.buckets, bucket*uintptr(t.bucketsize))) bucket++ if bucket == bucketShift(it.B) { bucket = 0 it.wrapped = true } i = 0 } 字符串遍历，自动转换为Rune类型\n1 2 3 4 5 6 7 8 9 10 11 ha := s for hv1 := 0; hv1 \u0026lt; len(ha); { hv1t := hv1 hv2 := rune(ha[hv1]) if hv2 \u0026lt; utf8.RuneSelf { hv1++ } else { hv2, hv1 = decoderune(ha, hv1) } v1, v2 = hv1t, hv2 } 2. Defer 2.1. Implement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 // A _defer holds an entry on the list of deferred calls. // If you add a field here, add code to clear it in freedefer and deferProcStack // This struct must match the code in cmd/compile/internal/gc/reflect.go:deferstruct // and cmd/compile/internal/gc/ssa.go:(*state).call. // Some defers will be allocated on the stack and some on the heap. // All defers are logically part of the stack, so write barriers to // initialize them are not required. All defers must be manually scanned, // and for heap defers, marked. type _defer struct { siz int32 // includes both arguments and results 参数和返回值共占多少空间 started bool heap bool // openDefer indicates that this _defer is for a frame with open-coded // defers. We have only one defer record for the entire frame (which may // currently have 0, 1, or more defers active). openDefer bool // 表示当前 defer 是否经过开放编码的优化； sp uintptr // sp at time of defer 栈指针 pc uintptr // pc at time of defer 调用方的程序计数器； fn *funcval // can be nil for open-coded defers 关键字中传入的函数 _panic *_panic // panic that is running defer 是触发延迟调用的结构体，可能为空； link *_defer // If openDefer is true, the fields below record values about the stack // frame and associated function that has the open-coded defer(s). sp // above will be the sp for the frame, and pc will be address of the // deferreturn call in the function. fd unsafe.Pointer // funcdata for the function associated with the frame varp uintptr // value of varp for the stack frame // framepc is the current pc associated with the stack frame. Together, // with sp above (which is the sp associated with the stack frame), // framepc/sp can be used as pc/sp pair to continue a stack trace via // gentraceback(). framepc uintptr } A deferred function’s arguments are evaluated when the defer statement is evaluated. Deferred function calls are executed in Last In First Out order after the surrounding function returns. Deferred functions may read and assign to the returning function’s named return values. Go 语言在 1.13 中引入栈上分配的结构体，减少了 30% 的额外开销1，并在 1.14 中引入了基于开放编码的 defer，使得该关键字的额外开销可以忽略不计。\n2.2. Execve 2.2.1. In Heap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func (s *state) stmt(n *Node) { ... switch n.Op { case ODEFER: if s.hasOpenDefers { s.openDeferRecord(n.Left) // 开放编码 } else { d := callDefer // 堆分配 if n.Esc == EscNever { d = callDeferStack // 栈分配 } s.callResult(n.Left, d) } } } 1.12版本前的defer调用函数，函数需要：\n获取需要执行的函数名、闭包指针、代码指针和函数调用的接收方； 获取栈地址并将函数或者方法的参数写入栈中； 使用 cmd/compile/internal/gc.state.newValue1A 以及相关函数生成函数调用的中间代码； 如果当前调用的函数是 defer，那么会单独生成相关的结束代码块； 获取函数的返回值地址并结束当前调用； 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func (s *state) call(n *Node, k callKind, returnResultAddr bool) *ssa.Value { ... var call *ssa.Value if k == callDeferStack { // 在栈上初始化 defer 结构体 ... } else { ... switch { case k == callDefer: aux := ssa.StaticAuxCall(deferproc, ACArgs, ACResults) call = s.newValue1A(ssa.OpStaticCall, types.TypeMem, aux, s.mem()) ... } call.AuxInt = stksize } s.vars[\u0026amp;memVar] = call ... } 创建延迟调用，将defer A() 转换成 defproc(8, A)，整形参数在64位下占用8字节\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func deferproc(siz int32, fn *funcval) { sp := getcallersp() argp := uintptr(unsafe.Pointer(\u0026amp;fn)) + unsafe.Sizeof(fn) callerpc := getcallerpc() d := newdefer(siz) if d._panic != nil { throw(\u0026#34;deferproc: d.panic != nil after newdefer\u0026#34;) } d.fn = fn d.pc = callerpc d.sp = sp switch siz { case 0: case sys.PtrSize: *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp)) default: memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz)) } return0() } 从调度器的延迟调用缓存池 sched.deferpool 中取出结构体并将该结构体追加到当前 Goroutine 的缓存池中； 从 Goroutine 的延迟调用缓存池 pp.deferpool 中取出结构体； 通过 runtime.mallocgc 在堆上创建一个新的结构体；\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func newdefer(siz int32) *_defer { var d *_defer sc := deferclass(uintptr(siz)) gp := getg() if sc \u0026lt; uintptr(len(p{}.deferpool)) { pp := gp.m.p.ptr() if len(pp.deferpool[sc]) == 0 \u0026amp;\u0026amp; sched.deferpool[sc] != nil { for len(pp.deferpool[sc]) \u0026lt; cap(pp.deferpool[sc])/2 \u0026amp;\u0026amp; sched.deferpool[sc] != nil { d := sched.deferpool[sc] sched.deferpool[sc] = d.link pp.deferpool[sc] = append(pp.deferpool[sc], d) } } if n := len(pp.deferpool[sc]); n \u0026gt; 0 { d = pp.deferpool[sc][n-1] pp.deferpool[sc][n-1] = nil pp.deferpool[sc] = pp.deferpool[sc][:n-1] } } if d == nil { total := roundupsize(totaldefersize(uintptr(siz))) d = (*_defer)(mallocgc(total, deferType, true)) } d.siz = siz d.link = gp._defer gp._defer = d return d } 在函数结束前，转为runtime.deferreturn()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func deferreturn(arg0 uintptr) { gp := getg() d := gp._defer if d == nil { return } sp := getcallersp() ... switch d.siz { case 0: case sys.PtrSize: *(*uintptr)(unsafe.Pointer(\u0026amp;arg0)) = *(*uintptr)(deferArgs(d)) default: memmove(unsafe.Pointer(\u0026amp;arg0), deferArgs(d), uintptr(d.siz)) } fn := d.fn gp._defer = d.link freedefer(d) jmpdefer(fn, uintptr(unsafe.Pointer(\u0026amp;arg0))) } 2.2.2. Stack 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func deferprocStack(d *_defer) { gp := getg() d.started = false d.heap = false // 栈上分配的 _defer d.openDefer = false d.sp = getcallersp() d.pc = getcallerpc() d.framepc = 0 d.varp = 0 *(*uintptr)(unsafe.Pointer(\u0026amp;d._panic)) = 0 *(*uintptr)(unsafe.Pointer(\u0026amp;d.fd)) = 0 *(*uintptr)(unsafe.Pointer(\u0026amp;d.link)) = uintptr(unsafe.Pointer(gp._defer)) *(*uintptr)(unsafe.Pointer(\u0026amp;gp._defer)) = uintptr(unsafe.Pointer(d)) return0() } 与堆区类似，该方法没有本质上的不同，可以适用于更多场景。\n但是在循环中注册的defer，还是需要1.12的方式，在堆中分配，所以在defer结构体中新增了字段\n1 heap bool 2.2.3. Open Coded Go 语言在 1.14 中通过开放编码（Open Coded）实现 defer 关键字，该设计使用代码内联优化 defer 关键的额外开销并引入函数数据 funcdata 管理 panic 的调用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const maxOpenDefers = 8 func walkstmt(n *Node) *Node { switch n.Op { case ODEFER: Curfn.Func.SetHasDefer(true) Curfn.Func.numDefers++ if Curfn.Func.numDefers \u0026gt; maxOpenDefers { Curfn.Func.SetOpenCodedDeferDisallowed(true) } if n.Esc != EscNever { Curfn.Func.SetOpenCodedDeferDisallowed(true) } fallthrough ... } } 函数的 defer 数量少于或者等于 8 个； 函数的 defer 关键字不能在循环中执行； 函数的 return 语句与 defer 语句的乘积小于或者等于 15 个； 1.14中，使用df来标记每一个defer是否需要执行，将每一个defer放在函数返回处，通过编译阶段插入代码，把defer函数的逻辑展开在所属的函数内，从而省略了defer结构体和链表。\n但是，在函数执行过程中，如果调用了panic，或者是runtime.Goexit()函数，需要额外使用栈扫描方法来实现。\n所以在1.14版本中，新增了如下参数，保证栈扫描能够正确执行。在发生panic的情况下，效率要低于1.13版本\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // openDefer indicates that this _defer is for a frame with open-coded // defers. We have only one defer record for the entire frame (which may // currently have 0, 1, or more defers active). openDefer bool // 表示当前 defer 是否经过开放编码的优化； // If openDefer is true, the fields below record values about the stack // frame and associated function that has the open-coded defer(s). sp // above will be the sp for the frame, and pc will be address of the // deferreturn call in the function. fd unsafe.Pointer // funcdata for the function associated with the frame varp uintptr // value of varp for the stack frame // framepc is the current pc associated with the stack frame. Together, // with sp above (which is the sp associated with the stack frame), // framepc/sp can be used as pc/sp pair to continue a stack trace via // gentraceback(). framepc uintptr 2.3. Version Difference 堆上分配 · 1.1 ~ 1.12 编译期将 defer 关键字转换成 runtime.deferproc 并在调用 defer 关键字的函数返回之前插入 runtime.deferreturn； 运行时调用 runtime.deferproc 会将一个新的 runtime._defer 结构体追加到当前 Goroutine 的链表头； 运行时调用 runtime.deferreturn 会从 Goroutine 的链表中取出 runtime._defer 结构并依次执行； 栈上分配 · 1.13 当该关键字在函数体中最多执行一次时，编译期间的 cmd/compile/internal/gc.state.call 会将结构体分配到栈上并调用 runtime.deferprocStack； 开放编码 · 1.14 ~ 现在 编译期间判断 defer 关键字、return 语句的个数确定是否开启开放编码优化； 通过 deferBits 和 cmd/compile/internal/gc.openDeferInfo 存储 defer 关键字的相关信息； 如果 defer 关键字的执行可以在编译期间确定，会在函数返回前直接插入相应的代码，否则会由运行时的 runtime.deferreturn 处理； 3. Panic And Recover 3.1. Panic Implement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // A _panic holds information about an active panic. // // A _panic value must only ever live on the stack. // // The argp and link fields are stack pointers, but don\u0026#39;t need special // handling during stack growth: because they are pointer-typed and // _panic values only live on the stack, regular stack pointer // adjustment takes care of them. type _panic struct { argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink defer的参数空间地址 arg interface{} // argument to panic link *_panic // link to earlier panic pc uintptr // where to return to in runtime if this panic is bypassed sp unsafe.Pointer // where to return to in runtime if this panic is bypassed recovered bool // whether this panic is over 是否被恢复 aborted bool // the panic was aborted 是否终止 goexit bool } 在panic执行时，会把_defer的panic置位true，如果能够正常结束，\n然后执行如下代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func gopanic(e interface{}) { gp := getg() ... var p _panic p.arg = e p.link = gp._panic gp._panic = (*_panic)(noescape(unsafe.Pointer(\u0026amp;p))) for { d := gp._defer if d == nil { break } d._panic = (*_panic)(noescape(unsafe.Pointer(\u0026amp;p))) reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz)) d._panic = nil d.fn = nil gp._defer = d.link freedefer(d) if p.recovered { ... } } fatalpanic(gp._panic) *(*int)(nil) = 0 } 对于无法修复的崩溃，会调用如下代码打印所有panic信息\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func fatalpanic(msgs *_panic) { pc := getcallerpc() sp := getcallersp() gp := getg() if startpanic_m() \u0026amp;\u0026amp; msgs != nil { atomic.Xadd(\u0026amp;runningPanicDefers, -1) printpanics(msgs) } if dopanic_m(gp, pc, sp) { crash() } exit(2) } 3.2. Recover Implement 1 2 3 4 5 6 7 8 9 func gorecover(argp uintptr) interface{} { gp := getg() p := gp._panic if p != nil \u0026amp;\u0026amp; !p.recovered \u0026amp;\u0026amp; argp == uintptr(p.argp) { p.recovered = true return p.arg } return nil } recover将当前panic的recovered等于true，然后移除并且跳出当前的panic\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func gopanic(e interface{}) { ... for { // 执行延迟调用函数，可能会设置 p.recovered = true ... pc := d.pc sp := unsafe.Pointer(d.sp) ... if p.recovered { // 当标记为为true时， gp._panic = p.link for gp._panic != nil \u0026amp;\u0026amp; gp._panic.aborted { gp._panic = gp._panic.link } if gp._panic == nil { gp.sig = 0 } gp.sigcode0 = uintptr(sp) gp.sigcode1 = pc mcall(recovery) throw(\u0026#34;recovery failed\u0026#34;) } } ... } 编译器会负责做转换关键字的工作； 1.1. 将 panic 和 recover 分别转换成 runtime.gopanic 和 runtime.gorecover； 1.2. 将 defer 转换成 runtime.deferproc 函数； 1.3. 在调用 defer 的函数末尾调用 runtime.deferreturn 函数； 在运行过程中遇到 runtime.gopanic 方法时，会从 Goroutine 的链表依次取出 runtime._defer 结构体并执行； 如果调用延迟执行函数时遇到了 runtime.gorecover 就会将 _panic.recovered 标记成 true 并返回 panic 的参数； 3.1. 在这次调用结束之后，runtime.gopanic 会从 runtime._defer 结构体中取出程序计数器 pc 和栈指针 sp 并调用 runtime.recovery 函数进行恢复程序； 3.2. runtime.recovery 会根据传入的 pc 和 sp 跳转回 runtime.deferproc； 3.3. 编译器自动生成的代码会发现 runtime.deferproc 的返回值不为 0，这时会跳回 runtime.deferreturn 并恢复到正常的执行流程； 如果没有遇到 runtime.gorecover 就会依次遍历所有的 runtime._defer，并在最后调用 runtime.fatalpanic 中止程序、打印 panic 的参数并返回错误码 2（需要编译）； 如在Go的compile.Main执行期间，加入了defer hidePanic()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func hidePanic() { if base.Debug.Panic == 0 \u0026amp;\u0026amp; base.Errors() \u0026gt; 0 { // If we\u0026#39;ve already complained about things // in the program, don\u0026#39;t bother complaining // about a panic too; let the user clean up // the code and try again. if err := recover(); err != nil { if err == \u0026#34;-h\u0026#34; { panic(err) } base.ErrorExit() } } } 4. Make And New make 的作用是初始化内置的数据结构，也就是我们在前面提到的切片、哈希表和 Channel； new 的作用是根据传入的类型分配一片内存空间并返回指向这片内存空间的指针； Go 语言会将代表 make 关键字的 OMAKE 节点根据参数类型的不同转换成了 OMAKESLICE、OMAKEMAP 和 OMAKECHAN 三种不同类型的节点，这些节点会调用不同的运行时函数来初始化相应的数据结构。\n分别对应如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 type SliceHeader struct { Data uintptr Len int Cap int } type hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. // Make sure this stays in sync with the compiler\u0026#39;s definition. count int // # live cells == size of map. Must be first (used by len() builtin) flags uint8 B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items) noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details hash0 uint32 // hash seed buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0. oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated) extra *mapextra // optional fields } type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters sendq waitq // list of send waiters // lock protects all fields in hchan, as well as several // fields in sudogs blocked on this channel. // // Do not change another G\u0026#39;s status while holding this lock // (in particular, do not ready a G), as this can deadlock // with stack shrinking. lock mutex } 在调用new时，则会分为以下操作\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func callnew(t *types.Type) *Node { ... n := nod(ONEWOBJ, typename(t), nil) ... return n } func (s *state) expr(n *Node) *ssa.Value { switch n.Op { case ONEWOBJ: if n.Type.Elem().Size() == 0 { return s.newValue1A(ssa.OpAddr, n.Type, zerobaseSym, s.sb) } typ := s.expr(n.Left) vv := s.rtcall(newobject, true, []*types.Type{n.Type}, typ) return vv[0] } } 需要注意的是，无论是直接使用 new，还是使用 var 初始化变量，它们在编译器看来都是 ONEW 和 ODCL 节点。如果变量会逃逸到堆上，这些节点在这一阶段都会被 cmd/compile/internal/gc.walkstmt 转换成通过 runtime.newobject 函数并在堆上申请内存\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func walkstmt(n *Node) *Node { switch n.Op { case ODCL: v := n.Left if v.Class() == PAUTOHEAP { if prealloc[v] == nil { prealloc[v] = callnew(v.Type) } nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v]) nn.SetColas(true) nn = typecheck(nn, ctxStmt) return walkstmt(nn) } case ONEW: if n.Esc == EscNone { r := temp(n.Type.Elem()) r = nod(OAS, r, nil) r = typecheck(r, ctxStmt) init.Append(r) r = nod(OADDR, r.Left, nil) r = typecheck(r, ctxExpr) n = r } else { n = callnew(n.Type.Elem()) } } } 不过这也不是绝对的，如果通过 var 或者 new 创建的变量不需要在当前作用域外生存，例如不用作为返回值返回给调用方，那么就不需要初始化在堆上。\n","permalink":"https://chasing1020.github.io/post/go3-common-keywords/","summary":"\u003ch1 id=\"part3-common-keyword\"\u003ePart3. Common Keyword\u003c/h1\u003e\n\u003ch2 id=\"1-for-and-range\"\u003e1. For And Range\u003c/h2\u003e\n\u003cp\u003e对于所有的 range 循环，Go 语言都会在编译期将原切片或者数组赋值给一个新变量 \u003ccode\u003eha\u003c/code\u003e，在赋值的过程中就发生了拷贝，而我们又通过 \u003ccode\u003elen\u003c/code\u003e 关键字预先获取了切片的长度，所以在循环中追加新的元素也不会改变循环执行的次数，这也就解释了循环永动机一节提到的现象。\u003c/p\u003e","title":"Go(3) Common Keywords"},{"content":"Part2. Language Basic 1. Function 1 2 3 4 type funcval struct { fn uintptr // variable-size, fn-specific data here } funcval使用二级指针，可以处理闭包（1.外部定义，内部引用的自由变量 2.脱离闭包上下文也能保留这些变量）\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func create() (fs [2]func()) { for i := 0; i \u0026lt; 2; i++ { fs[i] = func() { fmt.Println(i) } } return } // i 在堆区发生变量逃逸 func main() { fs := create() for i := 0 ; i\u0026lt;2; i++{ fs[i]() // 2 2 } } 函数调用一般分为两种情况：\n传值：函数调用时会对参数进行拷贝，被调用方和调用方两者持有不相关的两份数据； 传引用：函数调用时会传递参数的指针，被调用方和调用方两者持有相同的数据，任意一方做出的修改都会影响另一方。 不同语言会选择不同的方式传递参数，Go 语言选择了传值的方式，无论是传递基本类型、结构体还是指针，都会对传递的参数进行拷贝。\n整型和数组类型都是值传递的 传递结构体时：会拷贝结构体中的全部内容； 传递结构体指针时：会拷贝结构体指针； 综上所述：应该尽量使用指针作为参数类型来避免发生数据拷贝进而影响性能。\n2. Interface 接口一般有两类，eface为不带方法的接口，iface表示带方法的接口。\n2.1. Empty Interface 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 type eface struct { _type *_type data unsafe.Pointer } // Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize, // ../cmd/compile/internal/gc/reflect.go:/^func.dcommontype and // ../reflect/type.go:/^type.rtype. // ../internal/reflectlite/type.go:/^type.rtype. // 所有类的类型元信息 type _type struct { size uintptr ptrdata uintptr // size of memory prefix holding all pointers hash uint32 // hash 是对 _type.hash 的拷贝，当我们想将 interface 类型转换成具体类型时，可以使用该字段快速判断目标类型和具体类型是否一致 tflag tflag align uint8 fieldAlign uint8 kind uint8 // function for comparing objects of this type // (ptr to object A, ptr to object B) -\u0026gt; ==? equal func(unsafe.Pointer, unsafe.Pointer) bool // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff } func efaceOf(ep *interface{}) *eface { return (*eface)(unsafe.Pointer(ep)) } 1 2 3 4 5 6 7 8 9 10 11 12 13 type _interface struct { dynamicTypeInfo *_implementation dynamicValue unsafe.Pointer // unsafe.Pointer means // *ArbitraryType in Go. } type _implementation struct { itype *_type // the interface type. dtype *_type // the dynamic type, which must implement itype. methods []*_func // the methods which are defined on dtype // and each of them implements a // corresponding method declared in itype. } 其中，Go语言每一个新定义的数据类型都会有一个类型元数据记为_type，并记录该类型的大小，边界，名称，是否可比较等等\n同时可以使用type方法来自定义新的类型元数据\n1 2 type mytype1 = int32 type mytype2 int32 前者为起别名，都是同样的数据类型（rune和int32的实现），后者则会完全分配新的类型信息。\n2.2. Non-empty interface 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type iface struct { tab *itab data unsafe.Pointer } type itab struct { inter *interfacetype _type *_type hash uint32 _ [4]byte fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. } type interfacetype struct { typ _type pkgpath name mhdr []imethod } Go在运行时，itab会拷贝动态类型方法的地址，以便快速定位到方法，而不用每次都去类型元数据那里去找。\n同时，运行时，对于每一个itab结构体，Go会生成一个512个大小的hashmap，以接口类型为key，动态类型为value类型来缓存itab的信息，与哈希表的table进行异或运算，得到类型。\n1 2 3 4 5 6 7 8 9 10 11 12 const itabInitSize = 512 // Note: change the formula in the mallocgc call in itabAdd if you change these fields. type itabTableType struct { size uintptr // length of entries array. Always a power of 2. count uintptr // current number of filled entries. entries [itabInitSize]*itab // really [size] large } func itabHashFunc(inter *interfacetype, typ *_type) uintptr { // compiler has provided some good hash codes for us. return uintptr(inter.typ.hash ^ typ.hash) } 隐式类型转换\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package main import \u0026#34;fmt\u0026#34; type GrpcError struct{} func (e GrpcError) Error() string { return \u0026#34;GrpcError\u0026#34; } func main() { err := cal() fmt.Println(err) // 打印：\u0026lt;nil\u0026gt; fmt.Println(err == nil) // 打印：false } func cal() error { var err *GrpcError = nil return err } 在 main 中判断 err 是否为 nil，答案是 false。error 是一个非空 interface，底层数据结构是 iface，尽管 data 是 nil，但是 *itab 并不为空，所以 err == nil 答案为 false。\n2.3. Type Assert 类型断言一共有四种组合\n空接口.(具体类型) 因为每种数据类型的元类型都是全局唯一的，所以只需要比较_type指向的是不是断言的具体类型指针即可 非空接口.(具体类型) 比较\u0026amp;itab是不是断言类型的指针即可，这里的比较通过itabHashFunc函数实现，只需计算一次异或即可 空接口.(非空接口) 比较类型元数据是不是断言类型的指针，成功则会返回指向断言类型的itab结构体指针 非空接口.(空接口) 比较itab是不时断言类型的指针，且要求fun[0]!=0，表示方法都已经全部实现 此外实现接口的类型和初始化返回的类型两个维度共组成了四种情况：\n结构体实现接口 结构体指针实现接口 结构体初始化变量 通过 不通过 结构体指针初始化变量 通过 通过 3.Reflection 3.1. Implement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package runtime // Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize, // ../cmd/compile/internal/gc/reflect.go:/^func.dcommontype and // ../reflect/type.go:/^type.rtype. // ../internal/reflectlite/type.go:/^type.rtype. type _type struct { size uintptr ptrdata uintptr // size of memory prefix holding all pointers hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 // function for comparing objects of this type // (ptr to object A, ptr to object B) -\u0026gt; ==? equal func(unsafe.Pointer, unsafe.Pointer) bool // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. gcdata *byte str nameOff ptrToThis typeOff } runtime的包的_type类型是非导出的，所以在reflect包下重写了一遍，称为rtype，并实现了Type接口\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package reflect // rtype is the common implementation of most values. // It is embedded in other struct types. // // rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr // number of bytes in the type that can contain pointers hash uint32 // hash of type; avoids computation in hash tables tflag tflag // extra type information flags align uint8 // alignment of variable with this type fieldAlign uint8 // alignment of struct field with this type kind uint8 // enumeration for C // function for comparing objects of this type // (ptr to object A, ptr to object B) -\u0026gt; ==? equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte // garbage collection data str nameOff // string form ptrToThis typeOff // type for pointer to this type, may be zero } 3.2. The Lows Of Reflection Reference : https://go.dev/blog/laws-of-reflection\n1 2 3 4 5 6 7 8 9 10 type Type interface { Align() int FieldAlign() int Method(int) Method MethodByName(string) (Method, bool) // MethodByName 可以获取当前类型对应方法的引用 NumMethod() int ... Implements(u Type) bool // Implements 可以判断当前类型是否实现了某个接口 ... } 反射三大原则：\n从 interface{} 变量可以反射出反射对象； 从反射对象可以获取 interface{} 变量； 要修改反射对象，其值必须可设置； reflect.ValueOf(1) 时，虽然看起来是获取了基本类型 int 对应的反射类型，其实是由以下两个方法反射获取数据原变量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // ValueOf returns a new Value initialized to the concrete value // stored in the interface i. ValueOf(nil) returns the zero Value. func ValueOf(i interface{}) Value { if i == nil { return Value{} } // TODO: Maybe allow contents of a Value to live on the stack. // For now we make the contents always escape to the heap. It // makes life easier in a few places (see chanrecv/mapassign // comment below). escapes(i) return unpackEface(i) } // unpackEface converts the empty interface i to a Value. func unpackEface(i interface{}) Value { e := (*emptyInterface)(unsafe.Pointer(\u0026amp;i)) // NOTE: don\u0026#39;t read e.word until we know whether it is really a pointer or not. t := e.typ if t == nil { return Value{} } f := flag(t.Kind()) if ifaceIndir(t) { f |= flagIndir } return Value{t, e.word, f} } 反射方法中，*emptyInterface其实就是eface的重写\n1 2 3 4 5 6 7 8 9 10 11 12 // TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(\u0026amp;i)) return toType(eface.typ) } func toType(t *rtype) Type { if t == nil { return nil } return t } 通过反射获取interface变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 type flag uintptr // To compare two Values, compare the results of the Interface method. // Using == on two Values does not compare the underlying values // they represent. type Value struct { // typ holds the type of the value represented by a Value. typ *rtype // Pointer-valued data or, if flagIndir is set, pointer to data. // Valid when either flagIndir is set or typ.pointers() is true. ptr unsafe.Pointer // flag holds metadata about the value. flag } // Interface returns v\u0026#39;s current value as an interface{}. // It is equivalent to: // var i interface{} = (v\u0026#39;s underlying value) // It panics if the Value was obtained by accessing // unexported struct fields. func (v Value) Interface() (i interface{}) { return valueInterface(v, true) } 1 2 v := reflect.ValueOf(1) v.Interface().(int) 从接口值到反射对象： 从基本类型到接口类型的类型转换； 从接口类型到反射对象的转换； 从反射对象到接口值： 反射对象转换成接口类型； 通过显式类型转换变成原始类型； 可以通过以下这张图展示数据类型之间的关系 Go语言修改反射后变量只能通过如下方法来实现：\n1 2 3 4 5 6 func main() { i := 1 v := reflect.ValueOf(\u0026amp;i) v.Elem().SetInt(10) fmt.Println(i) } 调用 reflect.ValueOf 获取变量指针； 调用 reflect.Value.Elem 获取指针指向的变量； 调用 reflect.Value.SetInt 更新变量的值：\n3.3. Type And Value Go 语言的 interface{}类型在语言内部是通过 reflect.emptyInterface结体表示的，其中的 rtype 字段用于表示变量的类型，另一个 word 字段指向内部封装的数据：\n1 2 3 4 type emptyInterface struct { typ *rtype word unsafe.Pointer } 用于获取变量类型的 reflect.TypeOf函数将传入的变量隐式转换成 reflect.emptyInterface类型并获取其中存储的类型信息 reflect.rtype：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(\u0026amp;i)) return toType(eface.typ) } func toType(t *rtype) Type { if t == nil { return nil } return t } func (t *rtype) String() string { s := t.nameOff(t.str).name() if t.tflag\u0026amp;tflagExtraStar != 0 { return s[1:] } return s } reflect.rtype 是一个实现了 reflect.Type 接口的结构体，该结构体实现的 reflect.rtype.String方法可以帮助我们获取当前类型的名称：\n1 2 3 4 5 6 7 func (t *rtype) String() string { s := t.nameOff(t.str).name() if t.tflag\u0026amp;tflagExtraStar != 0 { return s[1:] } return s } reflect.TypeOf的实现原理其实并不复杂，它只是将一个 interface{} 变量转换成了内部的 reflect.emptyInterface 表示，然后从中获取相应的类型信息。\n使用reflect.ValueOf()获取reflect.Value类型，这里先使用了escapes将变量逃逸到堆上，再获取结构体\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 func ValueOf(i interface{}) Value { if i == nil { return Value{} } escapes(i) return unpackEface(i) } // Dummy annotation marking that the value x escapes, // for use in cases where the reflect code is so clever that // the compiler cannot follow. func escapes(x interface{}) { if dummy.b { dummy.x = x } } //dummy 变量就是一个虚拟标注，标记入参 x 逃逸了。这样标记是为了防止反射代码写的过于高级，以至于编译器跟不上了。 var dummy struct { b bool x interface{} } func unpackEface(i interface{}) Value { e := (*emptyInterface)(unsafe.Pointer(\u0026amp;i)) t := e.typ if t == nil { return Value{} } f := flag(t.Kind()) if ifaceIndir(t) { f |= flagIndir } return Value{t, e.word, f} } type Value struct { // typ holds the type of the value represented by a Value. typ *rtype // 类型元数据指针 ptr unsafe.Pointer // 数据地址 flag // 位标识符信息 } 使用反射运行方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func Add(a, b int) int { return a + b } func main() { v := reflect.ValueOf(Add) if v.Kind() != reflect.Func { return } t := v.Type() argv := make([]reflect.Value, t.NumIn()) for i := range argv { if t.In(i).Kind() != reflect.Int { return } argv[i] = reflect.ValueOf(i) } result := v.Call(argv) if len(result) != 1 || result[0].Kind() != reflect.Int { return } fmt.Println(result[0].Int()) // #=\u0026gt; 1 } ","permalink":"https://chasing1020.github.io/post/go2-language-basic/","summary":"\u003ch1 id=\"part2-language-basic\"\u003ePart2. Language Basic\u003c/h1\u003e\n\u003ch2 id=\"1-function\"\u003e1. Function\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003efuncval\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estruct\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nx\"\u003efn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euintptr\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"c1\"\u003e// variable-size, fn-specific data here\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003efuncval使用二级指针，可以处理闭包（1.外部定义，内部引用的自由变量 2.脱离闭包上下文也能保留这些变量）\u003c/p\u003e","title":"Go(2) Language Basic"},{"content":"Part1. Builtin Data Structure 0. Type 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package builtin type bool bool const ( true = 0 == 0 // Untyped bool. false = 0 != 0 // Untyped bool. ) type uint8 uint8 type uint16 uint16 type uint32 uint32 type uint64 uint64 type int8 int8 type int16 int16 type int32 int32 type int64 int64 type float32 float32 type float64 float64 type complex64 complex64 type complex128 complex128 type string string type int int type uint uint type uintptr uintptr // byte is an alias for uint8 and is equivalent to uint8 in all ways. type byte = uint8 // rune is an alias for int32 and is equivalent to int32 in all ways. type rune = int32 const iota = 0 // Untyped int. // nil is a predeclared identifier representing the zero value for a // pointer, channel, func, interface, map, or slice type. var nil Type // Type must be a pointer, channel, func, interface, map, or slice type type Type int type Type1 int type IntegerType int type FloatType float32 type ComplexType complex64 func append(slice []Type, elems ...Type) []Type func copy(dst, src []Type) int func delete(m map[Type]Type1, key Type) func len(v Type) int func cap(v Type) int func make(t Type, size ...IntegerType) Type func new(Type) *Type func complex(r, i FloatType) ComplexType func real(c ComplexType) FloatType func imag(c ComplexType) FloatType func close(c chan\u0026lt;- Type) func panic(v interface{}) func recover() interface{} func print(args ...Type) func println(args ...Type) type error interface { Error() string } 对于类型的定义：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package types // A Type represents a Go type. type Type struct { Extra interface{} Width int64 // valid if Align \u0026gt; methods Fields allMethods Fields Nod *Node // canonical OTYPE node Orig *Type // original type (type literal or predefined type) // Cache of composite types, with this type being the element type. Cache struct { ptr *Type // *T, or nil slice *Type // []T, or nil } Sym *Sym // symbol containing name, for named types Vargen int32 // unique name for OTYPE/ONAME Etype EType // kind of type Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed) flags bitset8 } 特别的，在IEEE-754中，对于NaN的定义分为sNaN和qNaN。\nsNaN代表无效的操作，指数位全是1，小数位第一位是0\nqNaN代表无效或者异常的结果，指数位全是1，小数位第一位是1\n1. Array 1.1. New Array 1 2 3 4 5 // Array contains Type fields specific to array types. type Array struct { Elem *Type // element type Bound int64 // number of elements; \u0026lt;0 if unknown yet } 数组创建时会定义好两个部分：分别是元素类型 Elem 和数组的大小 Bound，使用[\u0026hellip;]初始化时，编译器会对数组大小自行推导，与使用固定数组作用相同，只是作为一种语法糖使用。\n1 2 3 4 5 6 7 8 9 10 // NewArray returns a new fixed-length array Type. func NewArray(elem *Type, bound int64) *Type { if bound \u0026lt; 0 { Fatalf(\u0026#34;NewArray: invalid bound %v\u0026#34;, bound) } t := New(TARRAY) // retrun types.Type t.Extra = \u0026amp;Array{Elem: elem, Bound: bound} t.SetNotInHeap(elem.NotInHeap()) return t } 在新建数组时，编译器会做出优化\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if var_.isSimpleName() \u0026amp;\u0026amp; n.List.Len() \u0026gt; 4 { // lay out static data vstat := readonlystaticname(t) ctxt := inInitFunction if n.Op == OARRAYLIT { ctxt = inNonInitFunction } fixedlit(ctxt, initKindStatic, n, vstat, init) // copy static to var a := nod(OAS, var_, vstat) a = typecheck(a, ctxStmt) a = walkexpr(a, init) init.Append(a) // add expressions to automatic fixedlit(inInitFunction, initKindDynamic, n, var_, init) break } 当元素数量小于或者等于 4 个时，会直接将数组中的元素放置在栈上，优化为1-4个简单的赋值语句； 当元素数量大于 4 个时，会将数组中的元素放置到静态区并在运行时取出到栈； 1.2. Array Access 在运行时，会通过cmd/compile/internal/gc.typecheck1实时检查数组的可用范围\n访问数组的索引是非整数时，报错 “non-integer array index %v”； 访问数组的索引是负数时，报错 “invalid array index %v (index must be non-negative)\u0026quot;； 访问数组的索引越界时，报错 “invalid array index %v (out of bounds for %d-element array)\u0026quot;； 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if n.Right.Type != nil \u0026amp;\u0026amp; !n.Right.Type.IsInteger() { yyerror(\u0026#34;non-integer %s index %v\u0026#34;, why, n.Right) break } if !n.Bounded() \u0026amp;\u0026amp; Isconst(n.Right, CTINT) { x := n.Right.Int64Val() if x \u0026lt; 0 { yyerror(\u0026#34;invalid %s index %v (index must be non-negative)\u0026#34;, why, n.Right) } else if t.IsArray() \u0026amp;\u0026amp; x \u0026gt;= t.NumElem() { yyerror(\u0026#34;invalid array index %v (out of bounds for %d-element array)\u0026#34;, n.Right, t.NumElem()) } else if Isconst(n.Left, CTSTR) \u0026amp;\u0026amp; x \u0026gt;= int64(len(n.Left.StringVal())) { yyerror(\u0026#34;invalid string index %v (out of bounds for %d-byte string)\u0026#34;, n.Right, len(n.Left.StringVal())) } else if n.Right.Val().U.(*Mpint).Cmp(maxintval[TINT]) \u0026gt; 0 { yyerror(\u0026#34;invalid %s index %v (index too large)\u0026#34;, why, n.Right) } } 一旦发生非法访问，立即会报出panic触发运行时错误并退出程序\n1 2 3 4 func goPanicIndex(x int, y int) { panicCheck1(getcallerpc(), \u0026#34;index out of range\u0026#34;) panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex}) } 2. Slice 2.1. New Slice 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // Fields is a pointer to a slice of *Field. // This saves space in Types that do not have fields or methods // compared to a simple slice of *Field. type Fields struct { s *[]*Field } // A Field represents a field in a struct or a method in an interface or // associated with a named type. type Field struct { flags bitset8 Embedded uint8 // embedded field Pos src.XPos Sym *Sym Type *Type // field type Note string // literal string annotation // For fields that represent function parameters, Nname points // to the associated ONAME Node. Nname *Node // Offset in bytes of this field or method within its enclosing struct // or interface Type. Offset int64 } // Slice returns the entries in f as a slice. // Changes to the slice entries will be reflected in f. func (f *Fields) Slice() []*Field { if f.s == nil { return nil } return *f.s } 编译期间的切片类型如上所示，创建新切片的方法如下\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // NewSlice returns the slice Type with element type elem. func NewSlice(elem *Type) *Type { if t := elem.Cache.slice; t != nil { if t.Elem() != elem { Fatalf(\u0026#34;elem mismatch\u0026#34;) } return t } t := New(TSLICE) t.Extra = Slice{Elem: elem} elem.Cache.slice = t return t } 但是在运行时，会自动转换为如下的数据结构：\n1 2 3 4 5 6 7 8 9 10 11 // SliceHeader is the runtime representation of a slice. // It cannot be used safely or portably and its representation may // change in a later release. // Moreover, the Data field is not sufficient to guarantee the data // it references will not be garbage collected, so programs must keep // a separate, correctly typed pointer to the underlying data. type SliceHeader struct { Data uintptr Len int Cap int } 其中Data是一片连续的内存空间，这片内存空间可以用于存储切片中的全部元素，且Data不必是连续空间的第一个元素。\n通过下标的方式获得数组或者切片的一部分：arr[0:3] or slice[0:3]\n会使用SliceMake 操作会，接受四个参数创建新的切片，元素类型、数组指针、切片大小和容量\n1 2 3 nums := [...]int{0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9} slice := nums[3:5] fmt.Println(len(slice), cap(slice)) // 2 8 ​ 创建后，长度默认是切片范围，但是容量会一直从切片起始值延续到数组的最后一位 2. 使用字面量初始化新的切片：slice := []int{1, 2, 3}\n创建一个数组并初始化，将静态存储区的数组，再用[:]获得一个切片 使用关键字 make 创建切片：slice := make([]int, 2, 5)，这样Len=2，数组里前两个元素是默认零值的，剩余三个元素保留为0，并非真正可访问的值，同时在创建时会发生以下校验： 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if i \u0026gt;= len(args) { yyerror(\u0026#34;missing len argument to make(%v)\u0026#34;, t) return n } l = args[i] i++ var r *Node if i \u0026lt; len(args) { r = args[i] } ... if Isconst(l, CTINT) \u0026amp;\u0026amp; r != nil \u0026amp;\u0026amp; Isconst(r, CTINT) \u0026amp;\u0026amp; l.Val().U.(*Mpint).Cmp(r.Val().U.(*Mpint)) \u0026gt; 0 { yyerror(\u0026#34;len larger than cap in make(%v)\u0026#34;, t) return n } n.Left = l n.Right = r n.Op = OMAKESLICE 检查切片的大小和容量是否足够小，切片是否发生了逃逸等，最终在堆上初始化。\n使用new关键字创建切片：slice := new([]string)，这样slice不会为Data分配底层的数组，标记为nil，在后续通过append会分配数组 1 2 slice := new([]string) // Can\u0026#39;t (*slice)[0] = \u0026#34;abc\u0026#34; 如果使用[]int{1, 2, 3}这样的字面量初始化，会创建一个array存储到静态区，程序启动时，将静态区的数据拷贝到堆区。\n2.2. Slice Grow 使用append扩容，如果没有覆盖原始变量，则直接返回\n1 2 3 4 5 6 7 8 9 10 11 // append(slice, 1, 2, 3) ptr, len, cap := slice newlen := len + 3 if newlen \u0026gt; cap { ptr, len, cap = growslice(slice, newlen) newlen = len + 3 } *(ptr+len) = 1 *(ptr+len+1) = 2 *(ptr+len+2) = 3 return makeslice(ptr, newlen, cap) 但是如果需要扩容后的结果进行赋值，则会变成如下状态\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // slice = append(slice, 1, 2, 3) a := \u0026amp;slice ptr, len, cap := slice newlen := len + 3 if uint(newlen) \u0026gt; uint(cap) { newptr, len, newcap = growslice(slice, newlen) vardef(a) *a.cap = newcap *a.ptr = newptr } newlen = len + 3 *a.len = newlen *(ptr+len) = 1 *(ptr+len+1) = 2 *(ptr+len+2) = 3 在cap不够时，发生扩容的规则如下\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func growslice(et *_type, old slice, cap int) slice { newcap := old.cap doublecap := newcap + newcap if cap \u0026gt; doublecap { newcap = cap } else { if old.len \u0026lt; 1024 { newcap = doublecap } else { for 0 \u0026lt; newcap \u0026amp;\u0026amp; newcap \u0026lt; cap { newcap += newcap / 4 } if newcap \u0026lt;= 0 { newcap = cap } } } } 如果期望容量大于当前容量的两倍就会使用期望容量； 如果当前切片的长度小于 1024 就会将容量翻倍； 如果当前切片的长度大于 1024 就会每次增加 25% 的容量，直到新容量大于期望容量； 如果最终容量过大导致溢出，则直接将最终容量设置为新申请容量。 此外，分配空间时，会考虑到内存对齐问题。会选择向上取整的内存段为切片分配，提高内存分配效率以减少外部碎片。\n比如当运行如下代码：\n1 2 var arr []int64 arr = append(arr, 1, 2, 3, 4, 5) 分配的期望cap是5，需要40个字节，会调用roundupsize，调整到合适的字节大小48。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // Returns size of the memory block that mallocgc will allocate if you ask for the size. func roundupsize(size uintptr) uintptr { if size \u0026lt; _MaxSmallSize { if size \u0026lt;= smallSizeMax-8 { return uintptr(class_to_size[size_to_class8[divRoundUp(size, smallSizeDiv)]]) } else { return uintptr(class_to_size[size_to_class128[divRoundUp(size-smallSizeMax, largeSizeDiv)]]) } } if size+_PageSize \u0026lt; size { return size } return alignUp(size, _PageSize) } 以下为判断是否需要发生扩容的代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var overflow bool var lenmem, newlenmem, capmem uintptr switch { case et.size == 1: lenmem = uintptr(old.len) newlenmem = uintptr(cap) capmem = roundupsize(uintptr(newcap)) overflow = uintptr(newcap) \u0026gt; maxAlloc newcap = int(capmem) case et.size == sys.PtrSize: lenmem = uintptr(old.len) * sys.PtrSize newlenmem = uintptr(cap) * sys.PtrSize capmem = roundupsize(uintptr(newcap) * sys.PtrSize) overflow = uintptr(newcap) \u0026gt; maxAlloc/sys.PtrSize newcap = int(capmem / sys.PtrSize) case isPowerOfTwo(et.size): ... default: ... } const _MaxSmallSize = 32768 var class_to_size = [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, ...,} 2.3. Slice Copy 需要发生切片拷贝时，非运行时拷贝会调用以下代码：\n1 2 3 4 5 6 7 n := len(a) if n \u0026gt; len(b) { n = len(b) } if a.ptr != b.ptr { memmove(a.ptr, b.ptr, n*sizeof(elem(a))) } 但是在运行时调用copy(a, b)时，编译器会优化为以下函数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func slicecopy(to, fm slice, width uintptr) int { if fm.len == 0 || to.len == 0 { return 0 } n := fm.len if to.len \u0026lt; n { n = to.len } if width == 0 { return n } // ... size := uintptr(n) * width if size == 1 { *(*byte)(to.array) = *(*byte)(fm.array) } else { memmove(to.array, fm.array, size) } return n } 两种拷贝都会调用memmove函数，将整块内存的内容拷贝到目标的内存区域中（如果目标内存不足就不会完全拷贝），这个函数以汇编方式实现。在大切片上执行拷贝要注意对性能的影响。\n3. Map 3.1. New Map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type hmap struct { count int // count 表示当前哈希表中的元素数量； flags uint8 B uint8 // B 表示当前哈希表持有的 buckets 数量，但是因为哈希表中桶的数量都 2 的倍数，所以该字段会存储对数，也就是 len(buckets) == 2^B； noverflow uint16 hash0 uint32 // hash0 是哈希的种子，它能为哈希函数的结果引入随机性，这个值在创建哈希表时确定，并在调用哈希函数时作为参数传入； buckets unsafe.Pointer oldbuckets unsafe.Pointer // oldbuckets 是哈希在扩容时用于保存之前 buckets 的字段，它的大小是当前 buckets 的一半； nevacuate uintptr extra *mapextra // 指向下一个溢出桶的地址 } type mapextra struct { overflow *[]*bmap // 已经使用的溢出桶 oldoverflow *[]*bmap nextOverflow *bmap // 下一个空闲的溢出桶 } hashmap的桶采用如下数据结构，每个桶可以存储8个键值对，桶的数量一定是2^B，\n并采用\u0026amp;运算符来选择桶\n1 2 3 4 5 6 7 8 9 10 11 12 // A bucket for a Go map. type bmap struct { // tophash generally contains the top byte of the hash value // for each key in this bucket. If tophash[0] \u0026lt; minTopHash, // tophash[0] is a bucket evacuation state instead. tophash [bucketCnt]uint8 // Followed by bucketCnt keys and then bucketCnt elems. // NOTE: packing all the keys together and then all the elems together makes the // code a bit more complicated than alternating key/elem/key/elem/... but it allows // us to eliminate padding which would be needed for, e.g., map[int64]int8. // Followed by an overflow pointer. } 在运行时，bmap中的其他字段在运行时也都是通过计算内存地址的方式访问的，因为kv的关系\n自动转化成如下数据结构，为达到内存对齐，topbits，keys，values的数组分别按如下方式存放\n1 2 3 4 5 6 7 type bmap struct { topbits [8]uint8 keys [8]keytype values [8]valuetype pad uintptr overflow uintptr // 指向下一个需要扩容的桶 } 使用字面量初始化的方式最终都会通过maplit进行初始化：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func maplit(n *Node, m *Node, init *Nodes) { a := nod(OMAKE, nil, nil) a.Esc = n.Esc a.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len()))) litas(m, a, init) entries := n.List.Slice() if len(entries) \u0026gt; 25 { // For a large number of entries, put them in an array and loop. // build types [count]Tindex and [count]Tvalue tk := types.NewArray(n.Type.Key(), int64(len(entries))) te := types.NewArray(n.Type.Elem(), int64(len(entries))) ... return } // Build list of var[c] = expr. // Use temporaries so that mapassign1 can have addressable key, elem. ... } 当哈希表中的元素数量少于或者等于 25 个时，编译器会将字面量初始化的结构体优化。将所有的键值对一次加入到哈希表中。一旦哈希表中元素的数量超过了 25 个，编译器会创建两个数组分别存储键和值，这些键值对则会通过 for 循环的方式加入哈希。\n运行时，调用makemap函数完成map初始化。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func makemap(t *maptype, hint int, h *hmap) *hmap { mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size) if overflow || mem \u0026gt; maxAlloc { hint = 0 } if h == nil { h = new(hmap) } h.hash0 = fastrand() B := uint8(0) for overLoadFactor(hint, B) { B++ } h.B = B if h.B != 0 { var nextOverflow *bmap h.buckets, nextOverflow = makeBucketArray(t, h.B, nil) if nextOverflow != nil { h.extra = new(mapextra) h.extra.nextOverflow = nextOverflow } } return h } 计算哈希占用的内存是否溢出或者超出能分配的最大值； 调用 runtime.fastrand 获取一个随机的哈希种子； 根据传入的 hint 计算出需要的最小需要的桶的数量； 使用 runtime.makeBucketArray 创建用于保存桶的数组； 其中makeBucketArray会使用如下方法分配连续空间初始化数据\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func makeBucketArray(t *maptype, b uint8, dirtyalloc unsafe.Pointer) (buckets unsafe.Pointer, nextOverflow *bmap) { base := bucketShift(b) nbuckets := base if b \u0026gt;= 4 { // 大于4个，大概率会发生扩容，先分配2^(b-4)个空桶以供备用 nbuckets += bucketShift(b - 4) sz := t.bucket.size * nbuckets up := roundupsize(sz) if up != sz { nbuckets = up / t.bucket.size } } buckets = newarray(t.bucket, int(nbuckets)) if base != nbuckets { nextOverflow = (*bmap)(add(buckets, base*uintptr(t.bucketsize))) last := (*bmap)(add(buckets, (nbuckets-1)*uintptr(t.bucketsize))) last.setoverflow(t, (*bmap)(buckets)) } return buckets, nextOverflow } 3.2. Map Access 当左侧使用一个或两个接收值时，分别会对应到两个方法：\n1 2 v := hash[key] // =\u0026gt; v := *mapaccess1(maptype, hash, \u0026amp;key) v, ok := hash[key] // =\u0026gt; v, ok := mapaccess2(maptype, hash, \u0026amp;key) 其中两个函数如下，mapaccess2在注释中加入两个情况的返回值\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { alg := t.key.alg hash := alg.hash(key, uintptr(h.hash0)) m := bucketMask(h.B) b := (*bmap)(add(h.buckets, (hash\u0026amp;m)*uintptr(t.bucketsize))) top := tophash(hash) bucketloop: for ; b != nil; b = b.overflow(t) { for i := uintptr(0); i \u0026lt; bucketCnt; i++ { if b.tophash[i] != top { if b.tophash[i] == emptyRest { break bucketloop } continue } k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize)) if alg.equal(key, k) { v := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.valuesize)) return v //, true } } } return unsafe.Pointer(\u0026amp;zeroVal[0]) //, false } 3.3. Map Grow 当发生以下两种情况时开始发生Map扩容，每次扩容都为原先的两倍，原来的桶会分流到两个新桶中\n装载因子已经超过 6.5；其中负载因子的定义如下：LoadFactor = count / m 哈希使用了太多溢出桶； 1 2 3 4 5 6 7 8 func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { ... if !h.growing() \u0026amp;\u0026amp; (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { hashGrow(t, h) goto again } ... } B \u0026lt;= 15，触发翻倍扩容，B \u0026gt; 15，触发等量扩容（有较多键值被删除）\n3.4. Map Delete Map的删除逻辑与写入逻辑很相似，只是触发哈希的删除需要使用关键字，如果在删除期间遇到了哈希表的扩容，就会分流桶中的元素，分流结束之后会找到桶中的目标元素完成键值对的删除工作。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func walkexpr(n *Node, init *Nodes) *Node { switch n.Op { case ODELETE: init.AppendNodes(\u0026amp;n.Ninit) map_ := n.List.First() key := n.List.Second() map_ = walkexpr(map_, init) key = walkexpr(key, init) t := map_.Type fast := mapfast(t) if fast == mapslow { key = nod(OADDR, key, nil) } n = mkcall1(mapfndel(mapdelete[fast], t), nil, init, typename(t), map_, key) } } 4. String 4.1. New String 1 2 3 4 5 6 7 8 9 10 // StringHeader is the runtime representation of a string. // It cannot be used safely or portably and its representation may // change in a later release. // Moreover, the Data field is not sufficient to guarantee the data // it references will not be garbage collected, so programs must keep // a separate, correctly typed pointer to the underlying data. type StringHeader struct { Data uintptr Len int } // 切片在 Go 语言的运行时表示与字符串高度相似，经常认为字符串是一个只读的切片类型 Go 语言不支持直接修改 string 类型变量的内存空间，我们仍然可以通过在 string 和 []byte 类型之间反复转换实现修改这一目的：\n先将这段内存拷贝到堆或者栈上； 将变量的类型转换成 []byte 后并修改字节数据； 将修改后的字节数组转换回 string； 使用反引号创建的字符串会在编译时进行解析\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func (s *scanner) stdString() { s.startLit() for { r := s.getr() if r == \u0026#39;\u0026#34;\u0026#39; { break } if r == \u0026#39;\\\\\u0026#39; { s.escape(\u0026#39;\u0026#34;\u0026#39;) continue } if r == \u0026#39;\\n\u0026#39; { s.ungetr() s.error(\u0026#34;newline in string\u0026#34;) break } if r \u0026lt; 0 { s.errh(s.line, s.col, \u0026#34;string not terminated\u0026#34;) break } } s.nlsemi = true s.lit = string(s.stopLit()) s.kind = StringLit s.tok = _Literal } 标准字符串使用双引号表示开头和结尾； 标准字符串需要使用反斜杠 \\ 来逃逸双引号； 标准字符串不能出现如下所示的隐式换行 \\n； 无论是标准字符串还是原始字符串都会被标记成 StringLit 并传递到语法分析阶段。在语法分析阶段，与字符串相关的表达式都会由以下函数处理。\n1 2 3 4 5 6 7 8 9 10 func (p *noder) basicLit(lit *syntax.BasicLit) Val { switch s := lit.Value; lit.Kind { case syntax.StringLit: if len(s) \u0026gt; 0 \u0026amp;\u0026amp; s[0] == \u0026#39;`\u0026#39; { s = strings.Replace(s, \u0026#34;\\r\u0026#34;, \u0026#34;\u0026#34;, -1) } u, _ := strconv.Unquote(s) return Val{U: u} } } 4.2. Concat String 在编译的AST阶段，调用strings.Join函数来完成字符串常量数组的拼接。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func concatstrings(buf *tmpBuf, a []string) string { idx := 0 l := 0 count := 0 for i, x := range a { n := len(x) if n == 0 { continue } l += n count++ idx = i } if count == 0 { return \u0026#34;\u0026#34; } if count == 1 \u0026amp;\u0026amp; (buf != nil || !stringDataOnStack(a[idx])) { return a[idx] } s, b := rawstringtmp(buf, l) for _, x := range a { copy(b, x) b = b[len(x):] } return s } 但是在正常情况下，运行时会调用 copy 将输入的多个字符串拷贝到目标字符串所在的内存空间。新的字符串是一片新的内存空间，与原来的字符串也没有任何关联，一旦需要拼接的字符串非常大，拷贝带来的性能损失是无法忽略的。\n4.3. Type Cast 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func slicebytetostring(buf *tmpBuf, b []byte) (str string) { l := len(b) if l == 0 { return \u0026#34;\u0026#34; } if l == 1 { stringStructOf(\u0026amp;str).str = unsafe.Pointer(\u0026amp;staticbytes[b[0]]) stringStructOf(\u0026amp;str).len = 1 return } var p unsafe.Pointer if buf != nil \u0026amp;\u0026amp; len(b) \u0026lt;= len(buf) { p = unsafe.Pointer(buf) } else { p = mallocgc(uintptr(len(b)), nil, false) } stringStructOf(\u0026amp;str).str = p stringStructOf(\u0026amp;str).len = len(b) memmove(p, (*(*slice)(unsafe.Pointer(\u0026amp;b))).array, uintptr(len(b))) return } 处理过后会根据传入的缓冲区大小决定是否需要为新字符串分配一片内存空间，runtime.stringStructOf 会将传入的字符串指针转换成 runtime.stringStruct 结构体指针，然后设置结构体持有的字符串指针 str 和长度 len，最后通过 runtime.memmove 将原 []byte 中的字节全部复制到新的内存空间中。\n1 2 3 4 5 6 7 8 9 10 11 func stringtoslicebyte(buf *tmpBuf, s string) []byte { var b []byte if buf != nil \u0026amp;\u0026amp; len(s) \u0026lt;= len(buf) { *buf = tmpBuf{} b = buf[:len(s)] } else { b = rawbyteslice(len(s)) } copy(b, s) return b } 当传入缓冲区时，它会使用传入的缓冲区存储 []byte； 当没有传入缓冲区时，运行时会调用 runtime.rawbyteslice 创建新的字节切片并将字符串中的内容拷贝过去；\n","permalink":"https://chasing1020.github.io/post/go1-builtin-data-structure/","summary":"\u003ch1 id=\"part1-builtin-data-structure\"\u003ePart1. Builtin Data Structure\u003c/h1\u003e\n\u003ch2 id=\"0-type\"\u003e0. Type\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e39\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e40\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e41\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e42\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e43\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e44\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e45\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e46\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e47\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e48\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e49\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e50\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e51\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e52\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e53\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e54\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003epackage\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ebuiltin\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebool\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// Untyped bool.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// Untyped bool.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint8\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint8\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint16\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint16\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint32\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint64\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint64\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint8\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint8\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint16\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint16\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint32\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint64\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint64\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003efloat32\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003efloat32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003efloat64\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003efloat64\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ecomplex64\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ecomplex64\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ecomplex128\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ecomplex128\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euintptr\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euintptr\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// byte is an alias for uint8 and is equivalent to uint8 in all ways.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003euint8\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// rune is an alias for int32 and is equivalent to int32 in all ways.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003erune\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003eiota\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// Untyped int.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// nil is a predeclared identifier representing the zero value for a\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// pointer, channel, func, interface, map, or slice type.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enil\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"c1\"\u003e// Type must be a pointer, channel, func, interface, map, or slice type\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType1\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eIntegerType\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eFloatType\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003efloat32\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eComplexType\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003ecomplex64\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eappend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eslice\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eelems\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003ecopy\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edst\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esrc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[]\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003edelete\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003em\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"nx\"\u003eType1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ev\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003ecap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ev\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003emake\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003et\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003esize\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003eIntegerType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003ecomplex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003er\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eFloatType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eComplexType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003ereal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ec\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eComplexType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eFloatType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eimag\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ec\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eComplexType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eFloatType\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ec\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003echan\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;-\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003epanic\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ev\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003einterface\u003c/span\u003e\u003cspan class=\"p\"\u003e{})\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003erecover\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003einterface\u003c/span\u003e\u003cspan class=\"p\"\u003e{}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003efunc\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nb\"\u003eprintln\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eargs\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003eType\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003etype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eerror\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003einterface\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"nf\"\u003eError\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003estring\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e对于类型的定义：\u003c/p\u003e","title":"Go(1) Builtin Data Structure"},{"content":"Part0. Compilers Principles 1. Stage 编译器技术影响了计算机的体系结构，同时也受到体系结构发展的影响。\n体系结构的很多现代创新都依赖于编译器能从源程序中抽取出有效利用硬件能力的机会。\nGo语言的编译执行流程阶段：词法解析、语法分析、AST构建、类型检查、变量捕获、函数内联、逃逸分析、闭包重写、遍历函数、SSA生成、机器码生成。\n2. Lexical Analysis Lex 是用于生成词法分析器的工具，Lex 生成的代码能够将一个文件中的字符分解成 Token 序列，很多语言在设计早期都会使用它快速设计出原型。词法分析作为具有固定模式的任务，出现这种更抽象的工具必然的，Lex 作为一个代码生成器，使用了类似 C 语言的语法，可以将 Lex 理解为正则匹配的生成器，它会使用正则匹配扫描输入的字符流。这里以最简单的helloworld程序为例：\n1 2 3 4 5 6 7 8 9 package main import ( \u0026#34;fmt\u0026#34; ) func main() { fmt.Println(\u0026#34;Hello, world!\u0026#34;) } 使用lex命令转化为C语言代码：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int yylex (void) { ... while ( 1 ) { ... yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state \u0026gt;= 30 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 37 ); ... do_action: switch ( yy_act ) case 0: ... case 1: YY_RULE_SETUP printf(\u0026#34;PACKAGE \u0026#34;); YY_BREAK ... } 除了基本的宏定义以外，全部函数都使用有限自动机模型来解析输入的字符串，将上述Go代码作为lex输入可得：\n1 2 3 4 5 6 7 8 9 PACKAGE IDENT IMPORT LPAREN QUOTE IDENT QUOTE RPAREN IDENT IDENT LPAREN RPAREN LBRACE IDENT DOT IDENT LPAREN QUOTE IDENT QUOTE RPAREN RBRACE Go语言的词法解析通过scanner结构体实现\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type scanner struct { source mode uint nlsemi bool // current token, valid after calling next() line, col uint blank bool // line is blank up to col tok token lit string // valid if tok is _Name, _Literal, or _Semi (\u0026#34;semicolon\u0026#34;, \u0026#34;newline\u0026#34;, or \u0026#34;EOF\u0026#34;); may be malformed if bad is true bad bool // valid if tok is _Literal, true if a syntax error occurred, lit may be malformed kind LitKind // valid if tok is _Literal op Operator // valid if tok is _Operator, _AssignOp, or _IncOp prec int // valid if tok is _Operator, _AssignOp, or _IncOp } 其定义的next获取未解析的下一个字符，根据字符类型决定下一个case。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func (s *scanner) next() { ... s.stop() startLine, startCol := s.pos() for s.ch == \u0026#39; \u0026#39; || s.ch == \u0026#39;\\t\u0026#39; || s.ch == \u0026#39;\\n\u0026#39; \u0026amp;\u0026amp; !nlsemi || s.ch == \u0026#39;\\r\u0026#39; { s.nextch() } s.line, s.col = s.pos() s.blank = s.line \u0026gt; startLine || startCol == colbase s.start() if isLetter(s.ch) || s.ch \u0026gt;= utf8.RuneSelf \u0026amp;\u0026amp; s.atIdentChar(true) { s.nextch() s.ident() return } switch s.ch { case -1: s.tok = _EOF case \u0026#39;0\u0026#39;, \u0026#39;1\u0026#39;, \u0026#39;2\u0026#39;, \u0026#39;3\u0026#39;, \u0026#39;4\u0026#39;, \u0026#39;5\u0026#39;, \u0026#39;6\u0026#39;, \u0026#39;7\u0026#39;, \u0026#39;8\u0026#39;, \u0026#39;9\u0026#39;: s.number(false) ... } } 此外，在go/scanner和go/token也提供了一些接口用以扫描源代码，示例如下\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main() { src := []byte(\u0026#34;cos(x)+2i*sin(x)\u0026#34;) //So God exists var s scanner.Scanner fset := token.NewFileSet() file := fset.AddFile(\u0026#34;\u0026#34;, fset.Base(), len(src)) s.Init(file, src, nil, scanner.ScanComments) for { pos, tok, lit := s.Scan() if tok == token.EOF { break } fmt.Printf(\u0026#34;%s\\t%s\\t%q\\n\u0026#34;, fset.Position(pos), tok, lit) } } 3. Syntax Analysis 3.1. Definition 一般使用一个四元组(N, Σ, P, S)来定义一个文法，其作为形式化描述语言的有效工具。\nN 有限个非终结符的集合； Σ 有限个终结符的集合； P 有限个生产规则的集合； S 非终结符集合中唯一的开始符号； 每一个Go语言源码被编译为AST时，其最顶层的结构都是SourceFile\n1 SourceFile = PackageClause \u0026#34;;\u0026#34; { ImportDecl \u0026#34;;\u0026#34; } { TopLevelDecl \u0026#34;;\u0026#34; } . 每一个文件都包含一个 package 的定义以及可选的 import 声明和其他的顶层声明，其还包括syntax结构体。\n最顶层的声名包括了五大基本类型：常量、类型、变量、函数、方法\n1 2 3 4 5 6 7 8 9 10 ConstDecl = \u0026#34;const\u0026#34; ( ConstSpec | \u0026#34;(\u0026#34; { ConstSpec \u0026#34;;\u0026#34; } \u0026#34;)\u0026#34; ) . ConstSpec = IdentifierList [ [ Type ] \u0026#34;=\u0026#34; ExpressionList ] . TypeDecl = \u0026#34;type\u0026#34; ( TypeSpec | \u0026#34;(\u0026#34; { TypeSpec \u0026#34;;\u0026#34; } \u0026#34;)\u0026#34; ) . TypeSpec = AliasDecl | TypeDef . AliasDecl = identifier \u0026#34;=\u0026#34; Type . TypeDef = identifier Type . VarDecl = \u0026#34;var\u0026#34; ( VarSpec | \u0026#34;(\u0026#34; { VarSpec \u0026#34;;\u0026#34; } \u0026#34;)\u0026#34; ) . VarSpec = IdentifierList ( Type [ \u0026#34;=\u0026#34; ExpressionList ] | \u0026#34;=\u0026#34; ExpressionList ) . 此即对应着const、type 和 var，而函数和函数和方法的定义就更加复杂\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FunctionDecl = \u0026#34;func\u0026#34; FunctionName Signature [ FunctionBody ] . FunctionName = identifier . FunctionBody = Block . MethodDecl = \u0026#34;func\u0026#34; Receiver MethodName Signature [ FunctionBody ] . Receiver = Parameters . Block = \u0026#34;{\u0026#34; StatementList \u0026#34;}\u0026#34; . StatementList = { Statement \u0026#34;;\u0026#34; } . Statement = Declaration | LabeledStmt | SimpleStmt | GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt | FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt | DeferStmt . SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl . 这里的语法结构就对应了常见的 switch/case、if/else、for 循环以及 select 等语句。\n这里再次以基础的程序为示例\n1 2 3 4 5 6 7 8 9 10 11 12 package main // PkgName import \u0026#34;fmt\u0026#34; // ImportDecl const word = \u0026#34;world!\u0026#34; // ConstDecl type mystr string // TypeDecl var hello mystr = \u0026#34;Hello, \u0026#34; + world // VarDecl func main() { fmt.Println(hello) // FuncDecl -\u0026gt; CallExpr //-\u0026gt; SelectorExpr(Name:fmt, Name:Println), Name = hello } 在执行语法分析的过程中，常见两种方法：\n自顶向下：可以被看作找到当前输入流最左推导的过程，对于任意一个输入流，根据当前的输入符号，确定一个生产规则，使用生产规则右侧的符号替代相应的非终结符向下推导\n自底向上：语法分析器从输入流开始，每次都尝试重写最右侧的多个符号，这其实是说解析器会从最简单的符号进行推导，在解析的最后合并成开始符号\n和大多数语言一样，Go语言的解析器使用了 LALR(1) 的文法来解析词法分析过程中输出的 Token 序列。\n3.2. AST 对于Go源文件的每一个import，type，const，func都是一个根节点。在此之下包含当前声明的子节点。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 // noder transforms package syntax\u0026#39;s AST into a Node tree. type noder struct { posMap file *syntax.File linknames []linkname pragcgobuf [][]string err chan syntax.Error importedUnsafe bool importedEmbed bool trackScopes bool funcState *funcState } 利用decls将源文件的所有声明语句转换为Node数组\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) { var cs constState for _, decl := range decls { p.setlineno(decl) switch decl := decl.(type) { case *syntax.ImportDecl: p.importDecl(decl) case *syntax.VarDecl: l = append(l, p.varDecl(decl)...) case *syntax.ConstDecl: l = append(l, p.constDecl(decl, \u0026amp;cs)...) case *syntax.TypeDecl: l = append(l, p.typeDecl(decl)) case *syntax.FuncDecl: l = append(l, p.funcDecl(decl)) default: panic(\u0026#34;unhandled Decl\u0026#34;) } } return } stringer 充分利用了 Go 语言标准库对编译器各种能力的支持，其中包括用于解析抽象语法树的 go/ast、用于格式化代码的 go/fmt等，Go 通过标准库中的这些包对外直接提供了编译器的相关能力，让使用者可以直接在它们上面构建复杂的代码生成机制并实施元编程技术。\n作为二进制文件，stringer 命令的入口就是如下所示的 golang/tools/main.main函数，在下面的代码中，我们初始化了一个用于解析源文件和生成代码的 golang/tools/main.Generator，然后开始拼接生成的文件：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 func main() { types := strings.Split(*typeNames, \u0026#34;,\u0026#34;) ... g := Generator{ trimPrefix: *trimprefix, lineComment: *linecomment, } ... g.Printf(\u0026#34;// Code generated by \\\u0026#34;stringer %s\\\u0026#34;; DO NOT EDIT.\\n\u0026#34;, strings.Join(os.Args[1:], \u0026#34; \u0026#34;)) g.Printf(\u0026#34;\\n\u0026#34;) g.Printf(\u0026#34;package %s\u0026#34;, g.pkg.name) g.Printf(\u0026#34;\\n\u0026#34;) g.Printf(\u0026#34;import \\\u0026#34;strconv\\\u0026#34;\\n\u0026#34;) for _, typeName := range types { g.generate(typeName) } src := g.format() baseName := fmt.Sprintf(\u0026#34;%s_string.go\u0026#34;, types[0]) outputName = filepath.Join(dir, strings.ToLower(baseName)) if err := ioutil.WriteFile(outputName, src, 0644); err != nil { log.Fatalf(\u0026#34;writing output: %s\u0026#34;, err) } } 从这段代码中我们能看到最终生成文件的轮廓，最上面的调用的几次 golang/tools/main.Generator.Printf 会在内存中写入文件头的注释、当前包名以及引入的包等，随后会为待处理的类型依次调用 golang/tools/main.Generator.generate，这里会生成一个签名为 _ 的函数，通过编译器保证枚举类型的值不会改变：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func (g *Generator) generate(typeName string) { values := make([]Value, 0, 100) for _, file := range g.pkg.files { file.typeName = typeName file.values = nil if file.file != nil { ast.Inspect(file.file, file.genDecl) values = append(values, file.values...) } } g.Printf(\u0026#34;func _() {\\n\u0026#34;) g.Printf(\u0026#34;\\t// An \\\u0026#34;invalid array index\\\u0026#34; compiler error signifies that the constant values have changed.\\n\u0026#34;) g.Printf(\u0026#34;\\t// Re-run the stringer command to generate them again.\\n\u0026#34;) g.Printf(\u0026#34;\\tvar x [1]struct{}\\n\u0026#34;) for _, v := range values { g.Printf(\u0026#34;\\t_ = x[%s - %s]\\n\u0026#34;, v.originalName, v.str) } g.Printf(\u0026#34;}\\n\u0026#34;) runs := splitIntoRuns(values) switch { case len(runs) == 1: g.buildOneRun(runs, typeName) ... } } 随后调用的 golang/tools/main.Generator.buildOneRun 会生成两个常量的声明语句并为类型定义 String 方法，其中引用的 stringOneRun 常量是方法的模板，与 Web 服务的前端 HTML 模板比较相似：\n1 2 3 4 5 6 7 8 9 10 11 12 13 func (g *Generator) buildOneRun(runs [][]Value, typeName string) { values := runs[0] g.Printf(\u0026#34;\\n\u0026#34;) g.declareIndexAndNameVar(values, typeName) g.Printf(stringOneRun, typeName, usize(len(values)), \u0026#34;\u0026#34;) } const stringOneRun = func (i %[1]s) String() string { if %[3]si \u0026gt;= %[1]s(len(_%[1]s_index)-1) { return \u0026#34;%[1]s(\u0026#34; + strconv.FormatInt(int64(i), 10) + \u0026#34;)\u0026#34; } return _%[1]s_name[_%[1]s_index[i]:_%[1]s_index[i+1]] } 整个生成代码的过程就是使用编译器提供的库解析源文件并按照已有的模板生成新的代码，这与 Web 服务中利用模板生成 HTML 文件没有太多的区别，只是生成文件的用途稍微有一些不同。\n4. After AST 4.1. Type Checker 在完成AST的构建后，会对一些类型做特别的语法和语义检查，其不仅使用静态类型检查来保证程序运行的类型安全，还会在编程期间引入类型信息，让工程师能够使用反射来判断参数和变量的类型。当我们想要将 interface{} 转换成具体类型时会进行动态类型检查，如果无法发生转换就会发生程序崩溃。\n此外，这个阶段还会进行编译时常量，标识符声明绑定等。\n4.2. Variable Capturing 完成类型检查后，编译器会对AST分析重构，完成一些列优化，这部分主要针对Closures实现，会专门检查闭包变量捕获的情况。\n4.3. Function Inline 函数调用的主要成本在于参数与返回值的栈复制，较小的栈寄存器开销以及函数序言部分检查栈扩容等。\nGo编译器会对函数内联成本做估计，当函数有for，range，go，select，递归调用，存在//go:noinline这种编译器指示时，编译器不会进行优化。调试过程中，添加-gcflags=\u0026quot;-l\u0026quot;也不会发生内联。在编译时，添加-m=2可以查看不会被内联的原因\n1 2 3 4 5 6 func fib(index int) int { if index \u0026lt; 2 { return index } return fib(index-1) + fib(index-2) //cannot inline fib: recursive } 附其余编译器指示：\n//go:noescape 指令后面必须跟一个没有主体的函数声明（意味着该函数的实现不是用 Go 编写的）\n//go:uintptrescapes 指令后面必须跟一个函数声明。它指定函数的 uintptr 参数可能是已转换为 uintptr 的指针值，并且必须由垃圾收集器进行处理。\n//go:noinline 指令后面必须跟一个函数声明。它指定不应内联对函数的调用，从而覆盖编译器的通常优化规则。这通常仅在特殊运行时函数或调试编译器时需要。\n//go:norace 指令后面必须跟一个函数声明。它指定竞争检测器必须忽略函数的内存访问。这最常用于在调用竞争检测器运行时不安全时调用的低级代码。\n//go:nosplit 指令后面必须跟一个函数声明。它指定函数必须省略其通常的堆栈溢出检查。这最常用于在调用 goroutine 被抢占是不安全的时候调用的低级运行时代码。\n4.4. Escape Analysis Go的内存模型严格要求栈的对象指针不能存放到堆中，栈上对象的指针不能超过该栈对象的声明周期。\n通过对AST的静态数据流分析（static data-flow analysis），可以实现带权重有向图的分析来做逃逸判断。被引用的对象权重-1（即\u0026amp;），而权重大于0则表示存在解引用操作（即*），对于小于0且超过了当前生命周期的变量，进行逃逸处理。\n4.5. Closure Rewriting 闭包重写分为定义后立即调用和不被立即调用两种情况，在立即调用的情况下，会转换成普通的函数调用的形式，而不被立即调用，则会创建一个闭包对象。\n如果对象是按值应用的，且空间小于2*sizeof(int)，那么通过在函数体内创建局部变量的形式来产生该变量。如果存在指针或者引用，那么捕获的变量转换成指针类型的\u0026amp;var，会初始化为捕获变量的值。\n4.6. Function Traversal 遍历函数，在gc/walk.go中，会识别出生命但是未被使用的变量，带节点的操作转换为具体函数执行，如map获取值会转换为mapaccess_fast64函数，字符串拼接转换为concatstrings，forrange拆开等，同时会根据需要引入临时变量以及语句重排序，如x/=y拆成x=x/y\n5. SSA SSA生成阶段是编译器进行后续优化的保证，如常量传播，无效代码消除，冗余消除，强度降低等。\nGo在1.7版本引入并成为现代的编译器后端，此时会用于处理 defer 关键字的 runtime.deferproc、用于创建 Goroutine 的 runtime.newproc 和扩容切片的 runtime.growslice 等，\n这里会对关键字或者内建函数到运行时函数的映射其中涉及 Channel、哈希、make、new 关键字以及控制流中的关键字 select 等，经过walk函数遍历之后，SAA便不会再改变。\n1 2 3 4 5 6 7 func compileSSA(fn *Node, worker int) { f := buildssa(fn, worker) pp := newProgs(fn, worker) genssa(f, pp) pp.Flush() } 可以通过GOSSAFUNC=hello go build hello.go来查看SSA生成的过程。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func (s *state) stmt(n *Node) { ... switch n.Op { case OCALLMETH, OCALLINTER: s.call(n, callNormal) if n.Op == OCALLFUNC \u0026amp;\u0026amp; n.Left.Op == ONAME \u0026amp;\u0026amp; n.Left.Class() == PFUNC { if fn := n.Left.Sym.Name; compiling_runtime \u0026amp;\u0026amp; fn == \u0026#34;throw\u0026#34; || n.Left.Sym.Pkg == Runtimepkg \u0026amp;\u0026amp; (fn == \u0026#34;throwinit\u0026#34; || fn == \u0026#34;gopanic\u0026#34; || fn == \u0026#34;panicwrap\u0026#34; || fn == \u0026#34;block\u0026#34; || fn == \u0026#34;panicmakeslicelen\u0026#34; || fn == \u0026#34;panicmakeslicecap\u0026#34;) { m := s.mem() b := s.endBlock() b.Kind = ssa.BlockExit b.SetControl(m) } } s.call(n.Left, callDefer) case OGO: s.call(n.Left, callGo) ... } } 从AST到SAA的转换可以发现，在遇到函数调用、方法调用、使用 defer 或者 go 关键字时都会执行 cmd/compile/internal/gc.state.callResult 和 cmd/compile/internal/gc.state.call生成调用函数的 SSA 节点，这些在开发者看来不同的概念在编译器中都会被实现成静态的函数调用，上层的关键字和方法只是语言为我们提供的语法糖。\n某些中间代码仍然需要编译器优化以去掉无用代码并精简操作数：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func Compile(f *Func) { if f.Log() { f.Logf(\u0026#34;compiling %s\\n\u0026#34;, f.Name) } phaseName := \u0026#34;init\u0026#34; for _, p := range passes { f.pass = \u0026amp;p p.fn(f) } phaseName = \u0026#34;\u0026#34; } 6. Machine Code Generation SSA 降级是在中间代码生成的过程中完成的，其中将近 50 轮处理的过程中，lower 以及后面的阶段都属于 SSA 降级这一过程，这么多轮的处理会将 SSA 转换成机器特定的操作：\n1 2 3 4 5 6 7 8 var passes = [...]pass{ ... {name: \u0026#34;lower\u0026#34;, fn: lower, required: true}, {name: \u0026#34;lowered deadcode for cse\u0026#34;, fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again {name: \u0026#34;lowered cse\u0026#34;, fn: cse}, ... {name: \u0026#34;trim\u0026#34;, fn: trim}, // remove empty blocks } 转换过程会对应CPU的架构做分析\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func rewriteValue386(v *Value) bool { switch v.Op { case Op386ADCL: return rewriteValue386_Op386ADCL_0(v) case Op386ADDL: return rewriteValue386_Op386ADDL_0(v) || rewriteValue386_Op386ADDL_10(v) || rewriteValue386_Op386ADDL_20(v) ... } } func rewriteValue386_Op386ADCL_0(v *Value) bool { // match: (ADCL x (MOVLconst [c]) f) // cond: // result: (ADCLconst [c] x f) for { _ = v.Args[2] x := v.Args[0] v_1 := v.Args[1] if v_1.Op != Op386MOVLconst { break } c := v_1.AuxInt f := v.Args[2] v.reset(Op386ADCLconst) v.AuxInt = c v.AddArg(x) v.AddArg(f) return true } ... } Go 语言的汇编器是基于 Plan 9 汇编器的输入类型设计的\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package hello func hello(a int) int { c := a + 2 return c } $ GOOS=linux GOARCH=amd64 go tool compile -S hello.go \u0026#34;\u0026#34;.hello STEXT nosplit size=15 args=0x10 locals=0x0 0x0000 00000 (main.go:3) TEXT \u0026#34;\u0026#34;.hello(SB), NOSPLIT, $0-16 0x0000 00000 (main.go:3) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:3) FUNCDATA $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x0000 00000 (main.go:4) PCDATA $2, $0 0x0000 00000 (main.go:4) PCDATA $0, $0 0x0000 00000 (main.go:4) MOVQ \u0026#34;\u0026#34;.a+8(SP), AX 0x0005 00005 (main.go:4) ADDQ $2, AX 0x0009 00009 (main.go:5) MOVQ AX, \u0026#34;\u0026#34;.~r1+16(SP) 0x000e 00014 (main.go:5) RET 0x0000 48 8b 44 24 08 48 83 c0 02 48 89 44 24 10 c3 H.D$.H...H.D$.. ... 上述汇编代码都是由 cmd/internal/obj.Flushplist 这个函数生成的，该函数会调用架构特定的 Preprocess 和 Assemble 方法。自此完成最终的机器码生成。\n","permalink":"https://chasing1020.github.io/post/go0-compilers-principles/","summary":"\u003ch1 id=\"part0-compilers-principles\"\u003ePart0. Compilers Principles\u003c/h1\u003e\n\u003ch2 id=\"1-stage\"\u003e1. Stage\u003c/h2\u003e\n\u003cp\u003e编译器技术影响了计算机的体系结构，同时也受到体系结构发展的影响。\u003c/p\u003e\n\u003cp\u003e体系结构的很多现代创新都依赖于编译器能从源程序中抽取出有效利用硬件能力的机会。\u003c/p\u003e","title":"Go(0) Compilers Principles"},{"content":"Design Pattern 1. Creational Patterns 1.1.Factory Method 工厂方法模式是一种创建型设计模式， 其在父类中提供一个创建对象的方法， 允许子类决定实例化对象的类型。\nUsage 无法预知对象确切类别及其依赖关系。（只需要开发新的创建者子类， 然后重写其工厂方法） 希望用户能扩展软件库或框架的内部组件。 如果希望复用现有对象来节省系统资源， 而不是每次都重新创建对象。 Advantages 避免创建者和具体产品之间的紧密耦合。 单一职责原则。 将产品创建代码放在程序的单一位置， 从而使得代码更容易维护。 开闭原则。 无需更改现有客户端代码， 可以在程序中引入新的产品类型。 Disadvantages 应用工厂方法模式需要引入许多新的子类， 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public abstract class NumberFormat extends Format { private static NumberFormat getInstance(Locale desiredLocale, int choice) { LocaleProviderAdapter adapter; adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, desiredLocale); NumberFormat numberFormat = getInstance(adapter, desiredLocale, choice); if (numberFormat == null) { numberFormat = getInstance(LocaleProviderAdapter.forJRE(), desiredLocale, choice); } return numberFormat; } private static NumberFormat getInstance(LocaleProviderAdapter adapter, Locale locale, int choice) { NumberFormatProvider provider = adapter.getNumberFormatProvider(); NumberFormat numberFormat = null; switch (choice) { case NUMBERSTYLE: numberFormat = provider.getNumberInstance(locale); break; case PERCENTSTYLE: numberFormat = provider.getPercentInstance(locale); break; case CURRENCYSTYLE: numberFormat = provider.getCurrencyInstance(locale); break; case INTEGERSTYLE: numberFormat = provider.getIntegerInstance(locale); break; } return numberFormat; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 // Calendar 三个子类，都可以调用getInstance() -\u0026gt; createClendar // BuddhistCalendar // GregorianCalendar // JapaneseImperialCalendar private static Calendar createCalendar(TimeZone zone, Locale aLocale) { CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale) .getCalendarProvider(); if (provider != null) { try { return provider.getInstance(zone, aLocale); } catch (IllegalArgumentException iae) { // fall back to the default instantiation } } Calendar cal = null; if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType(\u0026#34;ca\u0026#34;); if (caltype != null) { switch (caltype) { case \u0026#34;buddhist\u0026#34;: cal = new BuddhistCalendar(zone, aLocale); break; case \u0026#34;japanese\u0026#34;: cal = new JapaneseImperialCalendar(zone, aLocale); break; case \u0026#34;gregory\u0026#34;: cal = new GregorianCalendar(zone, aLocale); break; } } } if (cal == null) { // If no known calendar type is explicitly specified, // perform the traditional way to create a Calendar: // create a BuddhistCalendar for th_TH locale, // a JapaneseImperialCalendar for ja_JP_JP locale, or // a GregorianCalendar for any other locales. // NOTE: The language, country and variant strings are interned. if (aLocale.getLanguage() == \u0026#34;th\u0026#34; \u0026amp;\u0026amp; aLocale.getCountry() == \u0026#34;TH\u0026#34;) { cal = new BuddhistCalendar(zone, aLocale); } else if (aLocale.getVariant() == \u0026#34;JP\u0026#34; \u0026amp;\u0026amp; aLocale.getLanguage() == \u0026#34;ja\u0026#34; \u0026amp;\u0026amp; aLocale.getCountry() == \u0026#34;JP\u0026#34;) { cal = new JapaneseImperialCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } return cal; } 1.2.Abstract Factory 抽象工厂模式是一种创建型设计模式， 它能创建一系列相关的对象， 而无需指定其具体类。\nUsage 如果代码需要与多个不同系列的相关产品交互， 但是由于无法提前获取相关信息， 或者出于对未来扩展性的考虑， 不希望代码基于产品的具体类进行构建， 在这种情况下， 可以使用抽象工厂。 如果有一个基于一组抽象方法的类， 且其主要功能因此变得不明确， 那么在这种情况下可以考虑使用抽象工厂模式。 Advantages 可以确保同一工厂生成的产品相互匹配。 可以避免客户端和具体产品代码的耦合。 单一职责原则。 可以将产品生成代码抽取到同一位置， 使得代码易于维护。 开闭原则。 向应用程序中引入新产品变体时， 无需修改客户端代码。 Disadvantages 由于采用该模式需要向应用中引入众多接口和类， 代码可能会比之前更加复杂。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // 使得应用程序可以通过XML文件，获得一个能生成DOM对象的解析器。 public abstract class DocumentBuilderFactory { //... public static DocumentBuilderFactory newInstance() { return FactoryFinder.find( /* The default property name according to the JAXP spec */ DocumentBuilderFactory.class, // \u0026#34;javax.xml.parsers.DocumentBuilderFactory\u0026#34; /* The fallback implementation class name */ \u0026#34;com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl\u0026#34;); } } public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory { public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException { /** Check that if a Schema has been specified that neither of the schema properties have been set. */ if (grammar != null \u0026amp;\u0026amp; attributes != null) { if (attributes.containsKey(JAXPConstants.JAXP_SCHEMA_LANGUAGE)) { throw new ParserConfigurationException( SAXMessageFormatter.formatMessage(null, \u0026#34;schema-already-specified\u0026#34;, new Object[] {JAXPConstants.JAXP_SCHEMA_LANGUAGE})); } else if (attributes.containsKey(JAXPConstants.JAXP_SCHEMA_SOURCE)) { throw new ParserConfigurationException( SAXMessageFormatter.formatMessage(null, \u0026#34;schema-already-specified\u0026#34;, new Object[] {JAXPConstants.JAXP_SCHEMA_SOURCE})); } } try { return new DocumentBuilderImpl(this, attributes, features, fSecureProcess); } catch (SAXException se) { // Handles both SAXNotSupportedException, SAXNotRecognizedException throw new ParserConfigurationException(se.getMessage()); } } } 1.3.Builder 构建器模式是一种创建型设计模式， 使能够分步骤创建复杂对象。 该模式允许使用相同的创建代码生成不同类型和形式的对象。\nUsage 使用生成器模式可避免 “重叠构造函数 （telescopic constructor）” 的出现。 当希望使用代码创建不同形式的产品 （例如石头或木头房屋） 时， 可使用生成器模式。 使用生成器构造组合树或其他复杂对象。 Advantages 可以分步创建对象， 暂缓创建步骤或递归运行创建步骤。 生成不同形式的产品时， 可以复用相同的制造代码。 单一职责原则。 可以将复杂构造代码从产品的业务逻辑中分离出来。 Disadvantages 由于该模式需要新增多个类， 因此代码整体复杂程度会有所增加。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence { @Override public StringBuilder append(Object obj) { return append(String.valueOf(obj)); } @Override public StringBuilder append(String str) { super.append(str); return this; } } 1.4.Prototype 原型模式是一种创建型设计模式， 使能够复制已有对象， 而又无需使代码依赖它们所属的类。\nUsage 如果需要复制一些对象， 同时又希望代码独立于这些对象所属的具体类， 可以使用原型模式。 如果子类的区别仅在于其对象的初始化方式， 那么可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。 Advantages 可以克隆对象， 而无需与它们所属的具体类相耦合。 可以克隆预生成原型， 避免反复运行初始化代码。 可以更方便地生成复杂对象，在内存中创建性能更好。 可以用继承以外的方式来处理复杂对象的不同配置。 Disadvantages 克隆包含循环引用的复杂对象可能会非常麻烦。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface Cloneable { } public class Object { protected native Object clone() throws CloneNotSupportedException; } class Prototype implements Cloneable { public Prototype clone() { Prototype prototype = null; try{ prototype = (Prototype)super.clone(); }catch(CloneNotSupportedException e) { e.printStackTrace(); } return prototype; } } class ConcretePrototype extends Prototype { public void show() { System.out.println(\u0026#34;原型模式实现类\u0026#34;); } } public class Client { public static void main(String[] args) { ConcretePrototype cp = new ConcretePrototype(); for(int i=0; i\u0026lt; 10; i++) { // 返回浅拷贝对象 ConcretePrototype clonecp = (ConcretePrototype)cp.clone(); clonecp.show(); } } } 1.5.Singleton 单例模式是一种创建型设计模式， 让能够保证一个类只有一个实例， 并提供一个访问该实例的全局节点。\nUsage 如果程序中的某个类对于所有客户端只有一个可用的实例， 可以使用单例模式。 如果需要更加严格地控制全局变量， 可以使用单例模式。 Advantages 可以保证一个类只有一个实例。 获得了一个指向该实例的全局访问节点。 仅在首次请求单例对象时对其进行初始化。 Disadvantages 违反了_单一职责原则_。 该模式同时解决了两个问题。 单例模式可能掩盖不良设计， 比如程序各组件之间相互了解过多等。 该模式在多线程环境下需要进行特殊处理， 避免多个线程多次创建单例对象。 单例的客户端代码单元测试可能会比较困难， 因为许多测试框架以基于继承的方式创建模拟对象。 由于单例类的构造函数是私有的， 而且绝大部分语言无法重写静态方法， 所以需要想出仔细考虑模拟单例的方法。 要么干脆不编写测试代码， 或者不使用单例模式。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public final class Singleton { private static Singleton instance; public String value; private Singleton(String value) { // The following code emulates slow initialization. try { Thread.sleep(1000); } catch (InterruptedException ex) { ex.printStackTrace(); } this.value = value; } public static Singleton getInstance(String value) { if (instance == null) { instance = new Singleton(value); } return instance; } } 多线程安全\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public final class Singleton { private static volatile Singleton instance; public String value; private Singleton(String value) { this.value = value; } public static Singleton getInstance(String value) { Singleton result = instance; if (result != null) { return result; } synchronized(Singleton.class) { if (instance == null) { instance = new Singleton(value); } return instance; } } } 静态内部类单例模式（推荐）\n1 2 3 4 5 6 7 8 9 10 11 //推荐写法 public class Singleton { private Singleton(){ } public static Singleton getInstance(){ return SingletonHolder.sInstance; //静态内部类完成加载 } private static class SingletonHolder { private static final Singleton sInstance = new Singleton(); } } 核心库示例\n1 2 3 4 5 6 public class Runtime { private static Runtime currentRuntime = new Runtime(); public static Runtime getRuntime() { return currentRuntime; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class System { private static native void registerNatives(); private static volatile SecurityManager security = null; static { registerNatives(); } /** Don\u0026#39;t let anyone instantiate this class */ private System() { } public static SecurityManager getSecurityManager() { return security; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; ) var lock = \u0026amp;sync.Mutex{} type single struct { } var singleInstance *single func getInstance() *single { if singleInstance == nil { lock.Lock() defer lock.Unlock() if singleInstance == nil { fmt.Println(\u0026#34;Creting Single Instance Now\u0026#34;) singleInstance = \u0026amp;single{} } else { fmt.Println(\u0026#34;Single Instance already created-1\u0026#34;) } } else { fmt.Println(\u0026#34;Single Instance already created-2\u0026#34;) } return singleInstance } 2. Structural Patterns 2.1.Adapter 适配器模式是一种结构型设计模式， 它能使接口不兼容的对象能够相互合作。\nUsage 当希望使用某个类， 但是其接口与其他代码不兼容时， 可以使用适配器类。 如果您需要复用这样一些类， 他们处于同一个继承体系， 并且他们又有了额外的一些共同的方法， 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。 Advantages 单一职责原则。可以将接口或数据转换代码从程序主要业务逻辑中分离。 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互， 就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。 Disadvantages 代码整体复杂度增加， 因为需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 //ArrayList 和 T[]是组合关系，想将T[]转换成list的操作，要有一个适配器来进行转换 public class Arrays { private static class ArrayList\u0026lt;E\u0026gt; extends AbstractList\u0026lt;E\u0026gt; implements RandomAccess, java.io.Serializable { @Override @SuppressWarnings(\u0026#34;unchecked\u0026#34;) public \u0026lt;T\u0026gt; T[] toArray(T[] a) { int size = size(); if (a.length \u0026lt; size) return Arrays.copyOf(this.a, size, (Class\u0026lt;? extends T[]\u0026gt;) a.getClass()); System.arraycopy(this.a, 0, a, 0, size); if (a.length \u0026gt; size) a[size] = null; return a; } } @SafeVarargs @SuppressWarnings(\u0026#34;varargs\u0026#34;) public static \u0026lt;T\u0026gt; List\u0026lt;T\u0026gt; asList(T... a) { return new ArrayList\u0026lt;\u0026gt;(a); } } InputStreamReader也作为适配器使用\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // 目标类 public abstract class Reader implements Readable, Closeable { // 字符流 abstract public int read(char cbuf[], int off, int len) throws IOException; abstract public void close() throws IOException; } // 适配器类 public class InputStreamReader extends Reader { private final StreamDecoder sd; public InputStreamReader(InputStream in) { super(in); try { //通过StreamDecoder类间接引用被适配的对象 sd = StreamDecoder.forInputStreamReader(in, this, (String)null); } catch (UnsupportedEncodingException e) { // The default encoding should always be available throw new Error(e); } } } // 被适配的类 public abstract class InputStream implements Closeable { // 字节流 public int read(byte b[]) throws IOException { return read(b, 0, b.length); } } 2.2.Bridge 桥接模式是一种结构型设计模式， 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构， 从而能在开发时分别使用。\nUsage 如果想要拆分或重组一个具有多重功能的庞杂类 （例如能与多个数据库服务器进行交互的类）， 可以使用桥接模式。 如果希望在几个独立维度上扩展一个类， 可使用该模式。 如果需要在运行时切换不同实现方法， 可使用桥接模式。 Advantages 可以创建与平台无关的类和程序，将抽象与实现解耦。 客户端代码仅与高层抽象部分进行互动， 不会接触到平台的详细信息。 开闭原则。 可以新增抽象部分和实现部分， 且它们之间不会相互影响。 单一职责原则。 抽象部分专注于处理高层逻辑， 实现部分处理平台细节。 Disadvantages 对高内聚的类使用该模式可能会让代码更加复杂。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 // JDBC public interface Driver { Connection connect(String url, java.util.Properties info) throws SQLException; boolean acceptsURL(String url) throws SQLException; DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException; int getMajorVersion(); int getMinorVersion(); boolean jdbcCompliant(); public Logger getParentLogger() throws SQLFeatureNotSupportedException; } public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException(\u0026#34;Can\u0026#39;t register driver!\u0026#34;); } } } // 桥 public class DriverManager{ public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put(\u0026#34;user\u0026#34;, user); } if (password != null) { info.put(\u0026#34;password\u0026#34;, password); } return (getConnection(url, info, Reflection.getCallerClass())); } } 2.3.Composite 组合模式是一种结构型设计模式， 可以使用它将对象组合成树状结构， 并且能像使用独立对象一样使用它们。\nUsage 如果需要实现树状对象结构， 可以使用组合模式。 如果希望客户端代码以相同方式处理简单和复杂元素， 可以使用该模式。 Advantages 可以利用多态和递归机制更方便地使用复杂树结构。 开闭原则。 无需更改现有代码， 就可以在应用中添加新元素， 使其成为对象树的一部分。 Disadvantages 对于功能差异较大的类， 提供公共接口或许会有困难。 在特定情况下， 需要过度一般化组件接口， 使其变得令人难以理解。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import java.util.ArrayList; import java.util.List; public class Employee { private String name; private String dept; private int salary; private List\u0026lt;Employee\u0026gt; subordinates; //构造函数 public Employee(String name,String dept, int sal) { this.name = name; this.dept = dept; this.salary = sal; subordinates = new ArrayList\u0026lt;Employee\u0026gt;(); } public void add(Employee e) { subordinates.add(e); } public void remove(Employee e) { subordinates.remove(e); } public List\u0026lt;Employee\u0026gt; getSubordinates(){ return subordinates; } public String toString(){ return (\u0026#34;Employee :[ Name : \u0026#34;+ name +\u0026#34;, dept : \u0026#34;+ dept + \u0026#34;, salary :\u0026#34; + salary+\u0026#34; ]\u0026#34;); } } 常用于前端表示与图形打交道的用户界面组件或代码的层次结构\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 public class Container extends Component { public Component add(Component comp) { addImpl(comp, null, -1); return comp; } protected void addImpl(Component comp, Object constraints, int index) { synchronized (getTreeLock()) { GraphicsConfiguration thisGC = this.getGraphicsConfiguration(); if (index \u0026gt; component.size() || (index \u0026lt; 0 \u0026amp;\u0026amp; index != -1)) { throw new IllegalArgumentException( \u0026#34;illegal component position\u0026#34;); } checkAddToSelf(comp); checkNotAWindow(comp); /* Reparent the component and tidy up the tree\u0026#39;s state. */ if (comp.parent != null) { comp.parent.remove(comp); if (index \u0026gt; component.size()) { throw new IllegalArgumentException(\u0026#34;illegal component position\u0026#34;); } } if (thisGC != null) { comp.checkGD(thisGC.getDevice().getIDstring()); } //index == -1 means add to the end. if (index == -1) { component.add(comp); } else { component.add(index, comp); } comp.parent = this; comp.setGraphicsConfiguration(thisGC); adjustListeningChildren(AWTEvent.HIERARCHY_EVENT_MASK, comp.numListening(AWTEvent.HIERARCHY_EVENT_MASK)); adjustListeningChildren(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, comp.numListening(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK)); adjustDescendants(comp.countHierarchyMembers()); invalidateIfValid(); if (peer != null) { comp.addNotify(); } /* Notify the layout manager of the added component. */ if (layoutMgr != null) { if (layoutMgr instanceof LayoutManager2) { ((LayoutManager2)layoutMgr).addLayoutComponent(comp, constraints); } else if (constraints instanceof String) { layoutMgr.addLayoutComponent((String)constraints, comp); } } if (containerListener != null || (eventMask \u0026amp; AWTEvent.CONTAINER_EVENT_MASK) != 0 || Toolkit.enabledOnToolkit(AWTEvent.CONTAINER_EVENT_MASK)) { ContainerEvent e = new ContainerEvent(this, ContainerEvent.COMPONENT_ADDED, comp); dispatchEvent(e); } comp.createHierarchyEvents(HierarchyEvent.HIERARCHY_CHANGED, comp, this, HierarchyEvent.PARENT_CHANGED, Toolkit.enabledOnToolkit(AWTEvent.HIERARCHY_EVENT_MASK)); if (peer != null \u0026amp;\u0026amp; layoutMgr == null \u0026amp;\u0026amp; isVisible()) { updateCursorImmediately(); } } } } 混入 (mixin) 提供了一种非常灵活的方式，来分发 Vue 组件中的可复用功能。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 定义一个混入对象 var myMixin = { created: function () { this.hello() }, methods: { hello: function () { console.log(\u0026#39;hello from mixin!\u0026#39;) } } } // 定义一个使用混入对象的组件 var Component = Vue.extend({ mixins: [myMixin] }) var component = new Component() // =\u0026gt; \u0026#34;hello from mixin!\u0026#34; 2.4.Decorator 装饰模式是一种结构型设计模式， 允许通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。\nUsage 如果希望在无需修改代码的情况下即可使用对象， 且希望在运行时为对象新增额外的行为， 可以使用装饰模式。 如果用继承来扩展对象行为的方案难以实现或者根本不可行， 可以使用该模式。 Advantages 无需创建新子类即可扩展对象的行为。 可以在运行时添加或删除对象的功能。 可以用多个装饰封装对象来组合几种行为。 单一职责原则。 可以将实现了许多不同行为的一个大类拆分为多个较小的类。 Disadvantages 在封装器栈中删除特定封装器比较困难。 实现行为不受装饰栈顺序影响的装饰比较困难。 各层的初始化配置代码看上去可能会很糟糕。 Demo java.io.InputStream、 OutputStream、 Reader 和 Writer 的所有代码都有以自身类型的对象作为参数的构造函数。 两者都继承了Reader抽象父类，都有自己的read方法，而在InputStreamReader中引用了一个StreamDecoder实例对象，虽然在InputStreamReader中并未对StreamDecoder的read方法添加额外的功能，但它引用StreamDecoder使用了关键字final ，让StreamDecoder不可变，从某种意义上来说它扩展的功能就是让InputStreamReader读取输入流时，编码格式不变，InputStreamReader作为一个装饰器角色。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // 装饰器对象 public class InputStreamReader extends Reader { private final StreamDecoder sd; public int read() throws IOException { return sd.read(); } } public class StreamDecoder extends Reader { private InputStream in; public int read() throws IOException { return this.read0(); } private int read0() throws IOException { Object var1 = this.lock; synchronized(this.lock) { if(this.haveLeftoverChar) { this.haveLeftoverChar = false; return this.leftoverChar; } else { char[] var2 = new char[2]; int var3 = this.read(var2, 0, 2); switch(var3) { case -1: return -1; case 0: default: assert false : var3; return -1; case 2: this.leftoverChar = var2[1]; this.haveLeftoverChar = true; case 1: return var2[0]; } } } } } 2.5.Facade 外观模式是一种结构型设计模式， 能为程序库、 框架或其他复杂类提供一个简单的接口。\nUsage 如果需要一个指向复杂子系统的直接接口， 且该接口的功能有限， 则可以使用外观模式。 如果需要将子系统组织为多层结构， 可以使用外观。 Advantages 可以让自己的代码独立于复杂子系统。 Disadvantages 外观可能成为与程序中所有类都耦合的上帝对象。 Demo javax.faces.context.FacesContext 在底层使用了 LifeCycle、ViewHandler 和 NavigationHandler 这几个类。（但绝大多数客户端不知道） javax.faces.context.ExternalContext 在内部使用了 ServletContext、HttpSession、 HttpServletRequest、 HttpServletResponse 和其他一些类。\n微服务中，API网关的设计也是门面模式\n2.6.Flyweight 享元模式是一种结构型设计模式， 它摒弃了在每个对象中保存所有数据的方式， 通过共享多个对象所共有的相同状态， 让能在有限的内存容量中载入更多对象。\nUsage 仅在程序必须支持大量对象且没有足够的内存容量时使用享元模式。 Advantages 如果程序中有很多相似对象， 那么将可以节省大量内存。 Disadvantages 可能需要牺牲执行速度来换取内存， 因为他人每次调用享元方法时都需要重新计算部分情景数据。 代码会变得更加复杂。 团队中的新成员总是会问： “为什么要像这样拆分一个实体的状态？”。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import java.awt.*; import java.util.HashMap; import java.util.Map; public class TreeFactory { static Map\u0026lt;String, TreeType\u0026gt; treeTypes = new HashMap\u0026lt;\u0026gt;(); public static TreeType getTreeType(String name, Color color, String otherTreeData) { TreeType result = treeTypes.get(name); if (result == null) { result = new TreeType(name, color, otherTreeData); treeTypes.put(name, result); } return result; } } public class Forest extends JFrame { private List\u0026lt;Tree\u0026gt; trees = new ArrayList\u0026lt;\u0026gt;(); public void plantTree(int x, int y, String name, Color color, String otherTreeData) { TreeType type = TreeFactory.getTreeType(name, color, otherTreeData); Tree tree = new Tree(x, y, type); trees.add(tree); } @Override public void paint(Graphics graphics) { for (Tree tree : trees) { tree.draw(graphics); } } } Integer中也使用了享元模式\n1 2 3 4 5 6 7 8 //在-128～127使用非常频繁，设置IntegerCache来包装 public final class Integer extends Number implements Comparable\u0026lt;Integer\u0026gt; { public static Integer valueOf(int i) { if (i \u0026gt;= IntegerCache.low \u0026amp;\u0026amp; i \u0026lt;= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } } 2.7.Proxy 代理模式是一种结构型设计模式， 让能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问， 并允许在将请求提交给对象前后进行一些处理。\nUsage 延迟初始化 （虚拟代理）。 如果有一个偶尔使用的重量级服务对象， 一直保持该对象运行会消耗系统资源时， 可使用代理模式。 访问控制 （保护代理）。 如果只希望特定客户端使用服务对象， 这里的对象可以是操作系统中非常重要的部分， 而客户端则是各种已启动的程序 （包括恶意程序）， 此时可使用代理模式。 本地执行远程服务 （远程代理）。 适用于服务对象位于远程服务器上的情形。 记录日志请求 （日志记录代理）。 适用于当需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录 智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。 Advantages 可以在客户端毫无察觉的情况下控制服务对象。 如果客户端对服务对象的生命周期没有特殊要求， 可以对生命周期进行管理。 即使服务对象还未准备好或不存在， 代理也可以正常工作。 开闭原则。 可以在不对服务或客户端做出修改的情况下创建新代理。 Disadvantages 代码可能会变得复杂， 因为需要新建许多类。 服务响应可能会延迟。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //javax.ejb.EJB public class UserServiceProxy extends UserService implements Serializable { public User find(Long id) { UserService instance = getAnAvailableInstanceFromPool(); User result = instance.find(id); releaseInstanceToPool(instance); return result; } public Long save(User user) { UserService instance = getAnAvailableInstanceFromPool(); Long result = instance.save(user); releaseInstanceToPool(instance); return result; } // ... } 在MVC模式中，也大量使用代理模式\n1 2 3 4 5 6 7 8 9 @Service public SomeServiceImpl implements SomeService{ @AutoWired private SomeService someService; public void doSome(){ someService.doSome(); } } 3. Behavioral Patterns 3.1.Chain of Responsibility 责任链模式是一种行为设计模式， 允许将请求沿着处理者链进行发送。 收到请求后， 每个处理者均可对请求进行处理， 或将其传递给链上的下个处理者。\nUsage 当程序需要使用不同方式处理不同种类请求， 而且请求类型和顺序预先未知时， 可以使用责任链模式。 当必须按顺序执行多个处理者时， 可以使用该模式。 如果所需处理者及其顺序必须在运行时进行改变， 可以使用责任链模式。 Advantages 可以控制请求处理的顺序。 单一职责原则。 可对发起操作和执行操作的类进行解耦。 开闭原则。 可以在不更改现有代码的情况下在程序中新增处理者。 Disadvantages 部分请求可能未被处理。 Demo 为系统or组件记录日志消息。如何体现了职责链模式：每个记录器都跟踪“父”记录器，所谓”父”记录器，就是Logger命名空间中最近的现有祖先。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class Logger { public void log(LogRecord record) { if (!isLoggable(record.getLevel())) { return; } Filter theFilter = filter; if (theFilter != null \u0026amp;\u0026amp; !theFilter.isLoggable(record)) { return; } // Post the LogRecord to all our Handlers, and then to // our parents\u0026#39; handlers, all the way up the tree. Logger logger = this; while (logger != null) { final Handler[] loggerHandlers = isSystemLogger ? logger.accessCheckedHandlers() : logger.getHandlers(); // 责任链模式 //每个日志记录都传递给分配给给定记录器的每个Handler，如果useParentHandlers是true，则将相同的算法一直应用于父级。 for (Handler handler : loggerHandlers) { handler.publish(record); } final boolean useParentHdls = isSystemLogger ? logger.useParentHandlers : logger.getUseParentHandlers(); if (!useParentHdls) { break; } logger = isSystemLogger ? logger.parent : logger.getParent(); } } } 在 javax.servlet.Filter#doFilter() 也有使用\n3.2.Command 命令模式是一种行为设计模式， 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中， 且能实现可撤销操作。\nUsage 如果需要通过操作来参数化对象， 可使用命令模式。 如果想要将操作放入队列中、 操作的执行或者远程执行操作， 可使用命令模式。 如果想要实现操作回滚功能， 可使用命令模式。 Advantages 单一职责原则。 可以解耦触发和执行操作的类。 开闭原则。 可以在不修改已有客户端代码的情况下在程序中创建新的命令。 可以实现撤销和恢复功能。 可以实现操作的延迟执行。 可以将一组简单命令组合成一个复杂命令。 Disadvantages 代码可能会变得更加复杂， 因为在发送者和接收者之间增加了一个全新的层次。 Demo Runnable担当命令的角色，Thread充当的是调用者，start方法就是其执行方法。\n通过实现Runable接口的类，将请求封装为一个对象，对请求排队或记录请求日志，以及支持可撤销操作。允许接受请求的一方决定是否要否决请求，最重要一点就是：命令模式把请求一个操作的对象和怎么执行一个操作的对象解耦。这就是Excutor框架执行实现Runable接口任务类的体现。\n1 2 3 4 5 6 7 8 package java.lang; /* Thread pools（线程池） 通常一个典型的线程池实现类可能有一个名为addTask()的public方法，用来添加一项工作任务到任务 队列中。该任务队列中的所有任务可以用command对象来封装，通常这些command对象会实现一个通用的接口比如java.lang.Runnable。 */ @FunctionalInterface public interface Runnable { public abstract void run(); } 3.3.Iterator 迭代器模式是一种行为设计模式， 让能在不暴露集合底层表现形式 （列表、 栈和树等） 的情况下遍历集合中所有的元素。\nUsage 当集合背后为复杂的数据结构， 且希望对客户端隐藏其复杂性时 （出于使用便利性或安全性的考虑）， 可以使用迭代器模式。 使用该模式可以减少程序中重复的遍历代码。 如果希望代码能够遍历不同的甚至是无法预知的数据结构， 可以使用迭代器模式。 Advantages 单一职责原则。 通过将体积庞大的遍历算法代码抽取为独立的类， 可对客户端代码和集合进行整理。 开闭原则。 可实现新型的集合和迭代器并将其传递给现有代码， 无需修改现有代码。 可以并行遍历同一集合， 因为每个迭代器对象都包含其自身的遍历状态。 相似的， 可以暂停遍历并在需要时继续。 Disadvantages 如果的程序只与简单的集合进行交互， 应用该模式可能会矫枉过正。 对于某些特殊集合， 使用迭代器可能比直接遍历的效率低。 Demo 许多框架和程序库都会使用它来提供遍历其集合的标准方式。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package java.util; import java.util.function.Consumer; public interface Iterator\u0026lt;E\u0026gt; { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException(\u0026#34;remove\u0026#34;); } default void forEachRemaining(Consumer\u0026lt;? super E\u0026gt; action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } } 1 2 3 4 5 6 7 8 package java.util; public interface Enumeration\u0026lt;E\u0026gt; { boolean hasMoreElements(); E nextElement(); } 3.4.Mediator 中介者模式是一种行为设计模式， 能让减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互， 迫使它们通过一个中介者对象进行合作。\nUsage 当一些对象和其他对象紧密耦合以致难以对其进行修改时， 可使用中介者模式。 当组件因过于依赖其他组件而无法在不同应用中复用时， 可使用中介者模式。 如果为了能在不同情景下复用一些基本行为， 导致需要被迫创建大量组件子类时， 可使用中介者模式。 Advantages 单一职责原则。 可以将多个组件间的交流抽取到同一位置， 使其更易于理解和维护。 开闭原则。 无需修改实际组件就能增加新的中介者。 可以减轻应用中多个组件间的耦合情况。 可以更方便地复用各个组件。 Disadvantages 一段时间后， 中介者可能会演化成为上帝对象。 Demo MVC中的Controller就是中介者\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package java.util; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; public class Timer { public void schedule(TimerTask task, long delay, long period) { if (delay \u0026lt; 0) throw new IllegalArgumentException(\u0026#34;Negative delay.\u0026#34;); if (period \u0026lt;= 0) throw new IllegalArgumentException(\u0026#34;Non-positive period.\u0026#34;); sched(task, System.currentTimeMillis()+delay, -period); } //以及其他schedule方法 } 以及JUC中的\n1 2 3 4 5 package java.util.concurrent; public interface Executor { void execute(Runnable command); } 与反射的invoke\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package java.lang.reflect; import ... public final class Method extends Executable { @CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class\u0026lt;?\u0026gt; caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); } } /* package sun.reflect; @Retention(RetentionPolicy.RUNTIME) @Target({METHOD}) public @interface CallerSensitive { } */ 3.5.Snapshot 备忘录模式是一种行为设计模式， 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。\nUsage 当需要创建对象状态快照来恢复其之前的状态时， 可以使用备忘录模式。 当直接访问对象的成员变量、 获取器或设置器将导致封装被突破时， 可以使用该模式。 Advantages 可以在不破坏对象封装情况的前提下创建对象状态快照。 可以通过让负责人维护原发器状态历史记录来简化原发器代码。 Disadvantages 如果客户端过于频繁地创建备忘录， 程序将消耗大量内存。 负责人必须完整跟踪原发器的生命周期， 这样才能销毁弃用的备忘录。 绝大部分动态编程语言 （例如 PHP、 Python 和 JavaScript） 不能确保备忘录中的状态不被修改。 Demo 1 2 3 package java.io; public interface Serializable { } 3.6.Observer 观察者模式是一种行为设计模式， 允许定义一种订阅机制， 可在对象事件发生时通知多个 “观察” 该对象的其他对象。\nUsage 当一个对象状态的改变需要改变其他对象， 或实际对象是事先未知的或动态变化的时， 可使用观察者模式。\n当应用中的一些对象必须观察其他对象时， 可使用该模式。 但仅能在有限时间内或特定情况下使用。\nAdvantages 开闭原则。 无需修改发布者代码就能引入新的订阅者类 （如果是发布者接口则可轻松引入发布者类）。 可以在运行时建立对象之间的联系。 Disadvantages 订阅者的通知顺序是随机的。 Demo 1 2 3 4 5 6 package java.util; public interface EventListener { } //广泛应用于Swing组件 在servlet中javax.servlet.http.HttpSessionBindingListener和javax.servlet.http.HttpSessionAttributeListener出现\n1 2 3 4 5 6 7 8 9 10 // 安卓开发中的button观察者注册 Button button = (Button) findViewById(R.id.button); //注册观察者 button.setOnClickListener(new View.OnClickListener(){ //观察者实现 @Override public void onClick(View arg0) { Log.d(\u0026#34;test\u0026#34;, \u0026#34;Click button \u0026#34;); } }); 3.7.State 状态模式是一种行为设计模式， 让能在一个对象的内部状态变化时改变其行为， 使其看上去就像改变了自身所 属的类一样。（有限状态机）\nUsage 如果对象需要根据自身当前状态进行不同行为， 同时状态的数量非常多且与状态相关的代码会频繁变更的话， 可使用状态模式。 如果某个类需要根据成员变量的当前值改变自身行为， 从而需要使用大量的条件语句时， 可使用该模式。 当相似状态和基于条件的状态机转换中存在许多重复代码时， 可使用状态模式。 Advantages 单一职责原则。 将与特定状态相关的代码放在单独的类中。 开闭原则。 无需修改已有状态类和上下文就能引入新状态。 通过消除臃肿的状态机条件语句简化上下文代码。 Disadvantages 如果状态机只有很少的几个状态， 或者很少发生改变， 那么应用该模式可能会显得小题大作。 Demo 1 javax.faces.lifecycle.LifeCycle#execute() 计算机网络中的rdt模型\n包括github.com/askervin/goresctrl\n3.8.Strategy 策略模式是一种行为设计模式， 它能让定义一系列算法， 并将每种算法分别放入独立的类中， 以使算法的对象能够相互替换。\nUsage 当想使用对象中各种不同的算法变体， 并希望能在运行时切换算法时， 可使用策略模式。 当有许多仅在执行某些行为时略有不同的相似类时， 可使用策略模式。 如果算法在上下文的逻辑中不是特别重要， 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时， 可使用该模式。 Advantages 可以在运行时切换对象内的算法。 可以将算法的实现和使用算法的代码隔离开来。 可以使用组合来代替继承。 开闭原则。 无需对上下文进行修改就能够引入新的策略。 Disadvantages 如果的算法极少发生改变， 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂。 客户端必须知晓策略间的不同——它需要选择合适的策略。 许多现代编程语言支持函数类型功能， 允许在一组匿名函数中实现不同版本的算法。 这样， 使用这些函数的方式就和使用策略对象时完全相同， 无需借助额外的类和接口来保持代码简洁。 Demo Lambda方法就是代替策略模式的简单实现 javax.servlet.http.HttpServlet： service()方法， 还有所有接受 Http.Servlet.Request和 Http.Servlet.Response对象作为参数的 doXXX()方法。 以及javax.servlet.Filter#doFilter()\n1 2 3 4 5 6 7 8 9 10 @FunctionalInterface public interface Comparator\u0026lt;T\u0026gt; { int compare(T o1, T o2); } public class Collections { public static \u0026lt;T\u0026gt; void sort(List\u0026lt;T\u0026gt; list, Comparator\u0026lt;? super T\u0026gt; c) { list.sort(c); } } 3.9.Template Method 模板方法模式是一种行为设计模式， 它在超类中定义了一个算法的框架， 允许子类在不修改结构的情况下重写算法的特定步骤。\nUsage 当只希望客户端扩展某个特定算法步骤， 而不是整个算法或其结构时， 可使用模板方法模式。\n当多个类的算法除一些细微不同之外几乎完全一样时， 可使用该模式。 但其后果就是， 只要算法发生变化， 就可能需要修改所有的类。\nAdvantages 可仅允许客户端重写一个大型算法中的特定部分， 使得算法其他部分修改对其所造成的影响减小。 可将重复代码提取到一个超类中。 Disadvantages 部分客户端可能会受到算法框架的限制。 通过子类抑制默认步骤实现可能会导致违反里氏替换原则。 模板方法中的步骤越多， 其维护工作就可能会越困难。 Demo java.io.InputStream、 java.io.OutputStream、 java.io.Reader 和 java.io.Writer 的所有非抽象方法。 java.util.AbstractList、 java.util.AbstractSet 和 java.util.AbstractMap 的所有非抽象方法。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public abstract class AbstractList\u0026lt;E\u0026gt; extends AbstractCollection\u0026lt;E\u0026gt; implements List\u0026lt;E\u0026gt; { public boolean add(E e) { add(size(), e); return true; } public boolean addAll(int index, Collection\u0026lt;? extends E\u0026gt; c) { rangeCheckForAdd(index); boolean modified = false; for (E e : c) { add(index++, e); modified = true; } return modified; } public int indexOf(Object o) { ListIterator\u0026lt;E\u0026gt; it = listIterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return it.previousIndex(); } else { while (it.hasNext()) if (o.equals(it.next())) return it.previousIndex(); } return -1; } // ... } 3.10.Visitor 访问者模式是一种行为设计模式， 它能将算法与其所作用的对象隔离开来。\nUsage 如果需要对一个复杂对象结构 （例如对象树） 中的所有元素执行某些操作， 可使用访问者模式。 可使用访问者模式来清理辅助行为的业务逻辑。 当某个行为仅在类层次结构中的一些类中有意义， 而在其他类中没有意义时， 可使用该模式。 优点 开闭原则。 可以引入在不同类对象上执行的新行为， 且无需对这些类做出修改。 单一职责原则。 可将同一行为的不同版本移到同一个类中。 访问者对象可以在与各种对象交互时收集一些有用的信息。 当想要遍历一些复杂的对象结构 （例如对象树）， 并在结构中的每个对象上应用访问者时， 这些信息可能会有所帮助。 Disadvantages 每次在元素层次结构中添加或移除一个类时， 都要更新所有的访问者。 在访问者同某个元素进行交互时， 它们可能没有访问元素私有成员变量和方法的必要权限。 Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package java.nio.file; import java.nio.file.attribute.BasicFileAttributes; import java.io.IOException; public interface FileVisitor\u0026lt;T\u0026gt; { FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException; FileVisitResult visitFileFailed(T file, IOException exc) throws IOException; FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException; } 3.11.Interpreter 解释器模式用于对于一些固定文法构建一个解释句子的解释器。\nUsage 如果一种特定类型的问题发生的频率足够高，那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器，该解释器通过解释这些句子来解决该问题。\nAdvantages 可扩展性比较好，灵活。\n增加了新的解释表达式的方式。\n易于实现简单文法。\nDisadvantages 可利用场景比较少。 对于复杂的文法比较难维护。 解释器模式会引起类膨胀。 Demo 1 2 3 4 5 public abstract class Format implements Serializable, Cloneable { public final String format (Object obj) { return format(obj, new StringBuffer(), new FieldPosition(0)).toString(); } } ","permalink":"https://chasing1020.github.io/post/design-pattern-note/","summary":"\u003ch1 id=\"design-pattern\"\u003eDesign Pattern\u003c/h1\u003e\n\u003ch2 id=\"1-creational-patterns\"\u003e1. Creational Patterns\u003c/h2\u003e\n\u003ch3 id=\"11factory-method\"\u003e1.1.Factory Method\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e工厂方法模式\u003c/strong\u003e是一种创建型设计模式， 其在父类中提供一个创建对象的方法， 允许子类决定实例化对象的类型。\u003c/p\u003e\n\u003ch4 id=\"usage\"\u003eUsage\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e无法预知对象确切类别及其依赖关系。（只需要开发新的创建者子类， 然后重写其工厂方法）\u003c/li\u003e\n\u003cli\u003e希望用户能扩展软件库或框架的内部组件。\u003c/li\u003e\n\u003cli\u003e如果希望复用现有对象来节省系统资源， 而不是每次都重新创建对象。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"advantages\"\u003eAdvantages\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e避免创建者和具体产品之间的紧密耦合。\u003c/li\u003e\n\u003cli\u003e单一职责原则。 将产品创建代码放在程序的单一位置， 从而使得代码更容易维护。\u003c/li\u003e\n\u003cli\u003e开闭原则。 无需更改现有客户端代码， 可以在程序中引入新的产品类型。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"disadvantages\"\u003eDisadvantages\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e应用工厂方法模式需要引入许多新的子类， 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch4 id=\"demo\"\u003eDemo\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e35\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eabstract\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eNumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003eextends\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eLocale\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003edesiredLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                           \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003echoice\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eLocaleProviderAdapter\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eadapter\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eadapter\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eLocaleProviderAdapter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetAdapter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormatProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                                   \u003c/span\u003e\u003cspan class=\"n\"\u003edesiredLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eadapter\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003edesiredLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003echoice\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eLocaleProviderAdapter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eforJRE\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                       \u003c/span\u003e\u003cspan class=\"n\"\u003edesiredLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003echoice\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eLocaleProviderAdapter\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eadapter\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                            \u003c/span\u003e\u003cspan class=\"n\"\u003eLocale\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003elocale\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kt\"\u003eint\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003echoice\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormatProvider\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eadapter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetNumberFormatProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eNumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eswitch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003echoice\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eNUMBERSTYLE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetNumberInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ePERCENTSTYLE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetPercentInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eCURRENCYSTYLE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetCurrencyInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eINTEGERSTYLE\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetIntegerInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003enumberFormat\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e27\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e28\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e29\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e30\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e31\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e32\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e33\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e34\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e35\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e36\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e37\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e38\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e39\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e40\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e41\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e42\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e43\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e44\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e45\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e46\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e47\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e48\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e49\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e50\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e51\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e52\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e53\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e54\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Calendar 三个子类，都可以调用getInstance() -\u0026gt; createClendar\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// BuddhistCalendar\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// GregorianCalendar\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// JapaneseImperialCalendar\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kd\"\u003estatic\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eCalendar\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"nf\"\u003ecreateCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeZone\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                           \u003c/span\u003e\u003cspan class=\"n\"\u003eLocale\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eCalendarProvider\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003eLocaleProviderAdapter\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetAdapter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eCalendarProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                                 \u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetCalendarProvider\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eprovider\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eIllegalArgumentException\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eiae\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"c1\"\u003e// fall back to the default instantiation\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"n\"\u003eCalendar\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ehasExtensions\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"n\"\u003eString\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ecaltype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetUnicodeLocaleType\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ca\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecaltype\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e!=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003eswitch\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecaltype\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;buddhist\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eBuddhistCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;japanese\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eJapaneseImperialCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"k\"\u003ecase\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;gregory\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eGregorianCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                    \u003c/span\u003e\u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// If no known calendar type is explicitly specified,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// perform the traditional way to create a Calendar:\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// create a BuddhistCalendar for th_TH locale,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// a JapaneseImperialCalendar for ja_JP_JP locale, or\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// a GregorianCalendar for any other locales.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"c1\"\u003e// NOTE: The language, country and variant strings are interned.\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetLanguage\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;th\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetCountry\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;TH\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eBuddhistCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetVariant\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;JP\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetLanguage\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;ja\u0026#34;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                       \u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetCountry\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;JP\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eJapaneseImperialCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e                \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eGregorianCalendar\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ezone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003eaLocale\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e            \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e        \u003c/span\u003e\u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"n\"\u003ecal\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"w\"\u003e    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"12abstract-factory\"\u003e1.2.Abstract Factory\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e抽象工厂模式\u003c/strong\u003e是一种创建型设计模式， 它能创建一系列相关的对象， 而无需指定其具体类。\u003c/p\u003e","title":"Design Pattern Note"},{"content":"Architecture 计算机体系结构：量化研究方法（第6版） : 量化研究方法 （⭐️⭐️⭐️⭐️⭐️） 图灵奖得主的作品，之前看的是第五版，后因为毕业设计重新翻了一遍第六版RISC-V版本，建议看英文版，包括后面附录的内容更是有不少重点。整本书读起来需要较长的时间和一定的耐心，但是相对地收获也不少。 深入理解计算机系统（第四版） （⭐️⭐️⭐️⭐️⭐️） 大二期间跟着x86汇编一并看完，那时候的理解还不是很深，很多内容也是跳着过了，后面大三又回去补了CMU对应的lab，lab的内容主要包括gdb使用，对于反汇编的理解等。相对于实验而言，仔细读全书的收获要更大。作为经典必读书籍，涵盖的范围非常多，从程序的结构以及执行，到系统运行时，再到IPC，看完后对整个计算机体系有一个较全的理解。同样也因为难度较大，看完需要一定耐心。 计算机组成原理与设计——软件/硬件接口（⭐️⭐️⭐️⭐️⭐️） 大三时看的还是MIPS版本，同为了毕设重新看了一遍RISC-V版。在流水线一章图文并茂，内容十分详细，作为必读书目，非常适合作为计组的教课书来看。 汇编语言（第四版） （⭐️⭐️⭐️⭐️） 跟着CSAPP一块读的，作为第三章的补充，感觉比较适合零基础的阅读，不过美中不足的是有些内容相对较老。 程序员的自我修养——链接、装载与库（⭐️⭐️⭐️⭐️） 主要看完了前一大半，后面与Win/Linux运行库实例部分跳了，很多细节和API记不住，还是靠现查。本书前几章对与编译链接的介绍还是非常详尽的，可以同时作为编译原理的部分知识学习。 Operating Systems 现代操作系统——原理与实现（⭐️⭐️⭐️⭐️⭐️） 由交大IPADS团队编写的操作系统教材，但是与其说是传统教材，更像是囊括了很多系统前沿理论的导论。在书中由浅入深，包括了非常多样化的内容，代码和插图也是非常生动，认真读完后学到了非常多的知识。在看完电子版后甚至忍不住买了正版纸质书支持一下。 操作系统导论（⭐️⭐️⭐️⭐️⭐️） 跟着jyy的OS课一起看的，不过当时自己也是对操作系统有了一定了解，所以觉得这本书读起来并没有很惊艳的感觉，不过该有的内容都涵盖了。同时本书的中文翻译一言难尽，还是看英文的比较好。 Linux/UNIX Programming Interface（⭐️⭐️⭐️⭐️⭐️） 感觉更像是现代版的APUE，很适合作为字典查阅，是一本需要放在手边的书，尤其是在man太长看不下去的时候（。 鸟哥的Linux私房菜（⭐️⭐️⭐️） 因为推荐的人非常多，在刚入门Linux时看的，当时感觉越看越迷茫，并不知道该从哪里学习。感觉书的排列可以再优化一下，时而有抓不住重点的感觉，有一些语句也可以再精简。 Networking 计算机网络（第八版）（⭐️⭐️⭐️⭐️⭐️） 应该是本书单唯一一个国内计算机高校通用教材也是考研参考书？为数不多的国产好教材，内容算是比较全面，从5个层次自底向上去介绍计算机网络，读完后有一个较为完善的认识 （前提是不把这门课学成文科）。 计算机网络——自顶向下方法（⭐️⭐️⭐️⭐️⭐️） 和上科大郑老师的课程一块看的，其中印象最深刻的莫过于对可靠传输协议状态机的推导（当时还很年轻，对状态机的认识不深刻），前前后后由可靠传输的状态机到TCP的SEQ和ACK实现，能够有一个比较深刻的理解。后面又跟着umass的lab重新做了一遍，比较简单，就顺手学了一下wireshark的使用。总言之是非常不错的教材。 TCP/IP详解（⭐️⭐️⭐️⭐️⭐️） 相对于前两本，内容更厚。主要重温了一下TCP的机制，并没有全部看完，主要是随用随查。主要卷一部分后面对与TCP的介绍有一个非常深刻理解，同时这本书也补充了不少ipv6的知识，可以作为前者的补充读物来看。 Programming Functional Functional Programming in Scala（⭐️⭐️⭐️⭐️⭐️） 很想学好一门函数式编程语言，恰好毕设期间用到了Chisel3，同时自己肤浅地认为Haskell和Lisp学习的ROI不高，遂找了一门比较现代化的语言Scala。Scala本身作为OOP和FP兼容的语言，编程风格也相对自由，同时也支持大量语法糖。这本书从消除side-effect开始，一步步到链表，异常处理，到monad，内容由浅入深，比较具有可读性。只能说函数式太多奇技淫巧（ 计算机程序的构造和解释（SICP）（第二版）（⭐️⭐️⭐️⭐️⭐️） 实际上这本书不完全只有函数式编程，更多是理解程序语义的抽象，最早开始看的时候还是Lisp版本，当时实在是没有心境完整读下去。后来出了JavaScript ed.感觉上好了很多，又重零开始看了一遍，收获还是不少的，作为经典必读书，看完后对程序的形式化描述有了较深刻的理解。 Language Java Java核心技术（卷一二）（⭐️⭐️⭐️⭐️⭐️） 大二时学Java的入门书，主要是卷一的部分，卷二后续有些用不太上的部分直接跳了。这书对零基础的同学非常友好，书的内容也相对循序渐进，可以作为Java的入门教材来读。 深入理解Java虚拟机（第三版）（⭐️⭐️⭐️⭐️⭐️） 八股文重点考试题，第一遍看完后对整个Java有了一个全新的理解，包括整个JVM的设计，从思想上能学到不少东西，虽然长年不看，到现在也就记得一个cafebabe。这本书还是有一个相对全面的介绍，对源码的描述也不枯燥，比较易懂。 Effective Java （⭐️⭐️⭐️⭐️⭐️） 闲暇时间读完的书，里边的很多思想在OOP语言中都能够得到通用。虽然这本书不是很厚，但是其内容也十分详实，仔细读完能学会很多东西，最后到毕业了也没舍得送给学弟。 Java并发编程实战（⭐️⭐️⭐️⭐️） 看的比较老的版本，印象里只到了JDK5，不过内容比较充实，实践的代码写的也很好，比较适合从零开始学习并发的阅读。 Java编程思想（⭐️⭐️⭐️） 看了一半没看下去，语句太啰嗦，比较有跳跃感，感觉有一种你懂了以后才能看懂的感觉，阅读体验远远不如Effective Java的案例式教程。 Java并发编程的艺术（⭐️⭐️⭐️） 当时还是和同学对着源码讨论了半天，发现了里面一些瑕疵，可能作者写的时候都没有理解到。同时代码风格比较混乱，感觉并不是很适合读。 Spring源码深度解析（⭐️⭐️⭐️） 到底还是不如直接看源码，感觉很多东西也是翻译而来。不过看完后还是从容器的实现与加载，到AOP，事务等有一个较为完整的学习，可以作为闲暇时读物，有专心的时间还是建议直接去看源码。 Go Go 语言设计与实现（⭐️⭐️⭐️⭐️⭐️） 当时读的还是博客版本，因为阅读人数太多而出版了书。主要内容是第三部分即Go语言的运行时，包括调度循环，GC，内存管理等。对于进阶Go语言来说是非常有意义的读物，书里边图文并茂，同时也需要一定语言的基础，作为Go语言深入学习肯定是首选推荐。 Python Rust ","permalink":"https://chasing1020.github.io/post/reading-list/","summary":"\u003ch1 id=\"architecture\"\u003eArchitecture\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e计算机体系结构：量化研究方法（第6版） : 量化研究方法 （⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e图灵奖得主的作品，之前看的是第五版，后因为毕业设计重新翻了一遍第六版RISC-V版本，建议看英文版，包括后面附录的内容更是有不少重点。整本书读起来需要较长的时间和一定的耐心，但是相对地收获也不少。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e深入理解计算机系统（第四版） （⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e大二期间跟着x86汇编一并看完，那时候的理解还不是很深，很多内容也是跳着过了，后面大三又回去补了CMU对应的lab，lab的内容主要包括gdb使用，对于反汇编的理解等。相对于实验而言，仔细读全书的收获要更大。作为经典必读书籍，涵盖的范围非常多，从程序的结构以及执行，到系统运行时，再到IPC，看完后对整个计算机体系有一个较全的理解。同样也因为难度较大，看完需要一定耐心。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003e计算机组成原理与设计——软件/硬件接口（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e大三时看的还是MIPS版本，同为了毕设重新看了一遍RISC-V版。在流水线一章图文并茂，内容十分详细，作为必读书目，非常适合作为计组的教课书来看。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"4\"\u003e\n\u003cli\u003e汇编语言（第四版） （⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e跟着CSAPP一块读的，作为第三章的补充，感觉比较适合零基础的阅读，不过美中不足的是有些内容相对较老。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"5\"\u003e\n\u003cli\u003e程序员的自我修养——链接、装载与库（⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e主要看完了前一大半，后面与Win/Linux运行库实例部分跳了，\u003cdel\u003e很多细节和API记不住，还是靠现查\u003c/del\u003e。本书前几章对与编译链接的介绍还是非常详尽的，可以同时作为编译原理的部分知识学习。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"operating-systems\"\u003eOperating Systems\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e现代操作系统——原理与实现（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e由交大IPADS团队编写的操作系统教材，但是与其说是传统教材，更像是囊括了很多系统前沿理论的导论。在书中由浅入深，包括了非常多样化的内容，代码和插图也是非常生动，认真读完后学到了非常多的知识。在看完电子版后甚至忍不住买了正版纸质书支持一下。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e操作系统导论（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e跟着jyy的OS课一起看的，不过当时自己也是对操作系统有了一定了解，所以觉得这本书读起来并没有很惊艳的感觉，不过该有的内容都涵盖了。同时本书的中文翻译一言难尽，还是看英文的比较好。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003eLinux/UNIX Programming Interface（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e感觉更像是现代版的APUE，很适合作为字典查阅，是一本需要放在手边的书，尤其是在man太长看不下去的时候（。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"4\"\u003e\n\u003cli\u003e鸟哥的Linux私房菜（⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e因为推荐的人非常多，在刚入门Linux时看的，当时感觉越看越迷茫，并不知道该从哪里学习。感觉书的排列可以再优化一下，时而有抓不住重点的感觉，有一些语句也可以再精简。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"networking\"\u003eNetworking\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e计算机网络（第八版）（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e应该是本书单唯一一个国内计算机高校通用教材也是考研参考书？为数不多的国产好教材，内容算是比较全面，从5个层次自底向上去介绍计算机网络，读完后有一个较为完善的认识 （\u003cdel\u003e前提是不把这门课学成文科\u003c/del\u003e）。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e计算机网络——自顶向下方法（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e和上科大郑老师的课程一块看的，其中印象最深刻的莫过于对可靠传输协议状态机的推导（当时还很年轻，对状态机的认识不深刻），前前后后由可靠传输的状态机到TCP的SEQ和ACK实现，能够有一个比较深刻的理解。后面又跟着umass的lab重新做了一遍，比较简单，就顺手学了一下wireshark的使用。总言之是非常不错的教材。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003eTCP/IP详解（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e相对于前两本，内容更厚。主要重温了一下TCP的机制，并没有全部看完，主要是随用随查。主要卷一部分后面对与TCP的介绍有一个非常深刻理解，同时这本书也补充了不少ipv6的知识，可以作为前者的补充读物来看。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"programming\"\u003eProgramming\u003c/h1\u003e\n\u003ch2 id=\"functional\"\u003eFunctional\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003eFunctional Programming in Scala（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e很想学好一门函数式编程语言，恰好毕设期间用到了Chisel3，\u003cdel\u003e同时自己肤浅地认为Haskell和Lisp学习的ROI不高，遂找了一门比较现代化的语言Scala\u003c/del\u003e。Scala本身作为OOP和FP兼容的语言，编程风格也相对自由，同时也支持大量语法糖。这本书从消除side-effect开始，一步步到链表，异常处理，到monad，内容由浅入深，比较具有可读性。只能说函数式太多奇技淫巧（\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e计算机程序的构造和解释（SICP）（第二版）（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e实际上这本书不完全只有函数式编程，更多是理解程序语义的抽象，最早开始看的时候还是Lisp版本，当时实在是没有心境完整读下去。后来出了JavaScript ed.感觉上好了很多，又重零开始看了一遍，收获还是不少的，作为经典必读书，看完后对程序的形式化描述有了较深刻的理解。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"language\"\u003eLanguage\u003c/h2\u003e\n\u003ch3 id=\"java\"\u003eJava\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eJava核心技术（卷一二）（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e大二时学Java的入门书，主要是卷一的部分，卷二后续有些用不太上的部分直接跳了。这书对零基础的同学非常友好，书的内容也相对循序渐进，可以作为Java的入门教材来读。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e深入理解Java虚拟机（第三版）（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e八股文重点考试题，第一遍看完后对整个Java有了一个全新的理解，包括整个JVM的设计，从思想上能学到不少东西，\u003cdel\u003e虽然长年不看，到现在也就记得一个cafebabe\u003c/del\u003e。这本书还是有一个相对全面的介绍，对源码的描述也不枯燥，比较易懂。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003eEffective Java （⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e闲暇时间读完的书，里边的很多思想在OOP语言中都能够得到通用。虽然这本书不是很厚，但是其内容也十分详实，仔细读完能学会很多东西，最后到毕业了也没舍得送给学弟。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"4\"\u003e\n\u003cli\u003eJava并发编程实战（⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e看的比较老的版本，印象里只到了JDK5，不过内容比较充实，实践的代码写的也很好，比较适合从零开始学习并发的阅读。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"5\"\u003e\n\u003cli\u003eJava编程思想（⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e看了一半没看下去，语句太啰嗦，比较有跳跃感，感觉有一种你懂了以后才能看懂的感觉，阅读体验远远不如Effective Java的案例式教程。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"6\"\u003e\n\u003cli\u003eJava并发编程的艺术（⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e当时还是和同学对着源码讨论了半天，发现了里面一些瑕疵，可能作者写的时候都没有理解到。同时代码风格比较混乱，感觉并不是很适合读。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"7\"\u003e\n\u003cli\u003eSpring源码深度解析（⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e到底还是不如直接看源码，感觉很多东西也是翻译而来。不过看完后还是从容器的实现与加载，到AOP，事务等有一个较为完整的学习，可以作为闲暇时读物，有专心的时间还是建议直接去看源码。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"go\"\u003eGo\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eGo 语言设计与实现（⭐️⭐️⭐️⭐️⭐️）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e当时读的还是博客版本，因为阅读人数太多而出版了书。主要内容是第三部分即Go语言的运行时，包括调度循环，GC，内存管理等。对于进阶Go语言来说是非常有意义的读物，书里边图文并茂，同时也需要一定语言的基础，作为Go语言深入学习肯定是首选推荐。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"python\"\u003ePython\u003c/h3\u003e\n\u003ch3 id=\"rust\"\u003eRust\u003c/h3\u003e","title":"Reading List"},{"content":"MySQL基础笔记 1. 数据库简介 1.1.数据库的优势 使用数据库的优点\n实现数据持久化 使用完整的管理系统，易于查询 成本低，开源，性能高，使用简单 移植性好 1.2.数据库的相关概念 DB：（Database）即数据库本身，保存有组织数据的容器 DBMS：（Database Manage System）数据库管理系统，即创建、使用数据库的系统 SQL：（Structured Quey Language）结构化查询语言 常用的数据库类型：MySQL，Oracle，DB2，SqlServer（只限win） 注意：SQL不是某个特定版本才有的，而是泛指一种结构化查询用的语言\n1.3.存放数据 先放到表中，再放到库中 一个数据库可以有多个表，每个表有唯一标识 表有一定特性，类似于java的类 表中的列类似于java的对象的属性 表中的行类似于java的对象本身 1.4.DBMS补充 一般有两类数据库：\n基于文件共享管理系统的Access 基于C/S架构的MySQL 2. 数据库入门 2.1. 基础语法 2.1.1. 启动/关闭数据库 net start mysql net stop mysql 2.1.2. 数据库的登录与退出 在cmd命令行 mysql -h localhost -P 3306 -u root -p123456 h代表host，P代表端口号，如果连接本机可以直接省略这个步骤 退出：使用自带命令行，exit或者ctrl+c 2.1.3. 常见语句举例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 1.查看当前数据库 show databases; # 2.打开指定的数据库 use test; # 3.查看当前库的所有表 show tables; # 4.查看其他库的所有表 show tables from DB_; # 显示某表中的表，不切换数据库 # 5.创建表 create table 表名( 列名 列类型, 列名 列类型 ); # 6.查看表的结构 desc 表名; # 7.查看MySQL版本(dos命令) # mysql -V或者mysql --version 2.1.4. 语法规范 mysql 不区分大小写，但建议关键字大小写，表名和列名用小写 每条命令最好用分号结尾，不然同时执行语句会出现语法错误 可以根据需要换行或者使用缩进 注释： 1. 使用 # 注释内容 2. 使用 -- 注释内容 这里\u0026ndash; 后面有一个空格 3. 使用 /*注释内容*/\n2.2. DQL语言 Data Query Language\n2.2.1. 基础查询 关键词select\n1 select 查询列表 from 表名; 类似于System.out.println() 查询的表可以是表中的字段，常量值，表达式，函数 查询以后的结果是一个虚拟的表格\n举例\n1 2 3 SELECT last_name FROM employees; SELECT `last_name`, `salary`, `email` FROM employees; SELECT * FROM employees; 如果是关键字字段重合，如name，可以使用反斜点来标识一下变量名。\n单引号和双引号都可以表示字符串。\n字段名、表名通常不需要加任何引号，要加也是反引号。\n查询常量值\n1 2 SELECT 123456; SELECT \u0026#39;chasing\u0026#39;; 查询表达式\n1 SELECT 100%99; 查询函数\n1 SELECT VERSION(); 起别名\n1 2 3 SELECT 100%99 AS 结果; SELECT last_name AS 姓, first_name AS 名 FROM employees; SELECT last_name 姓, first_name 名 FROM employees; 起别名的AS可以省略，如果别名是关键字类型，则需要使用双引号标注\n1 SELECT salary AS \u0026#34;OUT PUT\u0026#34; FROM employees; 去重\n1 SELECT DISTINCT department_id FROM employees; +作用，仅仅只有运算符\n1 2 3 4 5 SELECT 1+\u0026#39;2\u0026#39; # 尝试转换为数值类型，即3 SELECT 1+\u0026#39;a\u0026#39; # 如果是非数值类型，则将数值转换为0 SELECT null+10 # 如果有null，则直接变为null -- 以下方法是错误的 -- SELECT last_name+first_name AS 姓名 FROM employee; mysql里1+\u0026ldquo;2\u0026rdquo;：转为数值即3\njava里1+\u0026ldquo;2\u0026rdquo;：转为字符型即\u0026quot;12\u0026quot;\npython里1+\u0026ldquo;2\u0026rdquo;：Type Error\nCONCAT连接\n1 2 SELECT CONCAT(last_name, first_name) AS 姓名 FROM employee; # CONCAT 拼接含有null则拼接结果都是null IFNULL判断非空\n1 2 select IFNULL(commission_pct, 0) from employees; 2.2.2. 条件查询 查询的列表可能有多种情况 基础语法\n1 2 3 select 查询列表 from 表名 where 筛选条件; 1.按照条件表达式筛选： 条件运算符\u0026gt; \u0026lt; = != \u0026lt;\u0026gt; \u0026lt;= \u0026gt;= \u0026lt;=\u0026gt;\n1 2 3 select * from employees where salary\u0026gt;10000; 2.逻辑表达式 \u0026amp;\u0026amp; || ! and or not\n以上方法都可以，但是更推荐使用下面的\n1 2 3 select salary from employees where salary\u0026gt;=10000 and salary\u0026lt;=20000; 3.模糊查询\n主要有以下四种\nlike between and in is null like 一般和通配符搭配\n% 任意多个匹配，0或者多个\n_ 单个通配符，必须占位一个\n\\ 转义字符\n1 2 3 select * from employees where last_name like \u0026#39;%a%\u0026#39;; 使用_\n1 2 3 4 select * from employees # 查询第二个为_的名字，这里\\ 被用作转义字符 where last_name like \u0026#39;_\\_%\u0026#39;; 对于转义字符，除了使用 \\ 也可以自定义\n1 2 3 4 select * from employees # 查询第二个为_的名字，这里a被用作自定义的转义字符 where last_name like \u0026#39;_a_%\u0026#39; escape \u0026#39;a\u0026#39;; between and使用\n1 2 3 4 select * from employees where employee_id \u0026gt;= 100 and employee_id \u0026lt;= 120; 可以改成\n1 2 3 select * from employees where employee_id between 100 and 120; 使用between and是包含临界值的，即闭区间，两个临界值不能颠倒\n使用in\n1 2 3 4 5 6 select first_name, job_id from employees where job_id = \u0026#39;IT_PROG\u0026#39; or job_id = \u0026#39;AD_VP\u0026#39; or job_id = \u0026#39;AD_PRES\u0026#39;; 以上查询可以简写成in的形式 使用in可以提升简洁度，效率上没有提升（❌），in()会使用二分法查找，效率是O(logn)比or要好 在in的列表中不支持通配符\n1 2 3 4 select first_name, job_id from employees where job_id in (\u0026#39;IT_PROG\u0026#39;, \u0026#39;AD_VP\u0026#39;, \u0026#39;AD_PRES\u0026#39;); is null is 只能和null成对出现，即只有is null和is not null\n1 2 3 4 select last_name, commission_pct from employees\twhere commission_pct is not null; 安全等于 \u0026lt;=\u0026gt; 既可以判断null值，也可以判断普通的数值 但是使用较少，不便于区分，可读性较低\n1 2 3 select last_name,salary from employees where salary \u0026lt;=\u0026gt; 12000; 简单查询测试\n1 2 3 4 select last_name, department_id, salary*12*(1+ifnull(commission_pct, 0 )) AS 年薪 from employees; 2.2.3. 排序查询 在查询的内容之后添加order by asc 和 desc\n1 2 3 select * from employees order by salary asc;# 升序，从小到大，为默认值 以及\n1 2 3 select * from employees order by salary desc; #降序，从大到小 排序查询可以添加表达式\n1 2 3 4 select first_name, ifnull(1+commission_pct, 0)*12*salary from employees order by ifnull(1+commission_pct, 0)*12*salary desc; 也可以使用别名\n1 2 3 4 select first_name, ifnull(1+commission_pct, 0)*12*salary total from employees order by total desc; 按照多个字段排序，则整体以第一个字段为主要，后面的字段在前面的字段相同时再排序。\n2.2.4. 常用函数 隐藏实现细节，提高代码重用性\nselect 函数名() from 表名\n分类\n单行函数 例如concat、length、ifnull\n分组函数 常用作统计处理\n1、字符函数\n函数名 作用 length() 获取字符串长度 concat() 拼接字符串 upper() 全部改大写 lower() 小写 substr() 字符串截取 instr() 返回字串的在主串的第一次位置索引，找不到返回0 trim() 去除首和尾字符串空格或者是特定的字符 lpad() 使用指定的字符填充左长度 rpad() 使用指定的字符填充右长度 replace() 替换 对于中文字节长度，这里取决于编码，gbk是2个字节，utf-8是3个字节。\nmysql字符串从1开始，这点需要特别注意。\n二、数学函数 后加参数数字，一般都表示小数点后位数\n函数名 作用 round() 四舍五入 ceil() 向上取整 floor() 向下取整 truncate() 截断 mod() 取模，跟%一致（对于负数取余，计算a-a/b*b） rand() 返回0-1之间的随机数 三 、日期函数\n函数名 作用 now() 返回当前日期+时间 curdate() 返回当前日期，不包含时间 curtime() 返回当前时间，不包含日期 str_to_date() 将字符转换成日期 date_format() 将日期转换成字符 datediff() 返回两个日期之间相差的天数 四、其他函数\n函数名 作用 version() 返回版本号 database() 当前打开的数据库 user() 返回当前的用户 password() 对输入的字符加密 MD5() 采用md5加密后的结果 五、流程控制函数\nif()三元运算符\ncase 条件表达式 when 常量1 语句1 when 常量2 语句2 else 语句n end\n或者 case when 条件 then 语句 when 条件 then 语句 else 语句n\n六、分组函数\n所有分组函数都会忽略null值。\nsum()、avg()、max()、count() 可以在参数中添加distinct count(*)在任何行只要有非空元素，在这行就+1 count(1)统计原始表中所有的行数\n2.2.5. 分组查询 select column, group_function(column) from table [where condition] [group by group_by_expression] [having after grouped] [order by column] where 一定放在from后面\norder by 一定放最后\n分组后的条件，在最后查询到的结果后加having 例如\n1 2 3 4 5 select max(salary), job_id from employees where commission_pct is not null group by job_id having max(salary) \u0026gt; 12000; where having 分组前筛选 分组后筛选 原始表 分组后的结果 group by前面 group by后面 分组函数做条件，那一定是用having 能用分组前筛选的就优先使用分组前筛选（提高性能）\n多组查询\n1 2 3 select avg(salary),department_id,job_id from employees group by job_id, department_id; 2.2.6. 连接查询 为了查询的限定，可以通过表名.键名来加以标识。 与sql92标准不同的是，sql99标准的数据库连接的条件放在on后面，where单独来放筛选的条件 而sql92的数据库连接条件和筛选条件都是放在where后面，用and连接 不管是92还是99，两者的效果是一样的，使用99更利于提升代码可读性。\n@以下为sql92标准 一、等值连接\n1 2 3 select last_name,department_name from employees,departments where employees.department_id = departments.department_id; #连接条件 如果有n个表，则需要n-1个连接条件，中间使用and连接 对于多表的顺序没有要求 在以上查询过程种，from处的内容不能颠倒 为了简化查询的语句，可以使用别名，即可以在查询过程中使用别名。 但是使用了别名以后就不能再出现原名了。\n二、非等值连接\n跟以上都一样，只是where后面的条件变成不等式\n1 2 3 4 select salary, grade_level from job_grades g, employees e where salary between g.lowest_sal and g.highest_sal; 三、自连接 即表中的字段与自身存在匹配关系 方法：连接时建立两个别名\n1 2 3 select e1.last_name,e2.employee_id,e2.last_name from employees e1,employees e2 where e1.manager_id=e2.employee_id; @以下语法为sql99标准 语法\nselect 查询列表 from 表1 别名 [连接类型][inner] join 表2 别名 on 连接条件 where 筛选条件 [group by 分组] [having 筛选条件] [order by 排序列表] 以上inner可以省略，但是不建议省略\n等值查询 @sql99 一、内连接（集合的交集）\nn个集合内连接需要n-1个条件。\n1 2 3 4 select last_name, department_name from employees, departments where employees.department_id = departments.department_id; 多表之间等值查询必须要有顺序，即join on 的内容有相关\n1 2 3 4 5 select last_name, department_name,job_title from employees e inner join departments d on e.department_id = d.department_id inner join jobs j on e.job_id = j.job_id order by department_name desc; 以上不能写为这种形式：（虽然没有报错，但是不符合规范）\n1 2 3 4 5 select last_name, department_name,job_title from employees e inner join jobs j on e.job_id = j.job_id inner join departments d on e.department_id = d.department_id order by department_name desc; 二、外连接（集合的差集） 外连接查询到的结果为主表和附表都有的字段，以及主表中有而在从表中没有的字段（这部分显示为null） 即：主有从有，主有从无 主表和从表分辨：\n左外连接：left outer join左边是主表 右外连接：right outer join右边是主表 或者理解为：A left join B，就是A主表，集合为A-B 可以通过切换左右的顺序达到同样的效果\n三、全外连接（集合的交集） 查询到两个表之间的交集 全外连接查询到的结果为主表和附表都有的字段，以及主表中有而在从表中没有的字段（这部分显示为null），以及从表中有而在主表中没有的字段 即：主有从有，主有从无，从无主有 full outer join 全外连接\n四、交叉连接（集合的笛卡尔积） cross join 即代表笛卡尔乘积。这里跟sql92相同，所以使用较少。\n2.2.7. 子查询 子查询可以写在其他语句中的select语句，这里的其他语句也即表示可以用于增删改语句中。 在其他语句中的查询称为子查询 外部的查询称之为主查询（外查询） 位置可能出现的情况 子查询的位置可以放在select后面，where后面，having后面，或者是exists后面（相关子查询）。 按结果集的行列数不同。 查询结果：\n标量子查询（一行一列，在select后面） 列子查询（一列多行，在where或者having后面） 行子查询（多行多列，在where或者having后面） 表子查询（以上都有，exists后面） 一、where及having后面 子查询一般放在小括号内，条件的右侧 1.标量子查询\n1 2 3 4 5 select * from employees where salary \u0026gt; (select salary from employees where last_name = \u0026#39;Abel\u0026#39;); 2.列子查询（多行子查询） 使用多行比较操作符，主要有以下几种\nin | not in any|some all 使用类比np库\n1 2 3 4 5 6 7 select * from employees where salary \u0026lt; ( select MIN(salary) from employees where job_id = \u0026#39;IT_PROG\u0026#39; ); 3.行子查询 由于很少有数据表满足这种情况，所以使用较少\n1 2 3 4 5 select * from employees where salary=(select max(salary) from employees) and employee_id=(select min(employee_id) from employees); 以上可以替换为\n1 2 3 4 select * from employees where (employee_id, salary) = (select min(employee_id), max(salary) from employees); 二、select 后面\n里面仅仅支持标量子查询（即只有一列）\n1 2 3 4 5 6 7 select d.*, ( select count(*) from employees e where e.department_id = d.department_id ) from departments as d; 三、from后面\n1 2 3 4 5 6 7 8 9 select * from ( select avg(salary) ag, department_id from employees group by department_id ) ag_dep inner join job_grades g on ag_dep.ag between lowest_sal and highest_sal; 四、exist 后面（相关子查询）\n由于能用exist的情况下一定能用in，所以使用较少 先使用外查询，再涉及子查询 exists后接完整查询语句，结果为1或者0 查询有员工的部门名\n1 2 3 4 5 select department_name from departments d where exists(select * from employees e where e.department_id = d.department_id); 2.2.8. 分页查询 当要显示的数据再一页显示不全，需要提交多次sql请求 7 select 查询列表 1 from 表 2 [连接类型] join 表2 3 on 连接条件 4 where 筛选条件 5 group by 分组字段 6 having 分组以后的筛选 8 order by 排序的字段 9 limit offset, size 前面的数字代表语句的执行顺序 这里offset是条目的起始索引，默认起始索引从1开始，如果是第一条，0可以省略 size是个数\n1 2 select * from employees limit 10,15; # 查询第11到25条 假设需要显示的页数为page ，每一页的条目数是size limit 语句都是在执行顺序的最后\n使用特定数字进行查询\n2.2.9. 联合查询 union 联合查询\n将多条查询语句合并成一个结果 查询某某条件或者某某条件的集合\n1 2 3 4 select last_name from employees where salary\u0026gt;10000 or employee_id \u0026gt;50; 通过联合查询可以得到如下结果\n1 2 3 4 5 6 7 select last_name from employees where salary \u0026gt; 10000 union select last_name from employees where employee_id \u0026gt; 50 作用，即同类型的数据存储在多个表中，如中国学生表和外国学生表，查询年龄大于18的。\n适用于：搜索结果返回多个类型的表，但是查询的信息相同的情况，即列数要对应。 如果同样的字段出现在多个表中 union自带去重的效果，如果需要保存所有信息，需要增加union all\n总结\n7 select 查询列表 1 from 表 2 [连接类型] join 表2 3 on 连接条件 4 where 筛选条件 5 group by 分组字段 6 having 分组以后的筛选 8 order by 排序的字段 9 limit offset, size\n2.3.DML语言 Dara Manipulation Language 数据操作语言 分为插入，修改，删除三大类\n2.3.1. 插入操作 方法一\ninsert into 表名(列名1, ...) values(值1, ...) insert into 表名(列名1, ...) values(值1, ...), (值2, ...), ... 在插入的过程中，插入的值的类型要和列类型相一致或者兼容（能够隐式转换），列数的个数和值的个数一定要相匹配。 可以省略列名，默认所有的列，并且值的顺序需要和位置相一致\n方法二\ninsert into 表名 set 元素1 = 值1, 元素2 = 值2, ... 方式一支持插入多行，方式二不支持插入多行，同时方式一支持子查询，方式二不支持\n即方法一支持\ninsert into 表名 select 查询列表 from 表名\n1 2 insert into boys (boyName, userCP) values (\u0026#39;zxa\u0026#39;,10); 所以方法一使用教多\n2.3.2. 修改操作 一、修改单个表\n1 update 表名 3 set 列1 = 新值1, 列2 = 新值2, \u0026hellip; 2 where 筛选条件;\n语句的逻辑顺序132\n二、 修改多个条件\n同样分为92语法和99语法\n@sql92语法中 update 表1 别名, 表2 别名 set 列 = 值 where 连接条件 and 筛选条件\n@sql99语法中 update 表1 别名 [inner|left|full] update 表2 别名 set 列 = 值 where 筛选条件\n2.3.3. 删除操作 方式一：\ndelete from 表名 where 筛选条件 删除的后面还可以添加limit作为补充，使用效果同查询部分\n@sql92 delete 表1的别名, 表2的别名 from 表1 别名, 表2 别名 where 筛选条件 and 筛选条件;\n@sql99语法 delete 表1的别名, 表2的别名 from 表1 别名 inner | left |right join 表2 别名 on 连接条件 where 筛选条件\n方式二：truncate table 表名\n[面试题]\ntruncate 一步实现删除整个表 不能添加where 不能实现多表删除 truncate 效率比delete效率高 表中如果有自增长列，在使用delete删除以后，自增长的值会从断点开始 如果使用truncate删除，则再插入数据，自增长列从1开始 truncate没有返回值，delete有返回值 truncate删除不能回滚，delete可以回滚事务 2.4.DDL语言 Data Define Language 数据定义语言\ncreate，alter，drop等\n2.4.1. 库的管理 一、创建数据库 create database 库名; 为了提高容错性，可以将创建语句写为 crate database if not exists 库名;\n二、库的修改 修改名称 rename database 原名 to 新名; 这种方法现在已经被废弃，出于安全原因\n一般不会轻易修改表名\n修改字符集 alter database 库名 character set 字符集名称;\n三、库名删除\ndrop database if exists 库名\n2.4.2. 表的管理 一、创建 create table 表名( 列名1 列的类型1 [(长度)约束], 列名2 列的类型2 [(长度)约束], 列名3 列的类型3 [(长度)约束]， 。。。 );\n添加稳定性 create table if not exists 表名( 列名1 列的类型1 [(长度)约束], 列名2 列的类型2 [(长度)约束], 列名3 列的类型3 [(长度)约束]， 。。。 );\n数据类型\n整型: typeint, 1字节 smallint, 2字节 mediumint, 3字节 int(integer), 4字节 bigint, 8字节 整型之间的区别只在于可以表示的范围，如果需要设置无符号，则需要在后面添加unsigned 如果超过了整型可以表示的范围，会报out of range 异常，并添加临界值 如果设置长度如int(7) zreo fill，同时该int类型会自动变为无符号类型，同时7代表长度不够7的情况下会用0填充 长度代表显示的最大宽度，无其他含义。\n小数 浮点数类型： float，4字节 double, 8字节 定点数类型： dec(M, D), M+2字节 最大范围和double相同，有效位有M,D决定 decimal(M, D), 定义 float(M, D) double(M, D) dec(M, D)\nM表示整数部分和小数部分合计的位数 后面的D表示小数点保留位数 如果插入超过临界值，则插入999.99（这里以5,2为例）\n一般而言M, D都可以省略 如果师float和double 则会随着插入的数值而改变 如果是dec，则默认M为10, D为0\n对于定点型，精度很高，对于插入数值的精度较高如货币运算可以考虑使用\n对于使用的原则：所选择类型越简单越好，能保存数值的类型越小越好\n字符串 较短的文本： char(M), 最多字符数是M，不是字节数，开辟空间时的长度固定，效率较高 varchar(M),可变长度的字符，效率低于char binary, varbinarty,与上类似，存二进制类型 enum(字段),如设置enum('男','女')那么在添加的时候只能添加这两种类型 set(多个成员), 与enum类似，不过可以一次选取多个成员 较长的文本： text blob 如果是存储季节如春夏秋冬，则使用char效率会高于varchar char的长度M可以省略，默认为1，而varchar的长度M不可以省略\n日期类型： date,4字节，范围1000-9999 datetime,8字节，范围1970-2038 timestamp,4字节 time,3字节 year,1字节 timestamp插入的内容实际的时区有关，更能反应当前的时期，受mysql版本影响 datetime只能反映出插入时的当地时区 查询修改数据库的时区 show variables like \u0026rsquo;time_zone' set time_zone = \u0026lsquo;+9:00\u0026rsquo;\n二、表的修改\n修改列名 alter table 表名 change column 原表名 新表名;\n修改列的类型以及约束 alter table 表名 modify column 列名 约束类型;\n添加新列 alter table 表名 add column 列名 约束类型;\n删除列 alter table 表名 drop column 列名; alter table 表名 drop column if exists 列名;\n修改表名 alter table 表名 rename to 新表名\n三、表的删除\ndrop table 表名; drop table id exists 表名\n查看所有表 show tables;\n四、 表的复制\n仅仅复制表的结构： create table 表名 like 原表名;\n复制表的数据+结构： create table 表名 select * from 原表名\n只复制部分列或者部分行 create table 表名 select 查询的内容\n只复制部分的内容作为表的结构 create table 表名 select 列名 from 原表名 where 1=2; # 或者直接写0\n2.2.3. 表的约束 约束用于限制表中的数据 为了保证添加到表中的数据可靠\n一、not null 该字段的值不能为空 常用作学号，姓名\n二、default 用于保证该字段有默认值比如性别\n三、primary key 保证该字段具有唯一性，并且非空 比如学号\n四、unique 保证该字段具有唯一性 比如座位号\n五、check 在MySQL中不支持，但是不会报错 限制性别只有男和女 参数校验一般由后端来做\n六、foreign key references 用于限制两个表之间的关系 保证该从表的字段值必须来自主表关联列的值 从表引用主表的值 show index from 表名\n在创建表时，修改表时都可以添加约束\n约束的类型： 列级约束：六大约束都有效果，但是外键约束没有效果 表级约束：除了非空和默认值，其余都支持 一般来说添加表级约束用constraint 语法 [constraint 约束名] 约束类型(字段名)\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 create table student ( id int, stuname varchar(20), gender char(1), seat int, age int, majorid int, constraint pk primary key (id), constraint uq unique(seat) constraint ck check(gender = \u0026#39;男\u0026#39; or gender = \u0026#39;女\u0026#39;), constraint fk_sudent_major foreign key(majorid) references major(id) ) 面试题 主键和唯一区别： 主键必定唯一，不允许为空。 可以设置联合主键：即只有多个列都完全相同才会报错，但是不推荐使用。\n外键要求 在从表上设置外键关系： 从表外键的列类型要和主表相一致或者兼容，对名称无要求 要求主表的关联列必须是一个key，一般是主键或者唯一 修改约束类型 列级约束： alter table 表名 modify column 列名 类型名 限定 表级约束： alter table 表名 add primary key(列名) 删除约束： alter table 表名modify column 列名 类型名 [null]; 一般这个null可以省略不写\n标识列：又称为自增长列 一个表只能有一个自增长列，且必须是数值类型 show variables like \u0026lsquo;%auto_increment%\u0026rsquo; set auto_increment = 3 #修改步长 初始值直接插入一个特殊的值就行\n2.5.TCL语言 Transaction Control Language 事务控制语言\n2.5.1. 事务 一个或者一组sql语句组成一个执行单元，这个执行单元要么全部执行，要么全部不执行。\n存储类型\n[面试题] 事务ACID的特性：\n原子性(Atomicity)：事务是一个不可分割的工作单位，事务中的操作要么都发生，要么都不发生。 一致性(Consistency)：事务必须使数据库从一个一致性状态切换到另一个一致性状态。 隔离性(Isolation)：事物的隔离性是指一个事务的执行不能被其他事务干扰，即一个事务内部的操作及使用的数据对并发的其他事务是隔离的，并发执行的事务之间不能互相干扰。 持久性(Durability)：持久性是指一个事务一旦被提交，它对数据库的改变就是永久性的，接下来的其他操作和数据库故障不应该对其有任何影响。 一、事务的创建 隐式：事务没有明显的开始和结束语句 比如update insert insert语句\n1 2 3 4 5 6 7 8 9 10 11 #前提必须先设置明显的开启和结束的标记 set autocommit = 0 # 开启事务的语句 start transaction # 提交事务 commit # 回滚事务 rollback 2.5.2. 数据库的隔离级别 同时运行多个事务，对于两个事务，一个事务读取了另一个事务更新但是未提交的字段，如果这个事务回滚，那么原事务是无效的。\n[面试题] 脏读：T1读取了T2已更新但是未提交的时段，如果T2回滚，则T1的数据无效。 不可重复读：T1读取了一个字段，但是T2把这个字段更新了，T1再读取该字段时数据发生了改变。 幻读：T1从表中读取了一个字段，然后T2在该表中插入了一些新的行，T1再读取的表就会多出几行\n数据库的隔离问题 数据库有四种隔离级别：\nREAD UNCOMMITED 允许事务读取未被其他事务提交的变更，三种问题都会出现 READ COMMITED 只允许读取已经被提交的变更，可以避免脏读，但是不可避免不可重复读和幻读 REPEATABLE READ（默认） 确保事务可以多次从一个字段中读取相同的值，可以避免脏读和不可重复读，但是不可避免幻读 SERIALIZABLE 确保事务可以从一个表中读取相同的行，事务持续时间禁止其他事务对该表执行插入，避免所有情况，效率极低 查看当前的数据库隔离级别 select @@tx_isolation;\nsavepoint 节点名; 设置事务的保存点\nrollback 节点名; 将事务回滚到保存点处\n2.5.3.视图 虚拟表和普通表一样使用\n一种虚拟存在的表，行列的数据来自定义视图查询中所使用的表，是动态生成的，只保留sql逻辑，不保留查询的结果\n语法 create view 视图名 as 查询语句\n好处：\n重用复杂的sql语句 简化了sql的操作，不必知道查询的细节 保护数据，提高安全性 视图的修改 create or replace view 视图名 as 查询语句\n或者使用 alter view 视图名 as 查询语句\n删除视图 drop view 视图名，视图名，……\n查看视图 desc 视图名;\n[美团2019秋招笔试] 具有以下关键字的视图不允许更新： 聚合函数（SUM(), MIN(), MAX(), COUNT()等）。 DISTINCT GROUP BY HAVING UNION或UNION ALL 位于选择列表中的子查询 Join FROM子句中的不可更新视图 WHERE子句中的子查询，引用FROM子句中的表。 仅引用文字值（在该情况下，没有要更新的基本表）。 ALGORITHM = TEMPTABLE（使用临时表总会使视图成为不可更新的）。\n视图虽然可以更新，但是最好不要更新，如果没有全面考虑在视图中更新数据的限制，就可能会造成数据更新失败\n2.6. 变量 系统变量 全局变量 会话变量 自定义变量 用户变量 局部变量 系统变量：由系统提供，属于服务器层面\nshow global|session variables\n查看满足条件的部分系统变量\nshow global variables like \u0026lsquo;%char%\u0026rsquo;\n查看指定的某个系统变量的值 select @@global|session.系统变量名\n为某个系统变量赋值 set @@global|session.系统变量名 = 赋的值\n二、会话变量 仅仅针对当前会话有效 使用方法与上相同，把global换成session\n三、自定义变量 作用域也仅仅针对于当前的会话，同于会话变量的作用域，针对当前变量有效 声明或者是赋值：\n1 2 3 set @用户变量名 = 值; set @用户变量名 := 值; select @用户变量名 := 值; 用户的自定义变量名为弱类型 赋值也可以使用\n1 2 select 字段 into @变量名 from 表; 四、局部变量 只能放在begin，end中\n声明和赋值 declare 变量名 数据类型 [default 值];\ndeclare 变量名 数据类型(值);\n用户变量 局部变量 当前会话 begin end中间 会话中任何地方都可以用 只能在begin end中且为第一句 一般加@，弱类型 不需要加@，强类型 2.7. 流程控制函数 存储过程：一组预先编译好的sql语句集合。\n提高代码的重用性，简化操作，减少连接服务器的次数\n1 2 3 4 create procedure pro_name(参数列表) begin 一组sql语句 end $; 在参数列表种，in作为输入，out作为输出，inout既可以输入也可以输出 存储过程的结尾可以使用 delimiter 结束标记\n调用call pro_name()\n删除 drop procedure pro_name\n函数创建 create function 函数名(参数列表) return 返回类型 begin\nend\n函数体种肯定会有return语句\n流程控制结构\n顺序结构 分支结构 循环结构 if(表达式1，表达式2，表达式3)\ncase 变量|表达式|字段 when 判断值 then 返回的值; when 判断值 then 返回的值; 。。。 else end case;\ncase when 判断条件 then 返回的值; when 判断条件 then 返回的值; 。。。 else end case;\nif 条件1 then 语句1; elseif 条件2 then 语句2; 。。。 [else 语句n;] end if;\n循环结构 iterate 类似于 continue\n[标签] loop 循环体 end loop [标签]\n","permalink":"https://chasing1020.github.io/post/mysql-basic-note/","summary":"\u003ch1 id=\"mysql基础笔记\"\u003eMySQL基础笔记\u003c/h1\u003e\n\u003ch2 id=\"1-数据库简介\"\u003e1. 数据库简介\u003c/h2\u003e\n\u003ch3 id=\"11数据库的优势\"\u003e1.1.数据库的优势\u003c/h3\u003e\n\u003cp\u003e使用数据库的优点\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e实现数据持久化\n使用完整的管理系统，易于查询\n成本低，开源，性能高，使用简单\n移植性好\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3 id=\"12数据库的相关概念\"\u003e1.2.数据库的相关概念\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eDB：（Database）即数据库本身，保存有组织数据的容器\nDBMS：（Database Manage System）数据库管理系统，即创建、使用数据库的系统\nSQL：（Structured Quey Language）结构化查询语言\n常用的数据库类型：MySQL，Oracle，DB2，SqlServer（只限win）\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e注意：SQL不是某个特定版本才有的，而是泛指一种结构化查询用的语言\u003c/p\u003e","title":"MySQL Basic Note"},{"content":"SHU社区学院资料整理 本着开源的原则，加之疫情期间在家全程的线上教学，在本人爱好收集文件的习惯之下，留下了一系列在社区学院期间学习的资料。\n本文仅包含相关电子书，已公开的历年试卷等资源，涵盖了微积分，大学物理，线性代数，工程制图，中国近现代史纲要，军事理论等学科。 本文不含任何历史作业，报告等敏感内容。相关教学课件、笔记等已标注出处。 本文所分享的所有资料，仅供上海大学计算机学院学生学习与交流使用。 本文的资料仅供参考，请自己判断其适用性。 本文一些文件如侵犯了您的权益，请向我邮件，我会及时彻底清除这些文件。 本文许可：CC-BY-NC-SA：署名-非商业性使用-相同方式共享\n文章版权归个人所有，未经作者授权禁止转载（或注明出处），不得将其中的资料用作任何商业用途。\n1. 大一课本 大一期间的绝大多数课本pdf版\n链接: https://pan.baidu.com/s/1XaGX4IoydftzUxTh_ABlEQ\n提取码: tkkc\n2. 历年期末试卷 部分学科的历年期末测试试卷\n链接: https://pan.baidu.com/s/13eB0v-wAEBkCBeHpp7NfUg\n提取码: qmsj\n3. 理工大类微积分试卷 理工大类的微积分测试卷以及练习题等\n链接: https://pan.baidu.com/s/1Purl_p4G2LMvmpwHeBqImA\n提取码: gssj\n4. 线性代数资料 史上最全的线性代数资料，包括大多数课件课本pdf，测试题，历年试卷，猴博士教程等等\n链接: https://pan.baidu.com/s/1P95PTcMIWNJUCoK4rQ9s1w\n提取码: xxds\n5. 高等数学电子教案 我校高等数学电子课件，转载自微信公众号“上大数学在线”，读者可以自行选择关注\n链接: https://pan.baidu.com/s/1gAOJcVsjovshoAyaSzk0qQ\n提取码: md5c\n6. 大学物理电子教案 大学物理的课件，由于线上上课质量不齐，整理的数目较少\n链接: https://pan.baidu.com/s/1oigVp21in-MPKVmmPHur4A\n提取码: dxwl\n7. 工程制图和计算机绘图基础 工程制图的习题集答案等\n链接: https://pan.baidu.com/s/1SlZDrEkvIYgibgKa8l8x3Q\n提取码: gczt\n8. 军事理论资料 军事理论复习提纲，anki背诵词典等，以及线上考试的题库\n链接: https://pan.baidu.com/s/1B5ECxo8G-IFMUJO9Xhm2VA\n提取码: jsll\n9. 中国近现代史纲要 中近纲的题库，教辅书等等\n链接: https://pan.baidu.com/s/1kXq7g2xi44dUmxUo1I44uw\n提取码: jxds\n10. 大学英语 视听说. 读写教程\n链接: https://pan.baidu.com/s/1QwYKqmDXa-L0wUe2173hgw\n提取码: dxyy\n11. 大物. 大化实验 大学物理实验以及大学化学实验用书， 实验课其实可以不用去教材科买纸质书的（\n链接: https://pan.baidu.com/s/1zIz8kGJFJ6GXlRsEB4King\n提取码: sykb\n","permalink":"https://chasing1020.github.io/post/community-college-materials/","summary":"\u003ch1 id=\"shu社区学院资料整理\"\u003eSHU社区学院资料整理\u003c/h1\u003e\n\u003cp\u003e本着开源的原则，加之疫情期间在家全程的线上教学，在本人爱好收集文件的习惯之下，留下了一系列在社区学院期间学习的资料。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e本文仅包含相关电子书，已公开的历年试卷等资源，涵盖了微积分，大学物理，线性代数，工程制图，中国近现代史纲要，军事理论等学科。\u003c/li\u003e\n\u003cli\u003e本文不含任何历史作业，报告等敏感内容。相关教学课件、笔记等已标注出处。\u003c/li\u003e\n\u003cli\u003e本文所分享的所有资料，仅供上海大学计算机学院学生学习与交流使用。\u003c/li\u003e\n\u003cli\u003e本文的资料仅供参考，请自己判断其适用性。\u003c/li\u003e\n\u003cli\u003e本文一些文件如侵犯了您的权益，请\u003ca href=\"mailto:chasing1020@gmail.com\"\u003e向我邮件\u003c/a\u003e，我会及时彻底清除这些文件。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp\u003e本文许可：\u003ca href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en\"\u003eCC-BY-NC-SA：署名-非商业性使用-相同方式共享\u003c/a\u003e\u003c/p\u003e","title":"Community College Materials"},{"content":"Selenium3实现网页自动化 1.初步工作 1.1 环境配置 安装Selenium\n本文使用的是python3.8.4 64bit\n使用pip指令安装selenium\npip install selenium\n1.2 设置浏览器驱动 首先设置浏览器的地址，手动创建一个存放浏览器驱动的目录，如： E:\\driver , 将下载的浏览器驱动文件（例如：chromedriver、geckodriver）丢到该目录下。\n我的电脑\u0026ndash;\u0026gt;属性\u0026ndash;\u0026gt;系统设置\u0026ndash;\u0026gt;高级\u0026ndash;\u0026gt;环境变量\u0026ndash;\u0026gt;系统变量\u0026ndash;\u0026gt;Path，将“E:\\driver”目录添加到Path的值中。\n验证不同的浏览器驱动是否正常使用。\nfrom selenium import webdriver\n1 2 3 4 5 6 7 8 9 10 11 driver = webdriver.Firefox() # Firefox浏览器 driver = webdriver.Chrome() # Chrome浏览器 driver = webdriver.Ie() # Internet Explorer浏览器 driver = webdriver.Edge() # Edge浏览器 driver = webdriver.Opera() # Opera浏览器 driver = webdriver.PhantomJS() # PhantomJS 2.基本操作 2.1 元素定位 Selenium提供了8种定位方式。\nid name class name name link text partial link text xpath css selector\n这8种定位方式在Python selenium中所对应的方法为：\nfind_element_by_id() find_element_by_name() find_element_by_class_name() find_element_by_tag_name() find_element_by_link_text() find_element_by_partial_link_text() find_element_by_xpath() find_element_by_css_selector()\n定位使用的方法\n假如我们有一个Web页面，通过前端工具（如，Firebug）查看到一个元素的属性是这样的。\n1 2 3 4 5 6 7 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;body link=\u0026#34;#0000cc\u0026#34;\u0026gt; \u0026lt;a id=\u0026#34;result_logo\u0026#34; href=\u0026#34;/\u0026#34; onmousedown=\u0026#34;return c({\u0026#39;fm\u0026#39;:\u0026#39;tab\u0026#39;,\u0026#39;tab\u0026#39;:\u0026#39;logo\u0026#39;})\u0026#34;\u0026gt; \u0026lt;form id=\u0026#34;form\u0026#34; class=\u0026#34;fm\u0026#34; name=\u0026#34;f\u0026#34; action=\u0026#34;/s\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;soutu-btn\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;input id=\u0026#34;kw\u0026#34; class=\u0026#34;s_ipt\u0026#34; name=\u0026#34;wd\u0026#34; value=\u0026#34;\u0026#34; maxlength=\u0026#34;255\u0026#34; autocomplete=\u0026#34;off\u0026#34;\u0026gt;输入框 我们的目的是要定位input标签的输入框。\n通过id定位: 1 dr.find_element_by_id(\u0026#34;kw\u0026#34;) 通过name定位: 1 dr.find_element_by_name(\u0026#34;wd\u0026#34;) 通过class name定位: 1 dr.find_element_by_class_name(\u0026#34;s_ipt\u0026#34;) 通过tag name定位: 1 dr.find_element_by_tag_name(\u0026#34;input\u0026#34;) **1、 通过xpath定位，xpath定位有多种写法，这里列几个常用写法: ** Xpath支持ID、Class、Name定位功能，以以下三者为例： 1）、通过ID定位 //[@id=\u0026lsquo;kw\u0026rsquo;] 2）、通过Class定位 //[@class=\u0026lsquo;class_name\u0026rsquo;] 3）、 通过Name定位 //[@name=\u0026lsquo;name\u0026rsquo;] 2、如果标签没有ID、Class、Name三总属性，Xpath还支持属性定位功能 @ 代表以属性定位，后面可以接标签中任意属性 //[@other=\u0026lsquo;attribute\u0026rsquo;] 3、当标签的属性重复时，Xpath提供了通过标签来进行过滤 将 * 换位任意标签名，则可根据标签进行筛选 //input[@placeholder=\u0026lsquo;用户名\u0026rsquo;] 4、当标签页重复时，Xpath提供了层级过滤 例如：找不到儿子，那么就先找他的爸爸，实在不行可以再找他的爷爷 1）、支持通过 / 进行层级递进，找到符合层级关系的标签 //form/div/input[@placeholder=\u0026ldquo;用户名\u0026rdquo;] 2）、当层级都重复时，可以通过单个层级的属性进行定位 //form/div[@class=\u0026lsquo;login-user\u0026rsquo;]/input 5、一个元素它的兄弟元素跟它的标签一样，这时候无法通过层级定位到。因为都是一个父亲生的，多胞胎兄弟。Xpath提供了索引过滤 通过索引，在List中定位属性，与python的索引有些差别，Xpath从1开始 //select[@name=\u0026lsquo;city\u0026rsquo;][1]/option[1] 6、上面几种如果都用上了之后还重复的话，我们就可以使用Xpath提供的终极神器，逻辑运算定位。and 或 or　1）、通过and来缩小过滤的范围，只有条件都符合时才能定位到 //select[@name=\u0026lsquo;city\u0026rsquo; and @size=\u0026lsquo;4\u0026rsquo; and @multiple=\u0026ldquo;multiple\u0026rdquo;] 2）、or就相反了，只要这些筛选中，其中一个出现那么久匹配到了 //select[@name=\u0026lsquo;city\u0026rsquo; or @size=\u0026lsquo;4\u0026rsquo;]\n1 2 3 4 5 6 7 dr.find_element_by_xpath(\u0026#34;//*[@id=\u0026#39;kw\u0026#39;]\u0026#34;) dr.find_element_by_xpath(\u0026#34;//*[@name=\u0026#39;wd\u0026#39;]\u0026#34;) dr.find_element_by_xpath(\u0026#34;//input[@class=\u0026#39;s_ipt\u0026#39;]\u0026#34;) dr.find_element_by_xpath(\u0026#34;/html/body/form/span/input\u0026#34;) dr.find_element_by_xpath(\u0026#34;//span[@class=\u0026#39;soutu-btn\u0026#39;]/input\u0026#34;) dr.find_element_by_xpath(\u0026#34;//form[@id=\u0026#39;form\u0026#39;]/span/input\u0026#34;) dr.find_element_by_xpath(\u0026#34;//input[@id=\u0026#39;kw\u0026#39; and @name=\u0026#39;wd\u0026#39;]\u0026#34;) 通过css定位，css定位有多种写法，这里列几个常用写法: 1 2 3 4 5 6 dr.find_element_by_css_selector(\u0026#34;#kw\u0026#34;) dr.find_element_by_css_selector(\u0026#34;[name=wd]\u0026#34;) dr.find_element_by_css_selector(\u0026#34;.s_ipt\u0026#34;) dr.find_element_by_css_selector(\u0026#34;html \u0026gt; body \u0026gt; form \u0026gt; span \u0026gt; input\u0026#34;) dr.find_element_by_css_selector(\u0026#34;span.soutu-btn\u0026gt; input#kw\u0026#34;) dr.find_element_by_css_selector(\u0026#34;form#form \u0026gt; span \u0026gt; input\u0026#34;) 接下来，我们的页面上有一组文本链接。\n1 2 \u0026lt;a class=\u0026#34;mnav\u0026#34; href=\u0026#34;http://news.baidu.com\u0026#34; name=\u0026#34;tj_trnews\u0026#34;\u0026gt;新闻\u0026lt;/a\u0026gt; \u0026lt;a class=\u0026#34;mnav\u0026#34; href=\u0026#34;http://www.hao123.com\u0026#34; name=\u0026#34;tj_trhao123\u0026#34;\u0026gt;hao123\u0026lt;/a\u0026gt; 通过link text定位: 1 2 dr.find_element_by_link_text(\u0026#34;新闻\u0026#34;) dr.find_element_by_link_text(\u0026#34;hao123\u0026#34;) 通过link text定位: 1 2 3 dr.find_element_by_partial_link_text(\u0026#34;新\u0026#34;) dr.find_element_by_partial_link_text(\u0026#34;hao\u0026#34;) dr.find_element_by_partial_link_text(\u0026#34;123\u0026#34;) 2.2 浏览器控制 控制浏览器大小 1 driver.set_window_size(480, 800) 前进，后退 1 2 driver.forward() driver.back() 刷新页面 1 driver.refresh() 2.3 模拟点击和输入 点击和输入 前面我们已经学习了定位元素， 定位只是第一步， 定位之后需要对这个元素进行操作， 或单击（按钮） 或输入（输入框） ， 下面就来认识 WebDriver 中最常用的几个方法：\nclear()： 清除文本。\nsend_keys (value)： 模拟按键输入。\nclick()： 单击元素。\n1 2 3 4 5 6 7 8 9 10 from selenium import webdriver driver = webdriver.Chrome() driver.get(\u0026#34;https://www.baidu.com\u0026#34;) driver.find_element_by_id(\u0026#34;kw\u0026#34;).clear() driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;selenium\u0026#34;) driver.find_element_by_id(\u0026#34;su\u0026#34;).click() driver.quit() 提交 submit()， submit()方法用于提交表单。 例如， 在搜索框输入关键字之后的“回车” 操作， 就可以通过该方法模拟。\n1 2 3 4 5 6 7 8 9 10 from selenium import webdriver driver = webdriver.Chrome() driver.get(\u0026#34;https://www.baidu.com\u0026#34;) search_text = driver.find_element_by_id(\u0026#39;kw\u0026#39;) search_text.send_keys(\u0026#39;selenium\u0026#39;) search_text.submit() driver.quit() 有时候 submit()可以与 click()方法互换来使用， submit()同样可以提交一个按钮， 但 submit()的应用范围远不及 click()广泛。\n其他常用方法 size： 返回元素的尺寸。\ntext： 获取元素的文本。\nget_attribute(name)： 获得属性值。\nis_displayed()： 设置该元素是否用户可见。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from selenium import webdriver driver = webdriver.Chrome() driver.get(\u0026#34;http://www.baidu.com\u0026#34;) # 获得输入框的尺寸 size = driver.find_element_by_id(\u0026#39;kw\u0026#39;).size print(size) # 返回百度页面底部备案信息 text = driver.find_element_by_id(\u0026#34;cp\u0026#34;).text print(text) # 返回元素的属性值， 可以是 id、 name、 type 或其他任意属性 attribute = driver.find_element_by_id(\u0026#34;kw\u0026#34;).get_attribute(\u0026#39;type\u0026#39;) print(attribute) # 返回元素的结果是否可见， 返回结果为 True 或 False result = driver.find_element_by_id(\u0026#34;kw\u0026#34;).is_displayed() print(result) driver.quit() 输出结果：\n{\u0026lsquo;width\u0026rsquo;: 500, \u0026lsquo;height\u0026rsquo;: 22} ©2015 Baidu 使用百度前必读 意见反馈 京 ICP 证 030173 号 text True 执行上面的程序并查看结果： size 方法用于获取百度输入框的宽、 高， text 方法用于获得百度底部的备案信息， get_attribute()用于获得百度输入的 type 属性的值， is_displayed()用于返回一个元素是否可见， 如果可见则返回 True， 否则返回 False。\n2.4 鼠标操作 在 WebDriver 中， 将这些关于鼠标操作的方法封装在 ActionChains 类提供。\nActionChains 类提供了鼠标操作的常用方法：\nperform()： 执行所有 ActionChains 中存储的行为；\ncontext_click()： 右击；\ndouble_click()： 双击；\ndrag_and_drop()： 拖动；\nmove_to_element()： 鼠标悬停。\n鼠标悬停操作\n1 2 3 4 5 6 7 8 9 10 11 from selenium import webdriver # 引入 ActionChains 类 from selenium.webdriver.common.action_chains import ActionChains driver = webdriver.Chrome() driver.get(\u0026#34;https://www.baidu.cn\u0026#34;) # 定位到要悬停的元素 above = driver.find_element_by_link_text(\u0026#34;设置\u0026#34;) # 对定位到的元素执行鼠标悬停操作 ActionChains(driver).move_to_element(above).perform() ……\nfrom selenium.webdriver import ActionChains 导入提供鼠标操作的 ActionChains 类。\nActionChains(driver) 调用 ActionChains()类， 将浏览器驱动 driver 作为参数传入。\nmove_to_element(above) context_click()方法用于模拟鼠标右键操作， 在调用时需要指定元素定位。\nperform() 执行所有 ActionChains 中存储的行为， 可以理解成是对整个操作的提交动作。\n2.5 键盘操作 Keys()类提供了键盘上几乎所有按键的方法。 前面了解到， send_keys()方法可以用来模拟键盘输入， 除此 之外， 我们还可以用它来输入键盘上的按键， 甚至是组合键， 如 Ctrl+A、 Ctrl+C 等。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from selenium import webdriver # 引入 Keys 模块 from selenium.webdriver.common.keys import Keys driver = webdriver.Chrome() driver.get(\u0026#34;http://www.baidu.com\u0026#34;) # 输入框输入内容 driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;seleniumm\u0026#34;) # 删除多输入的一个 m driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(Keys.BACK_SPACE) # 输入空格键+“教程” driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(Keys.SPACE) driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;教程\u0026#34;) # ctrl+a 全选输入框内容 driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(Keys.CONTROL, \u0026#39;a\u0026#39;) # ctrl+x 剪切输入框内容 driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(Keys.CONTROL, \u0026#39;x\u0026#39;) # ctrl+v 粘贴内容到输入框 driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(Keys.CONTROL, \u0026#39;v\u0026#39;) # 通过回车键来代替单击操作 driver.find_element_by_id(\u0026#34;su\u0026#34;).send_keys(Keys.ENTER) driver.quit() 需要说明的是， 上面的脚本没有什么实际意义， 仅向我们展示模拟键盘各种按键与组合键的用法。\nfrom selenium.webdriver.common.keys import Keys 在使用键盘按键方法前需要先导入 keys 类。\n以下为常用的键盘操作：\nsend_keys(Keys.BACK_SPACE) 删除键（BackSpace） send_keys(Keys.SPACE) 空格键(Space) send_keys(Keys.TAB) 制表键(Tab) send_keys(Keys.ESCAPE) 回退键（Esc） send_keys(Keys.ENTER) 回车键（Enter） send_keys(Keys.CONTROL,\u0026lsquo;a\u0026rsquo;) 全选（Ctrl+A） send_keys(Keys.CONTROL,\u0026lsquo;c\u0026rsquo;) 复制（Ctrl+C） send_keys(Keys.CONTROL,\u0026lsquo;x\u0026rsquo;) 剪切（Ctrl+X） send_keys(Keys.CONTROL,\u0026lsquo;v\u0026rsquo;) 粘贴（Ctrl+V） send_keys(Keys.F1) 键盘 F1 send_keys(Keys.F12) 键盘 F12 3. 事件处理 3.1 断言判断 不管是在做功能测试还是自动化测试，最后一步需要拿实际结果与预期进行比较。这个比较的称之为断言。\n我们通常可以通过获取title 、URL和text等信息进行断言。text方法在前面已经讲过，它用于获取标签对之间的文本信息。 下面同样以百度为例，介绍如何获取这些信息。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from selenium import webdriver from time import sleep driver = webdriver.Firefox() driver.get(\u0026#34;https://www.baidu.com\u0026#34;) print(\u0026#39;Before search================\u0026#39;) # 打印当前页面title title = driver.title print(title) # 打印当前页面URL now_url = driver.current_url print(now_url) driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;selenium\u0026#34;) driver.find_element_by_id(\u0026#34;su\u0026#34;).click() sleep(1) print(\u0026#39;After search================\u0026#39;) # 再次打印当前页面title title = driver.title print(title) # 打印当前页面URL now_url = driver.current_url print(now_url) # 获取结果数目 user = driver.find_element_by_class_name(\u0026#39;nums\u0026#39;).text print(user) driver.quit() 脚本运行结果如下：\nBefore search================ 百度一下，你就知道 https://www.baidu.com/ After search================ selenium_百度搜索 https://www.baidu.com/s?ie=utf-8\u0026f=8\u0026rsv_bp=0\u0026rsv_idx\u0026hellip; 搜索工具 百度为您找到相关结果约61,100,000个 title：用于获得当前页面的标题。\ncurrent_url：用户获得当前页面的URL。\ntext：获取搜索条目的文本信息。\n3.2 元素等待 WebDriver提供了两种类型的等待：显式等待和隐式等待。\n显式等待 显式等待使WebdDriver等待某个条件成立时继续执行，否则在达到最大时长时抛出超时异常（TimeoutException）。\n1 2 3 4 5 6 7 8 9 10 11 12 13 from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver = webdriver.Firefox() driver.get(\u0026#34;http://www.baidu.com\u0026#34;) element = WebDriverWait(driver, 5, 0.5).until( EC.presence_of_element_located((By.ID, \u0026#34;kw\u0026#34;)) ) element.send_keys(\u0026#39;selenium\u0026#39;) driver.quit() WebDriverWait类是由WebDirver 提供的等待方法。在设置时间内，默认每隔一段时间检测一次当前页面元素是否存在，如果超过设置时间检测不到则抛出异常。具体格式如下：\nWebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None) driver ：浏览器驱动。\ntimeout ：最长超时时间，默认以秒为单位。\npoll_frequency ：检测的间隔（步长）时间，默认为0.5S。\nignored_exceptions ：超时后的异常信息，默认情况下抛NoSuchElementException异常。\nWebDriverWait()一般由until()或until_not()方法配合使用，下面是until()和until_not()方法的说明。\nuntil(method, message=‘’) 调用该方法提供的驱动程序作为一个参数，直到返回值为True。\nuntil_not(method, message=‘’) 调用该方法提供的驱动程序作为一个参数，直到返回值为False。\n在本例中，通过as关键字将expected_conditions 重命名为EC，并调用presence_of_element_located()方法判断元素是否存在。\n隐式等待 WebDriver提供了implicitly_wait()方法来实现隐式等待，默认设置为0。它的用法相对来说要简单得多。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from selenium import webdriver from selenium.common.exceptions import NoSuchElementException from time import ctime driver = webdriver.Firefox() # 设置隐式等待为10秒 driver.implicitly_wait(10) driver.get(\u0026#34;http://www.baidu.com\u0026#34;) try: print(ctime()) driver.find_element_by_id(\u0026#34;kw22\u0026#34;).send_keys(\u0026#39;selenium\u0026#39;) except NoSuchElementException as e: print(e) finally: print(ctime()) driver.quit() implicitly_wait() 默认参数的单位为秒，本例中设置等待时长为10秒。首先这10秒并非一个固定的等待时间，它并不影响脚本的执行速度。其次，它并不针对页面上的某一元素进行等待。当脚本执行到某个元素定位时，如果元素可以定位，则继续执行；如果元素定位不到，则它将以轮询的方式不断地判断元素是否被定位到。假设在第6秒定位到了元素则继续执行，若直到超出设置时长（10秒）还没有定位到元素，则抛出异常。\n3.3 元素定位 WebDriver还提供了8种用于定位一组元素的方法。\nfind_elements_by_id() find_elements_by_name() find_elements_by_class_name() find_elements_by_tag_name() find_elements_by_link_text() find_elements_by_partial_link_text() find_elements_by_xpath() find_elements_by_css_selector() 定位一组元素的方法与定位单个元素的方法类似，唯一的区别是在单词element后面多了一个s表示复数。\n接下来通过例子演示定位一组元素的使用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from selenium import webdriver from time import sleep driver = webdriver.Chrome() driver.get(\u0026#34;https://www.baidu.com\u0026#34;) driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;selenium\u0026#34;) driver.find_element_by_id(\u0026#34;su\u0026#34;).click() sleep(1) # 定位一组元素 texts = driver.find_elements_by_xpath(\u0026#39;//div/h3/a\u0026#39;) # 循环遍历出每一条搜索结果的标题 for t in texts: print(t.text) driver.quit() 程序运行结果：\nSelenium - Web Browser Automation 官网 功能自动化测试工具——Selenium篇 selenium + python自动化测试环境搭建 - 虫师 - 博客园 selenium是什么?_百度知道 怎样开始用selenium进行自动化测试(个人总结)_百度经验 Selenium_百度百科 selenium_百度翻译 Selenium官网教程_selenium自动化测试实践_Selenium_领测软件测试网 Selenium(浏览器自动化测试框架)_百度百科 自动化基础普及之selenium是啥? - 虫师 - 博客园 python十大主流开源框架 「菜鸟必看」\n3.4 多表单切换 在Web应用中经常会遇到frame/iframe表单嵌套页面的应用，WebDriver只能在一个页面上对元素识别与定位，对于frame/iframe表单内嵌页面上的元素无法直接定位。这时就需要通过switch_to.frame()方法将当前定位的主体切换为frame/iframe表单的内嵌页面中。\n1 2 3 4 5 6 7 8 \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; ... \u0026lt;iframe id=\u0026#34;x-URS-iframe\u0026#34; ...\u0026gt; \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; ... \u0026lt;input name=\u0026#34;email\u0026#34; \u0026gt; 126邮箱登录框的结构大概是这样子的，想要操作登录框必须要先切换到iframe表单。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from selenium import webdriver driver = webdriver.Chrome() driver.get(\u0026#34;http://www.126.com\u0026#34;) driver.switch_to.frame(\u0026#39;x-URS-iframe\u0026#39;) driver.find_element_by_name(\u0026#34;email\u0026#34;).clear() driver.find_element_by_name(\u0026#34;email\u0026#34;).send_keys(\u0026#34;username\u0026#34;) driver.find_element_by_name(\u0026#34;password\u0026#34;).clear() driver.find_element_by_name(\u0026#34;password\u0026#34;).send_keys(\u0026#34;password\u0026#34;) driver.find_element_by_id(\u0026#34;dologin\u0026#34;).click() driver.switch_to.default_content() driver.quit() switch_to.frame() 默认可以直接取表单的id 或name属性。如果iframe没有可用的id和name属性，则可以通过下面的方式进行定位。 …… #先通过xpth定位到iframe\n1 xf = driver.find_element_by_xpath(\u0026#39;//*[@id=\u0026#34;x-URS-iframe\u0026#34;]\u0026#39;) #再将定位对象传给switch_to.frame()方法\n1 driver.switch_to.frame(xf) ……\n1 driver.switch_to.parent_frame() 除此之外，在进入多级表单的情况下，还可以通过switch_to.default_content()跳回最外层的页面。\n3.5 多窗口切换 在页面操作过程中有时候点击某个链接会弹出新的窗口，这时就需要主机切换到新打开的窗口上进行操作。WebDriver提供了switch_to.window()方法，可以实现在不同的窗口之间切换。 以百度首页和百度注册页为例，在两个窗口之间的切换如下图。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from selenium import webdriver import time driver = webdriver.Firefox() driver.implicitly_wait(10) driver.get(\u0026#34;http://www.baidu.com\u0026#34;) # 获得百度搜索窗口句柄 sreach_windows = driver.current_window_handle driver.find_element_by_link_text(\u0026#39;登录\u0026#39;).click() driver.find_element_by_link_text(\u0026#34;立即注册\u0026#34;).click() # 获得当前所有打开的窗口的句柄 all_handles = driver.window_handles # 进入注册窗口 for handle in all_handles: if handle != sreach_windows: driver.switch_to.window(handle) print(\u0026#39;now register window!\u0026#39;) driver.find_element_by_name(\u0026#34;account\u0026#34;).send_keys(\u0026#39;username\u0026#39;) driver.find_element_by_name(\u0026#39;password\u0026#39;).send_keys(\u0026#39;password\u0026#39;) time.sleep(2) # …… driver.quit() 在本例中所涉及的新方法如下：\ncurrent_window_handle：获得当前窗口句柄。 window_handles：返回所有窗口的句柄到当前会话。 switch_to.window()：用于切换到相应的窗口，与上一节的switch_to.frame()类似，前者用于不同窗口的切换，后者用于不同表单之间的切换。 3.6 警告框处理 在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单，具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt，然后使用text/accept/dismiss/ send_keys等方法进行操作。\ntext：返回 alert/confirm/prompt 中的文字信息。 accept()：接受现有警告框。 dismiss()：解散现有警告框。 send_keys(keysToSend)：发送文本至警告框。keysToSend：将文本发送至警告框。 如下图，百度搜索设置弹出的窗口是不能通过前端工具对其进行定位的，这个时候就可以通过switch_to_alert()方法接受这个弹窗。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time driver = webdriver.Firefox() driver.implicitly_wait(10) driver.get(\u0026#39;http://www.baidu.com\u0026#39;) # 鼠标悬停至“设置”链接 link = driver.find_element_by_link_text(\u0026#39;设置\u0026#39;) ActionChains(driver).move_to_element(link).perform() # 打开搜索设置 driver.find_element_by_link_text(\u0026#34;搜索设置\u0026#34;).click() # 保存设置 driver.find_element_by_class_name(\u0026#34;prefpanelgo\u0026#34;).click() time.sleep(2) # 接受警告框 driver.switch_to.alert.accept() driver.quit() 通过switch_to_alert()方法获取当前页面上的警告框，并使用accept()方法接受警告框。\n3.7 警告框处理 在WebDriver中处理JavaScript所生成的alert、confirm以及prompt十分简单，具体做法是使用 switch_to.alert 方法定位到 alert/confirm/prompt，然后使用text/accept/dismiss/ send_keys等方法进行操作。\ntext：返回 alert/confirm/prompt 中的文字信息。 accept()：接受现有警告框。 dismiss()：解散现有警告框。 send_keys(keysToSend)：发送文本至警告框。keysToSend：将文本发送至警告框。 如下图，百度搜索设置弹出的窗口是不能通过前端工具对其进行定位的，这个时候就可以通过switch_to_alert()方法接受这个弹窗。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time driver = webdriver.Firefox() driver.implicitly_wait(10) driver.get(\u0026#39;http://www.baidu.com\u0026#39;) # 鼠标悬停至“设置”链接 link = driver.find_element_by_link_text(\u0026#39;设置\u0026#39;) ActionChains(driver).move_to_element(link).perform() # 打开搜索设置 driver.find_element_by_link_text(\u0026#34;搜索设置\u0026#34;).click() # 保存设置 driver.find_element_by_class_name(\u0026#34;prefpanelgo\u0026#34;).click() time.sleep(2) # 接受警告框 driver.switch_to.alert.accept() driver.quit() 通过switch_to_alert()方法获取当前页面上的警告框，并使用accept()方法接受警告框。\n3.8 下拉框选择 有时我们会碰到下拉框，WebDriver提供了Select类来处理下拉框。 如百度搜索设置的下拉框。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from selenium import webdriver from selenium.webdriver.support.select import Select from time import sleep driver = webdriver.Chrome() driver.implicitly_wait(10) driver.get(\u0026#39;http://www.baidu.com\u0026#39;) # 鼠标悬停至“设置”链接 driver.find_element_by_link_text(\u0026#39;设置\u0026#39;).click() sleep(1) # 打开搜索设置 driver.find_element_by_link_text(\u0026#34;搜索设置\u0026#34;).click() sleep(2) # 搜索结果显示条数 sel = driver.find_element_by_xpath(\u0026#34;//select[@id=\u0026#39;nr\u0026#39;]\u0026#34;) Select(sel).select_by_value(\u0026#39;50\u0026#39;) # 显示50条 # …… driver.quit() Select类用于定位select标签。\nselect_by_value() 方法用于定位下接选项中的value值。\n4. 特殊处理 4.1 文件上传 对于通过input标签实现的上传功能，可以将其看作是一个输入框，即通过send_keys()指定本地文件路径的方式实现文件上传。\n创建upfile.html文件，代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;content-type\u0026#34; content=\u0026#34;text/html;charset=utf-8\u0026#34; /\u0026gt; \u0026lt;title\u0026gt;upload_file\u0026lt;/title\u0026gt; \u0026lt;link href=\u0026#34;http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.css\u0026#34; rel=\u0026#34;stylesheet\u0026#34; /\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div class=\u0026#34;row-fluid\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;span6 well\u0026#34;\u0026gt; \u0026lt;h3\u0026gt;upload_file\u0026lt;/h3\u0026gt; \u0026lt;input type=\u0026#34;file\u0026#34; name=\u0026#34;file\u0026#34; /\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;script src=\u0026#34;http://cdn.bootcss.com/bootstrap/3.3.0/css/bootstrap.min.js\u0026#34;\u0026gt;\u0026lt;/scrip\u0026gt; \u0026lt;/html\u0026gt; 通过浏览器打开upfile.html文件.\n接下来通过send_keys()方法来实现文件上传。\n1 2 3 4 5 6 7 8 9 10 11 from selenium import webdriver import os driver = webdriver.Firefox() file_path = \u0026#39;file:///\u0026#39; + os.path.abspath(\u0026#39;upfile.html\u0026#39;) driver.get(file_path) # 定位上传按钮，添加本地文件 driver.find_element_by_name(\u0026#34;file\u0026#34;).send_keys(\u0026#39;D:\\\\upload_file.txt\u0026#39;) driver.quit() 4.2 cookies处理 有时候我们需要验证浏览器中cookie是否正确，因为基于真实cookie的测试是无法通过白盒和集成测试进行的。WebDriver提供了操作Cookie的相关方法，可以读取、添加和删除cookie信息。\nWebDriver操作cookie的方法：\nget_cookies()： 获得所有cookie信息。 get_cookie(name)： 返回字典的key为“name”的cookie信息。 add_cookie(cookie_dict) ： 添加cookie。“cookie_dict”指字典对象，必须有name 和value 值。 delete_cookie(name,optionsString)：删除cookie信息。“name”是要删除的cookie的名称，“optionsString”是该cookie的选项，目前支持的选项包括“路径”，“域”。 delete_all_cookies()： 删除所有cookie信息。 下面通过get_cookies()来获取当前浏览器的cookie信息。\n1 2 3 4 5 6 7 8 9 10 11 from selenium import webdriver driver = webdriver.Firefox() driver.get(\u0026#34;http://www.youdao.com\u0026#34;) # 获得cookie信息 cookie= driver.get_cookies() # 将获得cookie的信息打印 print(cookie) driver.quit() 从执行结果可以看出，cookie数据是以字典的形式进行存放的。知道了cookie的存放形式，接下来我们就可以按照这种形式向浏览器中写入cookie信息。\n1 2 3 4 5 6 7 8 9 10 11 12 13 from selenium import webdriver driver = webdriver.Firefox() driver.get(\u0026#34;http://www.youdao.com\u0026#34;) # 向cookie的name 和value中添加会话信息 driver.add_cookie({\u0026#39;name\u0026#39;: \u0026#39;key-aaaaaaa\u0026#39;, \u0026#39;value\u0026#39;: \u0026#39;value-bbbbbb\u0026#39;}) # 遍历cookies中的name 和value信息并打印，当然还有上面添加的信息 for cookie in driver.get_cookies(): print(\u0026#34;%s -\u0026gt; %s\u0026#34; % (cookie[\u0026#39;name\u0026#39;], cookie[\u0026#39;value\u0026#39;])) driver.quit() 输出结果：\n======================== RESTART: =========================\n1 2 3 4 5 YOUDAO_MOBILE_ACCESS_TYPE -\u0026gt; 1 _PREF_ANONYUSER__MYTH -\u0026gt; aGFzbG9nZ2VkPXRydWU= OUTFOX_SEARCH_USER_ID -\u0026gt; -1046383847@218.17.158.115 JSESSIONID -\u0026gt; abc7qSE_SBGsVgnVLBvcu key-aaaaaaa -\u0026gt; value-bbbbbb 从执行结果可以看到，最后一条cookie信息是在脚本执行过程中通过add_cookie()方法添加的。通过遍历得到所有的cookie信息，从而找到key为“name”和“value”的特定cookie的value。\n4.3 js处理 虽然WebDriver提供了操作浏览器的前进和后退方法，但对于浏览器滚动条并没有提供相应的操作方法。在这种情况下，就可以借助JavaScript来控制浏览器的滚动条。WebDriver提供了execute_script()方法来执行JavaScript代码。\n用于调整浏览器滚动条位置的JavaScript代码如下：\n1 2 \u0026lt;!-- window.scrollTo(左边距,上边距); --\u0026gt; window.scrollTo(0,450); window.scrollTo()方法用于设置浏览器窗口滚动条的水平和垂直位置。方法的第一个参数表示水平的左间距，第二个参数表示垂直的上边距。其代码如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from selenium import webdriver from time import sleep # 访问百度 driver=webdriver.Firefox() driver.get(\u0026#34;http://www.baidu.com\u0026#34;) # 设置浏览器窗口大小 driver.set_window_size(500, 500) # 搜索 driver.find_element_by_id(\u0026#34;kw\u0026#34;).send_keys(\u0026#34;selenium\u0026#34;) driver.find_element_by_id(\u0026#34;su\u0026#34;).click() sleep(2) # 通过javascript设置浏览器窗口的滚动条位置 js=\u0026#34;window.scrollTo(100,450);\u0026#34; driver.execute_script(js) sleep(3) driver.quit() 通过浏览器打开百度进行搜索，并且提前通过set_window_size()方法将浏览器窗口设置为固定宽高显示，目的是让窗口出现水平和垂直滚动条。然后通过execute_script()方法执行JavaScripts代码来移动滚动条的位置。\n4.4 窗口截图 自动化用例是由程序去执行的，因此有时候打印的错误信息并不十分明确。如果在脚本执行出错的时候能对当前窗口截图保存，那么通过图片就可以非常直观地看出出错的原因。WebDriver提供了截图函数get_screenshot_as_file()来截取当前窗口。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 from selenium import webdriver from time import sleep driver = webdriver.Firefox() driver.get(\u0026#39;http://www.baidu.com\u0026#39;) driver.find_element_by_id(\u0026#39;kw\u0026#39;).send_keys(\u0026#39;selenium\u0026#39;) driver.find_element_by_id(\u0026#39;su\u0026#39;).click() sleep(2) # 截取当前窗口，并指定截图图片的保存位置 driver.get_screenshot_as_file(\u0026#34;E:\\\\baidu_img.jpg\u0026#34;) driver.quit() 脚本运行完成后打开E盘，就可以找到baidu_img.jpg图片文件了。\n4.5 窗口关闭 在前面的例子中我们一直使用quit()方法，其含义为退出相关的驱动程序和关闭所有窗口。除此之外，WebDriver还提供了close()方法，用来关闭当前窗口。例多窗口的处理，在用例执行的过程中打开了多个窗口，我们想要关闭其中的某个窗口，这时就要用到close()方法进行关闭了。\nclose() 关闭单个窗口 quit() 关闭所有窗口 ","permalink":"https://chasing1020.github.io/post/selenium-automation/","summary":"\u003ch1 id=\"selenium3实现网页自动化\"\u003eSelenium3实现网页自动化\u003c/h1\u003e\n\u003ch2 id=\"1初步工作\"\u003e1.初步工作\u003c/h2\u003e\n\u003ch3 id=\"11-环境配置\"\u003e1.1 环境配置\u003c/h3\u003e\n\u003cp\u003e安装Selenium\u003c/p\u003e\n\u003cp\u003e本文使用的是python3.8.4 64bit\u003c/p\u003e\n\u003cp\u003e使用pip指令安装selenium\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epip install selenium\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"12-设置浏览器驱动\"\u003e1.2 设置浏览器驱动\u003c/h3\u003e\n\u003cp\u003e首先设置浏览器的地址，手动创建一个存放浏览器驱动的目录，如： E:\\driver , 将下载的浏览器驱动文件（例如：chromedriver、geckodriver）丢到该目录下。\u003c/p\u003e","title":"Selenium Automation"},{"content":"SHU每日两报脚本 1. 实现原理 利用python中的Selenium库，并结合浏览器驱动，来自动完成每日两报的操作，最后测试结果将每天的体温设置区间为36度至37度之间（含小数点后一位）。同时，在使用前可以自己选择相应的填报日期，也可以在特定日期之内完成相应的填报操作。\n2. 使用教程 2.1. 使用步骤 1、首先，此程序基于python 3.8.5版本运行，使用的浏览器为Edge，版本号86.0.622.51 (Official build) (64-bit)，IDE为PyCharm 2020.1.3 (Professional Edition) 2、打开命令行，使用pip指令安装selenium pip install selenium 3、通过浏览器官网下载相应的浏览器启动插件，将其设置为系统环境变量(可选)，也可以在程序中自行设置浏览器启动驱动位置，如在E盘的E:\\Edgedriver目录，就更改默认的地址 driverUrl = r\u0026quot;E:\\Edgedriver\\msedgedriver.exe\u0026quot; 4、运行程序时，请关闭其他任何可能影响浏览器运行的插件或者软件，如果网络信号不佳，则需要将time.sleep(1)中的数值调大，以等待网页的元素彻底加载成功\n3. 代码实现 3.1. 准备工作 在本程序中，我使用了三个库，分别是selenium（主角），time以及random。 其次，设置好自己的浏览器驱动位置以及每日一报链接位置，并填写好自己的账号密码\n1 2 3 4 5 6 7 myUsername=r\u0026#39;\u0026#39; #在此输入你的学号 myPassword=r\u0026#39;\u0026#39; #在此输入你的姓名 baseUrl=r\u0026#39;https://selfreport.shu.edu.cn/\u0026#39; #默认的每日两报地址 driverUrl = r\u0026#34;E:\\Edgedriver\\msedgedriver.exe\u0026#34; #浏览器驱动，这里以Edge示例，不同的浏览器可以去官网下载 from selenium #完成web自动化的一系列操作 import time #设置程序等待时间，等待浏览器加载元素完全 import random #设置温度随机数 3.2. 登录账号 准备好以上步骤以后，我们就可以打开浏览器并对其进行相应的操作。\n输入代码\n1 2 3 4 5 6 7 driver = webdriver.Edge(driverUrl)#通过驱动器打开浏览器 driver.get(baseUrl)#访问健康之路链接 search_username = driver.find_element_by_id(\u0026#39;username\u0026#39;)#找到用户名位置 search_username.send_keys(myUsername)#填写用户名 search_password = driver.find_element_by_id(\u0026#39;password\u0026#39;)#找到密码位置 search_password.send_keys(myPassword)#填写密码 driver.find_element_by_id(\u0026#39;submit\u0026#39;).click()#找到元素并且自动点击登录 程序执行时，就会将自己的账号密码自动填入进入下章页面。\n3.3. 进入报送历史界面 如图，需要点进报送历史： 首先输入\n1 driver.find_element_by_id(\u0026#39;lbReportHistory\u0026#39;).click()#找到对应历史报送的按钮，点击 然后即可进入历史界面，在这里每天的报送记录罗列如下：\n3.4. 填写对应的信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # 点击对应的天数： object = str(date) + item driver.find_element_by_partial_link_text(object).click() # 勾选承诺项： driver.find_element_by_id(\u0026#34;p1_ChengNuo-inputEl-icon\u0026#34;).click() # 填写体温，随机在36.0-37.0之间 search_temperature = driver.find_element_by_id(\u0026#34;p1_TiWen-inputEl\u0026#34;) temperature = str(random.randint(360, 370) / 10) search_temperature.send_keys(temperature) # 勾选状态\u0026#34;良好\u0026#34; element = driver.find_element_by_id(\u0026#34;fineui_0-inputEl\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) # 当天随申码颜色：\u0026#34;绿色\u0026#34; driver.find_element_by_id(\u0026#34;fineui_7-inputEl-icon\u0026#34;).click() element = driver.find_element_by_id(\u0026#39;fineui_7-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) # webdriver.ActionChains(driver).move_to_element(element).click(element).perform() # 明天是否到食堂就餐：\u0026#34;早餐，中餐，晚餐\u0026#34; element = driver.find_element_by_id(\u0026#39;fineui_8-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) element = driver.find_element_by_id(\u0026#39;fineui_9-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) element = driver.find_element_by_id(\u0026#39;fineui_10-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) 以上代码均是对于填写页面元素的捕捉，并对发现的第一个元素发起提交按钮。 在这里，程序代码不能写成如下：\n1 2 3 4 5 # 以下操作无法实现，原因应该是元素定位相互覆盖。 # driver.find_element_by_id(\u0026#34;fineui_8-inputEl-icon\u0026#34;).click() # driver.find_element_by_id(\u0026#34;fineui_9-inputEl-icon\u0026#34;).click() # driver.find_element_bu_id(\u0026#34;fineui_10-inputEl-icon\u0026#34;).click() # driver.find_element_by_id(\u0026#34;p1_ctl00_btnSubmit\u0026#34;).click() 原因是代码中的元素相互覆盖，无法实现操作 最终选择并点击时的效果如下： 3.5. 点击确认按钮 1 2 3 4 5 6 7 8 9 10 11 12 # 点击提交 element = driver.find_element_by_id(\u0026#34;p1_ctl00_btnSubmit\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(1) # 点击确认 element = driver.find_element_by_id(\u0026#34;fineui_14\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(3) element = driver.find_element_by_id(\u0026#34;fineui_19\u0026#34;) # element = driver.find_element_by_class_name(\u0026#34;f-btn-text\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(1) 在这里我使用了time.sleep()函数，为了让程序停顿，原页面加载的过程中，会出现提交框晚出现的情况，所以需要让程序“等待”浏览器加载结束，最后填报。\n4. 完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #-*- coding = utf-8 -*- #@Time : 2020-10-25 15:24 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : release.py #@Software: PyCharm myUsername=r\u0026#39;\u0026#39; #在此输入你的学号 myPassword=r\u0026#39;\u0026#39; #在此输入你的姓名 baseUrl=r\u0026#39;https://selfreport.shu.edu.cn/\u0026#39; #默认的每日两报地址 driverUrl = r\u0026#34;E:\\Edgedriver\\msedgedriver.exe\u0026#34; #浏览器驱动，这里以Edge示例，不同的浏览器可以去个官网下载 from selenium import time import random def main(): print(\u0026#34;Hello,\u0026#34;) driver = webdriver.Edge(driverUrl) driver.get(baseUrl) search_username = driver.find_element_by_id(\u0026#39;username\u0026#39;) search_username.send_keys(myUsername) search_password = driver.find_element_by_id(\u0026#39;password\u0026#39;) search_password.send_keys(myPassword) driver.find_element_by_id(\u0026#39;submit\u0026#39;).click() for date in range(19,26,1):#在此修改你想实现的日期，左闭右开，如这里为[19,26)，即19至25号 for item in [\u0026#39;晨报\u0026#39;,\u0026#39;晚报\u0026#39;]: # print(\u0026#34;Hello, world!\u0026#34;) # driver = webdriver.Edge(driverUrl) # driver.get(baseUrl) # driver.find_element_by_id(\u0026#39;username\u0026#39;) # search_username = driver.find_element_by_id(\u0026#39;username\u0026#39;) # search_username.send_keys(myUsername) # search_password = driver.find_element_by_id(\u0026#39;password\u0026#39;) # search_password.send_keys(myPassword) # driver.find_element_by_id(\u0026#39;submit\u0026#39;).click() # 进入报送历史： driver.find_element_by_id(\u0026#39;lbReportHistory\u0026#39;).click() # 点击对应的天数： object = str(date) + item driver.find_element_by_partial_link_text(object).click() # 勾选承诺项： driver.find_element_by_id(\u0026#34;p1_ChengNuo-inputEl-icon\u0026#34;).click() # 填写体温，随机在36.0-37.0之间 search_temperature = driver.find_element_by_id(\u0026#34;p1_TiWen-inputEl\u0026#34;) temperature = str(random.randint(360, 370) / 10) search_temperature.send_keys(temperature) # 勾选状态\u0026#34;良好\u0026#34; element = driver.find_element_by_id(\u0026#34;fineui_0-inputEl\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) # 当天随申码颜色：\u0026#34;绿色\u0026#34; driver.find_element_by_id(\u0026#34;fineui_7-inputEl-icon\u0026#34;).click() element = driver.find_element_by_id(\u0026#39;fineui_7-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) # webdriver.ActionChains(driver).move_to_element(element).click(element).perform() # 明天是否到食堂就餐：\u0026#34;早餐，中餐，晚餐\u0026#34; element = driver.find_element_by_id(\u0026#39;fineui_8-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) element = driver.find_element_by_id(\u0026#39;fineui_9-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) element = driver.find_element_by_id(\u0026#39;fineui_10-inputEl-icon\u0026#39;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) # 以下操作无法实现，原因应该是元素定位相互覆盖。 # driver.find_element_by_id(\u0026#34;fineui_8-inputEl-icon\u0026#34;).click() # driver.find_element_by_id(\u0026#34;fineui_9-inputEl-icon\u0026#34;).click() # driver.find_element_bu_id(\u0026#34;fineui_10-inputEl-icon\u0026#34;).click() # driver.find_element_by_id(\u0026#34;p1_ctl00_btnSubmit\u0026#34;).click() # 点击提交 element = driver.find_element_by_id(\u0026#34;p1_ctl00_btnSubmit\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(1) # 点击确认 element = driver.find_element_by_id(\u0026#34;fineui_14\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(3) element = driver.find_element_by_id(\u0026#34;fineui_19\u0026#34;) # element = driver.find_element_by_class_name(\u0026#34;f-btn-text\u0026#34;) driver.execute_script(\u0026#34;arguments[0].click();\u0026#34;, element) time.sleep(1) driver.quit() print(\u0026#34;world!\u0026#34;) # 输出Hello,world! 完美的结束 if __name__ == \u0026#34;__main__\u0026#34;: #当程序执行时 main() #开始 ","permalink":"https://chasing1020.github.io/post/shu-autoselfreport/","summary":"\u003ch1 id=\"shu每日两报脚本\"\u003eSHU每日两报脚本\u003c/h1\u003e\n\u003ch2 id=\"1-实现原理\"\u003e1. 实现原理\u003c/h2\u003e\n\u003cp\u003e利用python中的Selenium库，并结合浏览器驱动，来自动完成每日两报的操作，最后测试结果将每天的体温设置区间为36度至37度之间（含小数点后一位）。同时，在使用前可以自己选择相应的填报日期，也可以在特定日期之内完成相应的填报操作。\u003c/p\u003e","title":"SHU AutoSelfReport"},{"content":"创建数组对象 一维数组 1 2 3 name=np.array([\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;6\u0026#39;]) print(name.ndim)#维数 print(name.size)#大小 二维数组 创建 1 2 import numpy as np name=np.array([[\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;6\u0026#39;],[\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;6\u0026#39;],[\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;6\u0026#39;],[\u0026#39;1\u0026#39;,\u0026#39;2\u0026#39;,\u0026#39;3\u0026#39;,\u0026#39;4\u0026#39;,\u0026#39;5\u0026#39;,\u0026#39;6\u0026#39;]]) 查看属性 1 2 3 4 print(name.ndim)#维数，也可以说是矩阵的秩 print(name.size)#大小 print(name.shape)#行数和列数 print(name.dtype)#查看数据类型 访问下标 1 2 print(name[2])#索引为[0,n-1] print(name[-3])#索引为[-n,-1]，表示倒数 切片(slicing) 1 2 3 4 for i in name[[1,2],[2,4]]:#表示下标1，2和下标2，4#如果使用:，则表示所有的行和列 print(i) for i in name[1:2,2:4]:#表示下标1，2和下标2，4#如果使用:，则表示所有的行和列 print(i) 条件筛选 1 print(name[(name==1)|(name==2)]) 创建多维数组 1 2 3 4 a=np.arange(1,10,1)#生成1-9之间的连续的数组 a=np.arange(0,15).reshape(3,5)#将一维数组转为二维数组 a=np.zeros((3,4))#生成3*4的为0的数组 a=np.ones((4,3))#生成4*3的为1的数组 多维数组运算 1 2 3 a=np.ones((4,3)) a=a*5#生成全部为5的数组 a=a+3#生成全部为4的数组 常用函数、属性 函数 函数 作用 np.array(列表) 通过列表创建一个数组对象 np.arange(起始, 结束, 步长) 创建一个等差数组(注意区间是左闭右开的) np.zeros( (m, n) ) 创建一个m行n列的全零数组 np.ones( (m, n) ) 创建一个m行n列的全一数组 np.eye(m) 创建一个m阶单位方阵 常用对象属性 属性 作用 my_array.ndim 维数 my_array.size 大小 my_array.shape 以元组形式返回my_array的(行, 列) my_array.dtype 返回my_array中元素的数据类型 *注意库函数和对象属性的不同，表一中np.是固定的，指的是numpy库；而表二中my_array.xxx()中的my_array要改成你对应的数组的名字（即实例名）。 ## 切片 切片 我们将选出一个数组的某一行、某一列或者某一个位置上的元素的操作成为“切片” 我们先来讨论二维数组的切片: 最基本的格式是：my_array[m, n]，其中m和n可以为整数 列表 还可以是冒号: 当m和n是整数时，表示选取m行n列的那个数。 当m和n其中一个是冒号的时候，表明选中对应的所有行或列。例如my_array[ :, n]表示选择整个第n列\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 arr = np.arange(1,10).reshape(3,3) print(\u0026#39;切片前：\u0026#39;) print(arr) m = [0,1] n = [1,2] print(\u0026#39;切片后：\u0026#39;) print(arr[m, n]) \u0026#39;\u0026#39;\u0026#39; 切片前： [[1 2 3] [4 5 6] [7 8 9]] 切片后： [2 6] \u0026#39;\u0026#39;\u0026#39; 通用函数func 常用的一元函数 函数 描述 abs、fabs 计算整数、浮点数、复数的绝对值 sqrt 计算平方根 square 计算平方 exp 计算指数 log、log10 计算自然对数、底数为10的log sign 计算正负号 ceil、floor 天花板、地板函数 sin、cos、cosh\u0026hellip; 三角函数 常用的二元函数 函数 描述 add 将对应的元素相加 subtract 从第一个数组中减去第二个数组的元素 multiply 数组元素相乘 divide 数组元素相除 power 计算幂次 mod 计算模 copysign 将第二个数组的符号赋值给第一个数组 equal、not_equal 执行元素比较，返回布尔类型的数组 聚集函数 函数 描述 sum 求和 mean 算数平均值 min、max 最大值和最小值 argmin、argmax 最大值和最小值的索引 cumsum 从0开始累加 cumprod 从1开始累乘 随机数组生成函数 函数 描述 random 随机产生[0,1) randint 随机生成给定范围内的一组整数 uniform 随机生成给定范围内服从均匀分布的一组浮点数 choice 在给定的范围内随机选择元素 normal 随机生成一组服从给定均值和方差正态分布的随机数 ## 课后作业 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #P21 import numpy as np names = np.array([\u0026#39;王微\u0026#39;,\u0026#39;肖良英\u0026#39;,\u0026#34;方绮雯\u0026#34;,\u0026#39;刘旭阳\u0026#39;,\u0026#39;钱易铭\u0026#39;]) subjects = np.array([\u0026#39;Math\u0026#39;, \u0026#39;English\u0026#39;, \u0026#39;Python\u0026#39;, \u0026#39;Chinese\u0026#39;, \u0026#39;Art\u0026#39;, \u0026#39;Database\u0026#39;, \u0026#39;Physics\u0026#39;]) scores = np.array([[70,85,77,90,82,84,89],[60,64,80,75,80,92,90],[90,93,88,87,86,90,91],[80,82,91,88,83,86,80],[88,72,78,90,91,73,80]]) #1. #(1) print(subjects[[1,2,4]]) print(names[-3]) #(2) print(names[2:]) print(subjects[2:5]) #(3) print(subjects[(subjects == \u0026#39;English\u0026#39;) | (subjects == \u0026#39;Physics\u0026#39;)]) #2. #(1) print(scores[[1,4],:]) #(2) print(scores[[2,4]][:,(subjects == \u0026#39;Python\u0026#39;)|(subjects == \u0026#39;Math\u0026#39;)]) #(3) print(scores[:,(subjects == \u0026#39;English\u0026#39;) | (subjects == \u0026#39;Art\u0026#39;)]) #(4) print(scores[(names==\u0026#39;王微\u0026#39;)|(names==\u0026#39;刘旭阳\u0026#39;),(subjects==\u0026#39;English\u0026#39;)|(subjects==\u0026#39;Math\u0026#39;)]) #3. a=np.arange(10,20).reshape(2,5) print(a) \u0026#39;\u0026#39;\u0026#39; 1. 一维数组访问。 1) 在 subjects 数组中选择并显示序号 1、 2、 4 门课的名称，使用倒序索引选择并显示 names 数组中“方绮雯“。 [\u0026#39;English\u0026#39; \u0026#39;Python\u0026#39; \u0026#39;Art\u0026#39;] 方绮雯 2) 选择并显示 names 数组从 2 到最后的数组元素；选择并显示 subjects 数组正序 2~4 的数组元素。 [\u0026#39;方绮雯\u0026#39; \u0026#39;刘旭阳\u0026#39; \u0026#39;钱易铭\u0026#39;] [\u0026#39;Python\u0026#39; \u0026#39;Chinese\u0026#39; \u0026#39;Art\u0026#39;] 3) 使用布尔条件选择并显示 subjects 数组中的英语和物理科目名称。 [\u0026#39;English\u0026#39; \u0026#39;Physics\u0026#39;] 2. 二维数组访问。 l) 选择并显示 scores 数组的 1、 4 行。 [[60 64 80 75 80 92 90] [88 72 78 90 91 73 80]] 2) 选择并显示 scores 数组中行序 2、 4 学生的数学和 Python 成绩 [[90 88] [88 78]] 3) 选择并显示 scores 数组中所有学生的数学和艺术课程成绩。 [[85 82] [64 80] [93 86] [82 83] [72 91]] 4) 选择并显示 scores 数组中“王微”和“刘旭阳”的英语和艺术课程成绩。 [[85 82] [82 83]] 3. 生成由整数 10~19 组成的 2x5 的二维数组。 [[10 11 12 13 14] [15 16 17 18 19]] \u0026#39;\u0026#39;\u0026#39; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # 数据准备 #P26 import numpy as np names = np.array([\u0026#39;王微\u0026#39;,\u0026#39;肖良英\u0026#39;,\u0026#34;方绮雯\u0026#34;,\u0026#39;刘旭阳\u0026#39;,\u0026#39;钱易铭\u0026#39;]) subjects = np.array([\u0026#39;Math\u0026#39;, \u0026#39;English\u0026#39;, \u0026#39;Python\u0026#39;, \u0026#39;Chinese\u0026#39;, \u0026#39;Art\u0026#39;, \u0026#39;Database\u0026#39;, \u0026#39;Physics\u0026#39;]) scores = np.array([[70,85,77,90,82,84,89],[60,64,80,75,80,92,90],[90,93,88,87,86,90,91],[80,82,91,88,83,86,80],[88,72,78,90,91,73,80]]) # 第一题 print(\u0026#39;1. 将 scores 数组中所有学生的英语成绩减去 3 分并显示。 \u0026#39;) print(scores[:, subjects == \u0026#39;Art\u0026#39;] -3) # 第二题 print(\u0026#39;\\n2. 统计 scores 数组中每名学生所有科目的平均分并显示。 \u0026#39;) for i in range(0,5) : print(scores[i].mean()) # 第三题 print(\u0026#39;\\n3. 使用随机函数生成[-1,1]之间服从均匀分布的 3x4 二维数组，并计算所有元素的和。\u0026#39;) uni = np.random.uniform(-1,1,(3,4)) print(uni) print(uni.sum()) \u0026#39;\u0026#39;\u0026#39; 1. 将 scores 数组中所有学生的英语成绩减去 3 分并显示。 [[79] [77] [83] [80] [88]] 2. 统计 scores 数组中每名学生所有科目的平均分并显示。 82.42857142857143 77.28571428571429 89.28571428571429 84.28571428571429 81.71428571428571 3. 使用随机函数生成[-1,1]之间服从均匀分布的 3x4 二维数组，并计算所有元素的和。 [[-0.5434021 -0.0569449 -0.10984966 -0.90260813] [-0.01882247 -0.46660599 0.52140256 0.42474122] [ 0.3122958 -0.06197657 -0.26717631 -0.08292239]] -1.2518689400759253 \u0026#39;\u0026#39;\u0026#39; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #P29 #一、 import numpy as np # 1.创建两个一维数组分别存储超市名称和水果名称。 shops = np.array([\u0026#39;DaRunFa\u0026#39;,\u0026#39;Walmart\u0026#39;,\u0026#39;HaoDe\u0026#39;,\u0026#39;NongGongShang\u0026#39;]) fruits = np.array([\u0026#39;apple\u0026#39;,\u0026#39;banana\u0026#39;,\u0026#39;orange\u0026#39;,\u0026#39;mango\u0026#39;]) # 2.创建一个 4x4 的二维数组存储不同超市的水果价格，其中价格由 4~10 范围内的随机数生成。 prices = np.random.randint(4,10,16).reshape(4,4) # 3.选择“大润发”的苹果和“好德”的香蕉，并将价格增加 1 元。 prices[shops == \u0026#39;DaRunFa\u0026#39;,fruits == \u0026#39;apple\u0026#39;] += 1 print(\u0026#39;the price of apple in DaRunFa now: %d\u0026#39; %prices[shops == \u0026#39;DaRunFa\u0026#39;,fruits == \u0026#39;apple\u0026#39;]) prices[shops == \u0026#39;HaoDe\u0026#39;,fruits == \u0026#39;banana\u0026#39;] += 1 print(\u0026#39;the price of banana in HaoDe now: %d\u0026#39; %prices[shops == \u0026#39;HaoDe\u0026#39;,fruits == \u0026#39;banana\u0026#39;]) # 4.“农工商”水果大减价，所有水果价格减 2 元。 prices[shops == \u0026#39;NongGongShang\u0026#39;] -= 2 print(\u0026#39;the price in NongGongShang now: \u0026#39;,end=\u0026#39;\u0026#39;) print(prices[shops == \u0026#39;NongGongShang\u0026#39;]) # 5.统计四个超市苹果和芒果的销售均价。 print(\u0026#39;ave of apple is: %f\u0026#39;%prices[: , fruits == \u0026#39;apple\u0026#39;].mean()) print(\u0026#39;ave of mango is: %f\u0026#39;%prices[: , fruits == \u0026#39;mango\u0026#39;].mean()) # 6.找出橘子价格最贵的超市名称（不是编号）。 t = 0 for i in range(0,4) : if prices[i, 2] \u0026gt; prices[t, 2] : t = i print(\u0026#39;the most expensive orange is in %s\u0026#39;%shops[t]) \u0026#39;\u0026#39;\u0026#39; the price of apple in DaRunFa now: 7 the price of banana in HaoDe now: 5 the price in NongGongShang now: [[6 7 4 6]] ave of apple is: 6.750000 ave of mango is: 6.750000 the most expensive orange is in Walmart \u0026#39;\u0026#39;\u0026#39; #二、 import numpy as np steps = 10 rndwlk = np.random.normal(0, 1, size = (3, steps)) print(\u0026#39;1）移动距离数组：\u0026#39;) print(rndwlk) position = rndwlk.cumsum(axis = 1) x = position[0] y = position[1] z = position[2] print(\u0026#39;\\n2）每步走完后在三维的空间位置：\u0026#39;) print(position) dists = np.sqrt(position[0]**2 + position[1]**2 + position[2]**2) #三维直角坐标系的距离公式 np.set_printoptions(precision=2) print(\u0026#39;\\n3）每步走完后到原点的距离：\u0026#39;) print(dists) print(\u0026#39;\\n4）Z轴到达的最远距离：%f\u0026#39;%abs(position[2]).max()) print(\u0026#39;\\n5）物体在三维空间距离原点的最近值：%f\u0026#39;%dists.min()) \u0026#39;\u0026#39;\u0026#39; 1）移动距离数组： [[ 0.09 0.52 -0.96 -0.96 -1.44 1.27 -0.61 -1.18 2.23 0.45] [-0.66 -2.22 -0.39 -0.25 0.36 -0.29 0.04 0.12 1.43 0.34] [ 0.56 0.56 0.96 0.33 2.15 1.56 -1.09 -2.05 -0.1 -0.48]] 2）每步走完后在三维的空间位置： [[ 0.09 0.62 -0.34 -1.3 -2.74 -1.47 -2.08 -3.26 -1.03 -0.57] [-0.66 -2.87 -3.26 -3.51 -3.16 -3.44 -3.4 -3.29 -1.86 -1.51] [ 0.56 1.11 2.07 2.4 4.55 6.12 5.02 2.97 2.87 2.39]] 3）每步走完后到原点的距离： [0.87 3.14 3.88 4.45 6.18 7.17 6.42 5.5 3.57 2.89] 4）Z轴到达的最远距离：6.116005 5）物体在三维空间距离原点的最近值：0.867622 \u0026#39;\u0026#39;\u0026#39; 数据汇总与统计 1 2 3 import pandas as pd import numpy as np from pandas import Series,DataFrame #使用pd. series对象 通过下标访问 1 2 3 4 5 6 7 8 9 #series([data,index,index,...]) import pandas as pd import numpy as np from pandas import Series,DataFrame height1=Series({\u0026#39;13\u0026#39; :187, \u0026#39;14\u0026#39; :190, \u0026#39;17\u0026#39;:185, \u0026#39;2\u0026#39;:178, \u0026#39;9\u0026#39;:185}) print(height1[\u0026#39;13\u0026#39;])#检索13号的身高 print(height1[1:3])#检索1、2号的身高 print(height1.values\u0026gt;=186)#检索大于186的球员 print(height1)#打印所有身高 通过append添加成员 1 2 3 4 5 6 7 import pandas as pd import numpy as np from pandas import Series,DataFrame a=Series([\u0026#39;5\u0026#39;,\u0026#39;10\u0026#39;],index=[180,185]) height1=Series({\u0026#39;13\u0026#39; :187, \u0026#39;14\u0026#39; :190, \u0026#39;17\u0026#39;:185, \u0026#39;2\u0026#39;:178, \u0026#39;9\u0026#39;:185}) height2=height1.append(a) print(height2) 删除成员 1 2 3 4 5 6 import pandas as pd import numpy as np from pandas import Series,DataFrame height1=Series({\u0026#39;13\u0026#39; :187, \u0026#39;14\u0026#39; :190, \u0026#39;17\u0026#39;:185, \u0026#39;2\u0026#39;:178, \u0026#39;9\u0026#39;:185}) height1.dorp(\u0026#39;13\u0026#39;,\u0026#39;9\u0026#39;) print(height1) Data_Frame对象 1 2 3 4 5 6 7 data = [[19,170, 68], [20, 165,65], [18,175, 65]] students=DataFrame (data, index= [1,2,3], columns=[\u0026#39;age\u0026#39;, \u0026#39;height\u0026#39;,\u0026#39;weight\u0026#39;]) print(students) # age height weight #1 19 170 68 #2 20 165 65 #3 18 175 65 添加数据 1 2 3 4 5 students[\u0026#39;expense\u0026#39;]=[1500,1600,1200] # age height weight expense #1 19 170 68 1500 #2 20 165 65 1600 #3 18 175 65 1200 修改数据 1 2 3 4 5 6 students[\u0026#39;expense\u0026#39;]=1000 print(stdents) # age height weight expense #1 19 170 68 1000 #2 20 165 65 1000 #3 18 175 65 1000 1 2 3 4 5 students.loc[1, :] = [21,188, 70,20] # age height weight expense #1 21 78 70 20 #2 20 165 65 1000 #3 18 175 65 1000 删除数据 1 2 3 4 students.drop(1,axis=0)#axis=0表示行 # age height weight expense #1 21 78 70 20 #2 20 165 65 1000 1 2 3 4 5 students.drop(\u0026#39;expense\u0026#39;, axis=1) # 删除expense列，axis=1表示列 # age height weight #1 21 78 70 #2 20 165 65 #3 18 175 65 1 students.drop([1,2],axis=0) # 删除多行 读写文件 1 2 3 4 5 csv=pd.read_csv(file,sep=\u0026#39;, \u0026#39;,header= \u0026#39;infer\u0026#39; , index_col=None , names, skiprows, ...) #读取csv文件 exc=pd.read_excel(file，sheetname,... ) #读取excel文件 file 字符串，文件路径和文件名 sep 字符串，每行各数据之间的分隔符，默认为“,” header header =None,文件中第一行不是列索引 index_col 数字，用作行索引的列序号 names 列表，定义列索引，默认文件中第- - 行为列索引 skiprows 整数或列表，需要忽略的行数或需要跳过的行号列表，skiprows=[2,3,5]，跳过2，3，5行 ","permalink":"https://chasing1020.github.io/post/data-analyzation-note/","summary":"\u003ch1 id=\"创建数组对象\"\u003e创建数组对象\u003c/h1\u003e\n\u003ch2 id=\"一维数组\"\u003e一维数组\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003earray\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;4\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;5\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;6\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003endim\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#维数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#大小\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"二维数组\"\u003e二维数组\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e创建\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003enumpy\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003enp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003earray\u003c/span\u003e\u003cspan class=\"p\"\u003e([[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;4\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;5\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;6\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e],[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;4\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;5\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;6\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e],[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;4\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;5\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;6\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e],[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;2\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;3\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;4\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;5\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;6\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"2\"\u003e\n\u003cli\u003e查看属性\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003endim\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#维数，也可以说是矩阵的秩\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#大小\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#行数和列数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edtype\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#查看数据类型\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"3\"\u003e\n\u003cli\u003e访问下标\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\u003cspan class=\"c1\"\u003e#索引为[0,n-1]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\u003cspan class=\"c1\"\u003e#索引为[-n,-1]，表示倒数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"4\"\u003e\n\u003cli\u003e切片(slicing)\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e],[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e]]:\u003c/span\u003e\u003cspan class=\"c1\"\u003e#表示下标1，2和下标2，4#如果使用:，则表示所有的行和列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e]:\u003c/span\u003e\u003cspan class=\"c1\"\u003e#表示下标1，2和下标2，4#如果使用:，则表示所有的行和列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"5\"\u003e\n\u003cli\u003e条件筛选\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e[(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e==\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e)])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"6\"\u003e\n\u003cli\u003e创建多维数组\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003earange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#生成1-9之间的连续的数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003earange\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e15\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereshape\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#将一维数组转为二维数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ezeros\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"c1\"\u003e#生成3*4的为0的数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eones\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"c1\"\u003e#生成4*3的为1的数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"7\"\u003e\n\u003cli\u003e多维数组运算\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003enp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eones\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"c1\"\u003e#生成全部为5的数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"c1\"\u003e#生成全部为4的数组\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"常用函数属性\"\u003e常用函数、属性\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e函数\u003c/li\u003e\n\u003c/ol\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e函数\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e作用\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003enp.array(列表)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e通过列表创建一个数组对象\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003enp.arange(起始, 结束, 步长)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e创建一个等差数组(注意区间是左闭右开的)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003enp.zeros( (m, n) )\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e创建一个m行n列的全零数组\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003enp.ones( (m, n) )\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e创建一个m行n列的全一数组\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003enp.eye(m)\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e创建一个m阶单位方阵\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e常用对象属性\u003c/li\u003e\n\u003c/ol\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e属性\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e作用\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003emy_array.ndim\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e维数\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003emy_array.size\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e大小\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003emy_array.shape\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e以元组形式返回my_array的(行, 列)\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003emy_array.dtype\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e返回my_array中元素的数据类型\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e*注意库函数和对象属性的不同，表一中np.是固定的，指的是numpy库；而表二中my_array.xxx()中的my_array要改成你对应的数组的名字（即实例名）。\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e## 切片\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e切片\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cblockquote\u003e\n\u003cp\u003e我们将选出一个数组的某一行、某一列或者某一个位置上的元素的操作成为“切片”\n我们先来讨论二维数组的切片:\n最基本的格式是：my_array[m, n]，其中m和n可以为整数 列表 还可以是冒号:\n当m和n是整数时，表示选取m行n列的那个数。\n当m和n其中一个是冒号的时候，表明选中对应的所有行或列。例如my_array[ :, n]表示选择整个第n列\u003c/p\u003e","title":"Data Analyzation Note"},{"content":"利用opencv进行人脸检测与识别 1. OpenCV基本操作 1.1 配置环境 按win+R输入cmd打开命令行，在命令行下，输入\npip install numpy\npip install opencv-python\n1 2 3 #程序运行时，加入模块 import cv2 import numpy as np 1.2 图片加载、显示和保存 1 2 3 4 5 6 7 8 9 10 11 12 13 import cv2 # 生成图片 img = cv2.imread(\u0026#34;1.jpg\u0026#34;) # 生成灰色图片 imgGrey = cv2.imread(\u0026#34;1.jpg\u0026#34;, 0) # 展示原图 cv2.imshow(\u0026#34;img\u0026#34;, img) # 展示灰色图片 cv2.imshow(\u0026#34;imgGrey\u0026#34;, imgGrey) # 等待图片的关闭 cv2.waitKey() # 保存灰色图片 cv2.imwrite(\u0026#34;Copy.jpg\u0026#34;, imgGrey) 1.3 图像显示窗口创建与销毁 cv2.namedWindow(窗口名，属性) 创建一个窗口\n属性—指定窗口大小模式：\ncv2.WINDOW_AUTOSIZE：根据图像大小自动创建大小 cv2.WINDOW_NORMAL：窗口大小可调整 cv2.destoryAllWindows(窗口名) 删除任何建立的窗口\n1 2 3 4 5 6 7 8 import cv2 img = cv2.imread(\u0026#34;1.jpg\u0026#34;) cv2.namedWindow(\u0026#34;img\u0026#34;, cv2.WINDOW_NORMAL) cv2.imshow(\u0026#34;img\u0026#34;, img) cv2.waitKey() cv2.destroyAllWindows() 1.4 图片宽、高、通道数获取 img.shape 返回图像高（图像矩阵的行数）、宽（图像矩阵的列数）和通道数3个属性组成的元组，若图像是非彩色图，则只返回高和宽组成的元组。\n1 2 3 4 5 6 7 8 9 10 11 12 13 import cv2 img = cv2.imread(\u0026#34;1.jpg\u0026#34;) imgGrey = cv2.imread(\u0026#34;1.jpg\u0026#34;, 0) sp1 = img.shape sp2 = imgGrey.shape print(sp1) print(sp2) # ======输出======= #(1200, 1920, 3) #(1200, 1920) 1.5 图像像素数目和图像数据类型的获取 图像矩阵img的size属性和dtype分别对应图像的像素总数目和图像数据类型。一般情况下，图像的数据类型是uint8\n1 2 3 4 5 6 7 8 9 10 11 12 import cv2 img = cv2.imread(\u0026#34;1.jpg\u0026#34;) imgSize = img.size print(imgSize) ty = img.dtype print(ty) #======输出======== #6912000 #uint8 1. 6 生成指定大小的空图像 emptyImage = np.zeros(img.shape, np.uint8)\n1 2 3 4 5 6 7 8 9 10 11 12 13 import cv2 import numpy as np img = cv2.imread(\u0026#34;1.jpg\u0026#34;) imgZero = np.zeros(img.shape, np.uint8) imgFix = np.zeros((300, 500, 3), np.uint8) # imgFix = np.zeros((300,500),np.uint8) cv2.imshow(\u0026#34;img\u0026#34;, img) cv2.imshow(\u0026#34;imgZero\u0026#34;, imgZero) cv2.imshow(\u0026#34;imgFix\u0026#34;, imgFix) cv2.waitKey() 1.7 访问和操作图像像素 OpenCV中图像矩阵的顺序是B、G、R。可以直接通过坐标位置访问和操作图像像素。\n1 2 3 4 5 6 #获取图像的三通道 blue,green,red = cv2.split(f) #或者 blue = f[:,:,0] green = f[:,:,1] red = f[:,:,2] OpenCV中图像矩阵的顺序是B、G、R。可以直接通过坐标位置访问和操作图像像素。\n1 2 3 4 5 6 7 8 9 10 import cv2 img = cv2.imread(\u0026#34;01.jpg\u0026#34;) numb = img[50,100] print(numb) img[50,100] = (0,0,255)#将50，100处的像素点改为红色 cv2.imshow(\u0026#34;img\u0026#34;,img) cv2.waitKey() 分开访问图像某一通道像素值\n1 2 3 4 5 6 7 8 9 10 import cv2 img = cv2.imread(\u0026#34;01.jpg\u0026#34;) img[0:100,100:200,0] = 255 img[100:200,200:300,1] = 255 img[200:300,300:400,2] = 255 cv2.imshow(\u0026#34;img\u0026#34;,img) cv2.waitKey() 更改某一矩阵中的像素值\n1 2 3 4 5 6 7 8 import cv2 img = cv2.imread(\u0026#34;01.jpg\u0026#34;) img[0:50,1:100] = (0,0,255) cv2.imshow(\u0026#34;img\u0026#34;,img) cv2.waitKey() 1.8 图像三通道分离和合并 分离图像通道可以使用cv2中的split函数，合并使用merge函数。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import cv2 img = cv2.imread(\u0026#34;01.jpg\u0026#34;) b , g , r = cv2.split(img) # b = cv2.split(img)[0] # g = cv2.split(img)[1] # r = cv2.split(img)[2] merged = cv2.merge([b,g,r]) cv2.imshow(\u0026#34;Blue\u0026#34;,b) cv2.imshow(\u0026#34;Green\u0026#34;,g) cv2.imshow(\u0026#34;Red\u0026#34;,r) cv2.imshow(\u0026#34;Merged\u0026#34;,merged) cv2.waitKey() 1.9 在图像上输出文字及图片 使用putText函数在图片上输出文字，函数原型： putText(img, text, org, fontFace, fontScale, color, thickness=None, lineType=None, bottomLeftOrigin=None)\nimg 图像 text 要输出的文本 org 文字的起点坐标 fontFace 字体 fontScale 字体大小 color 字体颜色 thickness 字图加粗 1 2 3 4 5 6 7 8 import cv2 img = cv2.imread(\u0026#34;01.jpg\u0026#34;) cv2.putText(img,\u0026#34;Print some text to img\u0026#34;,(100,100),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255)) cv2.imshow(\u0026#34;img\u0026#34;,img) cv2.waitKey() 绘制矩形和圆\n1 2 cv2.rectangle(img, (x, y, x + w, y + h), color=(0, 255, 0), thickness=12) cv2.circle(img, center=(x + w // 2, y + h // 2), radius=w // 2, color=(0, 0, 255), thickness=2) 1.10 图像缩放 1 2 3 4 5 6 7 8 9 10 import cv2 img = cv2.imread(\u0026#34;1.jpg\u0026#34;) cv2.imshow(\u0026#34;img\u0026#34;, img) img2 = cv2.resize(img, (200, 100)) cv2.imshow(\u0026#34;img2\u0026#34;, img1) cv2.waitKey() interpolation 选项 所用的插值方法 INTER_NEAREST 最近邻插值 INTER_LINEAR 双线性插值（默认设置） INTER_AREA 使用像素区域关系进行重采样。 它可能是图像抽取的首选方法，因为它会产生无云纹理的结果。 但是当图像缩放时，它类似于INTER_NEAREST方法。 INTER_CUBIC 4x4像素邻域的双三次插值 INTER_LANCZOS4 8x8像素邻域的Lanczos插值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import cv2 img = cv2.imread(\u0026#39;./res/aero3.jpg\u0026#39;) print(img.shape[:2]) height, width = img.shape[:2] reSize1 = cv2.resize(img, (2*width, 2*height), interpolation=cv2.INTER_CUBIC) reSize2 = cv2.resize(img, (int(width/2), int(height/2)), interpolation=cv2.INTER_CUBIC) cv2.imshow(\u0026#39;reSize1\u0026#39;, reSize1) cv2.imshow(\u0026#39;reSize2\u0026#39;, reSize2) cv2.waitKey() cv2.destroyAllWindows() 1.11 实现正常退出 cv2.waitkey(delaytime)\u0026mdash;\u0026mdash;-\u0026gt;returnvalue 在delaytime时间内,按键盘, 返回所按键的ASCII值;若未在delaytime时间内按任何键, 返回-1; 其中,dalaytime: 单位ms; note:\n当delaytime为0时,表示forever,永不退回. 当按ecs键时,因为esc键ASCII值为27,所有returnvalue的值为27, 一般用这个机制实现在delaytime内正常退出. 也使用 if cv2.waitKey(1) \u0026amp; 0xFF == ord(‘q’): break 来实现1ms之内的正常退出. 其中, ord(‘q’)：返回q对应的Unicode码对应的值，q对应的Unicode数值为113。 0xFF：0xFF是一个位掩码，十六进制常数，二进制值为11111111, 它将左边的24位设置为0,把返回值限制在在0和255之间。ord(’ \u0026lsquo;)返回按键对应的整数（ASCII码） 1.12 保存图像 1 2 3 4 5 6 7 #cv2.IMWRITE_JPEG_QUALITY的值默认是long，需要强制转为int #对于img格式，其质量为0-100的范围，其中默认是95 cv2.imwrite(\u0026#34;./5.jpg\u0026#34;, img, [int(cv2.IMWRITE_JPEG_QUALITY), 5]) cv2.imwrite(\u0026#34;./100.jpg\u0026#34;, img, [int(cv2.IMWRITE_JPEG_QUALITY), 100]) #对于png格式，其质量为0-9范围，其中默认是3 cv2.imwrite(\u0026#34;./0.png\u0026#34;, img, [int(cv2.IMWRITE_PNG_COMPRESSION), 0]) cv2.imwrite(\u0026#34;./9.png\u0026#34;, img, [int(cv2.IMWRITE_PNG_COMPRESSION), 9]) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import cv2 # 待检测的图片路径 imagepath=\u0026#34;1.jpg\u0026#34; image = cv2.imread(imagepath) gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) # 获取人脸识别训练数据 #对于人脸特征的一些描述，opencv在读取完数据后很据训练中的样品数据， #就可以感知读取到的图片上的特征，进而对图片进行人脸识别。 #xml数据下载：https://github.com/opencv/opencv/tree/master/data/haarcascades face_cascade = cv2.CascadeClassifier(r\u0026#39;./haarcascade_frontalface_default.xml\u0026#39;) # 探测人脸 # 根据训练的数据来对新图片进行识别的过程。 faces = face_cascade.detectMultiScale( gray, scaleFactor = 1.15, minNeighbors = 5, minSize = (5,5), #flags = cv2.HAAR_SCALE_IMAGE ) # 我们可以随意的指定里面参数的值，来达到不同精度下的识别。返回值就是opencv对图片的探测结果的体现。 # 处理人脸探测的结果 print (\u0026#34;发现{0}个人脸!\u0026#34;.format(len(faces))) for(x,y,w,h) in faces: cv2.rectangle(image,(x,y),(x+w,y+w),(0,255,0),2) # cv2.circle(image,((x+x+w)/2,(y+y+h)/2),w/2,(0,255,0),2) cv2.imshow(\u0026#34;image\u0026#34;,image) cv2.waitKey(0) cv2.destroyAllWindows() 2. Haar级联分类器 2.1 种类 顾名思义——\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 haarcascade_eye.xml haarcascade_eye_tree_eyeglasses.xml haarcascade_frontalface_alt.xml haarcascade_frontalface_alt_tree.xml haarcascade_frontalface_alt2.xml haarcascade_frontalface_default.xml haarcascade_fullbody.xml haarcascade_lefteye_2splits.xml haarcascade_lowerbody.xml haarcascade_mcs_eyepair_big.xml haarcascade_mcs_eyepair_small.xml haarcascade_mcs_leftear.xml haarcascade_mcs_lefteye.xml haarcascade_mcs_mouth.xml haarcascade_mcs_nose.xml haarcascade_mcs_rightear.xml haarcascade_mcs_righteye.xml haarcascade_mcs_upperbody.xml haarcascade_profileface.xml haarcascade_righteye_2splits.xml haarcascade_smile.xml haarcascade_upperbody.xml 2.2 detectMultiScale函数 函数原型\n1 objects = cv2.CascadeClassifier.detectMultiScale( image[, scaleFactor[,￼ minNeighbors[, flags[, minSize[, maxSize]]]]] ) 检测人脸和眼睛应该出现的位置\nimage 待检测图像，通常设置为灰度图像 scaleFactor 窗口缩放的比例 minNeighbors 检测的目标相邻矩形的最小个数，默认三，如果希望检测严格可以调高 flags 边缘检测器，拒绝一些区域 minSize 最小尺寸，小于这个尺寸的可以忽略 naxSize 最大尺寸，大于这个尺寸的可以忽略 2.3 LBPH识别 retval = cv2.face.LBPHFaceRecognizer_create( [, radius[, neighbors[,￼grid_x[, grid_y[, threshold]]]]])\n方法：PCA\n暂时并不需要完成识别，只需要检测\n3.识别 导入haarcascade_eye.xml和haarcascade_frontalface_default.xml\n在Github下载\n1 fileUrl=https://github.com/opencv/opencv 3.1 人脸导入以及灰度处理 1 2 3 4 5 6 7 8 9 import cv2 # 载入人脸识别和眼睛识别的两个xml文件 face_xml = cv2.CascadeClassifier(\u0026#39;haarcascade_frontalface_default.xml\u0026#39;) eye_xml = cv2.CascadeClassifier(\u0026#39;haarcascade_eye.xml\u0026#39;) # 载入图片 img = cv2.imread(\u0026#39;face.jpg\u0026#39;) cv2.imshow(\u0026#39;src\u0026#39;, img) # 灰度处理 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 3.2 识别人脸并且用方框标记 1 2 3 4 5 6 7 8 # 人脸识别 face = face_xml.detectMultiScale(gray, 1.3, 2) # 参数：1、灰度图片， 2、缩放比例， 3、阈值 print(\u0026#34;这张图片中有%d张人脸\u0026#34; % len(face)) # 绘制出识别到的人脸 for (x, y, w, h) in face: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绘制人脸方框 cv2.imshow(\u0026#39;dst\u0026#39;, img) cv2.waitkey(0) 3.3 识别眼睛并用方框标记 1 2 3 4 5 6 7 8 9 # 在人脸的基础上识别眼睛 face_gray = gray[y:y+h, x:x+w] face_color = img[y:y+h, x:x+w] # 眼睛识别 eyes = eye_xml.detectMultiScale(face_gray) print(\u0026#34;在这张脸上有%d个眼睛\u0026#34; % len(eyes)) # 绘制出识别到的眼睛 for (e_x, e_y, e_w, e_h) in eyes: cv2.rectangle(face_color, (e_x, e_y), (e_x+e_w, e_y+e_h), (0, 255, 0), 2) # 绘制眼睛方框 3.4 对图片进行完整处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # -*- coding: utf-8 -*- #@Time : 2020-10-17 18:38 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : test01.py #@Software: PyCharm # from PIL import Image __author__ = \u0026#39;WWQ\u0026#39; import cv2 # 载入人脸识别和眼睛识别的两个xml文件 face_xml = cv2.CascadeClassifier(\u0026#39;haarcascade_frontalface_default.xml\u0026#39;) eye_xml = cv2.CascadeClassifier(\u0026#39;haarcascade_eye.xml\u0026#39;) # 载入图片 img = cv2.imread(\u0026#39;face.jpg\u0026#39;) cv2.imshow(\u0026#39;src\u0026#39;, img) # 灰度处理 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 人脸识别 face = face_xml.detectMultiScale(gray, 1.3, 2) # 参数：1、灰度图片， 2、缩放比例， 3、阈值 print(\u0026#34;这张图片中有%d张人脸\u0026#34; % len(face)) # 绘制出识别到的人脸 for (x, y, w, h) in face: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) # 绘制人脸方框 # cv2.imshow(\u0026#39;dst\u0026#39;, img) # 在人脸的基础上识别眼睛 face_gray = gray[y:y+h, x:x+w] face_color = img[y:y+h, x:x+w] # 眼睛识别 eyes = eye_xml.detectMultiScale(face_gray) print(\u0026#34;在这张脸上有%d个眼睛\u0026#34; % len(eyes)) # 绘制出识别到的眼睛 for (e_x, e_y, e_w, e_h) in eyes: cv2.rectangle(face_color, (e_x, e_y), (e_x+e_w, e_y+e_h), (0, 255, 0), 2) # 绘制眼睛方框 cv2.imshow(\u0026#39;dst\u0026#39;, img) cv2.waitKey(0) 3.5 检测画面中人脸的个数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # -*- coding: utf-8 -*- #@Time : 2020-10-17 18:38 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : test01.py #@Software: PyCharm # from PIL import Image import cv2 # 待检测的图片路径 imagepath=\u0026#34;test01.jpg\u0026#34; image = cv2.imread(imagepath) gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) \u0026#39;\u0026#39;\u0026#39; # 获取人脸识别训练数据 对于人脸特征的一些描述，opencv在读取完数据后很据训练中的样品数据， 就可以感知读取到的图片上的特征，进而对图片进行人脸识别。 xml数据下载， 参考：https://github.com/opencv/opencv/tree/master/data/haarcascades \u0026#39;\u0026#39;\u0026#39; face_cascade = cv2.CascadeClassifier(r\u0026#39;./haarcascade_frontalface_default.xml\u0026#39;) # 探测人脸 # 根据训练的数据来对新图片进行识别的过程。 faces = face_cascade.detectMultiScale( gray, scaleFactor = 1.15, minNeighbors = 5, minSize = (5,5), #flags = cv2.HAAR_SCALE_IMAGE ) # 我们可以随意的指定里面参数的值，来达到不同精度下的识别。返回值就是opencv对图片的探测结果的体现。 # 处理人脸探测的结果 print (\u0026#34;发现{0}个人脸!\u0026#34;.format(len(faces))) for(x,y,w,h) in faces: cv2.rectangle(image,(x,y),(x+w,y+w),(0,255,0),2) # cv2.circle(image,((x+x+w)/2,(y+y+h)/2),w/2,(0,255,0),2) cv2.imshow(\u0026#34;image\u0026#34;,image) cv2.waitKey(0) cv2.destroyAllWindows() 4.提取人眼区域的瞳孔位置 4.1 提取人眼的位置，并且二值化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # -*- coding: utf-8 -*- #@Time : 2020-10-17 19:27 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : test01.py #@Software: PyCharm # from PIL import Image import cv2 import numpy as np src = cv2.imread(\u0026#34;/home/jon/code/python/img/eye_area.jpg\u0026#34;) gray = cv2.cvtColor(src, cv.COLOR_BGR2GRAY) cv2.imshow(\u0026#34;gray image\u0026#34;, gray) ret,binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) cv2.imshow(\u0026#34;binary image\u0026#34;, binary) 4.2 对其降噪处理 要想得到眼球，我们可以通过一个圆形的结构元素，对这张图像做个开操作(先腐蚀再膨胀)，但是还存在一个问题，中心的圆形区域还存在噪声，需要先把这个噪声剔除\n1 2 3 element1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3),(-1,-1)) tmp = cv2.morphologyEx(binary,cv2.MORPH_CLOSE,element1,None,(-1,-1),1) cv2.imshow(\u0026#34;tmp image\u0026#34;, tmp) 这样图像就干净了，去除了噪声后，我们也就可以通过圆形的结构元素提取眼球位置 注意：这里的矩形结构元素不能太大了，不然会把里面的2个大黑点当作噪声去除了，从而导致找不到眼球\n1 2 3 element2 = cv.getStructuringElement(cv.MORPH_ELLIPSE,(16,16),(-1,-1)) dst = cv.morphologyEx(tmp,cv.MORPH_OPEN,element2) cv.imshow(\u0026#34;eye image\u0026#34;, dst) 如图得到眼球，下面只需要来个轮廓发现并填充颜色即可 注意：这里的圆形区域的大小需要适当大点，或许需要根据不同的图片坐下微调以达到理想的效果\n1 2 3 4 5 6 cloneImage, contours, hierarchy = cv2.findContours(dst, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE, None, None, (0, 0)) for i, contour in enumerate(contours): print \u0026#34;find \u0026#34; cv2.drawContours(src, contours, i, (255, 0, 0), -1) cv2.imshow(\u0026#34;dst image\u0026#34;, src) 4.3 完整判断眼睛代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #-*- coding = utf-8 -*- #@Time : 2020-10-17 18:38 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : test01.py #@Software: PyCharm # from PIL import Image import cv2 import numpy as np src = cv2.imread(\u0026#34;/home/jon/code/python/img/tpl.jpg\u0026#34;) gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) cv2.imshow(\u0026#34;gray image\u0026#34;, gray) ret,binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) cv2.imshow(\u0026#34;binary image\u0026#34;, binary) element1 = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3),(-1,-1)) tmp = cv2.morphologyEx(binary,cv2.MORPH_CLOSE,element1,None,(-1,-1),1) cv2.imshow(\u0026#34;tmp image\u0026#34;, tmp) element2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(16,16),(-1,-1)) dst = cv2.morphologyEx(tmp,cv.MORPH_OPEN,element2) cv2.imshow(\u0026#34;eye image\u0026#34;, dst) cloneImage, contours, hierarchy = cv2.findContours(dst, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE, None, None, (0, 0)) for i, contour in enumerate(contours): print \u0026#34;find \u0026#34; cv2.drawContours(src, contours, i, (255, 0, 0), -1) cv2.imshow(\u0026#34;dst image\u0026#34;, src) cv2.waitKey(0) cv2.destroyAllWindows() 5. 视频操作 5.1 读取视频 1 2 3 4 5 6 7 8 9 10 import cv2 cap = cv2. VideoCapture (0) while True: ret, frame = cap. read() cv2. imshow(\u0026#39; Video\u0026#39;,frame) C = cv2. waitKey(1) if c == 27: break cap.release() cv2.destroyAllWindows() 1、cap = cv2.VideoCapture(0)\nVideoCapture()中参数是0，表示打开笔记本的内置摄像头，参数是视频文件路径则打开视频，如cap = cv2.VideoCapture(\u0026quot;../test.avi\u0026quot;)\n2、ret,frame = cap.read()\ncap.read()按帧读取视频，ret,frame是获cap.read()方法的两个返回值。其中ret是布尔值，如果读取帧是正确的则返回True，如果文件读取到结尾，它的返回值就为False。frame就是每一帧的图像，是个三维矩阵。\n3、cv2.waitKey(1)，waitKey（）方法本身表示等待键盘输入，\n参数是1，表示延时1ms切换到下一帧图像，对于视频而言；\n参数为0，如cv2.waitKey(0)只显示当前帧图像，相当于视频暂停,；\n参数过大如cv2.waitKey(1000)，会因为延时过久而卡顿感觉到卡顿。\nc得到的是键盘输入的ASCII码，esc键对应的ASCII码是27，即当按esc键是if条件句成立\n4、调用release()释放摄像头，调用destroyAllWindows()关闭所有图像窗口。\n5.2 将视频的每一帧识别出人像 将上述的方法整合 即可实现每一帧的视频中的图像展示出来 示例代码如下\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #-*- coding = utf-8 -*- #@Time : 2020-10-19 16:32 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : videoOpen.py #@Software: PyCharm import cv2 dataFaceUrl = r\u0026#39;E:\\opencv\\opencv-master\\data\\haarcascades\\haarcascade_frontalface_default.xml\u0026#39; dataEyesUrl = r\u0026#39;E:\\opencv\\opencv-master\\data\\haarcascades\\haarcascade_eye.xml\u0026#39; videoUrl=r\u0026#39;D:\\Desktop\\python\\demo\\testOpencv\\video1.mp4\u0026#39; eye_cascade = cv2.CascadeClassifier(dataEyeUrl) face_cascade = cv2.CascadeClassifier(dataFaceUrl) def main(): cap=cv2.VideoCapture(videoUrl) while True: ret,frame=cap.read() # cv2.imshow(\u0026#39;video\u0026#39;,frame) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) BBox = face_cascade.detectMultiScale(gray, 1.3, 5) for (x, y, w, h) in BBox: frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) height, width = frame.shape[:2] reSize2 = cv2.resize(frame, (int(width / 2), int(height / 2)), interpolation=cv2.INTER_CUBIC) cv2.imshow(\u0026#39;release\u0026#39;, reSize2) c = cv2.waitKey(100//30)#假定视频文件30帧 if c==27:#按下esc退出 break cap.release() cv2.destroyAllWindows() if __name__ == \u0026#34;__main__\u0026#34;: main() 5.3 识别人脸中的眼睛是否完整 1 2 3 4 5 6 7 8 9 10 def eyesDetect(x, y, w, h,img,gray): face_gray = gray[y:y + h, x:x + w] face_color = img[y:y + h, x:x + w] # 眼睛识别 eyes = eye_cascade.detectMultiScale(face_gray) # 绘制出识别到的眼睛 for (e_x, e_y, e_w, e_h) in eyes: cv2.rectangle(face_color, (e_x, e_y), (e_x + e_w, e_y + e_h), (0, 255, 0), 2) # 绘制眼睛方框 cv2.destroyAllWindows() 5.4 识别人脸及眼睛代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #-*- coding = utf-8 -*- #@Time : 2020-10-19 16:32 #@Author : Jiancong Zhu #@Email : 643601464@qq.com #@File : videoOpen.py #@Software: PyCharm import cv2 dataFaceUrl = r\u0026#39;E:\\opencv\\opencv-master\\data\\haarcascades\\haarcascade_frontalface_default.xml\u0026#39; dataEyeUrl = r\u0026#39;E:\\opencv\\opencv-master\\data\\haarcascades\\haarcascade_eye.xml\u0026#39; videoUrl=r\u0026#39;D:\\Desktop\\whx.mp4\u0026#39; eyeCascade = cv2.CascadeClassifier(dataEyeUrl) faceCascade = cv2.CascadeClassifier(dataFaceUrl) def main(): # cap=cv2.VideoCapture(0) cap = cv2.VideoCapture(videoUrl) while True: flag,frame=cap.read() if not flag: break flag1=1#代表眼睛数目正常 flag2=1#代表脸部数目正常 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)#灰化，转为黑白 faces = faceCascade.detectMultiScale(gray, 1.3, 6,minSize=(150,150))#返回四维列表，最小大小暂时设置为150，防止误识别，后期可以再修改 if(len(faces)==1): for (x, y, w, h) in faces: frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) faceGray = gray[y:y + int(h*2/3), x:x + w]#这里取脸部上方2/3，为了防止误识别嘴巴 faceFrame = frame[y:y + int(h*2/3), x:x + w] eyes = eyeCascade.detectMultiScale(faceGray,1.3,7) if(len(eyes)==2): for (e_x, e_y, e_w, e_h) in eyes: cv2.rectangle(faceFrame, (e_x, e_y), (e_x + e_w, e_y + e_h), (0, 255, 0), 2) else: flag1=0#代表眼睛个数不为2 else: flag2=0#代表眼睛个数不为1 if flag1==1 and flag2==1:#只有当脸的个数为1，眼睛数为2，输出 cv2.putText(frame,\u0026#34;ok\u0026#34;,(30,50),cv2.FONT_HERSHEY_SIMPLEX,3,(0,255,0)) else: cv2.putText(frame,\u0026#34;error\u0026#34;,(30,50),cv2.FONT_HERSHEY_SIMPLEX,3,(0,0,255)) height, width = frame.shape[:2] reSize2 = cv2.resize(frame, (int(width / 1.3), int(height / 1.3)), interpolation=cv2.INTER_CUBIC) cv2.imshow(\u0026#39;release\u0026#39;, reSize2) esc = cv2.waitKey(3) if esc == 27: break cv2.distoryAllWindows() cap.release() if __name__ == \u0026#34;__main__\u0026#34;: main() ","permalink":"https://chasing1020.github.io/post/opencv-human-detection/","summary":"\u003ch1 id=\"利用opencv进行人脸检测与识别\"\u003e利用opencv进行人脸检测与识别\u003c/h1\u003e\n\u003ch2 id=\"1-opencv基本操作\"\u003e1. OpenCV基本操作\u003c/h2\u003e\n\u003ch3 id=\"11-配置环境\"\u003e1.1 配置环境\u003c/h3\u003e\n\u003cp\u003e按win+R输入cmd打开命令行，在命令行下，输入\u003c/p\u003e\n\u003cp\u003epip install numpy\u003c/p\u003e\n\u003cp\u003epip install opencv-python\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#程序运行时，加入模块\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecv2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003enumpy\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003enp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"12-图片加载显示和保存\"\u003e1.2 图片加载、显示和保存\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecv2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 生成图片\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eimg\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1.jpg\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 生成灰色图片\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eimgGrey\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;1.jpg\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 展示原图\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimshow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;img\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimg\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 展示灰色图片\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimshow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;imgGrey\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimgGrey\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 等待图片的关闭\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewaitKey\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 保存灰色图片\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ecv2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eimwrite\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Copy.jpg\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimgGrey\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"13-图像显示窗口创建与销毁\"\u003e1.3 图像显示窗口创建与销毁\u003c/h3\u003e\n\u003cp\u003ecv2.namedWindow(窗口名，属性) 创建一个窗口\u003c/p\u003e","title":"OpenCV Human Detection"},{"content":"网页爬虫项目实战经验 此篇记录了网页爬虫的基本使用库，以及常用的正则表达式操作等。 同时给出了豆瓣电影top250的爬取方式（经典爬虫入门项目）。 以及对于CSDN的博主主页单个以及多个文章的爬取，并利用工具将其转换为pdf格式\n1.Bs4 1 2 3 4 5 6 bs4将复杂html文档转换成一个复杂的树形结构，每个节点都是python对象，所有对象可以分为4种 -Tag -NavigableString -BeautifulSoup -Comment 1.1 analyze 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #bs4分析文档 from bs4 import BeautifulSoup file = open(\u0026#34;./baidu.html\u0026#34;, \u0026#34;rb\u0026#34;) html = file.read() bs = BeautifulSoup(html, \u0026#34;html.parser\u0026#34;) print(type(bs.head))#\u0026lt;class \u0026#39;bs4.element.Tag\u0026#39;\u0026gt;标签及其内容 #1.tag 标签及内容：拿到它所找到的第一个内容 print(type(bs.title.string))#\u0026lt;class \u0026#39;bs4.element.NavigableString\u0026#39;\u0026gt;标签里的内容 #2.NavigableString标签里的内容 print(type(bs.a.attrs))#\u0026lt;class \u0026#39;dict\u0026#39;\u0026gt;#\u0026lt;class \u0026#39;dict\u0026#39;\u0026gt; #3.标签内部的字典信息 print(type(bs))#\u0026lt;class \u0026#39;bs4.BeautifulSoup\u0026#39;\u0026gt; #4.表示整个文档 print(type(bs.a.string))#\u0026lt;class \u0026#39;bs4.element.Comment\u0026#39;\u0026gt; #5.第一个a中的注释里的内容,不包含注释符号 1.2 Traversal 1 2 3 4 5 6 7 #文档的遍历 from bs4 import BeautifulSoup file = open(\u0026#34;./baidu.html\u0026#34;, \u0026#34;rb\u0026#34;) html = file.read().decode(\u0026#34;utf-8\u0026#34;) bs = BeautifulSoup(html, \u0026#34;html.parser\u0026#34;) print(bs.head.contents)#列表类型，可以用下标访问 print(bs.head.contents[1]) 1.3 find 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #文档搜索 import re from bs4 import BeautifulSoup file = open(\u0026#34;./baidu.html\u0026#34;, \u0026#34;rb\u0026#34;) html = file.read().decode(\u0026#34;utf-8\u0026#34;) bs = BeautifulSoup(html, \u0026#34;html.parser\u0026#34;) #1.find_all() #查找与字符串完全匹配的内容 t_list=bs.find_all(\u0026#34;a\u0026#34;) for i in t_list: print(i) #所有\u0026lt;a\u0026gt;标签下面的内容 #2.search() #正则表达式来搜索 t_list=bs.find_all(re.compile(\u0026#34;a\u0026#34;))#匹配与正则表达式a有关的全部内容 for i in t_list: print(i) #3.根据函数的要求来搜索 #自定义函数查找 def name_is_exists(tag): return tag.has_attr(\u0026#34;name\u0026#34;) t_list=bs.find_all(name_is_exists) for i in t_list: print(i) #4.kwargs 寻找id=\u0026#34;head\u0026#34;内的全部内容 t_list=bs.find_all(id=\u0026#34;head\u0026#34;) for i in t_list: print(i) #5.文本参数 寻找文本的内容 t_list=bs.find_all(text=[\u0026#34;hao123\u0026#34;,\u0026#34;贴吧\u0026#34;,\u0026#34;地图\u0026#34;]) t_list=bs.find_all(text=re.compile(\u0026#34;\\d\u0026#34;)) #寻找符合正则表达式的项目，这里是寻找所有整数 for i in t_list: print(i) #6.limit参数 t_list=bs.find_all(\u0026#34;a\u0026#34;,limit=3) #只搜索三个 for i in t_list: print(i) #7.css选择器 t_list=bs.select(\u0026#39;title\u0026#39;) t_list=bs.select(\u0026#34;.mnav\u0026#34;)#寻找类名 t_list=bs.select(\u0026#39;#u1\u0026#39;)#通过id来查找 t_list=bs.select(\u0026#34;a[class=\u0026#39;bri\u0026#39;]\u0026#34;)#通过属性来查找 t_list=bs.select(\u0026#34;head\u0026gt;title\u0026#34;)#通过子标签来查找 t_list=bs.select(\u0026#34;.mnav ~ .bri\u0026#34;)#通过子标签来查找 print(t_list[0].get_text())#获取文本 for i in t_list: print(i) 2. Re #正则表达式：字符串模式（判断字符串是否符合一定标准） import re #创建模式对象\n2.1 new object 1 2 3 4 5 6 #search pat=re.compile(\u0026#34;AA\u0026#34;)#此处的AA属于正则表达式 ans=pat.search(\u0026#34;ABC\u0026#34;)#这里的search内部属于被搜索的内容 print(ans)#输出None ans2=pat.search(\u0026#34;AABCAA\u0026#34;)#优先找到第一个结果 print(ans2)#输出\u0026lt;re.Match object; span=(3, 5), match=\u0026#39;AA\u0026#39;\u0026gt; 2.2 findall 1 2 3 #findall ans=re.findall(\u0026#34;a+\u0026#34;,\u0026#34;aaabc\u0026#34;)#前面是正则表达式，后面是待求的结果 print(ans)#输出[\u0026#39;aaa\u0026#39;] 2.3 sub 1 2 3 4 #sub(1,2,3) ans=re.sub(\u0026#34;a\u0026#34;,\u0026#34;A\u0026#34;,\u0026#34;abcdcasd\u0026#34;)#对于最后一个表达式，用a换成A print(ans) #建议在正则表达式种，被比较的字符前面加上r，不用担心转义字符的问题 2.4 demo 1、匹配中文:[\\u4e00-\\u9fa5]\n2、英文字母:[a-zA-Z]\n3、数字:[0-9]\n4、匹配中文，英文字母和数字及下划线：^[\\u4e00-\\u9fa5_a-zA-Z0-9]+$ 同时判断输入长度： [\\u4e00-\\u9fa5_a-zA-Z0-9_]{4,10}\n5、 (?!)　不能以_开头 (?!.*?$)　不能以_结尾 [a-zA-Z0-9_\\u4e00-\\u9fa5]+　至少一个汉字、数字、字母、下划线 $　与字符串结束的地方匹配\n6、只含有汉字、数字、字母、下划线，下划线位置不限： ^[a-zA-Z0-9_\\u4e00-\\u9fa5]+$\n7、由数字、26个英文字母或者下划线组成的字符串 ^\\w+$\n8、2~4个汉字 \u0026ldquo;^[\\u4E00-\\u9FA5]{2,4}$\u0026rdquo;;\n9、最长不得超过7个汉字，或14个字节(数字，字母和下划线)正则表达式 ^[\\u4e00-\\u9fa5]{1,7}$|^[\\dA-Za-z_]{1,14}$\n10、匹配双字节字符(包括汉字在内)：[^x00-xff] 评注：可以用来计算字符串的长度（一个双字节字符长度计2，ASCII字符计1）\n11、匹配空白行的正则表达式：ns*r 评注：可以用来删除空白行\n12、匹配HTML标记的正则表达式：\u0026lt;(S*?)[^\u0026gt;]\u0026gt;.?|\u0026lt;.*? /\u0026gt; 评注：网上流传的版本太糟糕，上面这个也仅仅能匹配部分，对于复杂的嵌套标记依旧无能为力\n13、匹配首尾空白字符的正则表达式：^s*|s*$ 评注：可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)，非常有用的表达式\n14、匹配Email地址的正则表达式：^[a-zA-Z0-9][\\w.-][a-zA-Z0-9]@[a-zA-Z0-9][\\w.-][a-zA-Z0-9].[a-zA-Z][a-zA-Z.]*[a-zA-Z]$\n评注：表单验证时很实用\n15、手机号：^((13[0-9])|(14[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$\n16、身份证：(^\\d{15}$)|(^\\d{17}([0-9]|X|x)$)\n17、匹配网址URL的正则表达式：[a-zA-z]+://[^s]* 评注：网上流传的版本功能很有限，上面这个基本可以满足需求\n18、匹配帐号是否合法(字母开头，允许5-16字节，允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 评注：表单验证时很实用\n19、匹配国内电话号码：d{3}-d{8}|d{4}-d{7} 评注：匹配形式如 0511-4405222 或 021-87888822\n20、匹配腾讯QQ号：[1-9][0-9]{4,} 评注：腾讯QQ号从10000开始\n21、匹配中国邮政编码：[1-9]d{5}(?!d) 评注：中国邮政编码为6位数字\n22、匹配身份证：d{15}|d{18} 评注：中国的身份证为15位或18位\n23、匹配ip地址：d+.d+.d+.d+ 评注：提取ip地址时有用\n24、匹配特定数字： ^[1-9]d*$　//匹配正整数 ^-[1-9]d*$ //匹配负整数 ^-?[1-9]d*$　//匹配整数 ^[1-9]d*|0$　//匹配非负整数（正整数 + 0） ^-[1-9]d*|0$　//匹配非正整数（负整数 + 0） ^[1-9]d*.d*|0.d*[1-9]d*$　//匹配正浮点数 ^-([1-9]d*.d*|0.d*[1-9]d*)$　//匹配负浮点数 ^-?([1-9]d*.d*|0.d*[1-9]d*|0?.0+|0)$　//匹配浮点数 ^[1-9]d*.d*|0.d*[1-9]d*|0?.0+|0$　//匹配非负浮点数（正浮点数 + 0） ^(-([1-9]d*.d*|0.d*[1-9]d*))|0?.0+|0$　//匹配非正浮点数（负浮点数 + 0） 评注：处理大量数据时有用，具体应用时注意修正\n25、匹配特定字符串： ^[A-Za-z]+$　//匹配由26个英文字母组成的字符串 ^[A-Z]+$　//匹配由26个英文字母的大写组成的字符串 ^[a-z]+$　//匹配由26个英文字母的小写组成的字符串 ^[A-Za-z0-9]+$　//匹配由数字和26个英文字母组成的字符串 ^w+$　//匹配由数字、26个英文字母或者下划线组成的字符串\n26、 在使用RegularExpressionValidator验证控件时的验证功能及其验证表达式介绍如下: 只能输入数字：“^[0-9]$” 只能输入n位的数字：“^d{n}$” 只能输入至少n位数字：“^d{n,}$” 只能输入m-n位的数字：“^d{m,n}$” 只能输入零和非零开头的数字：“^(0|[1-9][0-9])$” 只能输入有两位小数的正实数：“^[0-9]+(.[0-9]{2})?$” 只能输入有1-3位小数的正实数：“^[0-9]+(.[0-9]{1,3})?$” 只能输入非零的正整数：“^+?[1-9][0-9]$” 只能输入非零的负整数：“^-[1-9][0-9]$” 只能输入长度为3的字符：“^.{3}$” 只能输入由26个英文字母组成的字符串：“^[A-Za-z]+$” 只能输入由26个大写英文字母组成的字符串：“^[A-Z]+$” 只能输入由26个小写英文字母组成的字符串：“^[a-z]+$” 只能输入由数字和26个英文字母组成的字符串：“^[A-Za-z0-9]+$” 只能输入由数字、26个英文字母或者下划线组成的字符串：“^w+$” 验证用户密码:“^[a-zA-Z]w{5,17}$”正确格式为：以字母开头，长度在6-18之间， 只能包含字符、数字和下划线。 验证是否含有^%\u0026amp;\u0026rsquo;,;=?$\u0026ldquo;等字符：“[^%\u0026amp;\u0026rsquo;,;=?$x22]+” 只能输入汉字：“^[u4e00-u9fa5],{0,}$” 验证Email地址：“^w+[-+.]w+)@w+([-.]w+).w+([-.]w+)$” 验证InternetURL：“^http://([w-]+.)+[w-]+(/[w-./?%\u0026amp;=])?$” 验证身份证号（15位或18位数字）：“^d{15}|d{}18$” 验证一年的12个月：“^(0?[1-9]|1[0-2])$”正确格式为：“01”-“09”和“1”“12” 验证一个月的31天：“^((0?[1-9])|((1|2)[0-9])|30|31)$” 正确格式为：“01”“09”和“1”“31”。 匹配中文字符的正则表达式： [u4e00-u9fa5] 匹配双字节字符(包括汉字在内)：[^x00-xff] 匹配空行的正则表达式：n[s| ]r 匹配HTML标记的正则表达式：/\u0026lt;(.)\u0026gt;.|\u0026lt;(.) /\u0026gt;/ 匹配首尾空格的正则表达式：(^s*)|(s*$) 匹配Email地址的正则表达式：w+([-+.]w+)@w+([-.]w+).w+([-.]w+)* 匹配网址URL的正则表达式：http://([w-]+.)+[w-]+(/[w- ./?%\u0026amp;=]*)?\n3. Urllib 3.1 get 1 2 3 4 5 6 7 import urllib.request #1、获取一个get请求 try: responce = urllib.request.urlopen(\u0026#34;http://httpbin.org/get\u0026#34;,timeout=0.01) print(responce.read().decode(\u0026#39;utf-8\u0026#39;)) #对获取到的网页源码进行utf-8解码 except urllib.error.URLError as e: print(\u0026#34;time out\u0026#34;) #超时处理 3.2 post 1 2 3 4 5 6 7 8 9 10 #2、获取一个post请求 import urllib.parse data = bytes(urllib.parse.urlencode({\u0026#34;hello\u0026#34;: \u0026#34;world\u0026#34;}), encoding=\u0026#34;utf-8\u0026#34;) response = urllib.request.urlopen(\u0026#34;http://httpbin.org/post\u0026#34;,data = data) print(response.read().decode(\u0026#34;utf-8\u0026#34;)) responce = urllib.request.urlopen(\u0026#34;http://www.baidu.com\u0026#34;) print(responce.status) #获取状态码 print(responce.getheaders()) #获取全部信息 print(responce.getheader(\u0026#34;Server\u0026#34;)) #获取全部信息 3.3 418 error 1 2 3 4 5 6 7 8 9 #3、post请求，反418 url=\u0026#34;http://httpbin.org/post\u0026#34; headers={ \u0026#34;User-Agent\u0026#34; : \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 Edg/84.0.522.44\u0026#34; } data = bytes(urllib.parse.urlencode({\u0026#34;hello\u0026#34;:\u0026#34;world\u0026#34;}), encoding=\u0026#34;utf-8\u0026#34;) req=urllib.request.Request(url=url, data=data, headers=headers, method=\u0026#34;POST\u0026#34;) response=urllib.request.urlopen(req) print(response.read().decode(\u0026#34;utf-8\u0026#34;)) 3.4 test 1 2 3 4 5 6 7 8 9 #4、爬取豆瓣 url=\u0026#34;https://www.douban.com\u0026#34; headers={ \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36 Edg/84.0.522.44\u0026#34; } data = bytes(urllib.parse.urlencode({\u0026#34;hello\u0026#34;:\u0026#34;world\u0026#34;}), encoding=\u0026#34;utf-8\u0026#34;) req=urllib.request.Request(url=url, headers=headers) response=urllib.request.urlopen(req) print(response.read().decode(\u0026#34;utf-8\u0026#34;)) 4. Xwlt 4.1 new 1 2 3 4 5 6 7 8 9 import xlwt workbook = xlwt.Workbook(encoding=\u0026#34;utf-8\u0026#34;)#创建对象 worksheet = workbook.add_sheet(\u0026#39;sheet1\u0026#39;)#创建工作表 for i in range(1,10): for j in range(1,i+1): worksheet.write(i-1, j-1, \u0026#39;%d*%d=%d\u0026#39;%(i,j,i*j)) # 行,列，内容 workbook.save(\u0026#34;student.xls\u0026#34;) 5. Release 5.1 Top250 爬取豆瓣电影Top250，并将其保存在sql数据库中\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 #-*- coding = utf-8 -*- #@Time : 2020/10/6 21:00 #@Author : chasing #@File : spyder.py #@Software: PyCharm from bs4 import BeautifulSoup # 网页解析，获取数据 import re # 正则表达式，进行文字匹配 import urllib.request import urllib.error # 制定URL，获取网页数据 import xlwt # 进行excel操作 import sqlite3 # 进行SQLite数据库操作 def main(): baseurl = \u0026#34;https://movie.douban.com/top250?start=\u0026#34; # 1.爬取网页 datalist = getData(baseurl) #savepath = \u0026#34;豆瓣电影Top250.xls\u0026#34; dbpath = \u0026#34;movie.db\u0026#34; # 3.保存数据 # saveData(datalist,savepath) saveData2DB(datalist, dbpath) # askURL(\u0026#34;https://movie.douban.com/top250?start=\u0026#34;) # 影片详情链接的规则 findLink = re.compile(r\u0026#39;\u0026lt;a href=\u0026#34;(.*?)\u0026#34;\u0026gt;\u0026#39;) # 创建正则表达式对象，表示规则（字符串的模式） # 影片图片 findImgSrc = re.compile(r\u0026#39;\u0026lt;img.*src=\u0026#34;(.*?)\u0026#34;\u0026#39;, re.S) # re.S 让换行符包含在字符中 # 影片片名 findTitle = re.compile(r\u0026#39;\u0026lt;span class=\u0026#34;title\u0026#34;\u0026gt;(.*)\u0026lt;/span\u0026gt;\u0026#39;) # 影片评分 findRating = re.compile( r\u0026#39;\u0026lt;span class=\u0026#34;rating_num\u0026#34; property=\u0026#34;v:average\u0026#34;\u0026gt;(.*)\u0026lt;/span\u0026gt;\u0026#39;) # 找到评价人数 findJudge = re.compile(r\u0026#39;\u0026lt;span\u0026gt;(\\d*)人评价\u0026lt;/span\u0026gt;\u0026#39;) # 找到概况 findInq = re.compile(r\u0026#39;\u0026lt;span class=\u0026#34;inq\u0026#34;\u0026gt;(.*)\u0026lt;/span\u0026gt;\u0026#39;) # 找到影片的相关内容 findBd = re.compile(r\u0026#39;\u0026lt;p class=\u0026#34;\u0026#34;\u0026gt;(.*?)\u0026lt;/p\u0026gt;\u0026#39;, re.S) # 爬取网页 def getData(baseurl): datalist = [] for i in range(0, 10): # 调用获取页面信息的函数，10次 url = baseurl + str(i*25) html = askURL(url) # 保存获取到的网页源码 # 2.逐一解析数据 soup = BeautifulSoup(html, \u0026#34;html.parser\u0026#34;) for item in soup.find_all(\u0026#39;div\u0026#39;, class_=\u0026#34;item\u0026#34;): #查找符合要求的字符串，形成列表 # print(item) #测试：查看电影item全部信息 data = [] # 保存一部电影的所有信息 item = str(item) # 影片详情的链接 link = re.findall(findLink, item)[0] # re库用来通过正则表达式查找指定的字符串 data.append(link) # 添加链接 imgSrc = re.findall(findImgSrc, item)[0] data.append(imgSrc) # 添加图片 titles = re.findall(findTitle, item) # 片名可能只有一个中文名，没有外国名 if(len(titles) == 2): ctitle = titles[0] # 添加中文名 data.append(ctitle) otitle = titles[1].replace(\u0026#34;/\u0026#34;, \u0026#34;\u0026#34;) # 去掉无关的符号 data.append(otitle) # 添加外国名 else: data.append(titles[0]) data.append(\u0026#39; \u0026#39;) # 外国名字留空 rating = re.findall(findRating, item)[0] data.append(rating) # 添加评分 judgeNum = re.findall(findJudge, item)[0] data.append(judgeNum) # 提加评价人数 inq = re.findall(findInq, item) if len(inq) != 0: inq = inq[0].replace(\u0026#34;。\u0026#34;, \u0026#34;\u0026#34;) # 去掉句号 data.append(inq) # 添加概述 else: data.append(\u0026#34; \u0026#34;) # 留空 bd = re.findall(findBd, item)[0] bd = re.sub(\u0026#39;\u0026lt;br(\\s+)?/\u0026gt;(\\s+)?\u0026#39;, \u0026#34; \u0026#34;, bd) # 去掉\u0026lt;br/\u0026gt; bd = re.sub(\u0026#39;/\u0026#39;, \u0026#34; \u0026#34;, bd) # 替换/ data.append(bd.strip()) # 去掉前后的空格 datalist.append(data) # 把处理好的一部电影信息放入datalist return datalist # 得到指定一个URL的网页内容 def askURL(url): head = { # 模拟浏览器头部信息，向豆瓣服务器发送消息 \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36\u0026#34; } # 用户代理，表示告诉豆瓣服务器，我们是什么类型的机器、浏览器（本质上是告诉浏览器，我们可以接收什么水平的文件内容） request = urllib.request.Request(url, headers=head) html = \u0026#34;\u0026#34; try: response = urllib.request.urlopen(request) html = response.read().decode(\u0026#34;utf-8\u0026#34;) # print(html) except urllib.error.URLError as e: if hasattr(e, \u0026#34;code\u0026#34;): print(e.code) if hasattr(e, \u0026#34;reason\u0026#34;): print(e.reason) return html # 保存数据 def saveData(datalist, savepath): print(\u0026#34;save....\u0026#34;) book = xlwt.Workbook(encoding=\u0026#34;utf-8\u0026#34;, style_compression=0) # 创建workbook对象 sheet = book.add_sheet(\u0026#39;豆瓣电影Top250\u0026#39;, cell_overwrite_ok=True) # 创建工作表 col = (\u0026#34;电影详情链接\u0026#34;, \u0026#34;图片链接\u0026#34;, \u0026#34;影片中文名\u0026#34;, \u0026#34;影片外国名\u0026#34;, \u0026#34;评分\u0026#34;, \u0026#34;评价数\u0026#34;, \u0026#34;概况\u0026#34;, \u0026#34;相关信息\u0026#34;) for i in range(0, 8): sheet.write(0, i, col[i]) # 列名 for i in range(0, 250): print(\u0026#34;第%d条\u0026#34; % (i+1)) data = datalist[i] for j in range(0, 8): sheet.write(i+1, j, data[j]) # 数据 book.save(savepath) # 保存 def saveData2DB(datalist, dbpath): init_db(dbpath) conn = sqlite3.connect(dbpath) cur = conn.cursor() for data in datalist: for index in range(len(data)): if index == 4 or index == 5: continue data[index] = \u0026#39;\u0026#34;\u0026#39;+data[index]+\u0026#39;\u0026#34;\u0026#39; sql = \u0026#39;\u0026#39;\u0026#39; insert into movie250 ( info_link,pic_link,cname,ename,score,rated,instroduction,info) values(%s)\u0026#39;\u0026#39;\u0026#39; % \u0026#34;,\u0026#34;.join(data) print(sql) cur.execute(sql) conn.commit() cur.close() conn.close() def init_db(dbpath): sql = \u0026#39;\u0026#39;\u0026#39; create table movie250 ( id integer primary key autoincrement, info_link text, pic_link text, cname varchar, ename varchar, score numeric , rated numeric , instroduction text, info text ) \u0026#39;\u0026#39;\u0026#39; # 创建数据表 conn = sqlite3.connect(dbpath) cursor = conn.cursor() cursor.execute(sql) conn.commit() conn.close() if __name__ == \u0026#34;__main__\u0026#34;: # 当程序执行时 # 调用函数 main() # init_db(\u0026#34;movietest.db\u0026#34;) print(\u0026#34;爬取完毕！\u0026#34;) 6. CSDN 6.1 for a certain passage 对于一个博主的确切的文章链接，可以使用以下代码爬取其中的文章内容，并保存为pdf格式\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #-*- coding = utf-8 -*- #@Time : 2020-10-12 18:08 #@Author : chasing #@File : csdn.py #@Software: PyCharm import re import requests import parsel import pdfkit BaseUrl=\u0026#39;https://blog.csdn.net/justidle/article/details/106850487\u0026#39; cmp=re.compile(r\u0026#39;\u0026lt;meta name=\u0026#34;keywords\u0026#34; content=\u0026#34;(.*?)\u0026#34;\u0026gt;\u0026#39;, re.S) headers = {\u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 Edg/86.0.622.38\u0026#39;} response = requests.get(BaseUrl, headers=headers) print(\u0026#34;响应体：\u0026#34;+response.text) Title=re.findall(cmp,response.text) FileUrl=\u0026#39;D:\\\\desktop\\\\\u0026#39;+\u0026#39; \u0026#39;.join(Title)+\u0026#39;.pdf\u0026#39; print(\u0026#34;期望保存位置：\u0026#34;+FileUrl) Html1=r\u0026#39;\u0026lt;!doctype html\u0026gt;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt;\u0026lt;title\u0026gt;\u0026#39; Html2=r\u0026#39;\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body\u0026gt;{content}\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#39; html=Html1+\u0026#39; \u0026#39;.join(Title)+Html2 selector = parsel.Selector(response.text) article = selector.css(\u0026#39;article\u0026#39;).get() print(\u0026#34;文章本体：\u0026#34;+article) with open(\u0026#39;1.html\u0026#39;, mode=\u0026#39;w\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(html.format(content=article)) config = pdfkit.configuration(wkhtmltopdf=\u0026#39;E:\\\\wkhtmltopdf\\\\bin\\\\wkhtmltopdf.exe\u0026#39;) pdfkit.from_file(\u0026#34;1.html\u0026#34;,FileUrl,configuration=config) print(\u0026#34;文件保存成功，保存文件的路径为：\u0026#34;+FileUrl) print(\u0026#34;Hello World!\u0026#34;) 6.2 All passages 对于博主的全部文章，可以使用以下方法，获取其所有文章的链接\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #-*- coding = utf-8 -*- #@Time : 2020-10-12 20:09 #@Author : chasing #@File : Release.py #@Software: PyCharm #导入相应的模块 import re import requests import parsel import pdfkit def main(): # Pages=askPages(BaseUrl) # print(Pages) i=1 askUrl(baseUrl, i) for i in range(2,100,1): tempUrl = baseUrl+str(i) askUrl(tempUrl, i) Response() findChinese=re.compile(r\u0026#39;[\\u4e00-\\u9fa5]+\u0026#39;,re.S)#pdfkit不能能保存含有特殊符号名称的文件 findBranch=re.compile(r\u0026#39;\u0026lt;a href=\u0026#34;(.*?)\u0026#34; target=\u0026#34;_blank\u0026#34;\u0026gt;\u0026#39;) findFile=re.compile(r\u0026#39;\u0026lt;meta name=\u0026#34;keywords\u0026#34; content=\u0026#34;(.*?)\u0026#34;\u0026gt;\u0026#39;, re.S) # findPages=re.compile(r\u0026#39;\u0026lt;li data-page=\u0026#34;(\\d+)\u0026#34; class=\u0026#34;ui-pager\u0026#34;\u0026gt;.*?\u0026lt;/li\u0026gt;\u0026#39;,re.S) headers = { \u0026#34;user-agent\u0026#34;:\u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 Edg/86.0.622.38\u0026#34; } baseUrl=\u0026#39;https://blog.csdn.net/qq_27133869\u0026#39; # 页面数量为动态 # def askPages(BaseUrl):#js # response = requests.get(BaseUrl, headers=headers) # print(response.text) # Pages = re.findall(FindPages, response.text) # return Pages def askUrl(baseUrl,i): baseResponse = requests.get(baseUrl, headers=headers) # print(baseResponse.text) branchUrls=re.findall(findBranch,baseResponse.text) assert branchUrls for j in range(7): branchUrls.pop()#删除帮助文档 assert branchUrls#为空直接跳出，节省资源 # print(branchUrls) times=1 for BranchUrl in branchUrls: branchResponse = requests.get(BranchUrl, headers=headers) #保存的文件名 tempTitle = \u0026#39;\u0026#39;.join(re.findall(findFile, branchResponse.text)) print(tempTitle) print(re.findall(findChinese, tempTitle)) finalTitle = \u0026#34; \u0026#34;.join(re.findall(findChinese, tempTitle)) print(\u0026#34;第 %d 面的 %d 篇文章名为:\u0026#34; %(i, times)+str(finalTitle)+\u0026#39;\\n\u0026#39;) #最终网页源代码 finalHtml =r\u0026#39;\u0026lt;!doctype html\u0026gt;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt;\u0026lt;title\u0026gt;\u0026#39; + \u0026#39; \u0026#39;.join(finalTitle) + r\u0026#39;\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body\u0026gt;{content}\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#39; #pdf文件的保存位置 fileUrl = \u0026#39;D:\\\\desktop\\\\CSDN\\\\littlePING\\\\\u0026#39; + \u0026#39;\u0026#39;.join(finalTitle) + \u0026#39;.pdf\u0026#39; print(\u0026#34;第 %d 面的 %d 篇文章保存路径为:\u0026#34; %(i, times) + str(fileUrl) + \u0026#39;\\n\u0026#39;) selector = parsel.Selector(branchResponse.text) article = selector.css(\u0026#39;article\u0026#39;).get() #文件保存的位置 with open(\u0026#39;temp.html\u0026#39;, mode=\u0026#39;w\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: f.write(finalHtml.format(content=article)) try: config = pdfkit.configuration(wkhtmltopdf=\u0026#39;E:\\\\wkhtmltopdf\\\\bin\\\\wkhtmltopdf.exe\u0026#39;) # config = pdfkit.configuration(wkhtmltopdf=path_wk) with open(\u0026#39;temp.html\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: pdfkit.from_file(f, fileUrl, configuration=config) print(\u0026#34;第 %d 面的 %d 篇文章pdf文件保存成功！\u0026#34; % (i, times)+\u0026#39;\\n\u0026#39;) except: print(\u0026#34;第 %d 面的 %d 篇文章pdf文件保存失败！\u0026#34; % (i, times)+\u0026#39;\\n\u0026#39;) pass # continue times+=1 print(\u0026#34;页面 %d 中的所有文件保存成功\u0026#34;%i+\u0026#39;\\n\u0026#39;) def Response(): print(\u0026#34;Hello World\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: # 当程序执行时 # 调用函数 main() ","permalink":"https://chasing1020.github.io/post/crawler-note/","summary":"\u003ch1 id=\"网页爬虫项目实战经验\"\u003e网页爬虫项目实战经验\u003c/h1\u003e\n\u003cp\u003e此篇记录了网页爬虫的基本使用库，以及常用的正则表达式操作等。\n同时给出了豆瓣电影top250的爬取方式（经典爬虫入门项目）。\n以及对于CSDN的博主主页单个以及多个文章的爬取，并利用工具将其转换为pdf格式\u003c/p\u003e","title":"Crawler Note"},{"content":"第一章——基础知识 1、模块的引入 1 2 3 4 5 6 7 8 9 10 11 12 #demo： # 随机数 #导入import 模块 import random a=random.randint(1,5) #a最终被赋值为1，2，3，4，5之间的随机一个数，左闭右闭 #这里包括1和5！ 2、数据类型 1 2 3 4 5 6 7 8 9 10 11 #type() 获取信息 #例如 a=\u0026#39;520.0\u0026#39; b=float(a) type(a)#输出\u0026lt;class \u0026#39;str\u0026#39;\u0026gt; type(b)#输出\u0026lt;class \u0026#39;float\u0026#39;\u0026gt; #函数isinstance(var,class) #对比前后类型 isinstance(10,int)#输出True 强制类型转换\n不同于C/C++的(int)a和(double)b\npy中的强制类型转换的括号框住表达式\n改为 int(a)和float(b)\n3、符号运算 + 加 - 减 * 乘 / 除，会自动转换为浮点类型，与C/C++不同 % 取余 ** 幂运算，等同于计算器中的^，结果为浮点型或整形，根据结果而定 // 带余除法，就算是浮点型运算也会保留整数部分，例如3.0//2=1.0 4、逻辑运算 and 与 or 或 not 非 三目运算符\na= 语句1 if 条件 else 语句2 例如:\n1 2 a=x if x\u0026lt;y else y #a赋值为x,y中小的那个 5、其余函数 assert: 同C/C++的asset(断言)\n同C/C++一样 assert 1\u0026gt;2 结果为False，程序直接终止 第二章——循环 1、for循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #for循环 name = \u0026#34;chasing\u0026#34; a = [\u0026#34;aa\u0026#34;, \u0026#34;bb\u0026#34;, \u0026#34;cc\u0026#34;, \u0026#34;dd\u0026#34;] for i in range(5): #5表示结束，[0,5)，左闭右开 print(i,end=\u0026#34;\u0026#34;)#end=\u0026#34;\u0026#34;表示不换行，在循环结束以后不执行任何操作 #输出01234 for i in range(1, 10): #从1到10，[1,10)，但是不会包括10，左闭右开 print(i,end=\u0026#34;\u0026#34;) #输出123456789 for i in range(0, 100, 10):#for(int i=0;i\u0026lt;100;i+=10) #从1到100，每次i增加10，但是不会包括100，直到100跳出循环，左闭右开 print(i,end=\u0026#34;，\u0026#34;) #输出0，10，20，30，40，50，60，70，80，90， for i in name: #遍历字符串 print(i,end=\u0026#34;\u0026#34;) #输出chasing for i in range(len(a)): #遍历每一个列表 print(a[i], end=\u0026#34;,\u0026#34;) #输出aa,bb,cc,dd, 2、while循环 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #while循环 count=0 while count\u0026lt;5: print(count,\u0026#34;小于5\u0026#34;) count+=1 if count\u0026gt;3: break else:#只要进入while循环，正常出来，或者没有进入while循环，则else写不写都一样 #触发else 的唯一情况就是while里面有break且被执行，for同理 print(count,\u0026#34;大于或等于5\u0026#34;) 3、break和continue break 跳出循环 continue 跳过当前循环 pass 占位语句，相当于C/C++的;（空语句）， 不做任何事情 样例1\n1 2 3 4 5 6 7 8 9 10 11 #输出2019到2100第一个闰年 for i in range(2019,2100): if (i%4==0) and (i%100!=0) or (i%400==0): break print(\u0026#34;2019-2100第一个闰年是%d\u0026#34;%i) #2019-2100第一个闰年是2020 样例2\n1 2 3 4 5 6 7 8 9 10 #打印99乘法表 for i in range(1,10): for j in range(1,i+1): print(\u0026#34;%d*%d=%d\u0026#34;%(i,j,i*j),end=\u0026#34; \u0026#34;) print(\u0026#34;\\n\u0026#34;) 第三章——字符串 注意，python3默认是utf-8编码。字符串都是unicode字符串。\n字符串可以使用单引号，双引号，三引号（三个单引号，三个双引号）\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 word=\u0026#39;zifu\u0026#39; sentence =\u0026#34;juzi\u0026#34; paragraph=\u0026#34;\u0026#34;\u0026#34; duan luo \u0026#34;\u0026#34;\u0026#34; #\u0026#34;\u0026#34;\u0026#34; \u0026#34;\u0026#34;\u0026#34;保存原始的所有格式 print(word) print(sentence) print(paragraph) 双引号\u0026quot;\u0026ldquo;里面没有转义字符，但是可以使用 \u0026lsquo;，即str=\u0026ldquo;I\u0026rsquo;m a boy\u0026rdquo;，这里的\u0026rsquo;m不会报错\n而使用单引号，则需要str =\u0026lsquo;I\\\u0026rsquo;m a boy\u0026rsquo;\n1、截取、切片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 my_str=\u0026#34;shu_chasing\u0026#34; print(my_str) print(my_str[0:3])#输出shu print(my_str[1:11:2])#输出h_hsn #[起始位置:结束位置:步进值]，这里也是不包括11在内 print(my_str[6:])#输出从第6个到结尾，即asing，这里不包括第6个 print(my_str[:6])#输出从第一个到第6个，即shu_ch print(my_str+\u0026#34;,nihao\u0026#34;)#输出shu_chasing,nihao print(my_str*3)#输出shu_chasingshu_chasingshu_chasing 2、转义字符 1 2 3 4 5 6 7 8 9 10 #使用、实现转义字符的功能 mystring=r\u0026#39;c:\\now\u0026#39; print(mystring) #输出就是c:\\now，去掉前面的r则会出现换行 #在字符串前面加上r表示原始字符串，不会翻译转义字符 #字符串不能以\\结尾 3、字符串常用函数 1.大写转小写casefold 1 2 3 str=\u0026#39;SHU_chasing\u0026#39; str=str.casefold() print(str)#shu_chasing 2.第一个转大写capitalize 1 2 3 str=\u0026#39;shu_chasing\u0026#39; str=str.casefold() print(str)#Shu_chasing 3.查找find 函数原型find(sub[,start[,end]]) 可以选择范围 也可以不写范围搜索全部\n1 2 3 str=\u0026#39;shu_chasing\u0026#39; print(str.find(\u0026#39;s\u0026#39;)) #输出0 print(str.find(\u0026#39;s\u0026#39;, 3 ,10)) #7 4.统计数目count 函数原型count(sub[,start[,end]]) 可以选择范围 也可以不写范围搜索全部\n1 2 3 str=\u0026#39;shu_chasing\u0026#39; print(str.count(\u0026#39;s\u0026#39;)) #输出2 print(str.count(\u0026#39;s\u0026#39;, 3 ,10)) #输出1 5.替换replace 函数原型replace(old, new[,count]) 将old的字符串转换为指定的字符串\n1 2 3 str=\u0026#39;I love math\u0026#39; str=str.replace(\u0026#39;math\u0026#39;,\u0026#39;programming\u0026#39;) print(str) #输出I love programming 6.拆分split split(sep=None, maxsplit=-1)\n1 2 3 str=\u0026#39;D:\\software\\python\u0026#39; str=str.split(sep=\u0026#39;\\\\\u0026#39;) #注意这里是两个\\\\ print(str) #输出[\u0026#39;D:\u0026#39;, \u0026#39;software\u0026#39;, \u0026#39;python\u0026#39;]，转为列表类型 7.拼接join 不能写成str.join(\u0026rsquo;\\\u0026rsquo;) join被指定为字符串其中的一个用法 join的参数支持一切可以迭代的对象（列表，元组，字典，文件，集合，生成器） 推荐使用join替代加号拼接 +会频繁进行内存复制和触发垃圾回收机制\n1 2 3 4 5 6 str=[\u0026#39;D:\u0026#39;, \u0026#39;software\u0026#39;, \u0026#39;python\u0026#39;] str=\u0026#39;\\\\\u0026#39;.join(str) #转义字符 print(str) #D:\\software\\python str=[\u0026#39;D:\u0026#39;, \u0026#39;software\u0026#39;, \u0026#39;python\u0026#39;] str=\u0026#39;\u0026#39;.join(str) print(str) #输出D:softwarepython 第四章——列表list[ ] 1.非常类似于数组 2.可以同时使用字符串，整形，浮点型等等 3.可以为负数下标 4.可以嵌套，类似二维数组 1、遍历列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #namelist=[] #定义空列表 namelist=[\u0026#34;xiaozhang\u0026#34;,\u0026#34;2.3\u0026#34;,\u0026#34;10\u0026#34;]#可以为不同类型的变量，储存混合类型 print(namelist[0]) print(namelist[1]) print(namelist[2]) for i in range(0,3): print(namelist[i]) for i in range(-2,0):#输出最后两个元素 print(namelist[i]) for i in namelist: print(i) print(len(namelist))#输出长度 i=0 while i\u0026lt;len(namelist): print(namelist[i]) i+=1#不能写i++ 2、数据操作 1. append增加 1 2 3 4 5 6 7 8 9 10 11 12 13 14 namelist=[\u0026#34;xiaozhang\u0026#34;,\u0026#34;2.3\u0026#34;,\u0026#34;10\u0026#34;]#可以为不同类型的变量，储存混合类型 for i in namelist: print(i) #增加 nametemp=input(\u0026#34;请输入添加的数据\u0026#34;) namelist.append(nametemp) for i in namelist: print(i) #输出结果多了append的内容[\u0026#34;xiaozhang\u0026#34;,\u0026#34;2.3\u0026#34;,\u0026#34;10\u0026#34;, ____] #特别的，使用乘法 a=[1] a=a*3 #最后a就是[1, 1, 1]\n2. append和extend区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #append和extend的区别 a=[1,2] b=[3,4] a.append(b) print(a) #结果为[1,2,[3,4]]，将一个列表作为整个整体，加入列表中 a.extend(b) print(a) #结果为[1,2,[3,4],3,4]，将b列表中的每一个，逐一加入列表中 3. insert用法 1 2 3 4 5 6 7 #insert用法 a=[0,1,2] a.insert(1,200)#第一个表示下标，第二个表示元素 print(a)#结果为0,200,1,2 4. del删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #del删除 a=[0,10,20,30,10,40,50]#可以出现重复数据 print(a) del a[2]#删除指定下标的元素 print(a)#结果为[0, 10, 30, 10, 40, 50] a.pop()#弹出末尾最后一个元素 print(a)#结果为[0, 10, 30, 10, 40] a.remove(10)#删除第一个值为10的元素 print(a)#结果为[0, 30, 10, 40] #[]修改 b=[\u0026#34;chasing\u0026#34;] b[0]=\u0026#34;shu_chasing\u0026#34;#直接修改就可以了 5. index查找与count计数 1 2 3 4 5 6 7 8 9 10 11 12 a=[0,10,20,30,10,40,50] findname=int(input(\u0026#34;请输入想找的元素\u0026#34;))#记得修改为整数型 if findname in a: print(\u0026#34;找到了一样的元素\u0026#34;) else: print(\u0026#34;没有找到相同的元素\u0026#34;) #index查找 a=[0,10,20,30,10,40,50] print(a.index(10,1,4))#返回下标1，在1-3范围内查找元素10，这个范围左闭右开——[1,4) print(a.index(10,1,7))#返回第一个出现的下标1，在1-6范围内查找元素10，这个范围左闭右开——[1,7) #count计数 print(a.count(10))#统计查找的元素出现了几次 list.index(item) #从头到尾找\nlist.index(item, start, end) #在开始和结束之间找\n6.reverse反转和sort排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #反转和排序 a=[1,4,2,3] print(a)#输出[1, 4, 2, 3] a.reverse() print(a)#输出[3, 2, 4, 1] a.sort() print(a)#输出[1, 2, 3, 4] a.sort(reverse=True) print(a)#输出[4, 3, 2, 1] 3、列表的嵌套 类似于二维数组\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 a=[[10,20],[40,50],[30,60,70]] print(a[0])#输出[10, 20] print(a[1][0])#输出40 #test import random offices=[[],[],[]] names=[1,2,3,4,5] for name in names: index= random.randint(0,2) offices[index].append(name) i=1 for office in offices: print(\u0026#34;办公室%d的人数是：%d\u0026#34;%(i,len(office))) i+=1 for name in office: print(\u0026#34;%s\u0026#34;%name,end=\u0026#39;t\u0026#39;) print(\u0026#34;n\u0026#34;) #以下是随机结果 #办公室1的人数是：0 #办公室2的人数是：2 #1 #5 #办公室3的人数是：3 #2 #3 #4 sort默认是归并排序 实际上, sort有三个参数 sort(func, key, reverse)\n第五章——元组tuple( ) 1、创建 tuple(元组)\n与list类似，不同处在于tuple不能修改元素， 写在小括号里，元素之间用逗号隔开 元素不可变，但是包含可变对象 1 2 3 4 5 6 t1=(2.33,\u0026#39;abcd\u0026#39;,786,) t2=(1,) t3=(\u0026#39;a\u0026#39;,\u0026#39;ab\u0026#39;,[\u0026#39;a\u0026#39;,2.3]) print(t1) #(2.33, \u0026#39;abcd\u0026#39;, 786) print(t2) #(1,) print(t3[2][1]) #2.3 如果创建空的元组\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 tup1=() #空的元组 tup2=(50) #整形 tup3=(100,) #含有一个元素的元组，如果只有一个，则需要加逗号,在这里也可以写tup3=100, #可以不需要小括号，但是一定要逗号 tup4=(150,200) #含有多个元素的元组 print(type(tup1)) print(tup2) print(type(tup2)) print(tup3) print(type(tup3)) print(tup4) print(type(tup4)) #\u0026lt;class \u0026#39;tuple\u0026#39;\u0026gt; #50 #\u0026lt;class \u0026#39;int\u0026#39;\u0026gt;，这里不加，就是整形 #(100,) #\u0026lt;class \u0026#39;tuple\u0026#39;\u0026gt; #(150, 200) #\u0026lt;class \u0026#39;tuple\u0026#39;\u0026gt; 2、遍历与切片 1 2 3 4 5 6 7 tup1=(\u0026#34;abc\u0026#34;,10,23.4) print(tup1) #输出(\u0026#39;abc\u0026#39;, 10, 23.4) print(tup1[-1]) #输出23.4 print(tup1[0:2])# 输出(\u0026#39;abc\u0026#39;, 10)，这里只有[0,2)，即[0,1]，左闭右开 3、增加 1 2 3 tup1=(12,34,56) #tup[1]=100 #报错，不能修改tup的值，\u0026#39;tuple\u0026#39; object does not support item assignment 不能“新增” 只能设置新的变量 分配新的空间 1 2 3 4 tup1=(12,34,56) tup2=(\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;) tup3=tup1+tup2 print(tup3)#(12, 34, 56, \u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;) 4、删除 1 2 3 4 5 6 7 8 9 tup1=(12,34,56) print(\u0026#34;删除前\u0026#34;) print(tup1) del tup1#删除了整个元组变量，可以完成，以后不能访问tup1 #print(\u0026#34;删除后\u0026#34;) #print(tup1) #报错，不能访问tup1的内容，name \u0026#39;tup1\u0026#39; is not defined #del tup[1] #报错，不能删除某一个下标，\u0026#39;tuple\u0026#39; object doesn\u0026#39;t support item deletion 5、基本操作 操作名称 操作方法 举例 元素成员关系 in 2 in list1 得到重复元素数量 count tup1.count(1) 操作名称 操作方法 举例 访问元组中的元素 通过下标直接访问 print(tup[10]) 遍历元组 通过for循环 for i in tup: print(i) 元组的切片 使用[ : : ] tup[2:10:1]，[::-1]表示倒序 元组的加法操作 + tup3=tup1+tup2 6、基本函数 函数名称 操作方法 备注 获取数组长度 len() len(tup)-1就是最后一个元素的下标 获取元组元素最大值 max() 获取元组元素最小值 min() 强制类型转换 tuple() 获取随机元素 choice() 加上import random 模块 7、字符串操作 *同元组一样，字符串也是不能被修改的 如果必须修改，则需要使用切片和拼接\n1 2 3 4 5 str=\u0026#34;cgasing\u0026#34; str=str[:1]+\u0026#34;h\u0026#34;+str[2:]#修改第二个 print(str)#输出chasing 第六章——序列 1、总结 列表、元组、字符串的共同点\n可以通过索引得到每一个元素 默认索引从0开始 可以切片得到范围内的集合 有共同操作符（重复、拼接、成员） 以上统称为——序列\n2、操作函数 函数名 作用 示例 list([iterable]) 强制将可迭代对象转换为列表 a=list((1,2,3)) tuple([iterable]) 强制将可迭代对象转换为元组 a=tuple((1,2,3)) str(obj) 强制将对象转换为字符串 a=str(10) len(sub) 返回sub参数长度 len(str1) max() 返回参数集合最大值 max(list) min() 返回参数集合最小值（保证数据类型统一） min(list) sum(iterable[, start]) 返回所有元素值的总和 sum(list,5) sorted(iterable,key=None,reverse=False) 返回排序后的列表 sorted(list) reversed(sequence) 返回逆向迭代序列的结果 reversed(list) enumerate(iterable) 生成一个二元组构成的一个迭代对象 见下文 zip(iter1[,iter2[\u0026hellip;]]) 返回各个可迭代参数共同组成的元组 见下文 enumerate用法，可以单独理解为枚举\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #enumerate str=\u0026#39;chasing\u0026#39; for i in enumerate(str): print(i) for i,j in enumerate(str): print(i,j) #(0, \u0026#39;c\u0026#39;) #(1, \u0026#39;h\u0026#39;) #(2, \u0026#39;a\u0026#39;) #(3, \u0026#39;s\u0026#39;) #(4, \u0026#39;i\u0026#39;) #(5, \u0026#39;n\u0026#39;) #(6, \u0026#39;g\u0026#39;) # 0 c # 1 h # 2 a # 3 s # 4 i # 5 n # 6 g zip用法\n1 2 3 4 5 6 7 8 9 10 list1=[1,7,3,5,6] str1=\u0026#34;chasing\u0026#34; turple1=(2,4,6,8,10) for i in zip(list1, str1, turple1): print(i) #(1, \u0026#39;c\u0026#39;, 2) #(7, \u0026#39;h\u0026#39;, 4) #(3, \u0026#39;a\u0026#39;, 6) #(5, \u0026#39;s\u0026#39;, 8) #(6, \u0026#39;i\u0026#39;, 10) 第七章——字典 { } 1、字典dict定义 字典，即散列表\n是无序对象的集合，使用键-值(key-value)存储，具有极快的速度\n查找，插入、删除都为O(1)复杂度\n键(key)必须使用不可变类型 同一个字典的key是唯一的 类似于json的对象，C++的map，数据结构的红黑树，哈希表\n1 2 3 4 5 6 7 8 9 10 11 12 #字典的定义 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;18\u0026#34;} print(info[\u0026#34;name\u0026#34;]) #输出chasing print(info[\u0026#34;age\u0026#34;]) #输出18 #print(info[\u0026#34;gender\u0026#34;]) #错误，KeyError: \u0026#39;gender\u0026#39; #直接访问 print(info.get(\u0026#34;gender\u0026#34;)) #输出None，没有找到默认返回None print(info.get(\u0026#34;gender\u0026#34;,\u0026#34;not found\u0026#34;)) #修改默认的值 #输出not found，没有找到返回not found 2、添加 1 2 3 4 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;18\u0026#34;} newID=input(\u0026#34;请输入新的学号：\u0026#34;) info[\u0026#34;ID\u0026#34;]=newID print(info[\u0026#34;ID\u0026#34;]) 3、删除 1 2 3 4 5 6 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;18\u0026#34;} print(\u0026#34;删除前：%s\u0026#34;%info[\u0026#34;name\u0026#34;]) del info[\u0026#34;name\u0026#34;] #使用del info则删除整个字典 #print(\u0026#34;删除后：%s\u0026#34;%info[\u0026#34;name\u0026#34;])#删除后键值对后，再次访问就会报错 #报错KeyError \u0026#39;name\u0026#39; 4、清除 1 2 3 4 5 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;19\u0026#34;} print(\u0026#34;删除前：%s\u0026#34;%info) #删除前：{\u0026#39;name\u0026#39;: \u0026#39;chasing\u0026#39;, \u0026#39;age\u0026#39;: \u0026#39;18\u0026#39;} info.clear() #只是清空 print(\u0026#34;删除后：%s\u0026#34;%info) #删除后：{} 5、修改 1 2 3 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;19\u0026#34;} info[\u0026#34;age\u0026#34;]=20 print(info[\u0026#34;age\u0026#34;]) #输出20 6、查找 1 2 3 4 5 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;18\u0026#34;,\u0026#34;id\u0026#34;:19120397} print(info.keys()) #得到所有的键dict_keys([\u0026#39;name\u0026#39;, \u0026#39;age\u0026#39;, \u0026#39;id\u0026#39;]) print(info.values()) #得到所有的值dict_values([\u0026#39;chasing\u0026#39;, \u0026#39;18\u0026#39;, 19120397]) print(info.items()) #得到所有的项dict_items([(\u0026#39;name\u0026#39;, \u0026#39;chasing\u0026#39;), (\u0026#39;age\u0026#39;, \u0026#39;18\u0026#39;), (\u0026#39;id\u0026#39;, 19120397)]) #每个键值对都是元组 7、遍历所有的键、值、项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 info = {\u0026#34;name\u0026#34;:\u0026#34;chasing\u0026#34;,\u0026#34;age\u0026#34;:\u0026#34;18\u0026#34;,\u0026#34;id\u0026#34;:19120397} for i in info.keys(): print(i) for i in info.values(): print(i) for i,j in info.iems(): print(\u0026#34;%s\\t%s\u0026#34;%(i,j)) # name # age # id # chasing # 18 # 19120397 # name chasing # age 18 # id 19120397 第八章——集合 { } 1、定义 set和dict类似，也是key的集合，但是不储存value 同C++一样，set不能存相同的元素，重复元素自动过滤 但是不同的是，set是无序的 操作方法与其他相同 for、update、add、remove、pop、clear、del等等\n1 2 3 4 s=set([1,2,3]) print(s) #输出{1, 2, 3} s=set([1,2,3,2,2,1,1,1,3,1,3,3]) print(s) #输出{1, 2, 3} 2、小结 是否有序 是否可变类型 列表[] 有序 可变类型 元组() 有序 不可变类型 字典{} 无序 key不可变，value可变 集合{} 无序 可变类型，不重复 1 2 3 4 5 6 7 8 9 10 11 12 a = set(\u0026#39;abracadabra\u0026#39;) b = set(\u0026#39;alacazam\u0026#39;) print(a) print(a - b) # a 和 b 的差集 print(a | b) # a 和 b 的并集 print(a \u0026amp; b) # a 和 b 的交集 print(a ^ b) # a 和 b 中不同时存在的元素 第九章——函数 提高编码效率，减少重复\n1、定义 定义方式： 1 2 3 def func(): code return val demo： 1 2 3 def printname(): print(\u0026#34;chasing\u0026#34;) printname() #输出chasing 2、带参数函数 1 2 3 4 def add(a,b): c=a+b print(c) add(1, 2) #输出3 3、带返回值函数 返回单个值 1 2 3 4 def add(a,b): c=a+b return c print(add(1,2)) #输出3 返回多个值 和lua相同 1 2 3 4 5 6 def divid(a,b): shang=a//b yushu=a%b return shang,yushu #多个返回值用逗号分隔 sh,yu=devid(5,2) print(\u0026#34;商：%d，余数：%d\u0026#34;%(sh,yu)) #输出商：2，余数：1 局部变量和全局变量 \u0026lt;1\u0026gt;在函数内的作为局部变量 \u0026lt;2\u0026gt;在函数外定义的为全局变量 1 2 3 4 5 6 7 8 9 10 11 12 13 a=5 def test1(): a=10 #优先使用全局变量 print(a) a=200 print(a) def test2(): print(a) test1() test2() # 10 # 200 # 5 在函数中的使用全局变量 1 2 3 4 5 6 7 8 9 10 11 12 13 a=5 def test1(): global a=10 #添加global print(a) a=200 print(a) def test2(): print(a) test1() test2() # 5 # 200 # 200 第十章——拷贝 1、元组默认深拷贝 直接赋值：其实就是对象的引用（别名）。 浅拷贝(copy)：拷贝父对象，不会拷贝对象的内部的子对象。 深拷贝(deepcopy)： copy 模块的 deepcopy 方法，完全拷贝了父对象及其子对象。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import copy tuple1 = (1, 2, 3) tuple2 = tuple(tuple1) print(tuple2) print(\u0026#34;tuple1==tuple2 ?\u0026#34;,tuple1==tuple2) print(\u0026#34;tuple1 is tuple2 ?\u0026#34;,tuple1 is tuple2) tuple3 = copy.copy(tuple1) print(tuple3) print(\u0026#34;tuple1==tuple3 ?\u0026#34;,tuple1==tuple3) print(\u0026#34;tuple1 is tuple3 ?\u0026#34;,tuple1 is tuple3) tuple4 = tuple1[:] print(tuple4) print(\u0026#34;tuple1==tuple4 ?\u0026#34;,tuple1==tuple4) print(\u0026#34;tuple1 is tuple4 ?\u0026#34;,tuple1 is tuple4) #(1, 2, 3) #tuple1==tuple2 ? True #tuple1 is tuple2 ? True #(1, 2, 3) #tuple1==tuple3 ? True #tuple1 is tuple3 ? True #(1, 2, 3) #tuple1==tuple4 ? True #tuple1 is tuple4 ? True 2、地址类型的传递 Python中对象的赋值都是进行对象引用（内存地址）传递 使用copy.copy()，可以进行对象的浅拷贝，它复制了对象，但对于对象中的元素，依然使用原始的引用. 如果需要复制一个容器对象，以及它里面的所有元素（包含元素的子元素），可以使用copy.deepcopy()进行深拷贝 对于非容器类型（如数字、字符串、和其他\u0026rsquo;原子\u0026rsquo;类型的对象）没有被拷贝一说\n","permalink":"https://chasing1020.github.io/post/python-note/","summary":"\u003ch1 id=\"第一章基础知识\"\u003e第一章——基础知识\u003c/h1\u003e\n\u003ch2 id=\"1模块的引入\"\u003e1、模块的引入\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#demo：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# 随机数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#导入import 模块\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003erandom\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003erandom\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erandint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#a最终被赋值为1，2，3，4，5之间的随机一个数，左闭右闭\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#这里包括1和5！\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"2数据类型\"\u003e2、数据类型\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#type() 获取信息\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#例如\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;520.0\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#输出\u0026lt;class \u0026#39;str\u0026#39;\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#输出\u0026lt;class \u0026#39;float\u0026#39;\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#函数isinstance(var,class)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e#对比前后类型\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eisinstance\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"c1\"\u003e#输出True\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003chr\u003e\n\u003cp\u003e\u003cem\u003e强制类型转换\u003c/em\u003e\u003c/p\u003e","title":"Python Note"},{"content":" The word \u0026ldquo;Chasing\u0026rdquo; means \u0026ldquo;衣带渐宽终不悔，为伊消得人憔悴\u0026rdquo;.\nThe suffix \u0026ldquo;1020\u0026rdquo; is used only to avoid duplication of names, and October 20th is my birthday.\nAbout me 🎓 I am a Ph.D. candidate in Computer Science and Technology at the Institute of Computing Technology, Chinese Academy of Sciences (since 2023), co-advised by Xiaohui Peng and Zhiwei Xu. Prior to that, I received my B.Eng. in Computer Science and Technology from Shanghai University (2019 — 2023).\n🔭 My research focuses on distributed systems and machine learning systems. I am currently working on training and inference optimization for LLMs, with a particular focus on RL systems for Agentic AI.\n🤝 I am seeking research collaborations and research internship opportunities — feel free to reach out!\n🛞 I used to be obsessed with reinventing wheels and building systems from scratch — even though, in the age of vibe coding, those skills may no longer seem to matter. Still, I firmly believe that a deep understanding of systems remains essential.\n🎸 Introverted — a bit awkward around strangers, but talkative with people I know well.\n🕊️ I value freedom and dislike being constrained.\n🛠️ Languages sorted by preference: Golang \u0026gt; Rust \u0026gt; Scala \u0026gt; Python \u0026gt; Java. I have a soft spot for strongly-typed languages — especially Rust — and I believe designing a good type system is an art.\n🐧 Arch Linux user | 🦉 Night owl | 🎨 ACG fan | 🍺 IP location: Gensokyo\nContact 💬 You can contact me via:\nQQ: 643601464 WeChat: zjc643601464 Mail: chasing1020@gmail.com Telegram: Chasing1020 Link 🔗 I would appreciate it if you could add my blog to your links.\n🤩 Feel free to send me your link through any of the contacts above. An example is shown below.\n1 2 3 4 name: \u0026#34;Chasing1020\u0026#34; url: \u0026#34;https://chasing1020.github.io\u0026#34; avatar: \u0026#34;https://avatars.githubusercontent.com/chasing1020\u0026#34; description: \u0026#34;Why there is a universe?\u0026#34; ","permalink":"https://chasing1020.github.io/about/","summary":"\u003cblockquote\u003e\n\u003cp\u003eThe word \u0026ldquo;Chasing\u0026rdquo; means \u003ca href=\"https://zh.wikipedia.org/wiki/%E4%BA%BA%E9%97%B4%E8%AF%8D%E8%AF%9D#%E7%8E%8B%E5%9B%BD%E7%BB%B4%E7%BE%8E%E5%AD%A6\"\u003e\u0026ldquo;衣带渐宽终不悔，为伊消得人憔悴\u0026rdquo;\u003c/a\u003e.\u003cbr\u003e\nThe suffix \u0026ldquo;1020\u0026rdquo; is used only to avoid duplication of names, and \u003ca href=\"https://en.wikipedia.org/wiki/October_20\"\u003eOctober 20th\u003c/a\u003e is my birthday.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"about-me\"\u003eAbout me\u003c/h2\u003e\n\u003cp\u003e🎓 I am a Ph.D. candidate in Computer Science and Technology at the \u003ca href=\"http://www.ict.cas.cn/\"\u003eInstitute of Computing Technology, Chinese Academy of Sciences\u003c/a\u003e (since 2023), co-advised by \u003ca href=\"https://ictxh.github.io/\"\u003eXiaohui Peng\u003c/a\u003e and \u003ca href=\"https://novel.ict.ac.cn/zxu/\"\u003eZhiwei Xu\u003c/a\u003e. Prior to that, I received my B.Eng. in Computer Science and Technology from \u003ca href=\"https://www.shu.edu.cn/\"\u003eShanghai University\u003c/a\u003e (2019 — 2023).\u003c/p\u003e\n\u003cp\u003e🔭 My research focuses on distributed systems and machine learning systems. I am currently working on training and inference optimization for LLMs, with a particular focus on \u003cstrong\u003eRL systems for Agentic AI\u003c/strong\u003e.\u003c/p\u003e","title":"About"}]