计算机组成

cpu

CPU负责计算

  1. PC,(program container)指令寄存器,根据指令地址读取指令和所需数据

  2. registers,寄存器,存在多个,保存PC读取的数据,返回给内存计算结果

  3. ALU,算数逻辑单元,对寄存器中的指令和数据进行计算

  4. cache,缓存。三级(L1,L2,L3)
    多核cpu的每个核有自己的L1,L2
    多核共享L3
    多个CPU共享内存(主存)

    超线程【四核八线程】

在一个cpu中,存在多个pc和registers。

ALU在进行计算时会再多个线程之间来回切换,但是pc和register中只能保存一个线程的数据,所以在切换时就需要把当前线程的数据保存到内存后,切换到其他线程。当再次切换回来时,从内存中进行现场恢复继续执行。

但是从内存中进行恢复比较慢,所以,在cpu中多个pc和register可以保存多个线程的数据,ALU在计算时可以不用和内存进行IO,提高速度。

缓存行

cpu从内存中读取数据不是一个字节一个字节的读取,是一行一行的读取,叫做缓存行

缓存行大小是64byte。因为这样提高运算效率,不用需要数据就去内存中读取。

缓存行被读取后放到cpu的L1或者L2缓存中。

缓存一致性协议

MESI只是缓存一致性协议的一种。

缓存被读取到L1,L2中后,会存在这样的问题。如果多个线程共享缓存行中的数据,也就是说同一个缓存行,被读取到了多个cpu中。那么这时就会发生数据不一致的情况。(多线程并发问题)

缓存行的状态:

  • M:modified,被修改
  • E:exclusive,独占
  • S:shared,共享
  • I:invalid,失效

java中volatile关键字。被volatile修饰的变量,在被修改后,缓存行在当前线程的状态变为modified,并通过总线通知其他线程中缓存行变为invalid,其他线程需要重新从内存中读取。

所以,当两个volatile修饰的属性在同一个缓存行中的时候,当他们被频繁修改时,就会频繁通知对方从内存中重新读取,那么就造成运行效率变慢。

经典验证volatile影响运行效率的程序

因为缓存块的 大小是64byte,一个long型属性是8byte,所以可以通过控制数量的方法将两个volatile变量分开到不同的缓存块中与不分开的情况进行比较。

上述方法叫做缓存行对齐

指令重排序

在不影响执行结果的条件下,将代码进行重新排序执行,来提高执行效率。

内存

内存负责存储需要计算的数据,没有内存只能进行手动输入数据,执行一次计算

CPU + 内存实现了自动计算,是最主要的两部分。

CPU和内存之间需要进行通信,才能完成自动计算。

通信依靠的是总线

计算机网络

OSI7层参考模型

  1. 应用层
  2. 表示层
  3. 会话层
  4. 传输控制层,规定传输规则,进行几次握手,几次通信,发送几次数据包
  5. 网络层,网络层规定ip协议,确定数据包怎么发送
  6. 链路层,链路层实现数据包的发送
  7. 物理层

参考模型只是参考,实际并不这样设计的,分层的目的是解耦

程序员面向应用层开发应用程序,表示层和会话层也属于应用层的一部分也是开发人员需要解决的。

TCP

TCP是传输控制层的一个协议。面向连接(区别与udp)的可靠的传输协议。

三次握手

为了创建可靠的通信连接,A向B发送syn数据包,B收到后回复一个syn+ack说明已经接收到连接请求。A在接收到回应之后也会发送ack通知B,已经接收到回应。之后A会准备进行通信传输,B收到ack后确定A要开始进行通信了,会开辟端口用于通信。至此通信完全建立。

三次握手当中,前两次都不能携带数据。第三次可以携带数据。,因为B已经做好接受准备了。

B在发送完syn和ack响应之后,会开辟端口号,等待A的ack反馈,如果这是A不发送ack,B又没有设置超时时间,B会一直开辟端口号等待反馈。如果使用大量的肉鸡访问B,容易造成DOS攻击。

为什么两次不行

A发送请求B回应之后,不能确定A是否能够接收到数据。

而tcp要求可靠性,就要保证传输和接受双方发送和接收数据的能力,只有两次B无法确定A是否能够接收到数据。无法建立可靠连接。

TCP 长连接

客户端和服务器建立tcp连接之后,会一直存在,默认情况下如果其中一方网络断开,不会影响另一方的数据传输,也就是客户端断开了连接,服务端也会一直发送数据。是因为tcp是一个可靠的连接,连接建立之后不发起断开请求不会断开。

