网络基础
从输入url到显示页面都发生了什么
用户发起请求 --> 智能DNS的解析(根据IP判断地理位置、接入网类型、选择路由最短和负载最轻的服务器) --> 取得缓存服务器IP --> 把内容返回给用户(如果缓存中有) --> 向源站发起请求 --> 将结果返回给用户 --> 将结果存入缓存服务器
http缓存
如果请求成功,status code会有三种情况:
200 from cache:直接从本地缓存中获取响应,最快速,最省流量,因为根本没有向服务器发送请求。
304 not modified:协商缓存,浏览器在本地没有命中的情况下请求头中发送一定的校验数据到服务端,如果服务端数据没有改变,浏览器从本地缓存响应,返回304。
快速,发送的数据很少,只返回一些基本的响应头信息,数据量很小,不发送实际相应体。
200 ok 以上两种缓存全都失败,服务器返回完整响应。没有用缓存,相对最慢。
request 到 response 发生了啥,从应用层一直往物理层描述一遍
物理层:建立、维护、断开物理连接
数据链路层:建立逻辑连接,进行硬件地址寻址、差错校验等功能
网络层:进行逻辑地址寻址,实现不同网络之间的路径选择
传输层:定义传输数据的协议端口号,以及流控和差错校验。eg. TCP、UDP,数据包一旦离开网卡即进入网络传输层
会话层:建立、管理、终止会话
表示层:数据的表示、安全、压缩
应用层:网络服务与最终用户的一个接口。eg. HTTP、FTP、SMTP
TCP中的滑动窗口
TCP 的滑动窗口主要有两个作用: 一是提供TCP的可靠性;二是提供TCP流控特性。TCP的滑动窗口是以字节(byte)为单位的。TCP窗口大小值是TCP段中一个16bit字段,它代表窗口的字节容量。TCP窗口的最大值为2的16次方,即65535字节。 接受者在回复给发送者的每一个确认信息中都通告了自己的窗口大小。这个确认信息可以是报文段,即纯确认信息。也可以被捎带在反向的报文段中传送给发送者。这个窗口的大小,其实是接受者缓存中剩余的空间大小。
窗口构成 对于TCP会话发送端,任何时候在发送缓存内的数据可以分为4类:1. 已经发送并得到对端ACK;2.已经发送但还没收到ACK;3. 未发送但对端允许发送;4. 未发送而对端不允许发送;我们把“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部分数据称为发送窗口 对于TCP 的接收方,在某一时刻它的接受缓存内的数据可以分为3类:1.已接收;2. 未接收准备接收;3. 未接收不准备接收;其中“未接收准备接收”称为接收窗口。
三次握手四次挥手,还有中间状态(如半连接)
- 建立连接协议(三次握手)
(1)客户端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1。 (2)服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通讯。 (3)客户必须再次回应服务段一个ACK报文,这是报文段3。
-
为什么要三次握手?
网络中存在延迟的重复分组。
第三次握手失败怎么办?
当客户端收到服务端的SYN+ACK应答后,其状态变为ESTABLISHED,并会发送ACK包给服务端,准备发送数据了。如果此时ACK在网络中丢失,过了超时计时器后,那么Server端会重新发送SYN+ACK包,重传次数根据/proc/sys/net/ipv4/tcp_synack_retries来指定,默认是5次。如果重传指定次数到了后,仍然未收到ACK应答,那么一段时间后,Server自动关闭这个连接。但是Client认为这个连接已经建立,如果Client端向Server写数据,Server端将以RST包响应,方能感知到Server的错误。
当失败时服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。
-
四次离开
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。 (1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。 (2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。 (3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。 (4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
-
为什么要四次挥手?
tcp是全双工模式,接收到FIN时意味将没有数据再发来,但是还是可以继续发送数据。
进程线程
操作系统需要管理的两个对象构成
-
进程构成
线程:每个进程由多个线程组成
逻辑内存:每个进程的内存都是相互独立的
文件/网络句柄:所有进程共有
-
线程构成
栈
PC(process counter):下一条执行指令的地址(指向内存),进程其实只是容器
TLS(Thread Local Storage):线程独立内存,用来将数据与一个正在执行的指定线程关联起来
-
交互
进程之间交互用TCP/IP
线程有共享内存,通过指针访问
信号量
信号量的本质是一种数据操作锁、用来负责数据操作过程中的互斥、同步等功能。信号量用来管理临界资源的。它本身只是一种外部资源的标识、不具有数据交换功能,而是通过控制其他的通信资源实现进程间通信。
可以这样理解,信号量就相当于是一个计数器。当有进程对它所管理的资源进行请求时,进程先要读取信号量的值,大于0,资源可以请求,等于0,资源不可以用,这时进程会进入睡眠状态直至资源可用。当一个进程不再使用资源时,信号量+1(对应的操作称为V操作),反之当有进程使用资源时,信号量-1(对应的操作为P操作)。对信号量的值操作均为原子操作。
dns解析的过程(resolving name server -> root name server -> tld name server -> register/authorization name server)
在浏览器中输入www.google.com域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。
如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。
如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(http://google.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找http://google.com域服务器,重复上面的动作,进行查询,直至找到www.google.com主机。
如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。 从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。
TCP 安全性体现在哪些方面,采用了哪些机制?
网络层安全:IP封装技术。其本质是,纯文本的包被加密,封装 在外层的IP报头里,用来对加密的包进行Internet上的路由选择。到达另一端时,外层的IP报头被拆开,报文被解密,然后送到收报地点。
传输层安全:双端实体的认证,数据加密密钥的交换等。如,安全接层协议(SSL)。
应用层安全:,一个是对每个应用(及应用协议)分别进行修改。如,为基于SMTP的电子邮件系统提供安全服务。
https是怎么做到会话加密的(SSL握手确立会话密钥)
1.浏览器将自己支持的一套加密规则发送给网站。
2.网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
3.浏览器获得网站证书之后浏览器要做以下工作: a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。 b) 如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。 c) 使用约定好的HASH算法计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。 4.网站接收浏览器发来的数据之后要做以下的操作: a) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。 b) 使用密码加密一段握手消息,发送给浏览器。 5.浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。
301和302,502和504分别是什么情况
302: Temporarily Moved; 301: Permanently Moved。
502:Bad Gateway错误,504 Bad Gateway timeout 网关超时
502,504出现的可能性:
web
服务器故障,程序进程不够502 是指请求的php-fpm已经执行,但是由于某种原因而没有执行完毕,最终导致php-fpm 进程终止, 一般来说,与php-fpm.conf的设置有关,也有php的执行程序性有关,网站的访问量大,而php-cgi 的进程数偏少。大多数请求修改
php-fpm.conf
的max_children
,但这个也是适量增多504 表示超时,也就是客服端所发出的请求没有到达网关,请求没有到可以执行的
php-fpm
。与nginx.conf
的配置也有关系。
501: 服务器不具备完成请求的功能。例如:服务器无法识别请求方法时可能会返回此代码;
503: 服务器目前无法使用,通常,这只是暂时状态;
505: 服务器不支持请求中所有的http协议版本。
讲讲http状态码
1xx:指示信息--表示请求已接收,继续处理。
2xx:成功--表示请求已被成功接收、理解、接受。
3xx:重定向--要完成请求必须进行更进一步的操作。
4xx:客户端错误--请求有语法错误或请求无法实现。
5xx:服务器端错误--服务器未能实现合法的请求。
TCP和UDP的区别
TCP:
1)提供IP环境下的数据可靠传输(一台计算机发出的字节流会无差错的发往网络上的其他计算机,而且计算机A接收数据包的时候,也会向计算机B回发数据包,这也会产生部分通信量),有效流控,全双工操作(数据在两个方向上能同时传递),多路复用服务,是面向连接,端到端的传输;
2)面向连接:正式通信前必须要与对方建立连接。事先为所发送的数据开辟出连接好的通道,然后再进行数据发送,像打电话。
3)TCP支持的应用协议:Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传输协议)。TCP用于传输数据量大,可靠性要求高的应用。
UDP:
1)面向非连接的(正式通信前不必与对方建立连接,不管对方状态就直接发送,像短信,QQ),不能提供可靠性、流控、差错恢复功能。UDP用于一次只传送少量数据,可靠性要求低、传输经济等应用。
2)UDP支持的应用协议:NFS(网络文件系统)、SNMP(简单网络管理系统)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。
区别:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付 3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等) 4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信 5、TCP首部开销20字节;UDP的首部开销小,只有8个字节。
TCP是怎么做拥塞控制的
TCP的拥塞控制采用的是窗口机制,通过调节窗口的大小实现对数据发送速率的调整。TCP的发送端维持一个称为拥塞窗口cwnd的变量,单位为字节,用于表示在未收到接收端确认的情况下,可以连续发送的数据字节数。cwnd的大小取决于网络的拥塞程度,并且动态地发生变化。拥塞窗口调整的原则是:只要网络没有出现拥塞,就可以增大拥塞窗口,以便将更多的数据发送出去,相当于提高发送速率;一旦网络出现拥塞,拥塞窗口就减小一些,减少注入网络的数据量,从而缓解网络的拥塞。 发送端判断网络发生拥塞的依据是:发送端设置一个重传计时器RTO,对于某个已发出的数据报文段,如果在RTO计时到期后,还没有收到来自接收端的确认,则认为此时网络发生了拥塞。 TCP的拥塞控制算法包括了慢启动(slow start)、拥塞避免(congestion avoidance)、快速重传(fast retransmit)和快速恢复(fast recovery)四部分。
四次挥手时为什么需要等待2倍最长报文发送时间
1、为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
2、他还可以防止已失效的报文段。客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。从保证在关闭连接后不会有还在网络中滞留的报文段去骚扰服务器。
注意:在服务器发送了FIN-ACK之后,会立即启动超时重传计时器。客户端在发送最后一个ACK之后会立即启动时间等待计时器。
对比多进程和多线程的优劣
多进程 优:内存隔离,单个进程的异常不会导致整个应用的崩溃。 缺:进程间调用,通讯和切换开销均比多线程大。 使用场合:目标子功能间交互少。
多线程 优:提高系统的并行性,并且开销少。 缺:没有内存隔离,单个线程的崩溃会导致整个应用的退出。 使用场合:在存在大量IO,网络耗时操作,或需要用户交互时,使用多线程有利益提高系统的并行性,和用户界面快速响应。
协程
本质上是一种用户态的线程,不需要操作系统来进行抢占式调度,且在真正的实现中寄存于线程中,因此系统开销极小,可以有效提高线程的事物并发性,而避免多线程的缺点。使用协程的优点是变成简单,结构清晰;缺点是需要语言支持,如果不支持,则需要用户在程序中自行实现调度器。目前原生支持协程的语言还很少。
TCP如何保证传输的可靠性?
传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
解释一下僵尸进程和孤儿进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
如果进程不调用wait/waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,因此孤儿进程并不会有什么危害。任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。
Linux的内存管理方式
地址:1)逻辑地址:指由程序产生的与段相关的偏移地址部分。在C语言指针中,读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址。而数据段的基地址保存在全局描述符表/局部描述符表中。2)线性地址: 段中的偏移地址,加上相应段的基地址就生成一个线性地址。是个中间值。3)物理地址:寻址总线上的地址。4)虚拟地址: 保护模式下段和段内偏移量组成的地址。而逻辑地址就是代码段内的偏移量,或称进程的逻辑地址。
内存:1)虚拟内存:计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用内存,而实际上,他通常被分隔成多个物理内存碎片,还有部分暂时储存在外部磁盘的存储 器上,在需要的时候进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存的使用也更有效率。2) 物理内存: 实际的内存。物理地址被分成离散的单元,成为页(page)。目前大多数系统的页面大小都为4k。
地址转换:Linux采用段页式管理机制,有两个部件用于地址转换:分段部件和分页部件。1)分段部件: 将逻辑地址转换成线性地址。分段提供了隔绝各个代码,数据和堆栈区域的机制。因此多个程序可以同时运行在一个处理器上而不会相互干扰。2)分页部件:将线性地址转换成物理地址(页表和页目录),若没有分页机制,那么线性地址就是物理地址。
HTTP可以进行多路复用,具体是怎么复用
在HTTP1.1中是默认开启了Keep-Alive,他解决了多次连接的问题,但是依然有两个效率上的问题: 1) 串行的文件传输。当请求a文件时,b文件只能等待,等待a连接到服务器、服务器处理文件、服务器返回文件,这三个步骤。我们假设这三步用时都是1秒,那么a文件用时为3秒,b文件传输完成用时为6秒,依此类推。(注:此项计算有一个前提条件,就是浏览器和服务器是单通道传输) 2) 连接数过多。我们假设Apache设置了最大并发数为300,因为浏览器限制,浏览器发起的最大请求数为6,也就是服务器能承载的最高并发为50,当第51个人访问时,就需要等待前面某个请求处理完成。
HTTP/2的多路复用就是为了解决上述的两个性能问题.
- 在HTTP1.1的协议中,我们传输的request和response都是基本于文本的,这样就会引发一个问题:所有的数据必须按顺序传输,比如需要传输:hello world,只能从h到d一个一个的传输,不能并行传输,因为接收端并不知道这些字符的顺序,所以并行传输在HTTP1.1是不能实现的。 2) HTTP/2引入二进制数据帧和流的概念,其中帧对数据进行顺序标识,这样浏览器收到数据之后,就可以按照序列对数据进行合并,而不会出现合并后数据错乱的情况。同样是因为有了序列,服务器就可以并行的传输数据,这就是流所做的事情。解决第二个问题:HTTP/2对同一域名下所有请求都是基于流,也就是说同一域名不管访问多少文件,也只建立一路连接。同样Apache的最大连接数为300,因为有了这个新特性,最大的并发就可以提升到300,比原来提升了6倍.
CDN
Content Delivery Network 内容分发网络 在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络。CDN系统能够实时地根据 网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最 近的服务节点上。
优势 本地Cache加速,提高了企业站点(尤其含有大量图片和静态网页站点)的访问速度 跨运营商的网络加速,保证不同网络的用户都得到良好的访问质量 远程访问用户根据DNS负载均衡技术智能自动选择Cache服务器 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点web服务器负载等功能 广泛分布的CDN节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵
工作原理 传统访问:用户在浏览器输入域名发起请求 --> 解析域名获取服务器IP地址 --> 根据IP地址找到对应的服务器 - -> 服务器相应并返回数据 使用CDN访问:用户发起请求 --> 智能DNS的解析(根据IP判断地理位置、接入网类型、选择路由最短和负载 最轻的服务器) --> 取得缓存服务器IP --> 把内容返回给用户(如果缓存中有) --> 向源站发起请求 --> 将结果 返回给用户 --> 将结果存入缓存服务器
实现 BAT等提供CDN服务 可用LVS做4层负载均衡 可用Nginx、Varnish、Squid、Apache TrafficServer做7层负载均衡和cache
垃圾回收算法
1.运行结束时统一回收(没有自动GC) 这是最朴素的垃圾回收算法,在函数调用结束后直接把栈清空。如果你能把程序仔细地划分为一个个相互独立的子任务,每个子任务完成时自动删除其占用的内存空间。比如,Apache Web服务器对于每一个请求会分配一个内存池,请求处理完成之后直接把这个内存池销毁即可。
2.引用计数垃圾回收 这也是一个较为朴素的解决方案,对于内存中的每一个对象都保存它们的引用计数,当某个对象的引用计数减为0时,就把它所占用的内存空间回收。 但是引用计数会带来一堆其他的问题,最严重的应该就是循环引用,一部分对象其实已经没办法再使用,理应被回收内存空间,但是由于它们之间相互引用并形成一个回环,所以大家的引用计数都大于0,所以一直不能被回收,进而造成内存泄漏。 第二个问题就是造成大量的资源浪费,正如上面动画中的,就算程序所占用的内存空间不再增加,红色方块依旧在闪烁,说明程序在不断地读取并修改对象的引用计数,也就是造成了不必要的内存访问和修改。 第三个问题就是在多线程环境下,很难保证这些引用计数是线程安全的。 但是引用计数能保证大部分的”垃圾”能在第一时间被检测出来,然后在第一时间加以回收,有良好的时间分散性,是程序的表现性能更加平滑。
3.标记-清除算法 虽然标记-清除算法可以解决上面提到的引用计数的三个问题,但是它不能像引用计数回收算法那样马上检测出垃圾的存在,他一般会在内存占用到达某个比例时开始进行。 标记-清除算法的一大弱点在于它必须全面扫描一遍内存才能准确判断哪些对象才是真正的垃圾,然后把它标记出来。这对于那些内存不断增长,但真正的垃圾对象所占的比例并不大的程序来说,会极大地增加不必要的负担。
4.标记-整理算法 相信细心的你已经发现了,无论是引用计数还是标记-清除算法都有一个明显的问题,那就是对象被分配内存空间之后,它的位置就不会发生改变,这样随着垃圾回收的不断进行,内存空间就会出现许许多多的碎片,这些碎片是十分不方便被重新分配利用的。而且计算机组成原理告诉我们,连续的内存空间能使cache缓存的命中率大大增加,从而减少许多直接内存访问。 标记-整理算法就是在专门为了解决这个问题而被提出的,他不仅清除垃圾对象,而且会想办法把这些腾出来的空间马上利用起来。
5.复制算法 这是Java虚拟机分代垃圾回收算法的基础,它会像标记-整理那样根据情况重新调整存活对象的内存位置,但它实现起来会简单清晰很多。它一般会把内存空间划分为两个以上,对应着不同的世代。刚创建的对象被分配到其中一个指定的内存空间(年轻代),当它被确认存活时会被转移到另外一个内存空间(年老代)。 每一次扫描转移都会导致这个内存空间中的对象要么被清除,要么被转移到另外一个内存空间。 由于复制算法要操作的对象是存活对象,那么一旦发现这个对象可达,马上复制,扫描之后直接整体清空内存空间即可,而不需要像标记-清除和标记-整理那样要在扫描全过程结束后,才能最终确认哪些对象不可达。这样,额外的计算量只会由存活对象决定,垃圾对象直接清除即可。 同时,在对象进行空间转移的过程中,原有的数据(对象的参照地址)仍保留完整,而不像标记-整理算法那样,一边整理对象一边破坏原有的数据。那么这些信息可以,大大减少了计算各引用对象新地址的时间复杂度。 当然,这个算法最大的问题就是很难保证内存的使用率,因为你必须保证有多出一倍的额外空间让你进行拷贝。
直接 I/O 和缓冲 I/O 区别
无缓冲I/O :不是内核不提供缓冲,而是对于用户层来说,没有提供缓存,而对内核来说还是有缓存的。数据:数据流->内核缓存->磁盘 有缓冲I/O:是指在用户层上再建立了一层缓存区(流缓存区),目的是为了减少read,write等系统调用的使用次数,降低系统开销。数据:数据流->流缓存区->内核缓存->磁盘。当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。(双重缓冲)
http 1.X 2.0区别
HTTP1.0和HTTP1.1的区别
长连接
HOST域 在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,但在一台物理服务器上可以存在多个虚拟主机,并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
节约带宽 (1)HTTP/1.1加入了一个新的状态码100(Continue)。客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码401(Unauthorized);如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。100 (Continue) 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。 (2)HTTP/1.1中引入了Chunked transfer-coding来解决上面这个问题,发送方将消息分割成若干个任意大小的数据块,每个数据块在发送时都会附上块的长度,最后用一个零长度的块作为消息结束的标志。这种方法允许发送方只缓冲消息的一个片段,避免缓冲整个消息带来的过载。
HTTP1.1 HTTP 2.0主要区别
HTTP/2采用二进制格式而非文本格式
HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
使用报头压缩,HTTP/2降低了开销 假定一个页面有80个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1400字节的消息头(着同样也并不少见,因为Cookie和引用等东西的存在), 至少要7到8个来回去“在线”获得这些消息头。这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已。这全都由于TCP的慢启动机制,它会基于对已知有多少个包,来确定还要来回去获取哪些包 – 这很明显的限制了最初的几个来回可以发送的数据包的数量。相比之下,即使是头部轻微的压缩也可以是让那些请求只需一个来回就能搞定——有时候甚至一个包就可以了。这种开销是可以被节省下来的,特别是当你考虑移动客户端应用的时候,即使是良好条件下,一般也会看到几百毫秒的来回延迟。
HTTP/2让服务器可以将响应主动“推送”到客户端缓存中 当浏览器请求一个网页时,服务器将会发回HTML,在服务器开始发送JavaScript、图片和CSS前,服务器需要等待浏览器解析HTML和发送所有内嵌资源的请求。服务器推送服务通过“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。
get post的区别 put delete 知道吗 put和post (安全 幂等 长度 状态balabala)
幂等:在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。
根据HTTP规范,GET用于信息获取,而且是安全的和幂等的 GET请求是安全的。所谓安全是指不管进行多少次操作,资源的状态都不会改变。该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改、增加数据,不会影响资源的内容,即该请求不会产生副作用。无论进行多少次操作,结果都是一样的。
根据HTTP规范,POST一般用于创建数据,不是安全和幂等的 POST请求既不是安全的,也不是幂等的,比如常见的POST重复加载问题:当我们多次发出同样的POST请求后,其结果是创建出了若干的资源。
PUT一般用于创建或完整更新数据,而且是安全和幂等的 PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改完整的数据内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。
DELETE一般用于删除数据,而且是安全和幂等的 DELETE请求顾名思义,就是用来删除某一个资源的,该请求就像数据库的delete操作,无论进行多少次DELETE操作,其结果并没有不同。
PATCH一般用于更新部分数据,不是安全和幂等的 PATCH请求是对PUT请求的补充,一般用来对已知资源部分更新,是后来新出的标准,GitHub Api也开始使用。
进程通信方式
管道( pipe ) 管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
信号量( semophore ) 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列( message queue ) 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号 ( singal ) 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。主要作为进程间以及同一进程不同线程之间的同步手段。
共享内存( shared memory ) 共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。 使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
套接字( socket )
套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信更为一般的进程间通信机制,可用于不同机器之间的进程间通信。
各种通信方式的比较和优缺点: 管道:速度慢,容量有限,只有父子进程能通讯 FIFO:任何进程间都能通讯,但速度慢 消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题 信号量:不能传递复杂消息,只能用来同步 共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
Linux的共享内存如何实现
在内存划出一块区域来作为共享区;把这个区域映射到参与通信的各个进程空间。通常在内存划出一个区域的方法是,在内存中打开一个文件,若通过系统调用mmap()把这个文件所占用的内存空间映射到参与通信的各个进程地址空间,则这些进程就都可以看到这个共享区域,进而实现进程间的通信。
Reactor与proactor的区别
Reactor and Proactor。 Reactor模式是基于同步I/O的,而Proactor模式是和异步I/O相关的。 在Reactor模式中,事件分离者等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分离者就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。 而在Proactor模式中,事件处理者(或者代由事件分离者发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区,读的数据大小,或者用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分离者得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。这种异步模式的典型实现是基于操作系统底层异步API的,所以我们可称之为“系统级别”的或者“真正意义上”的异步,因为具体的读写是由操作系统代劳的。
对称加密与非对称加密
一方通过密钥将信息加密后,把密文传给另一方,另一方通过这个相同的密钥将密文解密,转换成可以理解的明文。 使用了一对密钥,公钥(public key)和私钥(private key)。私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。非对称加密使用这对密钥中的一个进行加密,而解密则需要另一个密钥。
IO有哪些?阻塞IO和多路IO的区别?
对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
等待数据准备 (Waiting for the data to be ready)
将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux系统产生了下面五种网络模式的方案。
阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
信号驱动 I/O( signal driven IO)
异步 I/O(asynchronous IO)
阻塞 I/O(blocking IO) 在linux中,默认情况下所有的socket都是blocking。当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。 所以,blocking IO的特点就是在IO执行的两个阶段都被block了。
非阻塞 I/O(nonblocking IO) linux下,可以通过设置socket使其变为non-blocking。当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。 所以,nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。
I/O 多路复用( IO multiplexing) IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。 当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。 所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。
异步 I/O(asynchronous IO) inux下的asynchronous IO其实用得很少。先看一下它的流程:用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
I/O 多路复用之select、poll、epoll
一般情况下,一次网络IO读操作会涉及两个系统对象:(1) 用户进程(线程)Process;(2)内核对象kernel
select 的核心功能是轮询,直到有一个连接有想要的消息为止。 缺点:1. 每次调用select,都需要把fd集合从用户态遍历和拷贝到内核态,这个开销在fd很多时会很大;2. select支持的文件描述符数量太小了,默认是1024。
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一 个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射技术,这 样便彻底省掉了这些文件描述符在系统调用时复制的开销。
epoll的优点就是改进了前面所说缺点:
支持一个进程打开大数目的socket描述符:相比select,epoll则没有对FD的限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
IO效率不随FD数目增加而线性下降:epoll不存在这个问题,它只会对"活跃"的socket进行操作--- 这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数。
使用mmap加速内核与用户空间的消息传递:这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就 很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
三者对比与区别:
select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。
docker
由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。 Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。由于容器是进程级别的,相比虚拟机有很多优势。Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
半开连接
TCP套接字不是有个保持存活选项SO_KEEPALIVE嘛,如果在两个小时之内在该套接字的任何一个方向上都没数据交换,TCP就自动给对端发送一个保持存活探测分节,如果此TCP探测分节的响应为RST,说明对端已经崩溃且已经重新启动,该套接字的待处理错误被置为ECONNRESET,套接字本身则被关闭。如果没有对此TCP探测分节的任何响应,该套接字的处理错误就被置为ETIMEOUT,套接字本身则被关闭。
SO_KEEPALIVE由操作系统负责探查,即便是进程死锁或有其他异常,操作系统也会正常收发TCP keepalive消息,而对方无法得知这一异常。其实我们可以在应用层模拟SO_KEEPALIVE的方式,用心跳包来模拟保活探测分节。