要失业了

要失业了

计算机网络

OSI

七层划分为

  • 应用层(HTTP,FTP,DNS) 文件传输,电子邮件,文件服务,虚拟终端
  • 表示层(JPEG,ASII)数据格式化,代码转换,数据加密
  • 会话层(RPC,NFS) 解除或建立与别的接点的联系
  • 传输层(TCP,UDP) 提供端对端的接口
  • 网络层 (IP,ARP,ICMP) 为数据包选择路由
  • 数据链路层(MAC,VLAN,PPP) 传输有地址的帧以及错误检测功能
  • 物理层。

五层划分为:应用层、传输层、网络层、数据链路层、物理层。

四层划分为:应用层、传输层、网络层、网络接口层。

输入URL之后

  • 浏览器查找域名对应的IP地址
    DNS查找过程为:
    浏览器缓存->系统缓存->路由器缓存->ISP DNS缓存->递归搜索
    递归搜索过程为:从根域名服务器到顶级域名服务器到你查询的域名服务器。

  • 浏览器打开TCP连接(默认端口为80),向该IP的服务器发送一条HTTP请求,如果浏览器存储了该域名下的cookie,那么cookie也会放入http请求中

  • 服务器给浏览器进行一个301永久重定向响应。该IP对应的服务器很可能是代理服务器,比如你输入“http://baidu.com”,而不是“http://www.baidu.com”,按道理这两个网址对应的是同一个网页,因此通过代理服务器的方式进行重定向相应,让这两个网址访问的是同一个网页。
  • TCP连接
  • 浏览器根据重定向地址再次进行HTTP请求。
  • 服务器分析HTTP请求,生成HTTP响应,将响应发给客户端。
  • 浏览器收到响应内容之后,生成主页框架,同时向服务端继续发送请求,请求的内容是主页里的一些资源,比如说图片、视频等。
  • 对于静态的页面内容,浏览器通常进行缓存,对于动态的内容通常不缓存,缓存的时间也是有期限的。
  • 浏览器向服务器发送异步请求,因为有些页面显示完成之后客户端仍需要与服务端保持联系。
  • 整个过程结束之后,浏览器关闭TCP连接。

构建请求

  • 应用层进行DNS解析
  • 应用层生成HTTP请求报文
  • 传输层建立TCP连接 因TCP是一个可靠的传输控制协议,传输层还会加入序列号、确认号、窗口大小、校验和等参数,共添加20字节的头部信息
  • 网络层使用IP协议来选择路线

TCP IP

区别

  • TCP是面向连接的,可靠性高,UDP是基于非连接的,可靠性低。
  • TCP三次握手延迟高,实时性较强。
  • 在传输相同大小数据时,TCP首部开销大,报头更复杂,实际保护数据较少。 但TCP提供超时重传机制,保证不会出现丢包乱序。UDP有丢包。
  • TCP提供双全工通信,每条TCP的连接只能点到点。UDP支持一对多,多对一,多对多的交互通信。
  • 数据链路层实现网络相邻结点间可靠的数据通信
  • 物理层传输数据

构建TCP请求会增加大量的网络时延,常用的优化方式如下所示

  • 资源打包,合并请求

  • 多使用缓存,减少网络传输

  • 使用keep-alive建立持久连接

  • 使用多个域名,增加浏览器的资源并发加载数,或者使用HTTP2的管道化连接的多路复用技术

TCP的可靠连接 报文的往返时间RTT

应用数据被分割成TCP认为最适合发送的数据块。

校验和:

发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。

确认应答(ACK)机制:

TCP将每个字节的数据都进行了编号,即为序列号。确认序号=序号+1
接收方收到报文就会确认(累积确认:对所有按序接收的数据的确认)
TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。

超时重传:

当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

如果主机A未收到B发来的确认应答,也可能是因为ACK丢了。因此主机B会收到很多重复的数据,那么,TCP协议需要能够识别出那些包是重复的包,并且把重复的包丢弃,这时候我们可以用前面提到的序列号,很容易做到去重的效果

超时重传的时间设置的太短,会引起很多报文的不必要重传;
时间设置的过长,又会使网络的空闲时间增大,降低传输效率;
TCP采用了一种自适应算法,它记录一个报文发出的时间,以及收到相应确认的时间,者两个时间之差就是报文的往返时间RTT;
最理想的情况下,找到一个最小的时间,保证确认应答一定能在这个时间内返回
Linux中,超时以500s为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍;如果重发一次仍然得不到应答,等待2500ms后再进行重传;如果仍然得不到应答,等待4500ms进行重传,依次类推,以指数形式递增;累积到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

