目录

​前言​

​一、进程与线程​

​1、进程与线程概念​

​2、进程与线程的联系与区别​

​1)联系

​2) 区别 ​

​3、 同一进程下的线程共享了哪些资源​

​4、什么是堆栈​

​5、进程与线程的通信方式​

​1) 进程之间的通信方式​

​2) 线程之间的通信方式​

​6、进程与线程状态转换​


​二、在浏览器中输入url回车后发生了什么​

​三、TCP和UDP的区别​

​1、TCP的11种状态描述​

​四、DNS解析的过程​

​五、 GET POST区别,POST安全性更高为什么; ​

​六、三次握手 四次挥手​

​七、分页管理和分段管理的区别​

​1、目的​

​2、长度​

​3、地址空间​

​4、碎片​

​5、共享和动态链接​

​八、HTTPS的常见状态、HTTPS的加密机制​

​1. HTTPS 概述​

​2. 对称加密​

​3. 非对称加密​

​4. 非对称加密改良方案​

​5. 非对称加密 + 对称加密​

​6. 中间人攻击​

​7. 数字证书​

​8. 数字签名​

​9. HTTPS 工作原理​

​[参考文献]​

​九.什么悲观锁,什么是乐观锁​

​CAS实现乐观锁​

​十 死锁的四个必要条件?如何避免与预防死锁​

​1、什么是死锁​

​2、死锁产生的原因​

​3、死锁的四个必要条件​

​4、 死锁的避免与预防​

​1. 死锁避免​

​2. 死锁预防​

​十一 B树和B+树的区别​

​十二 静态链接和动态链接的区别​

​4.动态链接地址是如何重定位的呢?​

​十三 C/C++编译过程​

​编译​

​十四 CPU多级缓存与缓存一致性​

​ 十五 阻塞与非阻塞的区别​

​十六 为什么需要内存对齐​

​十七 什么是内存抖动?内存抖动的主要原因?​

​十八 FTP文件传输协议、SMTP简单邮件传输协议、DNS域名系统的实现各用了应用层的设么协议(TCP or UDP)​

​十九 LRU涉及的数据结构  ​

​二十  struct里seziof的计算方法​

前言



以下内容均是学习他人博客+自己复习专用+自己理解,内容可能不太正确,不适合学习,欢迎大佬斧正。




一、进程与线程

1、进程与线程概念

       1)进程(运行的程序) 是系统进行资源分配和调用的最小单位,每一个进程都有自己独立的内存空间和系统资源

       2)线程 是进程的一条执行线路。程序执行的最小单位,是任务调度和执行的最小单位。

总结:进程是资源分配的最小单位,线程是cpu调度的最小单位,进程包含许多线程,而线程只属于一个进程,创建进程的时候系统会为其分配地址空间、资源 所以创建进程的花费比较的高。

2、进程与线程的联系与区别

1)联系

            进程含有多条线程,线程只属于一个进程,一个进程多个线程之间是竞争关系,竞争独立的内存空间和系统资源。

2) 区别 

            (1) 系统开销

                    创建进程比创建线程的系统开销要大,进程的切换比线程的切换效率低。

因为进程需要独立的数据段,代码段、扩展段等系统资源,创建时需要复制(写时复制)父进程的所有资源。而线程与其它线程共享进程的系统资源,每个线程只需要拥有自己的栈段和寄存器,用于存储局部变量和临时变量。但是进程因为有独立的数据段、堆栈段,所以更加稳定、安全。

            (2)通信方面

                   进程之间的通信成本要比线程的高   

            (3) 资源分配
                 进程所维护的是程序所包含的资源(静态资源),比如:虚拟地址空间(代码、数据、堆、共享库)、文件系统信息、文件描述符表和信号处理程序等;线程所维护的运行相关的资源(动态资源),比如:运行栈、调度相关的控制信息、待处理的信号集等。

             (4) 通信机制
                  进程间相互独立,互不打扰,因此通信方式较为复杂,包括Pipe(管道),Signal(信号),Semaphore(信号量),Message(消息队列),SharedMemory(共享内存),Socket(套接字),文件。(PS:PIC包括Semaphore,Message,SharedMemory)而线程之间由于共享进程数据段,所以通过全局变量来实现通信,当然访问时需要加锁。

             (5) 控制权
线程可以销毁,挂起、恢复其它线程,也可以通过销毁主线程来销毁进程,而进程没有权利控制其父(子)进程的状态。子线程终止不会引起其它线程或者进程的终止,但进程(主线程)的终止能够引起所有子线程的终止,同时任何一个子线程执行exit()会引起进程中的全部线程同时灭亡。


