后端开发 / C++面经学习过程 2021春招 (边学习边更新)

牛客面试宝典:链接
(感觉这个不够详细,一抠细节问就不会了,背完之后,第一次面试(字节)基础的答得上来,但是细问都不会,直接被感谢信了)

牛客面经区:链接
(背完面经必知后,主看别人问的问题,在github cs note / 书上找细学不会的知识)

Github cs note(lxl大佬的提供):链接
(详细知识)

某大佬的后端面经必知:链接
(知识点总结)

3.18 ~ 3.31 一直在背牛客的面试宝典,4.01一面直接感谢信后,发现这个玩意背完了也不能学到基础知识,一扣细节就问啥啥不会。从现在开始在春招结束前,必须******了

不会新学:
1、讲讲 cookie 与 session,如果 cookie 被恶意修改怎么办 :cookie(百度百科) session(百度百科)
一、cookie保存在客户端、session保存在服务端
二、session被默认保存在服务器的一个文件(不是内存),也可以保存在数据库,内存。
三、session的运行依赖于session id,而session id存在cookie里(也可以通过其他方式传递session id,如url中)
四、用户验证的场合一般用session
保证cookie的安全性:
一、避免用户凭证的泄露:客户端中,内存cookie比文件cookie更难泄露,特别是经过特殊处理的浏览器,几乎不可能截取,当浏览器关闭,内存cookie也就失效了。所以应该用内存cookie。
二、不应该在cookie中直接保存用户凭证,而是通过一个无意义的标识符表示,服务器端可以通过这个标识符找到真正的用户凭证,每次登陆都随机生成这个标识符,即便同一用户登录。
三、设置一个cookie有效时间,超过这个时间cookie就失效。
四、通过设置secure属性来指定https安全协议发送cookie。
五、通过设置HTTPonly来防止客户端脚本通过document.cookie访问Cookie,保证不被脚本攻击和篡改。

2、波兰序列和逆波兰序列: 链接

3、堆排序、基数排序(思路,复杂度,应用):堆排基排
基排时间复杂度:最好最坏都是 O ( d ∗ ( n + r ) ) O(d*(n+r)) O(d(n+r)) (D是位数,n是元素个数,r是采用的基数(比如10进制则r=10)),空间复杂度: O ( n + r ) O(n+r) O(n+r),稳定;
堆排时间复杂度:最好最坏都是 O ( n l o g n ) O(nlogn) O(nlogn) ,空间复杂度:O(1),不稳定;应用场景:数据量很大的情况

3、分治思想解决大整数乘法:(不会,感觉大概不会考)

4、TCP超时时间RTO的计算:RTTs(加权平均往返时间),RTT(报文往返时间)
R T T s = ( 1 − a ) ∗ ( R T T s ) + a ∗ ( R T T ) RTTs=(1-a)*(RTTs)+a*(RTT) RTTs=(1a)(RTTs)+a(RTT) 0 ≤ a < 1 0 \le a<1 0a<1
R T O = ( R T T s + R T T d ∗ 4 ) RTO=(RTTs+RTTd*4) RTO=(RTTs+RTTd4) RTTd为偏差的加权平均值

5、TCP的滑动窗口:
发送方:如果窗口最左边的字节已经发送并且受到确认,就把窗口向右移。
接收方:如果有字节未按序到达,例如{31,34,35},34,35未按序到达,则只发送31的确认,发送3个确认回复,表示31下一个字节没有按序到达。
在这里插入图片描述
6、TCP连接的11种状态变迁:连接

7、数据库 having 和 where 的区别: 连接
where是约束声明,在返回结果之前起作用(不需要分组,分组前过滤数据,所以不包含聚组函数)
having是过滤声明,在返回结果之后起作用(分组之后过滤)
having 需要配合 group by 使用

8、socket的过程:连接
网络中如何识别一个进程:(IP地址,协议,端口) IP地址识别主机,端口+协议识别主机中唯一的程序。
socket():创建一个socket描述符;bind():绑定ip地址和端口号到socket上;listen() / connect():服务器bind后用listen监听socket(socket创建默认主动,使用listen后变为被动),客户端通过connect()请求建立连接;accept():服务端收到connect()之后用accept()接受请求;最后读写用read() /
write() 等函数。读写完毕之后用close()关闭对应的socket描述字,使socket描述字的引用计数减一,当引用计数为0,则客户端向服务端发送终止连接请求。
在这里插入图片描述
客户端的connect在三次握手的第二次返回,而服务器端的accept在三次握手的第三次返回。

9、数据在各层之间的传递过程
在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。