连接管理机制

TCP是面向连接的,进行可靠性传输

流量控制:

让发送方的发送速率不要太快

TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。

流量控制的实现方法:接收端将自己可接受的缓冲区大小放入TCP首部中的”窗口大小字段”,通过ACK通知发送端;窗口大小字段越大,说明网络的吞吐量越高;接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值给发送端;发送端接受到这个窗口之后,就会减慢自己的发送速率;如果接受缓冲区满了,就会将窗口设置为0,这时发送方不在发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。

接收方有即时窗口(滑动窗口),随ACK报文发送

  • 数据包在传送过程中丢失了
    当发送端的某一段报文丢失后,发送端会一直收到1001这样的ACK;如果发送端主机连续三次收到同样的”1001”这样的应答,就会将对应的数据1001-2000重新发送;当成功接收到1001之后,再次返回的ACK就是6001了,接收端在之前就已经收到(2001-7000)了,被放到了接收端操作系统内核的接受缓冲区中。这种机制被称作快重传
  • 数据包已经递达,ACK丢了
    部分ACK丢失了不要紧,因为可以通过后续的ACK进行确认。

拥塞控制:

当网络拥塞时,减少数据的发送。

慢开始 、 拥塞避免 、快重传 和 快恢复

停止等待协议

也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组

TCP三次握手

TCP的几个状态 (SYN, FIN, ACK, PSH, RST, URG)

  • SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时, 客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再 以 ACK)消息响应。这样在客户机和服务器之间才能建立 起可靠的TCP连接,数据才可以在客户机和服务器之间传递。

  • FIN表示关闭连接,

  • ACK Acknowledgement[,在数据通信传输中,接收站发给发送站的一种传输控制 字符。它表示确认发来的数据已经接受无误。 ]

    双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。

    TCP四次握手

为什么连接的时候是三次握手,关闭的时候却是四次握手?

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没 有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可 能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。

为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

http

HTTP

报文构成

  • 请求方法
  • 请求url
  • HTTP版本和协议
  • 报文头部
  • 报文体

响应报文

  • 状态行(HTTP版本 状态码 状态码原因)响应头部
  • 响应内容

HTTP 状态码

  • 1开头:信息状态码
  • 2开头:成功状态码
  • 3开头:重定向状态码
  • 4开头:客户端错误状态码 403 服务端收到请求单拒绝提供请求
  • 5开头:服务端错误状态码

    HTTP1.0和HTTP1.1的区别

  • HTTP 1.1支持长连接和请求的流水线处理。HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。

  • HTTP 1.1增加host字段
  • 节约带宽

http 和 https 的区别

  • https协议需要申请证书
  • http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  • http端口(80),https端口(443)。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议更加安全

https 优点 缺点

  • 安全
  • 可以认证用户和服务器,确保数据发送倒正确的用户上。
  • 握手延迟高
  • 部署成本高

GET和POST的区别

  • GET 通过URL 传递,POST放在request body
  • GET长度有限制
  • GET更不安全
  • GET只能进行URL编码,POST支持多种编码方式。
  • GET请求回浏览器主动Cache ,而POST 支持多组编码方式
  • GET 请求参数回完整保留在浏览历史记录里,POST不会被保留
  • 本质都是TCP,链接
  • GET产生一个TCP数据包,POST产生两个数据包

IP MAC地址

  • MAC是硬件地址,用来定位网络设备的位置的,主要有数据链路层负责。而IP地址是IP协议提供的统一地址格式,为互联网上每个网络和每一台主机分配一个逻辑地址以此来屏蔽物理地址的差异。

TCP/IP 数据链路交互过程。

网络层等数据链蹭用mac地址作为通信目标,数据包达到网络等准备往数据链路层层发送的时候,首先会去自己的ARP缓存标,寻找目标IP的目标地址,就将IP的mac底池封装倒链路层数据保的包头。如果缓存没找找到,就会发起广播,所有收到广播的机器看这个IP是不是自己的,则以单ba的形式将自己MAC地址回复给请求的机器。

数据库

redis

与mongodb 数据库

  • 内存管理机制上,redis 全部存在内存,定期写入磁盘,当内存不够选择指定的LRU算法删除数据。MongoDB数据存在内存,由Linux 系统map实习,内存不够将热点放入内存其他存在磁盘。
  • 支持的数据结构 HASH SET LIST

数据类型

  • 字符串
  • 列表
  • 哈希
  • 集合
  • 有序集合

C++

虚函数以及多态