3、 同一进程下的线程共享了哪些资源


  • 线程共享的内容包括:


    • 进程 代码段
    • 进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)
    • 进程打开的文件描述符
    • 信号的处理器
    • 进程的当前目录
    • 进程用户 ID 与进程组 ID


  • 线程独有的内容包括:


    • 线程 ID
    • 寄存器组的值
    • 线程的栈
    • 错误返回码
    • 线程的信号屏蔽码




4、什么是堆栈

​ 百度百科​

在计算机领域,堆栈是一个不容忽视的概念,​​堆​​​​栈​​是一种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。

总而言之就是操作系统中会产生各种中断,对于各类中断信息,操作系统的原则是用堆栈 保护 断点和现场,后来先出的原则执行中断请求

栈是为执行线程留出的内存空间。当函数被调用的时候,栈顶为局部变量和一些 bookkeeping 数据预留块。当函数执行完毕,块就没有用了,可能在下次的函数调用的时候再被使用。栈通常用后进先出(LIFO)的方式预留空间;因此最近的保留块(reserved block)通常最先被释放。这么做可以使跟踪堆栈变的简单;从栈中释放块(free block)只不过是指针的偏移而已。

  堆(heap)是为动态分配预留的内存空间。和栈不一样,从堆上分配和重新分配块没有固定模式;你可以在任何时候分配和释放它。这样使得跟踪哪部分堆已经被分配和被释放变的异常复杂;有许多定制的堆分配策略用来为不同的使用模式下调整堆的性能。

  每一个线程都有一个栈,但是每一个应用程序通常都只有一个堆(尽管为不同类型分配内存使用多个堆的情况也是有的)。

5、进程与线程的通信方式

1) 进程之间的通信方式

1、管道( pipe ): 
    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2、有名管道 (namedpipe) : 
    有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3、信号量(semophore ) : 
    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4、消息队列( messagequeue ) : 
    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5、信号 (sinal ) : 
    信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6、共享内存(shared memory ) : 
    共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
7、套接字(socket ) : 
    套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。

2) 线程之间的通信方式

1、锁机制:包括互斥锁、条件变量、读写锁 
    互斥锁提供了以排他方式防止数据结构被并发修改的方法。 
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。 
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
2、信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
3、信号机制(Signal):类似进程间的信号处理 
    线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

 

6、进程与线程状态转换


进程有5种状态
创建状态(new) :进程正在被创建,尚未到就绪状态。
就绪状态(ready) :进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
运行状态(running) :进程正在处理器上上运行(单核CPU下任意时刻只有一个进程处于运行状态)。
阻塞状态(waiting) :又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行

【面试题 计网&操作系统】_缓存
线程有6种状态
 

初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

阻塞(BLOCKED):表示线程阻塞于锁。注意和进程的区别,进程是IO阻塞

等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

终止(TERMINATED):表示该线程已经执行完毕。

【面试题 计网&操作系统】_缓存_02




二、在浏览器中输入url回车后发生了什么

1、解析url获取域名

2、接着域名解析

3、浏览器与网站进行TCP连接

4、请求和传输数据

5、浏览器渲染页面


先浏览器缓存

系统缓存

路由器缓存

本地域名服务器

根域名服务器

顶级域名服务器

极限域名服务器




三、TCP和UDP的区别

    前期描述    TCP 和UDP都是工作在运输层的协议

UDP

TCP

开销小

开销大

UDP是无连接通信 

TCP是有连接通信

UDP支持一对多,多对多,多对一,多对一通信 

TCP只能1对1通信

对应用层交付的报文直接打包 

面向字节流

不可靠服务,无流量控制、无拥塞控制 

可靠通信,支持流量控制、拥塞控制

首部开销小 只有8字节   

首部20字节到60字节  (因为包含ack,FIN 序列号 确认序列号等等)


 1、由于TCP保证可靠的通信,头部比UDP的头部要长,因此开销比UDP的大

2、TCP通过三次握手建立可靠的通信信道进行通信,只能单播通信,UDP不建立信道

3、UDP直接给应用层报文添加UDP首部,形成用户数据报,然后进行发送(面向报文的)。TCP 对字节流进行编号,相当于把报文进行了分割小部分,分别发送 因此是面向字节流的

4、UDP 可能会发生丢包、误码,适用于实时应用。TCP流量控制(滑动窗口)、拥塞控制(慢启动、拥塞避免、快速重传、快速恢复)




tcp开销大,udp开销小

流量控制:滑动窗口
tcp有拥塞控制,可以慢开始,拥塞避免,快重传和快恢复,udp没有,不管网络是否拥塞,udp客户端都可以一直发
tcp数据完整,按时,按序到达,udp尽力满足
tcp延时高,适用于数据传输准确度要求高的场合,udp适用于数据传输量大,实时性要求高的场合



