1.请你说说 TCP 和 UDP 的区别
1.TCP是面向对象连接;而UDP是无面向对象连接
2.TCP确保所传输的数据一定会到达目的地,但时间却不能保证;而UDP不确保数据会按原来顺序到达,但具有实时性
3.TCP的使用场景是不在意传输数据速度,但在意传输数据准确;而UDP的使用场景则是对实时性要求较高
4.TCP的首部开销大,占用20字节;而UDP的首部开销小,只占用8个字节
5.TCP只支持点对点的数据传输;而UDP支持一对一、一对多、多对多等各种场景
6.TCP有拥塞控制和流量控制来确保数据的安全性,但如果网络拥塞的话,速度会比较慢;UDP即使在网络拥塞的时候,传输速度也不会慢
2.请你说说 TCP 三次握手四次挥手过程
tcp的三次挥手:的作用是保证 通信双方都能够正常的收发信息;三次握手的发生阶段是在客户端连接服务器的connect阶段开始的.
1.首先客户端发送一个syn=1,ack给服务器,
2.服务器接收到之后将ack+1,syn=1,ACK包给客户端
3.客户端在将ACK+1,ack +1包发送给服务器,这样服务器收到之后就能够,服务器客户端就能够双方都知道可以相互通信,三次握手完成可以开始传输数据。
tcp的四次挥手:的作用是将服务器和客户端的连接安全的断开,四次挥手是发生在客户端或者服务器断开连接的时候.
4.客户端向服务器发送一个fin=1,ack包,
5.服务器收到之后将ack+1包发送给客户端,
6.这时候,服务器会讲传输的数据传输完,之后再发一个fin的包给客户端,
7.客户端收到之后发送一个ACK=1,ack+1包给服务器,这样四次挥手就完成了,服务器和客户端双方都断开了链接。
3.请你说说 GET 和 POST 的区别
GET方法是向服务器请求数据的,而POST方法是向服务器上传数据的
1.最明显的功能用途不同,GET用于查询/获取数据,POST用于提交或修改数据
2.GET有长度限制(2048字节),POST没有长度限制
3.GET的参数会附在url之后,以?分隔url和传输的数据,而POST的内容会放在请求体之中。 4.GET是明文传输,可以直接在url中看到信息,而POST传输的数据无法直接看到
5.GET请求会保存在浏览器历史记录中,会被浏览器主动缓存,而post不会,除非手动设置
6.GET请求的参数类型只接受ASCII字符,而Post没有限制
7.get在浏览器回退时是无害的,而post会再次提交请求。
4.简述一下 C++ 中的多态
C++中的多态分为两种,静态多态和动态多态。
静态多态一般是在编译时实现,主要有函数重载,运算符重载和泛型编程。
动态多态一般是运行时实现,主要有虚函数的实现
1.静态多态有函数重载,运算符重载等,是在编译阶段就确认了函数地址,编译器会根据实参类型来选择调用合适的函数;
2.动态多态在程序运行阶段确认函数地址,通过派生类和虚函数实现运行时的多态,
在运行时,通过基类指针或者引用指向的对象来确认应该调用哪个类的虚函数,当父类指针(引用)指向的是父类,则调用父类函数,当指向的是子类则调用子类的函数.
3.实现动态多态的条件:要有继承关系以及虚函数重写(virtual),父类指针(引用)指向子类对象
4.动态多态实现原理:当类中声明虚函数时,编译器会在类中生成一个虚函数表,这个虚函数表由编译器自己维护;存在虚函数时,每一个对象都有一个指向虚函数表的指针,多态条用时虚函数指针会根据这个对象对应类的虚函数找到被调用函数入口.
5.请你介绍一下死锁,产生的必要条件,产生的原因,怎么预防死锁
1.死锁的概念以及产生条件:死锁是两个或两个以上的进程之间由于竞争现象而导致进程都不能继续执行的现象。
2.产生死锁的三个必要条件是互斥、不抢占和占有并等待,这些条件都具备只是有可能会造成死锁,只有第四个条件循环等待也具备的时候才会必然出现死锁。
3.如何避免:
破坏不可抢占条件(可以让优先级高的进程抢占优先级低的进程的资源)、
破坏请求与保持条件(当进程申请不到自己所需要的的资源时,必须释放掉自己所持有的资源)
破坏循环等待条件(进程只能往前申请资源,不能往后申请资源)
如何预防:有序资源分配法、银行家算法.
6.请你说说 MySQL 索引,以及它们的好处和坏处
1.索引是对数据库表中的一列或多列的值,进行排序的一种结构 ,使用索引可以快速访问数据库表中的特定信息,是加快数据库查询的技术。
2.索引好处:可以避免全表扫描,确保索引数据的唯一性,提升数据库查询性能。
3.索引坏处:创建和维护索引需要消耗时间,占用物理空间,当表中数据增加、删除和修改时,索引需要动态维护,降低了数据的维护速度。
7.简述一下什么是面向对象
面向对象的三大思想:
1.封装:将客观事物进行抽象,将其属性和方法合成为一个类,类封装了成员变量和成员函数,同时又实现对属性和方法的权限控制,降低与外界的耦合度.
2.继承:子类继承父类的各种属性和方法,同时子类还可以在父类的基础上重新定义和扩展父类的属性和方法,使其具有不同的功能,继承提高了代码的复用性及可维护性.
3.多态:同一调用语句在父类和子类间使用时具有不同的表现形式,可以使用同一段代码处理不同类型的对象,提高代码的复用性.
8.说一说进程通信的方式有哪些?
1.管道
2.消息队列
3.共享内存
4.信号量
5.套接字
6.信号、
一个链接:
系统编程__She001的博客-CSDN博客
9.请你说说进程和线程的区别
1. 进程有独立的地址空间,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间;
2. 进程和线程切换时,需要切换进程和线程的上下文,进程的上下文切换时间开销远远大于线程上下文切换时间,耗费资源较大,效率要差一些;
3. 进程的并发性较低,线程的并发性较高;
4. 每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制;
5. 系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了 CPU 外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源;
6. 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
10.说一说 epoll 的原理
得分点 epoll_create、epoll_ctrl、epoll_wt、红黑树、双向链表、epoll的两种工作模式 标准回答 epoll 是一种更加高效的 IO 复用技术,epoll 的使用步骤及原理如下:
1. 调用 epoll_create() 会在内核中创建一个 eventpoll 结构体数据,称之为 epoll 对象,在这个结构体中有 2 个比较重要的数据成员,一个是需要检测的文件描述符的信息 struct_root rbr(红黑树),还有一个是就绪列表struct list_head rdlist,存放检测到数据发送改变的文件描述符信息(双向链表);
2. 调用 epoll_ctrl() 可以向 epoll 对象中添加、删除、修改要监听的文件描述符及事件;
3. 调用 epoll_wt() 可以让内核去检测就绪的事件,并将就绪的事件放到就绪列表中并返回,通过返回的事件数组做进一步的事件处理。 epoll 的两种工作模式:
1. LT 模式(水平触发) LT(Level - Triggered)是缺省的工作方式,并且同时支持 Block 和 Nonblock Socket。在这种做法中,内核检测到一个文件描述符就绪了,然后可以对这个就绪的 fd 进行 IO 操作,如果不作任何操作,内核还是会继续通知。
2. ET 模式(边沿触发) ET(Edge - Triggered)是高速工作方式,只支持 Nonblock socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 检测到。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 进行 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。 ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
11.浏览器从输入 URL 开始到页面显示内容,中间发生了什么?
1.输入地址,浏览器查找域名的 IP 地址。
2.浏览器向 该 IP 地址的web 服务器发送一个 HTTP 请求,在发送请求之前浏览器和服务器建立TCP的三次握手,判断是否是HTTP缓存,如果是强制缓存且在有效期内,不再向服务器发请求,如果是HTTP协商缓存向后端发送请求且和后端服务器对比,在有效期内,服务器返回304,直接从浏览器获取数据,如果不在有效期内服务器返回200,返回新数据。
3.请求发送出去服务器返回重定向,浏览器再按照重定向的地址重新发送请求。
4.如果请求的参数有问题,服务器端返回404,如果服务器端挂了返回500。
5.如果有数据一切正常,当浏览器拿到服务器的数据之后,开始渲染页面同时获取HTML页面中图片、音频、视频、CSS、JS,在这期间获取到JS文件之后,会直接执行JS代码,阻塞浏览器渲染,因为渲染引擎和JS引擎互斥,不能同时工作,所以通常把Script标签放在body标签的底部。
6.渲染过程就是先将HTML转换成dom树,再将CSS样式转换成stylesheet,根据dom树和stylesheet创建布局树,对布局树进行分层,为每个图层生成绘制列表,再将图层分成图块,紧接着光栅化将图块转换成位图,最后合成绘制生成页面。
12.请你说说 HTTP 状态码及其含义
标准回答 HTTP状态码有:
1xx代表服务器端已经接受了请求。
2xx代表请求已经被服务器端成功接收,最常见的有200、201状态码。
3xx代表路径被服务器端重定向到了一个新的URL,最常见的有301、302状态码。
4xx代表客户端的请求发生了错误,最常见的有401、404状态码。
5xx代表服务器端的响应出现了错误。
加分回答 - 1xx:指定客户端相应的某些动作,代表请求已被接受,需要继续处理。由于 HTTP/1.0 协议中没有定义任何 1xx 状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送 1xx 响应。 - 2xx:代表请求已成功被服务器接收、理解、并接受。这系列中最常见的有200、201状态码。 - 200(成功):服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 - 201(已创建):请求成功并且服务器创建了新的资源。 - 202(已接受):服务器已接受请求,但尚未处理。 - 203(非授权信息):服务器已成功处理了请求,但返回的信息可能来自另一来源。 - 204(无内容):服务器成功处理了请求,但没有返回任何内容。 - 205(重置内容):服务器成功处理了请求,但没有返回任何内容。 - 206(部分内容):服务器成功处理了部分 GET 请求。 - 3xx:代表需要客户端采取进一步的操作才能完成请求,这些状态码用来重定向,后续的请求地址(重定向目标)在响应头Location字段中指明。这系列中最常见的有301、302状态码。 - 300(多种选择):针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。 - 301(永久移动):请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。 - 302(临时移动):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 - 303(查看其他位置):请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。 - 304(未修改):自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。 - 305(使用代理):请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。 - 307(临时重定向):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。 - 4xx:表示请求错误。代表了客户端看起来可能发生了错误,妨碍了服务器的处理。常见有:401、404状态码。 - 400(错误请求):服务器不理解请求的语法。 - 401(未授权):请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。 - 403(禁止):服务器拒绝请求。 - 404(未找到):服务器找不到请求的网页。 - 405(方法禁用):禁用请求中指定的方法。 - 406(不接受):无法使用请求的内容特性响应请求的网页。 - 407(需要代理授权):此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。 - 408(请求超时):服务器等候请求时发生超时。 - 409(冲突):服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。 - 410(已删除):如果请求的资源已永久删除,服务器就会返回此响应。 - 411(需要有效长度):服务器不接受不含有效内容长度标头字段的请求。 - 412(未满足前提条件):服务器未满足请求者在请求中设置的其中一个前提条件。 - 413(请求实体过大):服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。 - 414(请求的 URI 过长):请求的 URI(通常为网址)过长,服务器无法处理。 - 415(不支持的媒体类型):请求的格式不受请求页面的支持。 - 416(请求范围不符合要求):如果页面无法提供请求的范围,则服务器会返回此状态代码。 - 417 (未满足期望值):服务器未满足"期望"请求标头字段的要求。 - 5xx:代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。常见有500、503状态码。 - 500(服务器内部错误):服务器遇到错误,无法完成请求。 - 501(尚未实施):服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。 - 502(错误网关):服务器作为网关或代理,从上游服务器收到无效响应。 - 503(服务不可用):服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。 - 504(网关超时):服务器作为网关或代理,但是没有及时从上游服务器收到请求。 - 505(HTTP 版本不受支持):服务器不支持请求中所用的 HTTP 协议版本。
13.说一说常用的 Linux 命令
常用的 Linux 命令有:
1. cd:切换当前目录
2. ls:查看当前文件与目录
3. grep:通常与管道命令一起使用,用于对一些命令的输出进行筛选加工
4. cp:复制文件或文件夹
5. mv:移动文件或文件夹
6. rm:删除文件或文件夹
7. ps:查看进程情况
8. kill:向进程发送信号
9. tar:对文件进行打包
10. cat:查看文件内容
11. top:查看操作系统的信息,如进程、CPU占用率、内存信息等(实时)
12. free:查看内存使用情况
13. pwd:显示当前工作目录
14.请你说说 HTTP 和 HTTPS 的区别
1.由于HTTP简单快速的特性,当客户端向服务器端请求数据的时候,只需要传送请求方法和路径就可以取到结果,基于TCP,默认端口号为80,耗时可以简略计算为1RTT,传递的数据全部是明文传输,几乎没有安全性。 HTTPS是基于TLS的,而TLS又基于TCP,当客户端向服务器端请求数据的时候,服务器大概率会将客户端重定向到该服务器的443端口,进行新的TCP连接,此时服务器会返回一个证书文件,而不是响应报文体。此时客户端验证证书文件紧接创建对称密钥,之后重新和服务器建立TLS连接,当服务器返回ACK确认之后,连接正式建立,此时上方整个过程耗时为3RTT,并且之后和服务器的通信数据都是通过对称密钥加密过的,几乎无法破解。
HTTP和HTTPS的不同点总结如下:
1. HTTP是基于TCP的,而HTTPS是基于TLS的 - HTTP的往返时间为1RTT,而HTTPS的往返时间为3RTT
2. HTTP只需要创建一次TCP连接,而HTTPS需要创建两次TCP连接
3. HTTP的默认端口号为80,而HTTPS的默认端口号为443
4. HTTP的安全性很差,而HTTPS的安全性很强 。加分回答 HTTPS虽然在安全方面有很大的优势,但是缺点也很明显,如下: - HTTPS握手阶段耗费时间,几乎是HTTP的数倍,会延长页面的首次绘制时间和增加耗电 - HTTPS的效率没有HTTP高,如果部分数据内容实际上并不需要加密,会平白浪费计算机资源 - HTTPS的证书需要购买,功能越强大的证书价格更高 - HTTPS的加密并不能阻止某些网络攻击,如黑客攻击、拒绝服务攻击等。
15.简述一下浅拷贝和深拷贝 (类里面的构造函数)
1. 浅拷贝 浅拷贝又称为值拷贝,将源对象的值拷贝到目标对象中,如果对象中有某个成员是指针类型数据,并且是在堆区创建,则使用浅拷贝仅仅拷贝的是这个指针变量的值,也就是在目标对象中该指针类型数据和源对象中的该成员指向的是同一块堆空间。这样会带来一个问题,就是在析构函数中释放该堆区数据,会被释放多次。默认的拷贝构造函数和默认的赋值运算符重载函数都是浅拷贝。
2. 深拷贝 深拷贝在拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样指针成员就指向了不同的内存位置。并且里面的内容是一样的,这样不但达到了拷贝的目的,还不会出现问题,两个对象先后去调用析构函数,分别释放自己指针成员所指向的内存。即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误。
16.请你说说三种智能指针实现原理和使用场景,以及其线程安全
1. 智能指针实现原理 建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它,这样只有拥有对象的智能指针的析构函数会删除该对象。然后,让赋值操作转让所有权。这就是用于 auto_ptr 和 unique_ptr 的策略,但 unique_ptr 的策略更严格,unique_ptr 能够在编译期识别错误。 跟踪引用特定对象的智能指针计数,这称为引用计数(reference counting)。例如,赋值时,计数将加 1,而指针过期时,计数将减 1. 仅当最后一个指针过期时,才调用 delete。这是 shared_ptr 采用的策略。
2. 使用场景 如果程序要使用多个指向同一个对象的指针,应该选择 shared_ptr; 如果程序不需要多个指向同一个对象的指针,则可以使用 unique_ptr; 如果使用 new [] 分配内存,应该选择 unique_ptr; 如果函数使用 new 分配内存,并返回指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。
3. 线程安全 shared_ptr 智能指针的引用计数在手段上使用了 atomic 原子操作,只要 shared_ptr 在拷贝或赋值时增加引用,析构时减少引用就可以了。首先原子是线程安全的,所有 shared_ptr 智能指针在多线程下引用计数也是安全的,也就是说 shared_ptr 智能指针在多线程下传递使用时引用计数是不会有线程安全问题的。 但是指向对象的指针不是线程安全的,使用 shared_ptr 智能指针访问资源不是线程安全的,需要手动加锁解锁。智能指针的拷贝也不是线程安全的。
17.请你说说 TCP 如何实现可靠传输 和UDP 怎么样可以实现可靠的传输?
TCP部分
可靠传输就是通过TCP连接传送的数据是没有差错、不会丢失、不重复并且按序到达的。TCP是通过序列号、检验和、确认应答信号、重发机制、连接管理、窗口控制、流量控制、拥塞控制一起保证TCP传输的可靠性的。
可靠传输的具体实现是:
1.应用层的数据会被分割成TCP认为最适合发送的数据块。
2.序列号:TCP给发送的每一个包都进行编号,接收方对数据包进行排序,把有序数据传送给应用层,TCP的接收端会丢弃重复的数据。
3. 检验和:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。
4.确认应答:如果收到的数据报报文段的检验和没有差错,就确认收到,如果有差错,TCP就丢弃这个报文段和不确认收到此报文段。
5. 流量控制:TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。
6.拥塞控制:当网络拥塞时,减少数据的发送。
7. 停止等待协议:它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
8.超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
UDP 部分
UDP不是面向连接的协议,因此资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。如果想要使用UDP还要保证数据的可靠传输,就只能通过应用层来做文章。实现的方式可以参考TCP的可靠传输机制,差别就是将TCP传输层功能,如确认机制、重传功能、流量控制、拥塞控制等功能实现在了应用层。 加分回答 在应用层实现可靠传输关键点有两个,从应用层角度考虑分别是:
1. 提供超时重传机制,能避免数据报丢失的问题。
2. 提供确认序列号,保证数据拼接时候的正确排序。 请求端:首先在UDP数据报定义一个首部,首部包含确认序列号和时间戳,时间戳是用来计算RTT(数据报传输的往返时间),计算出合适的RTO(重传的超时时间)。然后以等-停的方式发送数据报,即收到对端的确认之后才发送下一个的数据报。当时间超时,本端重传数据报,同时RTO扩大为原来的两倍,重新开始计时。 响应端:接受到一个数据报之后取下该数据报首部的时间戳和确认序列号,并添加本端的确认数据报首部之后发送给对端。根据此序列号对已收到的数据报进行排序并丢弃重复的数据报。
18.请你介绍一下数据库的 ACID (这是啥,没有听过)
事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。在事务中的操作,要么都执行修改,要么都不执行,这就是事务的目的,也是事务模型区别于文件系统的重要特征之一。 事务需遵循ACID四个特性:
1. A(atomicity),原子性。原子性指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,整个事务的执行才算成功。事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。
2. C(consistency),一致性。一致性指事务将数据库从一种状态转变为另一种一致的状态。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
3. I(isolation),隔离性。事务的隔离性要求每个读写事务的对象与其他事务的操作对象能相互分离,即该事务提交前对其他事务都不可见,这通常使用锁来实现。 -
4. D(durability) ,持久性。事务一旦提交,其结果就是永久性的,即使发生宕机等故障,数据库也能将数据恢复。持久性保证的是事务系统的高可靠性,而不是高可用性。 加分回答 事务可以分为以下几种类型:
- 扁平事务:是事务类型中最简单的一种,而在实际生产环境中,这可能是使用最为频繁的事务。在扁平事务中,所有操作都处于同一层次,其由BEGIN WORK开始,由COMMIT WORK或ROLLBACK WORK结束。处于之间的操作是原子的,要么都执行,要么都回滚。
- 带有保存点的扁平事务:除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点(savepoint)用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。
- 链事务:可视为保存点模式的一个变种。链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务。注意,提交事务操作和开始下一个事务操作将合并为一个原子操作。这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行的。
- 嵌套事务:是一个层次结构框架。有一个顶层事务(top-level transaction)控制着各个层次的事务。顶层事务之下嵌套的事务被称为子事务(subtransaction),其控制每一个局部的变换。
- 分布式事务:通常是一个在分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。对于分布式事务,同样需要满足ACID特性,要么都发生,要么都失效。 对于MySQL的InnoDB存储引擎来说,它支持扁平事务、带有保存点的扁平事务、链事务、分布式事务。对于嵌套事务,MySQL数据库并不是原生的,因此对于有并行事务需求的用户来说MySQL就无能为力了,但是用户可以通过带有保存点的事务来模拟串行的嵌套事务。
19.请你说说 C++11、C++14、C++17、 都有什么新特性
1. C++11 新特新 - static_assert 编译时断言 - 新增加类型 long long ,unsigned long long,char16_t,char32_t,原始字符串 - auto - decltype - 委托构造函数 - constexpr - 模板别名 - alignas - alignof - 原子操作库 - nullptr - 显示转换运算符 - 继承构造函数 - 变参数模板 - 列表初始化 - 右值引用 - Lambda 表达式 - override、final - unique_ptr、shared_ptr - initializer_list - array、unordered_map、unordered_set - 线程支持库
2. C++14 新特新 - 二进制字面量 - 泛型 Lambda 表达式 - 带初始化/泛化的 Lambda 捕获 - 变量模板 - [[deprecated]]属性 - std::make_unique - std::shared_timed_mutex、std::shared_lock - std::quoted - std::integer_sequence - std::exchange
3. C++17 新特新 - 构造函数模板推导 - 结构化绑定 - 内联变量 - 折叠表达式 - 字符串转换 - std::shared_mutex 4. C++20 新特新 - 允许 Lambda 捕获 [=, this] - 三路比较运算符 - char8_t - 立即函数(consteval) - 协程 - constinit
20.说一说进程有多少种状态,如何转换
1. 进程有五种状态:创建、就绪、执行、阻塞、终止:
- 创建:一个进程启动,首先进入创建状态,需要获取系统资源创建进程管理块(PCB:Process Control Block)完成资源分配。
- 就绪状态:在创建状态完成之后,进程已经准备好,处于就绪状态,但是还未获得处理器资源,无法运行。
- 运行状态:获取处理器资源,被系统调度,当具有时间片开始进入运行状态。如果进程的时间片用完了就进入就绪状态。
- 阻塞状态:在运行状态期间,如果进行了阻塞的操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。等待再次获取处理器资源,被系统调度,当具有时间片就进入运行状态。
- 终止状态:进程结束或者被系统终止,进入终止状态。
21.说一说 select 的原理以及缺点
select 是 一种 IO 多路复用技术,它的主旨思想是:
1. 首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中,这个文件描述符的列表数据类型为 fd_set,它是一个整型数组,总共是 1024 个比特位,每一个比特位代表一个文件描述符的状态。比如当需要 select 检测时,这一位为 0 就表示不检测对应的文件描述符的事件,为 1 表示检测对应的文件描述符的事件。
2. 调用 select() 系统调用,监听该列表中的文件描述符的事件,这个函数是阻塞的,直到这些描述符中的一个或者多个进行 I/O 操作时,该函数才返回,并修改文件描述符的列表中对应的值,0 表示没有检测到该事件,1 表示检测到该事件。函数对文件描述符的检测的操作是由内核完成的。
3. select() 返回时,会告诉进程有多少描述符要进行 I/O 操作,接下来遍历文件描述符的列表进行 I/O 操作。
select 的缺点:
1. 每次调用select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
2. 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大;
3. select 支持的文件描述符数量太小了,默认是 1024(由 fd_set 决定);
4. 文件描述符集合不能重用,因为内核每次检测到事件都会修改,所以每次都需要重置;
5. 每次 select 返回后,只能知道有几个 fd 发生了事件,但是具体哪几个还需要遍历文件描述符集合进一步判断。
22.请你介绍一下 I/O 多路复用
I/O 多路复用是一种使得程序能同时监听多个文件描述符的技术,从而提高程序的性能。I/O 多路复用能够在单个线程中,通过监视多个 I/O 流的状态来同时管理多个 I/O 流,一旦检测到某个文件描述符上我们关心的事件发生(就绪),能够通知程序进行相应的处理(读写操作)。
Linux 下实现 I/O 复用的系统调用主要有 select、poll 和 epoll。
1. select 的主旨思想: - 首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中,这个文件描述符的列表数据类型为 fd_set,它是一个整型数组,总共是 1024 个比特位,每一个比特位代表一个文件描述符的状态。比如当需要 select 检测时,这一位为 0 就表示不检测对应的文件描述符的事件,为 1 表示检测对应的文件描述符的事件。 - 调用 select() 系统调用,监听该列表中的文件描述符的事件,这个函数是阻塞的,直到这些描述符中的一个或者多个进行 I/O 操作时,该函数才返回,并修改文件描述符的列表中对应的值,0 表示没有检测到该事件,1 表示检测到该事件。函数对文件描述符的检测的操作是由内核完成的。 - select() 返回时,会告诉进程有多少描述符要进行 I/O 操作,接下来遍历文件描述符的列表进行 I/O 操作。
select 的缺点:
1. 每次调用select,都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大;
2. 同时每次调用 select 都需要在内核遍历传递进来的所有 fd,这个开销在 fd 很多时也很大
3. select 支持的文件描述符数量太小了,默认是 1024(由 fd_set 决定);
4. 文件描述符集合不能重用,因为内核每次检测到事件都会修改,所以每次都需要重置;
5. 每次 select 返回后,只能知道有几个 fd 发生了事件,但是具体哪几个还需要遍历文件描述符集合进一步判断。
2. poll poll 的原理和 select 类似,poll 支持的文件描述符没有限制。
3. epoll 是一种更加高效的 IO 复用技术,epoll 的使用步骤及原理如下: - 调用 epoll_create() 会在内核中创建一个 eventpoll 结构体数据,称之为 epoll 对象,在这个结构体中有 2 个比较重要的数据成员,一个是需要检测的文件描述符的信息 struct_root rbr(红黑树),还有一个是就绪列表struct list_head rdlist,存放检测到数据发送改变的文件描述符信息(双向链表); - 调用 epoll_ctrl() 可以向 epoll 对象中添加、删除、修改要监听的文件描述符及事件; - 调用 epoll_wt() 可以让内核去检测就绪的事件,并将就绪的事件放到就绪列表中并返回,通过返回的事件数组做进一步的事件处理。 epoll 的两种工作模式: - LT 模式(水平触发) LT(Level - Triggered)是缺省的工作方式,并且同时支持 Block 和 Nonblock Socket。在这种做法中,内核检测到一个文件描述符就绪了,然后可以对这个就绪的 fd 进行 IO 操作,如果不作任何操作,内核还是会继续通知。 - ET 模式(边沿触发) ET(Edge - Triggered)是高速工作方式,只支持 Nonblock socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 检测到。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 进行 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。 ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件描述符的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
23.请你说说 new 的实现原理,new 和 malloc 的区别
1. new 的实现原理: 如果是简单类型,则直接调用 operator new(),在 operator new() 函数中会调用 malloc() 函数,如果调用 malloc() 失败会调用 _callnewh(),如果 _callnewh() 返回 0 则抛出 bac_alloc 异常,返回非零则继续分配内存。 如果是复杂类型,先调用 operator new()函数,然后在分配的内存上调用构造函数。
2. new 和 malloc 的区别 - new 是操作符,而 malloc 是函数;
3. 使用 new 操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而 malloc 则需要显式地指出所需内存的尺寸;
4. new 分配失败的时候会直接抛出异常,malloc 分配失败会返回 NULL;
5. 对于非简单类型,new 在分配内存后,会调用构造函数,而 malloc 不会;
6. new 分配成功后会返回对应类型的指针,而 malloc 分配成功后会返回 void * 类型;
7. malloc 可以分配任意字节,new 只能分配实例所占内存的整数倍数大小;
8. new 可以被重载,而 malloc 不能被重载;
9. new 操作符从自由存储区上分配内存空间,而 malloc 从堆上动态分配内存;
10. 使用 malloc 分配的内存后,如果在使用过程中发现内存不足,可以使用 realloc 函数进行内存重新分配实现内存的扩充,new 没有这样直观的配套设施来扩充内存。
24.说一说 STL 中有哪些常见的容器
STL 中容器分为顺序容器、关联式容器、容器适配器三种类型,
三种类型容器特性分别如下: 1. 顺序容器 容器并非排序的,元素的插入位置同元素的值无关,包含 vector、deque、list。
- vector:动态数组 元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能。
- deque:双向队列 元素在内存连续存放。随机存取任何元素都能在常数时间完成(仅次于 vector )。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
- list:双向链表 元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
2. 关联式容器 元素是排序的;插入任何元素,都按相应的排序规则来确定其位置;在查找时具有非常好的性能;通常以平衡二叉树的方式实现,包含set、multiset、map、multimap
- set/multiset set中不允许相同元素,multiset 中允许存在相同元素。
- map/multimap map 与 set 的不同在于 map 中存放的元素有且仅有两个成员变,一个名为 first,另一个名为 second,map 根据 first 值对元素从小到大排序,并可快速地根据 first 来检索元素。map 和multimap 的不同在于是否允许相同 first 值的元素。
3. 容器适配器 封装了一些基本的容器,使之具备了新的函数功能,包含 stack,queue,priority_queue。
- stack:栈 栈是项的有限序列,并满足序列中被删除、检索和修改的项只能是最进插入序列的项(栈顶的项),后进先出
- queue:队列 插入只可以在尾部进行,删除、检索和修改只允许从头部进行,先进先出。
- priority_queue:优先级队列 内部维持某种有序,然后确保优先级最高的元素总是位于头部,最高优先级元素总是第一个出列。
25.STL 容器用过哪些,查找的时间复杂度是多少,为什么?
STL 中常用的容器有 vector、deque、list、map、set、multimap、multiset、unordered_map、unordered_set 等。容器底层实现方式及时间复杂度分别如下:
1. vector 采用一维数组实现,元素在内存连续存放,不同操作的时间复杂度为: 插入: O(N) 查看: O(1) 删除: O(N)
2. deque 采用双向队列实现,元素在内存连续存放,不同操作的时间复杂度为: 插入: O(N) 查看: O(1) 删除: O(N)
3. list 采用双向链表实现,元素存放在堆中,不同操作的时间复杂度为: 插入: O(1) 查看: O(N) 删除: O(1)
4. map、set、multimap、multiset 上述四种容器采用红黑树实现,红黑树是平衡二叉树的一种。不同操作的时间复杂度近似为: 插入: O(logN) 查看: O(logN) 删除: O(logN)
5. unordered_map、unordered_set、unordered_multimap、 unordered_multiset 上述四种容器采用哈希表实现,不同操作的时间复杂度为: 插入: O(1),最坏情况O(N) 查看: O(1),最坏情况O(N) 删除: O(1),最坏情况O(N) 注意:容器的时间复杂度取决于其底层实现方式。
26.请你说说线程和协程的区别
线程和协程的区别如下:
1. 线程是操作系统的资源,线程的创建、切换、停止等都非常消耗资源,而创建协程不需要调用操作系统的功能,编程语言自身就能完成,所以协程也被称为用户态线程,协程比线程轻量很多;
2. 线程在多核环境下是能做到真正意义上的并行,而协程是为并发而产生的;
3. 一个具有多个线程的程序可以同时运行几个线程,而协同程序却需要彼此协作的运行;
4. 线程进程都是同步机制,而协程则是异步;
5. 线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力;
6. 操作系统对于线程开辟数量限制在千的级别,而协程可以达到上万的级别。
27.请你说说指针和引用的区别
指针和引用的区别有:
1. 定义和性质不同。指针是一种数据类型,用于保存地址类型的数据,而引用可以看成是变量的别名。指针定义格式为:数据类型 *;而引用的定义格式为:数据类型 &;
2. 引用不可以为空,当被创建的时候必须初始化,而指针变量可以是空值,在任何时候初始化;
3. 指针可以有多级,但引用只能是一级;
4. 引用使用时无需解引用(*),指针需要解引用;
5. 指针变量的值可以是 NULL,而引用的值不可以为 NULL;
6. 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了;
7. sizeof 引用得到的是所指向的变量(对象)的大小,而 sizeof 指针得到的是指针变量本身的大小;
8. 指针作为函数参数传递时传递的是指针变量的值,而引用作为函数参数传递时传递的是实参本身,而不是拷贝副本;
9. 指针和引用进行++运算意义不一样。
28.请你说说单例设计模式
1. 概念 单例设计模式(Singleton Pattern)是一种比较简单的设计模式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。注意: - 单例类只能有一个实例。 - 单例类必须自己创建自己的唯一实例。 - 单例类必须给所有其他对象提供这一实例。
2. 单例设计模式的优缺点 优点: - 单例模式可以保证内存里只有一个实例,减少了内存的开销。 - 可以避免对资源的多重占用。 - 单例模式设置全局访问点,可以优化和共享资源的访问。 缺点: - 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。 - 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。 - 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
3. C++ 单例设计模式的实现 - 私有化构造函数、拷贝构造函数、赋值函数 - 定义一个私有的本类的静态对象成员 - 定义一个公共的访问该示例静态成员方法,返回该静态对象成员
4. 单例设计模式的种类 - 懒汉式:获取该类的对象时才创建该类的实例 - 饿汉式:获取该类的对象之前已经创建好该类的实例
29.请你说说重载,重写,隐藏的区别
重载、重写、隐藏在定义、作用域、有无 virtual、函数名、形参列表、返回值类型等方面有区别。
1. 重载:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载,与返回值类型无关。
2. 重写:指不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。
隐藏的实质是:在函数查找时,名字查找先于类型检查。如果派生类中成员和基类中的成员同名,就隐藏掉。编译器首先在相应作用域中查找函数,如果找到名字一样的则停止查找。
3. 派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。
30.请你说说 TCP 和 UDP 的使用场景
UDP:语音、视频、寻址、游戏、广播。
TCP:邮件、远程登陆、超文本、文件、身份信息、重要内容
UDP的优点是快,没有TCP各种机制,少了很多首部信息和重复确认的过程,节省了大量的网络资源。缺点是不可靠不稳定,只管数据的发送不管过程和结果,网络不好的时候很容易造成数据丢失。又因为网络不好的时候不会影响到主机数据报的发送速率,这对很多实时的应用程序很重要,因为像语音通话、视频会议等要求源主机要以恒定的速率发送数据报,允许网络不好的时候丢失一些数据,但不允许太大的延迟。DNS和ARP协议也是基于UDP实现的,要求快速获取IP、MAC地址,如果基于TCP那么对整个因特网的资源占用过大且速度慢。还有游戏应用程序也是通过UDP来传输报文段,允许出现丢帧导致的卡顿,但是对游戏的整体体验不会产生严重的影响。所以UDP在语音、视频、寻址、游戏、广播方面有很好的应用前景,实时性高,允许部分的数据丢失。
TCP的优点是面向连接提供可靠交付,即对数据有保证、无差错的进行运输。当需要数据准确无误的运输给对方时,如浏览器中需要获取服务器资源使用的HTTP/HTTPS协议,需要保证文件准确、无差错,邮件服务器中使用的SMTP协议,保证邮件内容准确无误的传递给对方,或者是大型应用程序文件,这些都要保证文件的准确、无差错的运输给对方,所以一定要基于TCP来运输,而不是UDP。 加分回答 UDP的应用场景是根据它的部分特性决定的,如下: - 面向无连接 - 尽最大努力交付 - 面向报文 - 一对多 TCP的应用场景是根据它的部分特性决定的,如下: - 面向连接 - 单播,一对一 - 可靠交付(确认机制、重传机制、流量控制、拥塞控制等)