对于socket的一些理解
在大学刚接触到网络编程以及web编程的时候,听说过socket以及端口号,但是当时只是知道写法如下:
1234567// serverint port = xx;ServerSocket serverSocket new ServerSocket(port);;Socket socket = serverSocket.accept();// clientSocket socket = new Socket(ip, port);
但是并不明白为什么要这样做,在学习了计算机网络后,有了一些个人理解。
在计算机网络中,我们编写的代码,是属于应用层的内容,当需要传输数据时,需要先将数据传送给运输层,然后运输层送往网络层,依次往下传送。
其实我们可以将应用层与其他的几层拆分开,然后把运输层及其以下的几层当作是一个其他人已经封装好了的计算机网络。我们传输数据,只需要把数据交给这个计算机网络,它就能帮我们把数据传输到我们的目的地。而socket,就是我们的编写的程序与计算机网络之间的接口,也可以理解为一个桥梁,连接我们应用程序与计算机网络。
而我们在web编程,比如使用springboot框架, ...
计网中的多路分解与多路复用
多路复用与多路分解是将网络层提供的主机到主机交付服务延伸到为运行在主机上的应用程序提供进程到进程的交付服务。
在目的主机,运输层负责从网络层接受报文段,然后将报文段中的数据交付给对应的进程。而每个进程都会有一个或者多个套接字,这些套接字可以理解为网络层和运输层传输数据的门户(一个更形象的比喻,可以把套接字理解为插座的插口。传输数据需要先连接套接字,而充电也要先找到插口)。其实数据并不是直接由网络层传输给数据层,而是网络层传送给套接字,而套接字又传送给运输层。如下图:
多路分解将运输层报文段中的数据交付到正确的套接字的工作称为多路分解。
因为接受端不会只有一个套接字,所以需要找到正确的套接字。而每个套接字都会有唯一的标识符。
运输层为了找到正确的套接字,运输层的每个报文段都会有几个特殊字段,这几个字段,用于找到正确的套接字。
多路复用在源主机中,从不同套接字收集数据块,然后将数据块封装生成报文段,并将报文段传送到网络层,这些工作统称为多路复用。
而在目的主机中,运输层从网络层接受到的报文段分解后交给对应的进程,这个过程是通过将报文段定向到对应套接字来完成的。
多路复用要求1、套接字有 ...
空闲空间管理
这一部分,书中做了一些假设:
1、如果请求内存,就需要指定请求内存的大小,而释放该内存则不用指定大小。
2、只考虑外部碎片,不考虑因分配空间稍微大于请求内存而造成的内部碎片。
3、内存一旦被分配给用户,那么它就不可能被重定位。即这块内存分配给用户后,除非用户调用free函数,否则无法被其他人使用。
4、分配程序管理的内存区域是连续的,而且还可以增大这块区域。
底层机制1、分割与合并假如内存中空闲状态如下图:
那么它对应的空闲链表如图:
这也就意味着,任何长度大于10字节的分配请求都会失败。
如果请求分配的内存小于10字节,那么它会找到一块满足的空间区域,然后进行分割,第一块分给用户,剩下的加入空闲列表。
而用户在调用free时,它并不是把用户使用的那块区域直接加入,而是会看用户释放的空间左右两端是否是空闲的,如果是,则进行合并。
比如参考第一张图,如果用户释放中间用掉的10字节,它会变为如下:
而不是
2、追踪已分配空间的大小由于在释放对应区域空间时,并不需要指出大小,这是因为在大多数分配程序都会在头块中保存一些额外的信息,头块位于内存中,就在返回的内存区域前面。
这里释放 ...
分段
在分段之前,操作系统会直接把进程的地址空间完整的加载到内存当中,但是栈和堆中间却有很大一块空间没有使用,如下图:
但是由于进程的地址空间被加载到了内存中,那么就意味着这些虚拟的地址空间都会被分配对应的物理地址,虽然这些地址没有被该进程写入内容,但是他们已经不能再分配给其他进程使用。
为了解决这个问题,引入了分段的概念。有了分段的概念后,就不是给每个地址空间一个寄存器,而是让地址空间内的每个段都有自己的基址寄存器和界限寄存器。一个段是地址空间中连续的定长区域。
这样设计,就可以把进程的单个段加载到内存中,而不是将整个地址空间都加载到内存中,这样就能确保被加载到物理内存空间中的内容都是正在使用的,或者说最小程度的浪费一些空间。
在经典的地址空间中,有三个逻辑不同的段:代码,栈和堆。分段之后,我们可以把这三个部分分别加载到物理内存对应的位置,如下图所示:
而不需要像之前那样,必须把整个地址空间全部加载到内存当中。
分段存在的问题分段的出现会导致内存中零散的分布很多的段,如果现在有不连续的24k空间,现在有一个20k大小的段,则会导致该段进入内存失败。
即分段会造成一定的外部碎片。
...
地址转换
地址转换:硬件对每次的内存访问进行处理,将指令的虚拟地址转换为数据实际的物理地址。
但是仅仅靠硬件无法解决,它只能提高效率。还需要操作系统的帮助。
这使得每个程序好像拥有了自己私有的内存空间,存放着自己的代码和数据。但实际上是多个程序共用内存。
动态重定位每个CPU需要两个硬件寄存器:基址寄存器和界限寄存器。这两个寄存器能够让我们将地址空间放在物理内存的任何位置,同时又保证程序只访问自己的地址空间。
采用这种硬件的地址转换方式,进程中的内存引用都是虚拟地址,而虚拟地址加上基址寄存器中的内容,可以算出来真实的物理地址,再发送给内存系统。
由于这个由虚拟内存转换为具体的物理内存的过程是在运行时发生的,所以又叫做动态重定位。
这里面的界限寄存器,就是用来保证进程只访问自己范围内的空间,如果越界,则cpu会触发异常,进程会被终止。
操作系统要做什么1、进程创建时,操作系统要为进程的地址空间找到内存空间。这就需要操作系统来维护哪些空间是可用的,哪些是已经被使用了的。
2、当进程运行结束时,操作系统要负责回收它的内存,供其他进程使用。当进程结束时,操作系统会把这些内存放入空闲列表。
3、每个cpu ...
MySQL的next-key lock
InnoDB引擎为了解决幻读带来的问题,引入了间隙锁。而间隙锁和行锁组合起来叫做next-key lock,他是一个左开右闭的区间,代表锁住对应数据行以及数据行之间的间隙。
比如现在有两行数据,(5,5,5)和(10,10,10)。在可重复读得隔离级别下执行如下sql ,,
1select * from table where id = 7
就会锁住(5, 10]之间的间隙,以及第(10,10,10)这一行。因为锁是加在索引上面,而索引是有序的,所以需要保证这两条数据间没有新的数据插入。
在《MySQL45讲》中给出了加锁的一些规则:
原则1:加锁的基本单位是next-key lock。希望你还记得,next-key lock是前开后闭区间。
原则2:查找过程中访问到的对象才会加锁。
优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。
优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候(比如查询id = 5,而最后一个值是id = 10,那么就会变为间隙锁),next-key lock退化为间隙锁。
一个bu ...
运输层概述
概述运输层协议为运行在不同端的应用进程提供逻辑上的通信,该协议是在端系统中实现,而不是在路由器中实现。
在发送端,运输层将从发送应用程序进程接收到的报文转换成运输层分组,该分组称为运输层报文段(segment)。
转换的方法:
1、将应用报文划分为较小的块,并为每块加上一个运输层首部以生成运输层报文段。
2、然后,在发送端系统中,运输层将这些报文段传递给网络层,网路层将其封装成网络层分组(即数据报)并向目的地发送。
运输层和网络层关系运输层位于网络层之上。网络层提供主机到主机的逻辑通信,而运输层提供的是进程到进程的逻辑通信。
用书上的一个例子,场景是:假如现在有两个家庭A和B,每个家庭有4个孩子。而每个月两家孩子都会互相的写信。家庭A有一个孩子负责收集所有孩子的信件,然后送给邮局。家庭B同样有一个孩子来做这件事情。家庭A的孩子设为C,家庭B的孩子设为D。
在这个例子中,从这些孩子的角度来看,孩子C和孩子D就为他们提供了逻辑通信。因为其他孩子只需要把信交给孩子C和D,剩余事情都无需关心。邮局则提供了两个家庭间的逻辑通信,因为孩子C和D只需要把信交给邮局,其他的也无需关心。
所以可以做以下 ...
CDN
内容分发网络CDN现阶段,很多视频公司没日需要给用户推送大量的视频,这些视频如果都从公司的主服务器推送出去,则会给主服务器造成压力,而且由于区域原因,还会带来很高的时延。
CDN就是为了解决上面的问题。CDN服务器可以分布在多个地区,它存储那些视频以及图片的副本,用于用户请求时将视频等内容响应给用户。可以减小主服务器带宽压力,而且根据用户地理位置,分配不同的CDN,可以减少时延。
CDN采用集群部署,如果用户请求的CDN集群没有用户的目标视频,则该CDN集群会从中心仓库或者另一个集群搜索该视频,然后一边给用户传输,一边缓存在本集群中。
CDN操作当用于从浏览器请求某视频时,CDN需要先截获用户请求,才能确定适合用户的CDN集群,以及将用户请求重定向到该集群中。
如何截获和重定向截获请求和重定向需要依赖于DNS。假如公司A用了公司B提供的CDN服务,那么一个大概的流程如下:
1、用户访问公司A的网站,点击了一个视频,那么他会向对应的域名发起请求(例如请求https://www.bilibili.com/video/xxx),获取该视频。
2、用户的本地DNS服务器会将请求转发到权威DNS ...
地址空间
地址空间一个线程的地址空间如下图所示:
这个地址空间看似是从0KB开始,但这里的0KB映射到具体的物理地址并不一定是从内存的地址0开始,它可能是物理内存的任意位置。
参考《操作系统导论》
一个sleep操作引起的问题
在学习Java并发编程时,有以下demo,简单演示商品出售的问题。
12345678910111213141516171819public class Test { static int t = 1000000; public static void main(String[] args) { // 减少t new Thread(() -> { while (t > 0){ t--; System.out.println(Thread.currentThread().getName() + " " + t); } }).start(); // 减少t new Thread(() -> { while(t > 0){ t--; ...