路由器只有下面三层协议(物理层,数据链路层,网络层),因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。

10、IP地址的分类、私有地址的划分、子网掩码的作用、特殊地址的用途:IP地址分为A,B,C,D,E五类(IPv4)
A:0开头,网络号为0后7位,主机号为后24位,范围:1.0.0.1~126.255.255.254
B:10开头,网络号为10后14位,主机号为后18位,范围:128.0.0.1~191.255.255.254
C:110开头,网络号为110后21位,主机号为后8位,范围:192.0.0.1~223.255.255.254
D:1110开头(组播地址)范围:224.0.0.1~239.255.255.254
E:1111开头(科研地址)范围:240.0.0.1~255.255.255.254
私有地址划分:
A类:10.0.0.0~10.255.255.255
B类:172.16.0.0~172.31.255.255
C类:192.168.0.0~192.168.255.255
子网掩码的作用:一是用于屏蔽IP地址的一部分以区别网络标识和主机标识,并说明该IP地址是在局域网上,还是在远程网上。二是用于将一个大的IP网络划分为若干小的子网络。
192.168.0.0/26,/26表示子网掩码的缩写,有26个前缀1,192.168.0.0到192.168.0.63分别写出二进制,数出相同前缀有多少,即为/多少(属于同一网段)
特殊地址的用途:
0.0.0.0:未定地址,它表示这样一个集合:所有不清楚的主机和目的网络。
255.255.255.255:限制广播地址(本网段(同一广播域)的所有主机)
127.0.0.1:本机地址(可以用测试,比如ping 自己)
224.0.0.1:组播地址,从224.0.0.0到239.255.255.255都属于这类地址。244.0.0.1特指所有主机,224.0.0.2特指所有路由器,用于特定程序及多媒体程序

11、ICMP协议介绍,以及适用场景:
ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。
应用场景:
Ping:主要用来测试两台主机之间的连通性。通过向目的主机发送Echo请求报文,目的主机收到之后发送一个Echo回复报文,Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。
Traceroute:用来跟踪一个分组从源点到终点的路径。Traceroute 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报,并由目的主机发送终点不可达差错报告报文。

12、数据链路层差错检测的方式:循环冗余检验(CRC)检测比特差错。连接

13、HTTP协议中短轮询、长轮询、长连接、短连接的区别:连接

第一个区别是决定的方式,一个TCP连接是否为长连接,是通过设置HTTP的Connection Header(keep-alive,close)来决定的,而且是需要两边都设置才有效。而一种轮询方式是否为长轮询,是根据服务端的处理方式来决定的,与客户端没有关系。

第二个区别就是实现的方式,连接的长短是通过协议来规定和实现的。而轮询的长短,是服务器通过编程的方式手动挂起请求来实现的。

14、常见的应用层协议以及对应端口号:
FTP(文件传输协议)分为FTP客户端、FTP服务端,服务端存储文件,客户端通过协议访问服务器的资源,一般传输大文件也用FTP,端口号20(传输数据)、21(传输控制信息)
SSH (安全外壳协议)是较可靠,专为远程登录会话和其他网络服务提供安全性的协议。端口号22
Telnet(远程终端协议)是Internet远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。端口号23
HTTP(超文本传输协议)端口号80
DNS(域名解析协议)端口号53,基于UDP实现的

15、HTTP请求报文和响应报文的格式和内容:链接
HTTP请求报文:请求行,请求头部,空行,请求包体
HTTP响应报文:状态行,响应头部,空行,响应包体

16、HTTP的缓存首部:连接

17、Linux下怎么查询某端口是否已被占用:
一、lsof -i:<端口号> 可以显示出该端口被什么进程占用
二、killall pure-ftpd 杀死所有名叫pure-ftpd的进程
三、或者 netstat -apn|grep <端口号>

18、虚拟技术:把一个物理实体转换为多个逻辑实体;
两种虚拟技术:
时(时间)分复用技术:多个进程在一个处理器上并发运行,轮流占用处理器,每次只执行一个时间片并快速切换。
空(空间)分复用技术:虚拟内存,把物理内存抽象为地址空间,每个程序有自己的地址空间,地址空间被分成很多块,每块叫一页,页被映射到物理内存(不需要连续),用到没有在物理空间的页,就用页面置换算法将该页置换到内存。

19、同步和异步:
同步:提交请求之后,等待回复的过程不能干别的事,直到处理完成并返回;
异步:提交请求之后,等待回复的过程可以干别的事;