多态的实现主要分为静态多态和动态多态,静态多态,静态多态主要是重装载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。

虚函数的实现:在有虚函数的表中,类的的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际虚函数在代码段。当子类继承了父类的时候也会继承其函数表,当子类重写父类中虚函数时候,会将其继承到虚函数表中的地址替换为原来重新系的函数地址。使用了虚函数会增加访问内存开销,降低效率。

析构函数 虚函数

基类的析构函数要是虚函数的原因是,如果基类指针指向派生类,基类的析构函数不是虚函数的话,是不会调用派生类的析构函数的,只会调用基类的析构函数

关于虚函数的常见问题

虚函数的代价

1)带有虚函数的每个类会产生一个虚函数表,用来存储虚成员函数的指针

2)带有虚函数的每个类都会有一个指向虚函数表的指针

3)不再是内敛函数,因为内敛函数可以在编译阶段进行替代,而虚函数表示等待,在运行阶段才能确定到达采用哪种函数,所以虚函数不是内敛函数

那些函数不能是虚函数?

1)构造函数:对象的虚函数表指针需要通过构造函数初始化

2)内联函数:内联函数可以在编译阶段进行函数体的替换,而虚函数需要在运行期间进行确定

3)静态函数:静态函数不属于对象而属于类,因为静态成员函数没有this指针,所以无法访问对象的虚表指针,也就

无法访问类的虚函数表,将静态函数设置成虚函数也就没有任何意义,所以c++语法不支持将静态函数设置成虚函数

4)友元函数:友元函数不属于类,也不能被继承,没有继承特性的函数没有虚函数的说法

5)类外的普通函数:类外普通函数不是类的成员函数,同样不具备继承特性,也就没有虚函数的说法

虚函数和纯虚函数的区别?

1)纯虚函数只有定义,没有实现,虚函数既有定义,又有实现

2)含有纯虚函数的类不能定义对象,含有虚函数的类可以定义对象

菱形继承的内存结构?如何解决菱形继承存在的问题?

View Code

1)菱形继承的内存结构:现在有A,B,C,D四个类,B,C分别继承A类,D通过多重继承继承了BC两个类,现在D类中有两个getx(),D类不知道调用哪一个getx()

2)菱形继承的解决办法:虚继承

BC类都用Virtual标注,保证只有一个getx()被创建

View Code

虚析构函数的作用?父类的析构函数为什么一定要设置成虚函数?

父类虚析构函数就是为了避免内存泄漏,防止子类内存得不到释放造成内存泄漏

1.当父类的析构函数不声明成虚析构函数时,当子类继承父类,父类指针指向子类对象,delete掉父类指针,只会调动父类的析构函数,而不会调用子类的析构函数,从而造成子类对象内存泄漏

2.当父类的析构函数声明成虚析构函数时,当子类继承父类,父类指针指向子类对象,delete掉父类指针,先调动父类的析构函数,然后调用子类的析构函数,不存在子类对象内存泄漏的问题

只要存在继承关系,则父类的虚函数必须定义成虚函数!

构造函数和析构函数中为什么不可以调用虚函数?

背景知识:

1.构造子类对象时,首先调用父类构造函数初始化对象的父类部分,在执行父类的构造函数时,对象的子类部分都是未初始化的,实际上此时对象还不是一个子类对象

2.析构子类对象时,先析构子类部分,然后按照构造顺序逆序析构父类部分

所以在运行子类的构造和析构函数时,对象都是不完整的,为了适应这种不完整,编译器视对象类型为当前构造或析构函数所在类的类型,由此造成的结构就是:在父类的构造或析构函数中,会将子类对象当作父类对象看待

在这样的背景下

如果我们在父类的构造或析构函数中调用虚函数,调用的往往是当前类的虚函数,达不到多态的效果,跟普通函数调用没有区别

构造函数为什么不能为虚函数?什么情况下析构函数必须为虚函数?

1)因为虚函数表指针必须在构造函数中初始化,所以构造函数不能为虚函数!

2)当存在继承关系时,父类的析构函数必须为虚函数,这样在父类指针指向子类对象,delete父类指针时,子类对象才不会内存泄漏

字节对齐

让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。

字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding)
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)

C++中内存泄漏的几种情况

  • 在类的构造函数和析构函数中没有匹配的调用new和delete函数
  • 没有正确地清除嵌套的对象指针
  • 在释放对象数组时在delete中没有使用方括号
  • 缺少拷贝构造函数
  • 缺少重载赋值运算符
  • 关于nonmodifying运算符重载的常见迷思
  • 没有将基类的析构函数定义为虚函数