TCP实体:

源端口 目的端口

序号  解决乱序问题

确认序号  防止丢包

SYN发起一个连接

ACK 是回复

FIN是结束链接

RST是重新连接


1、TCP的11种状态描述

简单解释:

l  CLOSED:初始状态,表示TCP连接是“关闭着的”或“未打开的”。

l  LISTEN :表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接。

l  SYN_RCVD :表示服务器接收到了来自客户端请求连接的SYN报文。在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat很难看到这种状态,除非故意写一个监测程序,将三次TCP握手过程中最后一个ACK报文不予发送。当TCP连接处于此状态时,再收到客户端的ACK报文,它就会进入到ESTABLISHED 状态。

l  SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT 状态表示客户端已发送SYN报文。

l  ESTABLISHED :表示TCP连接已经成功建立。

l  FIN_WAIT_1 :这个状态得好好解释一下,其实FIN_WAIT_1 和FIN_WAIT_2 两种状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET进入到FIN_WAIT_1 状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2 状态。当然在实际的正常情况下,无论对方处于任何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1 状态一般是比较难见到的,而FIN_WAIT_2 状态有时仍可以用netstat看到。

l  FIN_WAIT_2 :上面已经解释了这种状态的由来,实际上FIN_WAIT_2状态下的SOCKET表示半连接,即有一方调用close()主动要求关闭连接。注意:FIN_WAIT_2 是没有超时的(不像TIME_WAIT 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),那这个 FIN_WAIT_2 状态将一直保持到系统重启,越来越多的FIN_WAIT_2 状态会导致内核crash。

l  TIME_WAIT :表示收到了对方的FIN报文,并发送出了ACK报文。 TIME_WAIT状态下的TCP连接会等待2*MSL(Max Segment Lifetime,最大分段生存期,指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值),然后即可回到CLOSED 可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(这种情况应该就是四次挥手变成三次挥手的那种情况)

l  CLOSING :这种状态在实际情况中应该很少见,属于一种比较罕见的例外状态。正常情况下,当一方发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING 状态表示一方发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?那就是当双方几乎在同时close()一个SOCKET的话,就出现了双方同时发送FIN报文的情况,这是就会出现CLOSING 状态,表示双方都正在关闭SOCKET连接。

l  CLOSE_WAIT :表示正在等待关闭。怎么理解呢?当对方close()一个SOCKET后发送FIN报文给自己,你的系统毫无疑问地将会回应一个ACK报文给对方,此时TCP连接则进入到CLOSE_WAIT状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话,那你也就可以close()这个SOCKET并发送FIN报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,继续发送或丢弃。简单地说,当你处于CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。

l  LAST_ACK :当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK 状态。当收到对方的ACK报文后,也就可以进入到CLOSED 可用状态了。

tcp的 11种状态 ​​博客​

【面试题 计网&操作系统】_数据_03

图片来自:​​博客​

【面试题 计网&操作系统】_缓存_04




四、DNS解析的过程

DNS 域名系统,将域名解析出IP地址。

先浏览器缓存 查询 是否之前已经解析过一次

系统缓存

路由器缓存

本地域名服务器

根域名服务器

顶级域名服务器

极限域名服务器



五、 GET POST区别,POST安全性更高为什么; 


六、三次握手 四次挥手

三次握手

客户端向服务端发送连接请求

服务端接受后发送  收到请求并确认

客户端收到指令 返回确认

四次挥手

客户端发送断开连接请求,

服务端收到发送确认,进入半关闭状态

之后服务端再次发送确认信息

客户端收到后 返回确认




七、分页管理和分段管理的区别

参考来自:​​博客​

​分页管理方式和分段管理方式的区别​

1、目的

分页:页是信息的物理单位,分页是为了实现离散分配方式,以消减内存的外零头,提高内存的利用率。或者说,分页仅仅是由于系统管理的需要不是用户的需要。

分段:段是信息的逻辑单位,它含有一组其意义相对完整的信息,分段的目的是为了能更好的满足用户的需要。

2、长度

分页:页的大小固定且由系统决定,由系统把逻辑地址分为页号和页内地址两部分,是由机器硬件实现的,因而在系统中只能有一种大小的页面,

分段:段的长度不固定,决定于用户所编写的程序,通常由编译程序在对流程序进行编译时,根据信息的性质来划分。

3、地址空间

分页:作业地址空间是一维的,即单一的线性地址空间,程序员只需利用一个级衣服,即可表示一个地址。

分段:作业地址空间是二维的,程序员在表示一个地址时,既需给出段名,又需给出段内地址。

4、碎片

分页:有内部碎片,无外部碎片。

分段:有外部碎片,无内部碎片。

5、共享和动态链接