20、进程的通信机制:
管道:通过调用pipe(int fd[2])创建,fd[0]用于读,fd[1]用于写(限制:只支持半双工通信(双向交替传输),只能在父子进程或兄弟进程中使用)
FIFO(命名管道):去除管道只能在父子进程之间使用的限制,常用与客户端-服务端的应用程序,其作为汇聚点,在客户端与服务端进程之间传递数据。
消息队列:
在这里插入图片描述共享内存:允许多个进程共享一个存储区,所以这是最快的一种IPC。
多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。

socket:与其它通信机制不同的是,它可用于不同机器间的进程通信。

21、用户态和内核态:
用户态和内核态是CPU执行级别的划分等级,在高执行级别下,代码可以执行特权指令,访问任意的物理地址
内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
为什么要有用户态和内核态:由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 – 用户态和内核态。

22、为什么需要分段,段页式 连接
例:一个编译器编译的时候建立了多个表,有四个表动态增长,如果用分页系统则会出错,因为动态增长会导致覆盖问题。
分段就是把每个表分成段,每段有一个独立地址空间,大小可以不一样,且能动态增长。
段页式:把程序的地址空间分成多个独立的段,每段再分成大小相同的页,这样可以既可以拥有分段系统的共享和保护,又可以有分页的虚拟内存功能。
在这里插入图片描述
23、磁盘与磁盘调度算法:
盘面:一个扇区有多个盘面;
磁道:一个盘面有多个磁道,是盘面上的圆形带状区域;
扇区:磁道上的一个弧段,一个磁道可以有多个扇区,是最小的物理储存单位;
磁头:与盘面很相近,用来把盘面的磁场信号转换成电信号;
在这里插入图片描述
影响读取磁盘块的时间的因素:寻道时间:把磁头移到对应磁道;旋转时间:把磁头移动到磁道对应扇区;实际的数据传输时间;
磁盘调度算法:
一、先来先服务:按磁盘请求顺序调度。
二、最短寻道时间优先:优先调度与当前磁道最近的磁道的请求。
三、电梯算法:按一个方向进行调度,直到当前方向没有请求位置。

24、宏定义和内联函数的区别:
宏定义是预处理器在预编译阶段替换到代码里,而内联函数是编译器控制的,他是真正的函数,只是在调用的时候像宏定义一样展开,可以避免被调用的开销,且编译器会检查参数,所以更安全。
类内部定义的函数体默认为内联函数,所以内联函数应用的最广的是在C++类中作为读写私有保护成员的函数。

25、结构体和类的区别:
一、结构体是实值类型,类是引用类型。实值类型直接存数据的值,而引用类型存的是实际数据的引用。所以可以把结构体当做值操作,而类则需要引用才能操作。
二、结构体的定义是用于多个数据描述一个整体,而类一般用于有层次的数据上。
三、类是反应现实事物的一种抽象,而结构体是对不同数据类别的包装,所以结构体没有类的继承多态。
四、构造函数是用来初始化类的字段存在,结构体不需要初始化,没有构造函数也能用,所以结构体没有默认的构造函数,也可以写构造函数。
五、结构体没有protect修饰符。
六、结构体没有析构函数。
七、类必须要用new进行初始化。
八、结构体用栈存储,而类用堆存储。
九、结构体默认数据是public类型,类是private类型.

26、结构体的内存对齐:连接
结构体的内存对齐是一种空间换时间的方法,以结构体中占用空间最大的长度和#pramma pack 指定的数值中取小的那个对齐。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

27、并发的一致性问题:
丢失修改:两个事务同时对一个数据修改,T1先提交,T2后提交,但是覆盖了T1的修改。
读脏数据:表示在不同事务下,一个事务可以读取到另一个事务未提交的数据,如果T2修改了数据,T1读取了,但是T2撤回了这次修改,则T1读的是脏数据。
不可重复读:一个事务内多次读取了同一数据集合,但是这个事务没结束时,另一事物也修改了这一数据集合,导致这个事务两次读取的数据可能不一样。
幻影读:一个事务读取某个范围的数据,另一事务在这期间插入了数据,导致这个事务两次读取的数据不同。
解决方法是通过并发控制来保证隔离性(事务的隔离级别)。
四个隔离级别:
未提交读:事务中的修改,即使没有提交,对其它事务也是可见的。
提交读:一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
可重复读:保证在同一个事务中多次读取同一数据的结果是一样的。
可串行化:强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。

28、五种IO模型:连接

阻塞IO模型:在应用调用recvfrom读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间一直会等待,进程从调用到返回这段时间内都是被阻塞的,称为阻塞IO;

非阻塞IO模型:在应用调用recvfrom读取数据时,如果该缓冲区没有数据的话,就会直接返回一个EWOULDBLOCK错误,不会让应用一直等待中。在没有数据的时候会即刻返回错误标识,那也意味着如果应用要读取数据就需要不断的调用recvfrom请求,直到读取到它数据要的数据为止。