new和malloc的区别

属性

new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。

参数

使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。

返回类型

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void ,需要通过强制类型转换将void指针转换成我们需要的类型。

分配失败

new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。

自定义类型

new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。

malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。

重载

C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。

内存区域

new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中

左值和右值的区别,右值引用的概念及其应用场景

左值(lvalue):

非临时性对象的表达式。有名字,可以取地址。如:非匿名对象(包括变量),函数返回的引用,const对象等都是左值。

右值(rvalue):

临时性对象的表达式,没有名字,临时生成的,不可取地址。如:立即数,函数的返回值。

左右值转换

右值转化为左值 :直接新建变量然后赋值就可以了。
int b=a+1;将a+1这个右值转变为左值

左值转化为右值: std::move()
move (a) ; 将a这个左值转变为了右值

左值引用

左值引用本质是指针常量,左值引用只能引用左值。

加 const 就可以常引用

右值引用

右值引用绑定到右值,绑定后本来会被销毁的右值的生存期会延长至与绑定到它的右值引用的生存期。

在汇编层面右值引用做的事情和常引用是相同的,即产生临时量来存储常量。但是,唯一 一点的区别是,右值引用可以进行读写操作,而常引用只能进行读操作。

右值引用的用途

  • 避免拷贝,提高性能,实现move()
  • 避免重载参数的复杂性,实现forward()

free与delete的区别

  1. delete 用于释放 new 分配的空间,free 有用释放 malloc 分配的空间

  2. delete [] 用于释放 new [] 分配的空间

  3. delete 释放空间的时候会调用 相应对象的析构函数

    顺便说一下new在分配空间的时候同时会调用对象的构造函数,对对象进行初始化,使用malloc则只是分配内存

  4. 调用free 之前需要检查 需要释放的指针是否为空,使用delete 释放内存则不需要检查指针是否为NULL

  5. free 和 delete 不能混用,也就是说new 分配的内存空间最好不要使用使用free 来释放,malloc 分配的空间也不要使用 delete来释放

堆和栈的区别是什么

1、堆栈空间分配区别

栈(操作系统):由操作系统(编译器)自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

2、堆栈缓存方式区别

栈使用的是一级缓存, 它们通常都是被调用时处于存储空间中,调用完毕立即释放。

堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

3、堆栈数据结构区别

堆(数据结构):堆可以被看成是一棵树,如:堆排序。

栈(数据结构):一种先进后出的数据结构。

静态函数

只能在本$CPP$里面使用。

类的静态成员

多个对象数据共享,安全

cast转换

  • const_cast 将const 转为非 const
  • static_cast,隐式转换,用于多态向上转化

dynamic_cast

动态类型转换。用于虚函数的类

reinterpret_cast

啥都可以转换

指针与引用

  • 指针有自己的空间大小是4
  • 可以有const 指针
  • 多级指针
  • ++运算符
  • 返回动态内存分类的对象或者内存必须使用指针

    virtual关键字的作用

  • 在派生类中重新定义基类的方法

  • 为多态基类声明virtual析构函数

  • 抽象基类

resize reserve

  • resize:
  • 改变最大容量,不会生成元素

    智能指针

  • auto_ptr,share_ptr,weak_ptr,unique_ptr

1) unique_ptr 实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能执政可以指向该对象。他对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delect”)特别有用
2) shared_ptr 实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在最后一个引用被销毁的时候释放。可以查看资源所有个数
3) weak_ptr 种一个种不控制对象生命周期的智能指针

智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束是忘记释放,造成内存泄漏。使用智能指针可以很大程度上避免这个问题。因为智能指针就是个类,超出了类的作用域,类会自动析构函数,析构函数就会自动释放资源。所有智能指针的就算自动释放内存。

静态函数与虚函数的区别

静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定,调用回增加一次内存开销。

重载 重写

  • 两个函数名一样,参数不一样
  • 子类继承父类,父类种是虚函数

虚函数和多态

多态的实现分成静态多态和动态多态

  • 静态主要是重载,编译的时候已经确定
  • 动态是用虚函数机制实现,在运行期间动态绑定。

例子:一个父类类型的指针指向一个子类对象的时候,使用父类指针去调用子类种重写了的父类中的虚函数,会调用子类重写过后的函数在父类种声明为加了virtual关键子的函数,子类种重写时候不需要加virtual也是虚函数

虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数标,表中放了虚函数的地址,实际的虚函数在代码段种。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中的虚函数的时候,会将继承倒虚函数的地址替换为小红鞋写的函数地址,使用虚函数会增加访问内存开销,降低效率。