分页:不容易实现

分段:容易实现


自己理解:为什么说分页是一维的分段是二维的呢?

因为分页管理 的页大小是一样的,通过 地址/页大小 可以直接得到一维页号。

而分段的每段长度是不一样的,所以需要二维  段号、段内位移 





八、HTTPS的常见状态、HTTPS的加密机制

参考来自​HTTPS的常见状态、​​​​HTTPS的加密机制​​  

1. HTTPS 概述

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer 或 Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https: URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。
众所周知,WEB服务存在http和https两种通信方式,http默认采用80作为通讯端口,对于传输采用不加密的方式;https默认采用443,对于传输的数据进行加密传输。目前主流的网站基本上开始默认采用HTTPS作为通信方式

2. 对称加密

【面试题 计网&操作系统】_缓存_05

对称加密算法的加密和解密都是用同一个密钥。

如果通信双方都各自持有同一个密钥,且没有别人知道,则两方的通信安全是可以被保证的(除非密钥被破解)。
然而,最大的问题就是这个密钥怎么让传输的双方知晓,同时不被别人知道。如果由服务器生成一个密钥并传输给浏览器,这个传输过程中密钥被别人劫持,之后他就能用密钥解开双方传输的任何内容。
如果浏览器内部预存了网站A的密钥,且可以确保除了浏览器和网站A,不会有任何外人知道该密钥,那理论上用对称加密是可以的。这样,浏览器只要预存好世界上所有HTTPS网站的密钥就可以了。显然,这样做是不现实的。
怎么办?解决这个问题,我们就需要非对称加密。

3. 非对称加密

【面试题 计网&操作系统】_服务器_06

基于对称加密存在的问题,又有了非对称加密。非对称加密算法需要一组密钥对,分别是公钥和私钥,这两个密钥是成对出现的。公钥加密的内容需要对应的私钥解密,私钥加密的内容需要对应的公钥解密。私钥由服务器自己保存,公钥发送给客户端。客户端拿到公钥后可以对请求进行加密后发送给服务端,这时候就算中间被截获,没有私钥也无法解密发送的内容,这样确保了客户端发送到服务端数据的安全。

4. 非对称加密改良方案

【面试题 计网&操作系统】_服务器_07

通过一组公钥私钥,已经可以保证单个方向传输的安全性,那用两组公钥私钥,是不是就能保证双向传输都安全了?请看下面的过程:


  1. 某网站拥有用于非对称加密的公钥A1、私钥A2;浏览器拥有用于非对称加密的公钥B1、私钥B2。
  2. 浏览器向网站服务器请求,服务器把公钥A1明文传输给浏览器。
  3. 浏览器把公钥B1明文传输给服务器。
  4. 之后浏览器向服务器传输的所有东西都用公钥A1加密,服务器收到后用私钥A2解密。由于只有服务器拥有私钥A2进行解密,所以能保证这条数据的安全。
  5. 服务器向浏览器传输的所有东西都用公钥B1加密,浏览器收到后用私钥B2解密。同上也可以保证这条数据的安全。

可见确实可行。抛开这里面仍有的漏洞不谈(下文会讲),HTTPS的加密却没使用这种方案,为什么?最主要的原因是非对称加密算法非常耗时,特别是加密解密一些较大数据的时候有些力不从心。而对称加密快很多。那我们能不能运用非对称加密的特性解决前面提到的对称加密的问题?

5. 非对称加密 + 对称加密

【面试题 计网&操作系统】_缓存_08

既然非对称加密耗时,我们考虑是否可以采用非对称加密+对称加密结合的方式,而且要尽量减少非对称加密的次数。
非对称加密、解密各只需一次的方法:


  1. 某网站拥有用于非对称加密的公钥A1、私钥A2。
  2. 浏览器向网站服务器请求,服务器把公钥A1明文给传输浏览器。
  3. 浏览器随机生成一个用于对称加密的密钥X,用公钥A1加密后传给服务器。
  4. 服务器拿到后用私钥A2解密得到密钥X。
  5. 这样双方就都拥有密钥X了,且别人无法知道它。之后双方所有数据都用密钥X加密解密即可。

HTTPS基本就是采用了这种方案。但还是有漏洞的。

6. 中间人攻击

【面试题 计网&操作系统】_缓存_09