IO复用模型:进程通过将一个或多个fd(网络请求)传递给select,阻塞在select操作上,select帮我们侦测多个fd是否准备就绪,当有fd准备就绪时,select返回数据可读状态,应用程序再调用recvfrom读取数据。复用IO的基本思路就是通过slect或poll、epoll 来监控多fd ,来达到不必为每个fd创建一个对应的监控线程,从而减少线程资源创建的目的。

信号驱动IO模型:首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数,此时请求即刻返回,当数据准备就绪时,就生成对应进程的SIGIO信号,通过信号回调通知应用线程调用recvfrom来读取数据。

异步IO:应用告知内核启动某个操作,并让内核在整个操作完成之后,通知应用,这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们合适可以开始下一个IO操作,而异步IO模型是由内核通知我们操作什么时候完成。

29、IO复用的函数,select、poll、epoll:
select 允许应用程序监视一组文件描述符,等待一个或者多个描述符成为就绪状态,从而完成 I/O 操作。
poll 的功能与 select 类似,也是等待一组描述符中的一个成为就绪状态。
在这里插入图片描述
在这里插入图片描述

29、Linux中常用的命令:连接

30、函数重载的底层是怎么实现的:
C++利用命名倾轧(name mangling)技术,来改名函数名,区分参数不同的同名函数。命名倾轧是在编译阶段完成的。
即根据参赛列表对同名函数翻译成不同的名字,这样就可以区分所有不同的同名函数了。

31、对于struct结构体,能不能用memcmp(用来比较两个存储区的前n个字节)进行内存比较:
不能,因为结构体自动进行对齐后填充的内容都是随机的。

32、拷贝构造函数的参数不是引用可以吗:
不可以,函数对于非引用的参数值而言,都会对其在栈内进行拷贝,而对类进行拷贝,又调用了拷贝构造函数,这样会造成无限的调用,直至函数栈溢出。

33、read函数设置成非阻塞的话,会有哪些返回值:-1出错、正数-读到的字节数、0-到了文件尾。

34、不用编译器,如何确定代码的错误位置,除了打日志,抛异常:不用编译器就是不用assert,不用打印日志就是不用printf,只能使用core dump,通过gdb去调试,定位堆栈信息。

35、memcpy和memmove的区别:memmove中有一个判断,如果目标地址比源地址小或者大于源地址加上要拷贝的字符长度的时候是正向拷贝,反之逆向拷贝,效率比memcpy低,但安全
memcpy,memmove,memset,memcmp:连接

36、二分图染色和二分图匹配:连接

36、怎么手写memcmp:判断内存覆盖问题,如果+size 大于等于需要拷贝的地址,就从后往前拷贝,否则就从前往后。

37、三数之和:枚举第一个数,用双指针找第二个数和第三个数, O ( n 2 ) O(n^2) O(n2)

38、C++4种 cast 转换:
一、const_cast:const转非const。
二、static_cast:用于隐式转换,例如非const转const,void*转指针,能用于多态向上转换。
三、dynamic_cast:用于动态类型转换,只能用于含有虚函数的类,只能转指针或者引用,用于类的向上或者向下转换。向下转换的时候如果非法对于指针返回NULL,对于引用抛异常。
四、reinterpret_cast:几乎什么都可以转,比如int转指针。

39、C++的四种智能指针:
智能指针的作用:智能指针的作用是管理一个指针,如果在申请空间之后没有释放,则会造成内存泄漏,而智能指针本身是一个类,当超过他的作用域,就自动调用析构函数,自动释放资源。
一、auto_ptr(c++11已弃用):采取所有权模式,比如命名两个auto_ptr,然后让第二个等于第一个,则再访问第一个的时候会出错。
二、unique_ptr:独占式,保证同一时间只有一个之智能指针可以指向一个对象,采取所有权模式,当碰到上面的例子时,编译器会报错。
三、shared_ptr:共享式,多个智能指针可以指向同一个对象,会在最后一个指针被销毁的时候释放资源(计数)。
四、weak_ptr:解决两个shared_ptr互相引用而死锁的问题。他是一种弱引用,不会增加计数,可以和shared_ptr互相转换,shared_ptr可以直接赋值给他,他可以通过lock函数来获得shared_ptr。因为他不会引起计数增加,且可以检测管理对象是否释放,所以可以

40、僵尸进程和孤儿进程:连接
进程的五种状态:
在这里插入图片描述
在这里插入图片描述