上述只是默认情况,会引起一个问题,就是其中一方断开后,另一方一直发送数据会占用资源。所以引入了心跳机制和超时机制,心跳机制就是需要检测对方是否存活,每隔一段时间就要发送数据包进行验证。如果长时间没有心跳通信,超过超时限制之后就会从传输队列中踢出,保证不会一直占用资源。

四次挥手

断开连接,需要双方销毁资源。

  1. A发送fin给B,表示请求断开连接
  2. B发送fin+ack,表示已经收到
  3. B发送fin给A,表示同意断开连接
  4. A发送ack给B,表示收到断开连接请求
  5. 双方销毁资源。

为什么要四次挥手?为什么三次不行?

因为A在发送断开请求之后,B可能存在没有发送完的数据,需要等待该数据发送完成后才能断开连接。如果B只给一次回应的话,可能会漏发数据。再有B接受到断开请求后要立马给A一个ack应答,表明自己收到了请求,这就导致了B不能等到数据都发送完之后一次性返回。所以中间的两次不能省略。

socket

socket是一个四元组:ip:port+ip:port。为了保证连接的唯一性

端口号最大的端口号数量65535,也可以设置数量

对于服务端B的某个服务有唯一的端口号如80。客户端A最多可以产生【65535】个连接。

而服务端消耗一个端口号用于提供服务。

ip:port+ip:port意味着双方的ip+端口合在一起组成一个四元组,四部分中存在一部分不同都是不同的连接。

如果A已经和B的80建立了65535个连接,此时B又建立了90端口的服务。A还是可以建立65535个连接来连接B的90端口

tcpdump

tcp抓包工具。

  1. tcpdump -nn -i eth0 port 80 //监听eth0的所有跟80端口有关的数据包
  2. -nn 代表显示ip地址
  3. -i 选择网卡
  4. port 端口号
  5. -x 显示包内容

实验:

监听80端口,然后使用 curl www.baidu.com访问百度。会完整走一次三次握手+四次挥手。

IP

ip是网络层的协议

A类地址:1-126,子网掩码:/8(8个1)

B类地址:128-191,子网掩码:/16

C类地址:192-223,子网掩码:/24

ipaddr是点分字节。使用【.】来划分字节,每个字节8个比特,最大是2的8次方-1,所以取值范围是0-255。

netmask掩码,由连续的0和1组成,不会出现01交叉。进行子网划分

gateway网关,

dns负责域名解析,例如访问www.baidu.com,会解析成百度服务器的ip

路由

计算机通过网络与其他计算机进行网络传输,需要经过路由转发

  1. 通信时,使用目标地址与路由中的掩码进行按位与运算。得到的结果与网络目标进行匹配,如果匹配成功,则跳转到指定网关进行传输。
  2. IPv4 路由表
  3. ===========================================================================
  4. 活动路由:
  5. 网络目标 网络掩码 网关 接口 跃点数
  6. 0.0.0.0 0.0.0.0 172.20.10.1 172.20.10.5 35
  7. ===========================================================================
  8. 当前情况下访问任何地址都会与0.0.0.0进行按位与运算得到的结果都是0.0.0.0能够与网络目标匹配到,符合第一条路由。也就是说所有网络传输都会跳到172.20.10.1的网关。发送到网关之后使用同样的方法匹配路由决定目标机器。

链路层

arp协议

广播协议,在局域网中寻找机器时,广播寻找目标ip的mac地址(由广播地址实现广播,广播地址是x.x.x.255),目标ip机器收到广播后发送反馈给广播地址。找到了固定的mac地址才能进行通信。

每个ip本地会存有一个局域网地址与mac的映射表。如果映射表中没有目标ip地址,就需要进行广播寻找。

所以一个数据包想要发送,需要在网络层进行一次封装确定目标IP地址,在链路层再次进行封装确定mac地址。在每一次路由跳转中修改mac地址,一步步的找到目标机器,完成数据包的发送。

mac地址是在局域网中唯一的地址,对应固定的物理网口

IO

strace命令:linux用于追踪每个线程的对内核的系统调用

BIO:blocking IO,阻塞io

每个连接创建一个线程来解决阻塞问题,达到可以接受多个连接的目的。

其问题的本质是,内核的accept(用于接受多个请求)和recv(接受数据)两个方法会阻塞。

想要使用strace检测到bio的调用过程,需要使用jdk1.4版本,后续版本都做了优化。

NIO:nonblocking IO,非阻塞IO

优势是规避了多线程的问题,如线程阻塞,资源抢占,线程调度等等

缺点是