中间人的确无法得到浏览器生成的对称密钥X,这个密钥本身被公钥A1加密,只有服务器才能用私钥A2进行解密。然而中间人却完全不需要拿到私钥A2就能劫持信息,请看:


  1. 某网站拥有用于非对称加密的公钥A1、私钥A2。
  2. 浏览器向网站服务器请求,服务器把公钥A1明文传输给浏览器。
  3. 中间人劫持到公钥A1,保存下来,把数据包中的公钥A1替换成自己伪造的公钥B1(它当然也拥有公钥B1对应的私钥B2)。
  4. 浏览器随机生成一个用于对称加密的密钥X,用公钥B1(浏览器不知道公钥被替换了)加密后传给服务器。
  5. 中间人劫持后用私钥B2解密得到密钥X,再用公钥A1加密后传给服务器。
  6. 服务器拿到后用私钥A2解密得到密钥X。

这样在双方都不会发现异常的情况下,中间人得到了对称密钥X。根本原因是浏览器无法确认自己收到的公钥是不是网站自己的。那么下一步就是解决这个问题:如何证明浏览器收到的公钥一定是该网站的公钥?

7. 数字证书

现实生活中,如果想证明某身份证号一定是小明的,怎么办?看身份证。这里政府机构起到了“公信”的作用,身份证是由它颁发的,它本身的权威可以对一个人的身份信息作出证明。互联网中也有这么一个公信机构,CA 机构。
网站在使用HTTPS前,需要向“CA机构”申请颁发一数字证书,数字证书里有证书持有者、证书持有者的公钥等信息。服务器把证书传输给浏览器,浏览器从证书里取公钥就可以了。然而这里又有一个显而易见的问题:证书本身的传输过程中,如何防止被篡改?即如何证明证书本身的真实性?数字证书怎么防伪呢?

8. 数字签名

我们把证书内容生成一份“签名”,比对证书内容和签名是否一致就能察觉是否被篡改。这种技术就叫数字签名。
下图中左侧是数字签名的制作过程,右侧是验证过程

【面试题 计网&操作系统】_缓存_10


数字签名的制作过程:


  1. CA拥有非对称加密的私钥和公钥。
  2. CA对证书明文信息进行hash。
  3. 对hash后的值用私钥加密,得到数字签名。

明文和数字签名共同组成了数字证书,这样一份数字证书就可以颁发给网站了。
那浏览器拿到服务器传来的数字证书后,如何验证它是不是真的?(有没有被篡改、掉包)

浏览器验证过程:


  1. 拿到证书,得到明文T1,数字签名S1。
  2. 用CA机构的公钥对S1解密(由于是浏览器信任的机构,所以浏览器保有它的公钥。详情见下文),得到S2。
  3. 用证书里说明的hash算法对明文T1进行hash得到T2。
  4. 比较S2是否等于T2,等于则表明证书可信。

为什么这样可以证明证书可信?
假设中间人篡改了证书的原文,由于他没有CA机构的私钥,所以无法得到此时加密后签名,无法相应地篡改签名。浏览器收到该证书后会发现原文和签名解密后的值不一致,则说明证书已被篡改,证书不可信,从而终止向服务器传输信息,防止信息泄露给中间人。

既然不可能篡改,那如果整个证书被掉包呢?
假设有另一个网站B也拿到了CA机构认证的证书,它想搞垮网站A,想劫持网站A的信息。于是它成为中间人拦截到了A传给浏览器的证书,然后替换成自己的证书,传给浏览器,之后浏览器就会错误地拿到B的证书里的公钥了,会导致上文提到的漏洞。
其实这并不会发生,因为证书里包含了网站A的信息,包括域名,浏览器把证书里的域名与自己请求的域名比对一下就知道有没有被掉包了。

制作数字签名时为什么需要hash一次?
最显然的是性能问题,前面我们已经说了非对称加密效率较差,证书信息一般较长,比较耗时。而hash后得到的是固定长度的信息(比如用md5算法hash后可以得到固定的128位的值),这样加密解密就会快很多。当然除此之外也有安全上的原因。

HTTPS必须在每次请求中都要先在SSL/TLS层进行握手传输密钥吗?
显然每次请求都经历一次密钥传输过程非常耗时,那怎么达到只传输一次呢?用session就可以。
服务器会为每个浏览器(或客户端软件)维护一个session ID,在TSL握手阶段传给浏览器,浏览器生成好密钥传给服务器后,服务器会把该密钥存到相应的session ID下,之后浏览器每次请求都会携带session ID,服务器会根据session ID找到相应的密钥并进行解密加密操作,这样就不必要每次重新制作、传输密钥了

9. HTTPS 工作原理

【面试题 计网&操作系统】_缓存_11



client向server发送请求https://baidu.com,然后连接到server的443端口。



服务端必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面,这套证书其实就是一对公钥和私钥。



传送证书
这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间、服务端的公钥,第三方证书认证机构(CA)的签名,服务端的域名信息等内容。



客户端解析证书
这部分工作是由客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随机值(密钥)。然后用证书对该随机值进行加密。