僵尸进程:当进程退出但是父进程并没有调用wait或waitpid获取子进程的状态信息时就会产生僵尸进程。

孤儿进程:是指当一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

因为僵尸进程的PCB(进程控制块)本身是个结构体,占用空间,所以会造成资源浪费。而孤儿进程则被init进程收养,init会进行循环的wait()回收资源,所以孤儿进程无危害。

41、什么是大端,什么是小端?
在这里插入图片描述
大端:低序字节存储在高位地址
小端:低序字节存储在低位地址

42、用户态怎么转换到内核态的:
有三种方式:
一、系统调用:通过系统调用函数(例如fork(),read(),write(),getpid(),open(),exit(),wait()等)切换,是用户进程主动要求切换到内核态的方式。
二、异常:CPU在执行用户态的时候发生了某些事件不可知的异常,触发当前运行进程切换去处理异常。异常的内核相关程序中也就到了内核态,比如缺页中断。
三、外围设备的中断:外围设备完成用户请求的操作,会像CPU发出相应的中断信号,CPU暂停下一条执行的命令而去执行中断信号的处理程序。如果先执行的指令是用户态下的程序,那么转换的过程也就有用户态到内核态的转换。

43、微内核和宏内核:
宏内核:除了最基本的进程、线程管理、内存管理之外,其他都在内核里。
优点:效率高。缺点:稳定性差。
微内核:内核中只有最基本的调度,内存管理。其他都是用户态的进程去实现的。
优点:稳定,驱动等错误只会导致相应的进程死掉,不会使整个操作系统挂掉。缺点:效率低。

44、GDB调试:GDB调试是自由软件基金会的软件工具之一。可以用来协助程序员找到代码的错误。没有GDB,则只能用大量的语句产生特定的输出。可以单步跟踪代码,也可以通过断点来调试,还可以随时查看变量和内存的当前状态。连接

45、就绪状态的进程在等待什么:被调度使用CPU的运行权。

46、怎么实现线程池:
一、设置一个生产者消费者队列,作为临界资源。
二、初始化N个线程,并且让他们运行,加锁去队列里取任务运行。
三、队列为空的时候,使所有线程阻塞。
四、当生产者队列有任务时,上锁,把任务挂在队列里,再用条件变量通知阻塞中的一个线程。

需要巩固:
1、平衡二叉树 / 普通二叉树找第K大节点:普通二叉树就把全部节点拿出来,平衡二叉树就直接中序遍历,找到的第个节点就是第K大,或者快排思路,右子树大小大于K-1就找右子树的第K大,右子树大小等于K-1,当前节点就是第K大,右子树大小小于K-1,就在当前节点的左子树找第K-1-(右子树大小)大。

2、AVL (平衡二叉树)、BST (二叉排序树)、红黑树、B树、B+树:链接 红黑树详解
AVL:是比较严格的二叉排序树(即带有平衡条件),时间复杂度O(nlogn)
BST:右子树比当前节点大,左子树比当前节点小,时间复杂度O(n),空间复杂度O(logn)
红黑树:每个节点都带红色或黑色;根节点,叶子节点都是黑色;红色节点的子节点都是黑色;每个节点到其叶子节点都包含相同数量的黑色节点;
B树:(一个M阶的B树):所有节点路径按关键字大小排序,左小右大;非叶子节点的子节点数大于1且小于等于M(M>=2)(M表示查找路径有多少条);每个节点的关键字数量 ≥ ⌊ m / 2 ⌋ − 1 \ge \lfloor m/2 \rfloor -1 m/21 ≤ m − 1 \le m-1 m1;所有叶子节点都在同一层;
B+树:在B树基础上优化,其非叶子节点不存放数据,仅存放键值,数据全部存放在其对应的叶子节点;同一层的非叶子节点之间有双向链表;

