处理器体系结构
前言了解了程序在机器级的表示方式和运作原理后,我们将进一步的研究处理器是如何实现指令集架构的,我们将从硬件电路开始,研究指令集架构在 cpu 内部是如何实现的。但是现在现代微处理器的体系结构是如此的精细复杂,可以称得上是人类创造出的最复杂的系统之一了,如果我们从相对熟悉的 x86-64 架构开始实现的话,可能会花上相当长的一段时间,所以我们将会实现一个受 x86-64 启发的架构,Y86-64,它是 x86-64 架构的精简版,可以让我们在实现上省去一些复杂精细的结构,在总体上对 cpu 的体系结构有个大致的了解。
Y86-64定义一个指令集体系架构(例如 Y86-64)包括定义各种状态单元、指令集和它们得编码、一组编程规范和异常事件处理体系。
状态在 Y86-64 中有几个可见状态,分别为:
RF:程序寄存器
CC:条件码
PC:程序计数器
Stat:程序状态
DMEM:内存
RF 中有 15 个程序寄存器:%rax、%rcx、%rdx、%rbx、%rsp、%rbp、%rsi、%rdi 和%r8 到%r14(省略了 x86-64 中的%r15 来简化指令编码)每个寄存器存储一个 ...
后端开发基础(三)- maven
前言在软件开发中,一个项目的依赖管理,项目结构以及项目构建流程对于不同的开发者来说可能有一套属于自己的方式,对于独立开发来说这不是什么大问题,但是一个人的力量往往是有限的,所以我们不可避免的要进行团队协作,如果每个开发者都坚守一套自己的规则,那是既不利于团队开发的。而 maven 就为我们提供了一套很好的解决方案,maven 是 apache 开源基金会的一个项目,它为 java 语言提供了一个很好的第三方依赖管理以及项目管理的解决方案。对于其他编程语言都有自己的第三方依赖管理,比如 python 的 pip,js 的 npm 等,这些依赖管理工具能让我们站在巨人的肩膀上生产出更好的作品来回馈社区。
maven 项目结构123456789101112项目文件夹├── src 源文件│ ├── main│ │ ├── java 主程序│ │ └── resources 静态资源│ └── test│ └── java 测试代码├── target 编译好的class文件和jar包等│ ├── classes│ ├── ... ...
程序的机器级表示
前言了解了基础的编码知识后,下一步我们开始研究程序是如何在计算机当中表示和执行,我们将会深入了解程序中的分支、循环还有函数调用等是如何在使用机器码(机器语言)来表示,但是我们不会直接研究机器语言,毕竟机器语言都是由 0、1 组成的,实在不利于阅读和理解,我们将会研究机器语言的助记符,汇编语言,它在底层是与机器语言等效的,所以研究它其实是等同于研究机器语言的。不同的 cpu 架构的指令集架构也不同,后续的研究中将会基于 Intel 的 x86-64 架构。
硬件抽象计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏具体的实现细节,即使是机器级代码也已经使用到了许多的抽象模型,其中两点尤为重要的。
指令集架构(ISA)
虚拟地址
指令集架构定义了处理器状态,指令的格式以及每条指令对状态的影响。大多数 ISA,比如 x86-64,arm 等,会将程序的行为描述成每条指令都是顺序执行的,一条指令执行完后,下一条才开始。可是处理器的硬件实际上要远比描述的更精密,硬件会并发的执行多条指令,但又会采取措施来保证整体行为和 ISA 指定的顺序执行的结果是一致的。
虚拟地址将机器级代码使 ...
三层架构和分层解耦
内聚和耦合在软件的设计开发中离不开两个词,内聚性和耦合性 🤔。内聚性是指机能相关的程序组合成一模块的程度,以下的情形会降低程序的内聚性:
许多机能封装在一类型内,可以借由方法供外界使用,但机能彼此类似之处不多。
在方法中进行许多不同的机能,使用的是相关性低或不相关的资料。
低内聚性的缺点如下:
增加理解模块的困难度。
增加维护系统的困难度,因为一个逻辑修改会影响许多模块,而一个模块的修改会使得一些相关模块也要修改。
增加模块复用困难度,因为大部分的应用程序无法复用一个由许多不一定相关的机能组成的模块。
耦合性是与耦合性是指一程序中模块及模块之间信息或参数依赖的程度。耦合性可以是低耦合性(或称为松散耦合),也可以是高耦合性(或称为紧密耦合)。
紧密耦合的系统在开发阶段有以下的缺点:
一个模块的修改会产生涟漪效应,其他模块也需随之修改。
由于模块之间的相依性,模块的组合会需要更多的精力及时间。
由于一个模块有许多的相依模块,模块的可复用性低。
一个优秀的应用程序应当是高内聚和松散耦合的。在开发 web 应用程序时,解决内聚性和耦合性是一个不得不面对的问题,而 spring 框 ...
controller响应请求
flowchart LR
A[浏览器(browser)]
B[前端控制器\nDispathcherServlet]
C[XxxController]
D[XxxController]
E[XxxController]
F[App]
G[小程序]
subgraph 客户端
direction TB
A
F
G
end
subgraph web服务器
direction LR
B -- 2 --> C -- 3 --> B
B -- 2 --> D -- 3 --> B
B -- 2 --> E -- 3 --> B
end
客户端 -- 1 --> web服务器 -- 4 --> 客户端
发起请求
将请求解析为 HttpServletRequest 对象
将响应封装为 HttpServletResponse 对象
返回响应
信息的表示和处理
前言计算机是以二进制(0 和 1)的方式存储信息的,包括数字,字符,图像,音频等。每个二进制位称为比特(bit),8 个二进制位为一个字节(byte)。内存的最小寻址单位就是一个字节。在研究二进制数据时使用 01 的方式不便于阅读,一般会转换十六进制来表示。
大端存储和小端存储对于二进制数如何在计算机中存储这个问题,人们提出了两种解决方案,大端法和小端法。对于数据 0x01234567 的存储:
大端法:
…
0x100
0x101
0x102
0x103
…
…
01
23
45
67
…
小端法:
…
0x100
0x101
0x102
0x103
…
…
67
45
23
01
…
大端法将高位字节 0x01 放在了前面,而小端法将低位字节放在了后面。下面有一个示例程序来验证机器使用的存储方式为何种:
12345678910111213#include <stdio.h>void show_bytes(unsigned char *p, int len) { for (int i = 0; i < len; ++i ...
衡量计算机性能标准
衡量计算机性能的几个指标
主频(Clock Speed):以 GHz 为单位,表示 CPU 每秒钟可以执行的时钟周期数。虽然主频是一个重要的性能指标,但并不能单独决定处理器的性能。
每个时钟周期的指令数(IPC,Instructions Per Cycle):表示 CPU 在每个时钟周期内可以执行的指令数量。更高的 IPC 意味着更高的效率。由于每种指令的 IPC 都不一样,所以计算 CPU 的 IPC 时一般是计算平均 IPC。
每秒钟的指令数(IPS,Instructions Per Second):IPS 与主频和 IPC 有关,计算公式如下:$$ \text{IPS} = \text{主频(GHz)} \times \text{IPC} $$
核心数和线程数:多核处理器可以并行处理多个任务。线程数(尤其是支持超线程的 CPU)也可以影响多任务处理的性能。
缓存(Cache):CPU 的三级缓存(L1、L2、L3)的大小和速度会影响数据访问速度,较大的缓存通常能提高性能。
TDP(Thermal Design Power):设计功耗,表示 CPU 在正常工作时的功耗,影 ...
深入理解计算机系统
前言本系列是为了记录一下自己在学习《深入理解计算机系统》这本书时的一些心得和重点。在使用一些接近底层的语言时,对计算机系统有深入的理解可以帮助写出更加优秀的程序。
相关文章深入理解计算机系统文章导航 🌏深入理解计算机系统衡量计算机性能标准信息的表示和处理程序的机器级表示处理器体系结构计算机系统漫游
表达式求值
前言我们平时见到的表达式一般为中缀表达式,比如3 + 5 * (2 - 1),这个表达式对于计算机来说并不是直接计算出它的答案的,而是先将其转换成后缀表达式然后再进行计算。
后缀表达式(逆波兰表达式)举个例子,计算一个中缀表达式的具体步骤:
中缀表达式:3 + 5 * (2 - 1)
转换为后缀表达式:3 5 2 1 - * +
计算结果
转换过程转换的步骤通常遵循运算符优先级和括号规则以及左优先原则:
操作数:遇到操作数(数字)时,将其放入输出列表。
操作符:遇到操作符时,根据优先级进行处理:
如果当前操作符的优先级高于栈顶操作符,则将其压入栈。
如果当前操作符的优先级低于或等于栈顶操作符,则将栈顶操作符弹出,加入输出列表,直到找到优先级较低的操作符或栈为空,然后将当前操作符压入栈。
括号:
遇到左括号 ( 时,将其压入栈。
遇到右括号 ) 时,将栈中的操作符弹出并加入输出列表,直到遇到左括号。
以3 + 5 * (2 - 1)为例:
栈->[],输出->[]
栈->[],输出->[3]
栈->[+],输出->[3]
栈-> ...
串与KMP
前言串结构是计算机程序中经常需要使用和运算的,深入研究其结构和相关算法是大有益处的。
串串为一种特殊的线性表结构,所以可以用顺序表和链表的方式来进行实现,由于串结构的使用非常平凡,基本上是每个应用程序都会使用的一种结构,所以主流的编程语言都对字符串有非常良好的工具库支持,因此我们在这里不研究串结构的实现,我们会将重心放在字符串模式匹配算法和实现上。
字符串模式匹配字符串模式匹配就是在主串中找到与模式串相同的子串,并返回其所在位置。模式串就是我们要在主串寻找字串,它不一定在主串中存在,而子串就是主串中的连续子集,子串是一定存在的,不要把模式串和子串的概念混淆了。
朴素模式匹配算法朴素模式匹配算法其实就是暴力搜索。假设主串长度为 n,模式串长度为 m,然后将主串中所有长度为 m 的子串依次与模式串对比,直到找到一个完全匹配的子串,或者所有子串都不匹配为止。
下面是代码实现:
1234567891011121314151617// 返回先匹配子串的索引,若都不匹配返回-1int index(std::string s, std::string t) { int sLen = ...