传送加密信息
这部分传送的是用证书加密后的密钥(随机值),目的就是让服务端得到这个密钥(随机值),以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。



服务端加密信息
服务端用私钥解密,得到了客户端传过来的密钥(随机值),然后把内容通过该值进行对称加密。



传输加密后的信息
这部分信息是服务端用密钥(随机值)对称加密后的信息,可以在客户端被还原。



客户端解密信息
客户端用之前生成的密钥(随机值)解密服务端传过来的信息,于是获取了解密后的内容。



[参考文献]


  1. 百度百科 HTTPS
  2. 看完这篇文章,我奶奶都懂了https的原理 ​​https://mp.weixin.qq.com/s/GZFiDQkZleSmriiNs4E1sA​​​
  3. 彻底搞懂HTTPS的加密机制 ​​https://zhuanlan.zhihu.com/p/43789231​​​
  4. HTTPS原理和CA证书申请(满满的干货) ​​https://blog.51cto.com/11883699/2160032​​​





九.什么悲观锁,什么是乐观锁

      悲观锁就是数据库里的两类锁,写锁(排他锁)、读锁(共享锁)

一级封锁协议可避免 更新丢失问题 (更新覆盖) 加写锁

二级封锁协议 可避免 读入脏数据( 发生异常rollback)加读锁

三级封锁协议 可解决 不可重复读的问题 (读出第一次求和,只读导致可悲其他事务修改   读出第二次进行验算  验算答案不对)

加写锁和读锁

      乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。类似对某一时刻的数据加了一个版本号 或者 时间戳  ,每次 冲突检测和数据更新 就行了

     悲观锁和乐观锁如何选择

在乐观锁与悲观锁的选择上面,主要看下两者的区别以及适用场景就可以了。


  1. 乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。
  2. 悲观锁依赖数据库锁,效率低。更新失败的概率比较低。

随着互联网三高架构(高并发、高性能、高可用)的提出,悲观锁已经越来越少的被应用到生产环境中了,尤其是并发量比较大的业务场景。


CAS实现乐观锁

CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS是一种非阻塞式的同步方式。




十 死锁的四个必要条件?如何避免与预防死锁

参考博客:​​博客​


1、什么是死锁

死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。例如,在某一个计算机系统中只有一台打印机和一台输入 设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。


2、死锁产生的原因

1. 系统资源的竞争

系统资源的竞争导致系统资源不足,以及资源分配不当,导致死锁。

2. 进程运行推进顺序不合适

进程在运行过程中,请求和释放资源的顺序不当,会导致死锁。


3、死锁的四个必要条件

互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

循环等待条件: 若干进程间形成首尾相接循环等待资源的关系

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。



4、 死锁的避免与预防


1. 死锁避免

死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。 
如果操作系统能保证所有进程在有限时间内得到需要的全部资源,则系统处于安全状态否则系统是不安全的。




    1. 安全状态是指:如果系统存在 由所有的安全序列{P1,P2,…Pn},则系统处于安全状态。一个进程序列是安全的,如果对其中每一个进程Pi(i >=1 && i <= n)他以后尚需要的资源不超过系统当前剩余资源量与所有进程Pj(j < i)当前占有资源量之和,系统处于安全状态则不会发生死锁。
    2. 不安全状态:如果不存在任何一个安全序列,则系统处于不安全状态。他们之间的对对应关系如下图所示:


 【面试题 计网&操作系统】_缓存_12

下面我们来通过一个例子对安全状态和不安全状态进行更深的了解 

【面试题 计网&操作系统】_服务器_13

如上图所示系统处于安全状态,系统剩余3个资源,可以把其中的2个分配给P3,此时P3已经获得了所有的资源,执行完毕后还能还给系统4个资源,此时系统剩余5个资源所以满足(P2所需的资源不超过系统当前剩余量与P3当前占有资源量之和),同理P1也可以在P2执行完毕后获得自己需要的资源。 
如果P1提出再申请一个资源的要求,系统从剩余的资源中分配一个给进程P1,此时系统剩余2个资源,新的状态图如下:那么是否仍是安全序列呢那我们来分析一下 

【面试题 计网&操作系统】_服务器_14

系统当前剩余2个资源,分配给P3后P3执行完毕还给系统4个资源,但是P2需要5个资源,P1需要6个资源,他们都无法获得资源执行完成,因此找不到一个安全序列。此时系统转到了不安全状态。



2. 死锁预防

我们可以通过破坏死锁产生的4个必要条件来 预防死锁,由于资源互斥是资源使用的固有特性是无法改变的。


  1. 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
  2. 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
  3. 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的资源才能申请较大编号的资源。




十一 B树和B+树的区别

都是m叉 多路平衡查找树