3、KMP : 数组PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度(所以 P M T [ i ] PMT[i] PMT[i]就是前i个字符的前缀集合与后缀集合中最长元素的长度,写出所有前缀,所有后缀,找到最长的相等字符串),next数组:PMT数组向后偏移一位(数组中全部数向右移一位)(第0位设为-1)
求next数组:对模式串从第一位(第一位开始即视为后缀)开始匹配自己的前缀(匹配成功记得先++再放进next数组,因为求得是PMT向后位移1)
KMP主算法:当匹配失败的时候模式串可以不用从0开始从新匹配,而可以变成 n e x t [ i ] next[i] next[i];(因为next是PMT向前移一位,所以原本应该是 j = P M T [ i − 1 ] j=PMT[i-1] j=PMT[i1]

4、快重传,快恢复:接收方连续三次回复收到的有序报文段,则说明下一个报文段丢失,需要重传,发送方立即重传。这种情况下不是网络拥塞,只是个别报文段丢失,因此进行快恢复,把当前的慢开始门限设为当前拥塞窗口大小的一半,再把拥塞窗口设置为慢启动门限的大小,即直接进入拥塞避免。
(慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。)

5、拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。

6、OSI七层模型和其协议及作用:(由下至上传)
物理层: 定义物理设备标准,主要作用是传输比特流。(IEEE802系列、RJ45)
数据链路层: 主机之间有很多链路,其为同一链路的主机之间提供传输服务,把网络层传下来的分组封装成帧(PPP,以太网协议)
网络层: 为主机提供数据传输服务。把传输层传下来的报文或者用户数据封装成分组。(IP、ICMP、ARP)
传输层: 为进程提供通用数据传输服务,传输单位为报文(TCP、UDP)
会话层: 建立及管理会话。(RPC、NFS)
表示层: 数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。(Telnet、Rlogin、SNMP)
应用层: 为特定应用程序提供数据传输服务(HTTP、DNS)

五层协议没有会话层和表示层,把这些功能留给开发者处理
TCP/IP四层协议把五层协议中物理层和数据链路层合并成了网络接口层

7、手打归并排序:连接

8、描述一下在浏览器中输入一个url(www.baidu.com),回车之后的过程:连接

一、解析域名
先查询浏览器自己的DNS缓存,若没有则查询操作系统的DNS缓存,若没有则操作系统把域名发送给本地域名服务器,本地域名服务器查询自己的DNS缓存(递归查询)。
若没有,则使用迭代查询:本地域名服务器向根域名服务器(没有每个域名的具体信息,但存储了每个域(如com,net…)的顶级域名服务器地址)发出请求,然后返回com域的顶级域名服务器的地址;本地域名服务器再向com域的顶级域名服务器的地址发起请求,返回baidu.com权限域名服务器(用于保存该区所有主机域名到IP地址的映射)的地址;本地域名服务器再向baidu.com权限域名服务器发送请求,得到www.baidu.com的IP地址。本地域名服务器把IP地址缓存起来,并且返回给操作系统,操作系统也缓存起来,再返回给浏览器。

二、建立TCP连接(TCP三次握手),建立连接后客户端按指定格式向服务端发送http请求get或post。

三、服务器接收请求并且回复http响应报文,把web页面放进报文主体里,发送给客户端。

四、浏览器解析html代码,并且请求html代码中的资源,通过connection header判断是否保持TCP连接

五、断开TCP连接(四次挥手)

9、线程进程与堆栈之间内存分配:线程独占进程的栈区,共享进程的堆区。堆区需要自己编程分配,如malloc,realloc,new等。而栈是系统自己分配。连接

10、常见的http状态码:
1XX(信息型状态码):接受的请求正在处理;2XX(成功状态码):请求处理完毕;3XX(重定向状态码):需要附加操作已完成请求;4XX(客户端错误):服务器无法处理请求;5XX(服务端错误):服务器处理请求出错;
100 Continue:到目前为止都正常,客户端可以继续发送请求;
200 OK:处理成功;
204 No content:处理成功,但响应报文没有主体部分,一般用于不需要返回数据时使用;
206 Partial content:客户端进行了范围请求,响应报文包含了范围内的主体;
301 Moved permanently:永久重定向;
302 Found:临时重定向;
303 See other:跟302一样的功能,但是要求客户端用get方式获取资源;
304 Not Modified:请求报文包含一些条件,如果不满足这些条件则返回304;
400 Bad request:请求报文有语法错误;
401 Unauthorized:客户端发送的请求需要有认证信息;
403 Forbidden:请求被拒绝;
404 Not found:找不到该链接对应的网页;
500 Internal Server Error:服务器正在执行请求时发生错误;
503:Service Unavailable:服务器超载或者在维修,无法处理请求;

11、http请求方法get和post的区别:链接
一、get请求的参数通过url传递,post放在request body上
二、get请求的参数长度是有限制的,post没有
三、get不安全,因为其参数暴露在url中
四、get请求浏览器会主动cache,而post不会,除非主动设置
五、get请求参数会被完整保留在浏览器历史记录,而post不会
六、get只能进行url编码,post支持多种编码方式
七、他们本质都是TCP链接,并无区别,但是由于http和浏览器的规定而产生区别
八、get请求发送一个数据包,而post发送两个

12、数字证书:
一、服务端工作人员向认证机构申请公开密钥
二、认证机构判明该申请者的身份,对这个申请的公开密钥进行数字签名,然后分配这个已签名的公开密钥在数字证书里,绑定该证书。
三、进行https连接的时候,服务端把这个密钥发送给客户端,客户端拿到公开密钥,对这个数字签名进行验证,如果验证通过即可连接。

13、https与http的区别:
一、http端口是80,https端口是443;
二、https在tcp三次握手之后还需要与SSL通信加密;
三、https需要服务端申请数字证书,浏览器安装对应的根证书;
https优点:安全性高,保证报文的完整(不被篡改),保证通信者的身份(认证);
https缺点:
一、三次握手之后还需要额外的SSL握手,握手阶段延时高;
二、部署成本高,需要支付高额的证书认证费用

14、进程调度算法:
批处理系统:
一、先来先服务(利于长作业不利于短作业)
二、短作业优先(利于短作业不利于长作业)
三、最短剩余时间优先(抢占式,如果当前进程运行时间比新来长,则新来的抢占)
交互式系统:
一、时间片轮转法
二、优先级调度(为每个进程分配一个优先度,最好设置随时间推移优先度增加)
三、多级反馈队列(设置很多队列,每个队列的时间片都不同,最上面的队列优先级最高,只有上层队列没有进程才运行下一层队列的进程。当进程在上层队列执行一个时间片没有执行完,则被移到下一个队列)

15、死锁产生的必要条件、判断死锁、解除死锁的方法:连接
四个条件:
一、互斥条件:一个资源每次只能被一个进程使用。
二、占有且等待条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
三、不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
四、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解除:
一、破坏占有且等待条件:①创建进程时一次性满足他所需要的所有资源,②在每个进程申请资源的时候先释放掉他之前已有的资源。
二、破坏不可抢占条件:①当进程申请资源被拒绝,就释放最初拥有的资源,②当一个进程请求的资源被另一个进程占用,允许操作系统抢占这个资源。
三、破坏循环等待条件:将系统中所有资源统一编号,虽然允许进程任意时刻申请资源,但是必须要按资源编号的顺序申请。
四、银行家算法
检测死锁的算法:类似银行家算法的安全性检查

16、临界区、互斥量、信号量、条件变量:
临界区:访问临界资源的程序片段。
互斥量:互斥锁,用于加锁或解锁。
信号量:相当于一个计数器,为多个进程提供对共享资源的访问。(小于0阻塞,大于0运行)
条件变量:与互斥锁一起使用,允许线程阻塞,等待其他线程发送信号,来唤醒这个线程,并判断条件是否满足。

17、页表:
在这里插入图片描述
页面置换算法:
一、最佳页面置换算法(OPT)
所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。(理论算法,无法实现)
二、最近最久未使用(LRU)
维护一个链表,把最新用的页面放在表头。
三、最近未使用(NRU)
为每个页面设置两个表示位R,M,页面被访问时设置R=1,被修改时设置M=1。按{R=0,M=0},{R=0,M=1},{R=1,M=0},{R=1,M=1}的顺序置换页面;
NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。
四、先进先出(FIFO)
优先换出最先进入的页面。
五、第二次机会算法
在FIFO的基础上设置一个标志位R,如果访问过页面则设为1。需要换页的时候检查最老的页面,如果R=1,则把R=0且放在链表尾,否则直接置换。
六、时钟算法
在第二次机会算法基础上把存页面的链表换成环形链表,用一个指针指向最老的页面,当需要置换的时候判断指针指向的页面R是不是1,如果是则变为0,指向下一个页面,不是则置换。

18、判断32位系统和64位系统:long和指针占用大小不同(32位占4,64位占8)

19、strlen和sizeof的区别:
一、strlen只能用char*,且要以’\0’结尾,而sizeof可以用数组,指针,数据,对象,函数等做参数;
二、sizeof是获得对象数据类型能容纳的最大对象字节的大小,而strlen是获得从0到’\0’之间字符的个数。
三、sizeof()是运算符,在编译的时候已经计算好了,而strlen是函数,在运行的时候才进行计算(传入参数为数组时就已经退化成指针了)

20、事务的ACID概念:
原子性(Atomicity):事务的所有操作要么全部提交成功,要么全部失败回滚。
一致性(Consistency):数据库在执行前后保持一致性状态。
例子:用户user1对表进行了更新操作,用户user2在user1还没有进行提交前读表中数据,而且是大批量的读取(打个比方:耗时3分钟)而在这3分钟内user1进行了提交操作,那又会产生什么影响呢?这个时候怎么保证读写一致性呢?这个时候DBMS就要保证有足够大的临时表来存放修改前的数值,以保证user2读取的数据是修改前的一致数据.然后下次再读取时候就是更新后的数据了。
隔离性(Isolation)一个事务所做的修改在最终提交以前,对其它事务是不可见的。
持久性(Durability):一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
在这里插入图片描述
为了明天:

1、MFC的底层实现:
Windows的消息机制:操作系统会通过捕捉外部设备输入系统的信息,然后把这些信息放到一个消息队列里,然后应用程序一直通过GetMessage()来获取消息队列里的消息,然后把获取到的消息通过DispatchMessage()分配到系统,操作系统在执行窗口过程。

2、Windows编程模型:
一、WinMain函数的定义(WinMain函数是Windows程序的入口)
二、创建一个窗口
三、进行消息循环
四、编写窗口过程函数

3、C++,Java,Python的区别: 连接
在这里插入图片描述
4、C++的继承:
在这里插入图片描述
5、C++11新增的特性:连接
一、nullptr:能隐式转换成任意指针或者成员指针的类型
二、auto:编译器自动推导数据的类型
三、decltype:能够自动推导表达式的数据类型
四、区间迭代for(auto i:k)
五、初始化列表:直接通过{1,3}初始化结构体
六、可以默认定义模板参数,给函数模板命名
七、lambda(匿名函数),用于定义不想定义函数名称又想实现函数的时候
八、新增STL:array(存储在栈里)、tuple
九、右值引用:右值引用用来绑定到右值,绑定到右值以后本来会被销毁的右值的生存期会延长至与绑定到它的右值引用的生存期。
右值引用的存在并不是为了取代左值引用,而是充分利用右值(特别是临时对象)的构造来减少对象构造和析构操作以达到提高效率的目的。意义

6、多线程编程会带来的问题:编写线程安全的函数、调试困难,比较难去重现bug、同步互斥过程中造成死锁问题

7、如果是线程池,固定了创建的线程数量,如果每个线程都阻塞了,怎么办?:资源分配不合理导致的问题,要么增加系统资源满足任务调用,要么重新分配任务的工作量,减少单个任务的资源占用

8、如果一个单线程阻塞了一个系统调用,比如read,如何解决:使用select函数进行定时读取,多次阻塞无效后,放弃资源,提示应用系统错误信息,交给用户进一步排查;也可以是将read设置为非阻塞。

9、TCP的滑动窗口协议:连接
选择重传:
在这里插入图片描述
只重传丢失的那部分帧,后面收到的有序帧先放在缓存区,拿到丢失的部分之后再一起递交给高层。

10、怎么让UDP保持很快的传输速度,并且还要保证可靠传输?
(被问傻了)连接
如果不利用Linux协议栈及上层的socket机制,自己通过抓包和发包的机制实现可靠传输,就需要实现:
发送:包的分片、包确认、包的重发
接收:包的调序、包的序号确认

11、你知道大端跟小端吧,写一个程序判断牛客是用大端存储还是小端存储
(被问傻了)

#include <bits/stdc++.h>
using namespace std;
int main {
	union u{
       int a;
        char k[2];
    };
    u test;
    test.a=0x1234;
    cout<<(int)test.k[0]<<" "<<(int)test.k[1]<<endl;
}

12、说一下C++的std::move:连接
把一个左值强制转换成右值引用,以用于移动语义。
一、C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。
二、std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能.。
三、对指针类型的标准库对象并不需要这么做.
在这里插入图片描述
移动就是把原本要销毁的内存所有权转移给新的对象。

13、线程和进程,多线程和多进程:
在这里插入图片描述
在这里插入图片描述
14、协程
协程之间切换(阻塞和唤醒)是由程序控制的,不需要内核来操作。所以切换的开销小。
在这里插入图片描述
协程在io操作比较多,且IO是瓶颈的地方才有用。
在这里插入图片描述
15、extern "C"的作用:
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
extern 声明变量或者函数时,使它们可以在其他模块使用。相对应的是static

16、解决share_ptr内存泄漏问题:
当两个share_ptr互相指向对方的时候会导致内存泄漏,要解决的话只需要把其中一个成员改成weak_ptr就行;但是在通过weak_ptr访问对象的时候,需要用weak_ptr.lock()把它变成share_ptr才能进行访问;
17、RTTI:
Runtime Type Identify,运行时类型检查,c++引入本意是用于通过基类的指针来获得该指针指向的实际类型;现在还用通过typeid获取到所有类型的变量对应的类型;typeid返回的是一个const type_info,动态多态中,虚函数表里的-1位置存放一个指向type_info的指针,对于存在虚函数的类型,typeid和dynamic_cast都会查询虚函数表中的type_info

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值