new/delete malloc/free 的区别

后者必须指明申请内存空间大小,后者对于类,不会调用构造函数 和析构函数

虚函数表具体是怎么实现运行时多态的?

子类若重写父类虚函数,虚函数表中,函数地址会被替换,对于存在虚函数二点类的对象,在VS种,对象的对象模型的头部存放指向虚函数表的指针,通过该机制实现多态。

操作系统

用户态与内核态

用户态和内核态是操作系统的两种运行级别,两者最大的区别就是特权级不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常和中断。

上下文切换

当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次 再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

进程与线程的概念

进程

  • 进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发

线程

  • 是进程的子任务,是CPU调度和分派的基本单位。用于保证程序的实时性,实现了进程内部的并发。
  • 线程是操作系统可识别的最小执行和调度单位。每个线程都肚子占用一个虚拟处理器,独自的寄存器组,指令计数器和处理器状态。
  • 每个线程完成不同的任务,但是共享同一地址空间的。(动态内存,映射文件,目标代码),打开的文件列表和其他内核资源

区别

  • 一个线程只能属于一个进程,一个进程多个线程,依赖关系。
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
  • 资源分配给进程,同一个进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段,数据段,扩展段。但是每个线程拥有自己的栈段,(用来存局部变量和临时变量)
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位
  • 系统开销,在创建或撤销进程时,系统都要为之分配或回收资源,如内存空间。线程切换只需保存或者设置少量寄存器的内容,不涉及存储器管理方面的操作,可见进程的切换开销比较大
  • 通信,线程同步和通信容易实现,需要进程同步和互斥手段的辅助,以保存数据的一致性。在有的系统中线程的切换,通信,同步 无须系统内核的干预。
  • 进程编程 简单可靠,创建开销大。
  • 进程不会互相影响。
  • 进程适用与多核,多机分布;线程适用于多核。

进程调度算法

  • 最短工作优先(SJF);
  • 最短剩余时间优先(SRTF);
  • 最高响应比优先(HRRF);
  • 优先级调度(Priority);
  • 轮转调度(RR)。

    进程间通信方式

  • 管道,系统IPC,套接字

普通管道PIPE

命名管道FIFO

系统IPC

  • 消息队列
  • 信号量,计数器,用来控制多个进程对共享资源的访问,实现进程兼得互斥和同步。
  • 信号
  • 共享内存

线程间通信方式

  • 临界区,通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问
  • 互斥量 采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限
  • 信号量,允许多个线程同时访问一个资源
  • 事件 通知操作的方式来保证多线程同步

Linux 虚拟地址空间

请求分页系统,请求分段系统和请求段页系统都是针对虚拟内存。

好处

  • 扩大地址空间
  • 内存保护
  • 公平内存分配
  • 当进程通信时,可采用虚拟内存共享的方式实现
  • 当不同的进程使用相同代码时比如库文件中的代码,物理内存中可以存储一分这样的代码,节省内存
  • 虚拟内存很适合在多道程序设计系统中使用,许多程的片段同时保存在内存中,当一个程序等待他的一部分读入内存,可以把CPU交给另一个进程使用。内存中可以保留多个进程,系统的并发度提高
  • 在程序分配连续内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际的物理内存的连续攻击

代价

  • 管理的数据结构占用内存
  • 虚拟地址到物理地址的转化,增加了指令的执行时间。
  • 页面的换入还出需要磁盘I/O
  • 一页一部分数据,浪费内存

并发 并行

  • 并发,运行
  • 并发,交织运行,不能提高性能只能提高效率
  • 并行,严格物理意义上的同时运行,多核CPU,两个程序分别运行在两个核,互不影响

OS缺页置换算法

  • FIFO,置换最先调入内存的页面,即置换在内存中驻留时间最久的页面。按进入内存的 先后次序排成队列,从队尾进入,从队首删除。
  • LRU:置换最近一段时间最长时间未访问的页面。

死锁

必要条件

  • 互斥条件
  • 请求和保持条件
  • 不可剥夺条件
  • 环路等待条件

解决方法

  • 资源一次性分配
  • 可剥夺资源
  • 资源有序分配法

epoll

IO多路复用

TCP和UDP可以同时监听相同的端口吗

可以

但一台设备里,
tcp协议里port号必须有唯一性。
同样,
udp协议里port号必须有唯一性。

COOKIE和SESSION有什么区别?

cookie保存在客户端,session保存在服务器端,
cookie目的可以跟踪会话,也可以保存用户喜好或者保存用户名密码
session用来跟踪会话