考虑磁盘IO 的话,B+树 更快,据库索引是存储在磁盘上的,当数据量大时,就不能把整个索引全部加载到内存了,只能逐一加载每一个磁盘页(对应索引树的节点)。所以我们要减少IO次数,对于树来说,IO次数就是树的高度。

B+树中间节点 不保存数据,于是有更多容量可以保存索引,于是更加的矮胖



十二 静态链接和动态链接的区别

​参考博客​

静态链接:

会对代码中需要的目标文件链接进来,同时会把很多没有的函数也一起引入进来,每个可执行程序会生成一个副本,

导致空间浪费。

优点:由于可执行程序里包含了所有需要的东西,执行的时候速度快。

缺点:浪费空间,更新困难,每当库函数发生改变,需要重新编译运行。

动态链接:

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件

优点:每个程序共用一个副本,节约空间,更新方便

缺点:性能有一定的损失。

4.动态链接地址是如何重定位的呢?

        前面我们讲过静态链接时地址的重定位,那我们现在就在想动态链接的地址又是如何重定位的呢?虽然动态链接把链接过程推迟到了程序运行时,但是在形成可执行文件时(注意形成可执行文件和执行程序是两个概念),还是需要用到动态链接库。比如我们在形成可执行程序时,发现引用了一个外部的函数,此时会检查动态链接库,发现这个函数名是一个动态链接符号,此时可执行程序就不对这个符号进行重定位,而把这个过程留到装载时再进行。



十三 C/C++编译过程

编译

简单的说,编译器将代码转化为可执行文件主要分为预编译、编译、汇编和链接四个过程,前三个是编译过程


  • 预编译: 预编译过程会对代码进行一些预处理,如删除所有的#define并将宏定义展开,删除所有的注释,将#include包含的文件都插入到预编译指令的位置,处理所有的条件编译指令等。
  • 编译: 这个过程较为复杂,包括词法分析(),语法分析,语义分析,中间语言生成等步骤,在编译期间也会对代码进行优化。最终会生成一个汇编文件。
  • 汇编: 将生成的汇编文件翻译成机器指令,生成一个目标文件。
  • 这个目标文件就是链接器所需要的文件,链接器会将所有的目标文件拼凑成一个可执行文件。



十四 CPU多级缓存与缓存一致性

​参考博客​

定义

cpu缓存是位于CPU与内存之间的临时存储器,它的容量比内存小的多,但是交换速度却比内存要快得多

为什么需要CPU cache?

cpu的频率太快了,快到主存跟不上,这样在处理器时钟周期内,cpu常常需要等待主存,浪费资源。cache的出现,是为了缓解cpu和主存之间速度的不匹配问题(结构:cpu->cache>memory)

CPU cache有什么意义?

1、时间局部性:如果某个数据被访问,那么在不久的将来它很可能被再次访问;

2、空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问;

CPU多级缓存-缓存一致性(MESI)

【面试题 计网&操作系统】_数据_15

用于保证多个CPU cache之间缓存共享数据的一致,也可以通过给总线加LOCK锁的方式来保证缓存一致性

每个缓存行有如下M、E、S、I 四种状态

【面试题 计网&操作系统】_数据_16

M(Modified):被修改,该缓存行只被缓存在该CPU的缓存中,并且是被修改过的,目前与主存的数据不一致,即该缓存需要在未来的某个时间点(并且允许其他CPU读取该主存中相应内存之间)写回主内存,当被写回主存之后,该缓存行的状态会变成独享(Exclusive)状态

E(Exclusive):独享的,该缓存行只被缓存在该CPU的缓存中,它是未被修改过的,与主存中的数据一致。该状态可以在任何时刻当有其它CPU读取该内存时变成共享状态。同样地,当CPU修改该缓存行中内容时,该状态可以变成Modified状态

S(Shared):共享的,该状态意味着该缓存行可能被多个CPU缓存,并且各个缓存中的数据与主存数据一致,当有一个CPU修改该缓存行时,其他CPU中对该缓存行的缓存可以被作废,变为无效状态(Invalid)

I(Invalid):该缓存时无效的



 十五 阻塞与非阻塞的区别

​参考博客​

阻塞和非阻塞可以看做一种性质,既然是性质,那么很多情况下都会存在。(可以多和io结合起来考虑)

阻塞:当某个事件或任务在执行过程中发出了一个请求操作,但是由于该请求操作需要的条件不满足,就会一直等在那,直到条件满足。比如我们都用过的:InputStream.read()/socket.read()/object.wait()等等,如果请求的资源没有准备好,那么请求的线程就阻塞在那了,只有等接受到数据之后才继续执行。

非阻塞:当某个事件或任务在执行过程中发出了一个请求操作后,但是由于该请求操作需要的条件不满足,那么被请求端会发出一个信息,告诉请求资源的线程,我这边资源没准备好,你可以去做其他的。



十六 为什么需要内存对齐

cpu把内存当成是一块一块的,块的大小可以是2,4,8,16 个字节,因此CPU在读取内存的时候是一块一块进行读取的,块的大小称为(memory granularity)内存读取粒度。

我们再来看看为什么内存不对齐会影响读取速度?

    假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:

           1.数据从0字节开始

        2.数据从1字节开始

解析:当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成了。

        当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,4,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。

     但是这还属于乐观情况,上文提到内存对齐的作用之一是平台的移植原因,因为只有部分CPU肯干,其他部分CPU遇到未对齐边界就直接罢工了。



十七 什么是内存抖动?内存抖动的主要原因?

内存抖动

现代操作系统都有一个叫虚拟内存的概念。操作系统如果只使用物理内存作为可用内存的话,会很受限制。于是就提出一种以廉价硬盘代替昂贵内存的方法,“扩充”可用内存。

   于是,就在硬盘上划出一部分硬盘空间用来暂时存放内存数据。当系统进程发现物理内存不够了,就在内存空间上找一些不活跃的进程,把它占用的内存复制到硬 盘上,空出来的内存就可以重新使用了。而这时内存被空出来的进程并不知道其实他们其实已经被停下来了。当这些被停下来的进程重新激活,就需要再找一块不那 么活跃的进程占用的内存空间,把它们的内存拷出来,把原来硬盘上的数据再拷回去。

  假如内存相对应用程序要求严重不足,就会导致这种数据的内存/硬盘频繁切换,反而占用了大量CPU时间,而其实这些CPU时间应该是用来运行程序的

1.内存抖动:指在短时间内有大量的对象被创建或者被回收的现象。

2.内存抖动产生原因:主要是频繁(很重要)在循环里创建对象(导致大量对象在短时间内被创建,由于新对象是要占用内存空间的而且是频繁,如果一次或者两次在循环里创建对象对内存影响不大,不会造成严重内存抖动这样可以接受也不可避免,频繁的话就很内存抖动很严重),内存抖动的影响是如果抖动很频繁,会导致垃圾回收机制频繁运行(短时间内产生大量对象,需要大量内存,而且还是频繁抖动,就可能会需要回收内存以用于产生对象,垃圾回收机制就自然会频繁运行了)。

3.内存抖动影响:频繁内存抖动会导致垃圾回收频繁运行,造成系统卡顿。



十八 FTP文件传输协议、SMTP简单邮件传输协议、DNS域名系统的实现各用了应用层的设么协议(TCP or UDP)

FTP、SMTP 均用了TCP协议,需要需要可信协议,SMTP是在FTP基础上实现的协议

DNS在进行区域传输的时候使用TCP协议,其它时候则使用UDP协议; 
    DNS的规范规定了2种类型的DNS服务器,一个叫主DNS服务器,一个叫辅助DNS服务器。在一个区中主DNS服务器从自己本机的数据文件中读取该区的DNS数据信息,而辅助DNS服务器则从区的主DNS服务器中读取该区的DNS数据信息。当一个辅助DNS服务器启动时,它需要与主DNS服务器通信,并加载数据信息,这就叫做区传送(zone transfer)。 参考来自:​​博客​



十九 LRU涉及的数据结构  

参考:​​博客​

LRU的典型实现是​​hash map + doubly linked list​​, 双向链表用于存储数据结点,并且它是按照结点最近被使用的时间来存储的。 如果一个结点被访问了, 我们有理由相信它在接下来的一段时间被访问的概率要大于其它结点。于是, 我们把它放到双向链表的头部。当我们往双向链表里插入一个结点, 我们也有可能很快就会使用到它,同样把它插入到头部。 我们使用这种方式不断地调整着双向链表,链表尾部的结点自然也就是最近一段时间, 最久没有使用到的结点。那么,当我们的Cache满了, 需要替换掉的就是双向链表中最后的那个结点(不是尾结点,头尾结点不存储实际内容)。

hash map + 双向链表



二十  struct里seziof的计算方法

struct 里面的sizeof的计算

如struct  exap

 {

short  a;

double b;

char c;

double d;

int e;

}size;

sizeof(struct  exap)=? 40

条件1:

Char
偏移量必须为sizeof(char)即1的倍数

int
偏移量必须为sizeof(int)即4的倍数

float
偏移量必须为sizeof(float)即4的倍数

double
偏移量必须为sizeof(double)即8的倍数
 

Short
偏移量必须为sizeof(short)即2的倍数




各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结 构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

总结:在一维的空间内,struct的内存分配每个值必须有一个偏移量,且偏移量满足上述条件1