大家好,我是小林。
互联网岗位里,可以说后端开发是最卷,投的人最多的,然而隔壁的客户端开发投的就很少,有后端同窗会被客户端部门捞起来去面试,所以假设卷不事先端,又想进大厂的同窗,可以尝试投客户端开发,面试相对没那么卷,薪资待遇跟后端也是一样的。
当蠢才享一位快手客户端一二三面的面经,同窗的技术栈是C++后端,然而面试不会问后端内容了,关键就围绕Cpp+操作系统+网络协定+算法来问,相比后端所须要预备的内容就少了 mysql 、redis、信息队列等后端组件,然而计算基础的深度会问的比拟深一点。
由其第三面,间接给两个场景代码题手写进去,还是有点难度。。
在网络出现拥挤时,假设继续发送少量数据包,或许会造成数据包时延、失落等,这时 TCP 就会重传数据,然而一重传就会造成网络的累赘更重,于是会造成更大的提前以及更多的丢包,这个状况就会进入恶性循环被不时地加大....
所以,TCP 不能疏忽网络上出现的事,它被设计成一个自私的协定,当网络发送拥塞时,TCP 会自我就义,降落发送的数据量。
于是,就有了拥塞控制,控制的目的就是防止「发送方」的数据填满整个网络。
为了在「发送方」调理所要发送数据的量,定义了一个叫做「拥塞窗口」的概念。
拥塞控制关键是四个算法:
TCP 在刚建设衔接成功后,首先是有个慢启动的环节,这个慢启动的意思就是一点一点的提高发送数据包的数量,假设一过去就发少量的数据,这不是给网络添堵吗?
慢启动的算法记住一个规则就行:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。
这里假设拥塞窗口cwnd和发送窗口swnd相等,上方举个栗子:
慢启动算法的变动环节如下图:
可以看出慢启动算法,发包的个数是指数性的增长。
那慢启动涨到什么时刻是个头呢?
有一个叫慢启动门限ssthresh(slow start threshold)形态变量。
当拥塞窗口cwnd「超越」慢启动门限ssthresh就会进入拥塞防止算法。
普通来说ssthresh的大小是65535字节。
那么进入拥塞防止算法后,它的规则是:每当收到一个 ACK 时,cwnd 参与 1/cwnd。
接上台面的慢启动的栗子,现假设ssthresh为8:
拥塞防止算法的变动环节如下图:
所以,咱们可以发现,拥塞防止算法就是将原本慢启动算法的指数增长变成了线性增长,还是增长阶段,然而增长速度缓慢了一些。
就这么不时增长着后,网络就会缓缓进入了拥塞的状况了,于是就会出现丢包现象,这时就须要对失落的数据包启动重传。
当触发了重传机制,也就进入了「拥塞出现算法」。
当网络出现拥塞,也就是会出现数据包重传,重传机制关键有两种:
当出现了「超时重传」,则就会经常使用拥塞出现算法。
这个时刻,ssthresh 和 cwnd 的值会出现变动:
拥塞出现算法的变动如下图:
接着,就从新开局慢启动,慢启动是会突然缩小数据流的。这真是一旦「超时重传」,马上回到束缚前。然而这种方式太保守了,反响也很剧烈,会形成网络卡顿。
还有更好的方式,前面咱们讲过「极速重传算法」。当接纳方发现丢了一个两边包的时刻,发送三次前一个包的 ACK,于是发送端就会极速地重传,不用期待超时再重传。
TCP 以为这种状况不重大,由于大部分没丢,只丢了一小部分,则ssthresh和cwnd变动如下:
极速重传和极速恢复算法普通同时经常使用,极速恢复算法是以为,你还能收到 3 个重复 ACK 说明网络也不那么蹩脚,所以没有必要像RTO超时那么剧烈。
正如前面所说,进入极速恢复之前,cwnd和ssthresh已被降级了:
而后,进入极速恢复算法如下:
极速恢复算法的变动环节如下图:
也就是没有像「超时重传」一夜回到束缚前,而是还在比拟高的值,后续呈线性增长。
基于 RSA 算法的 TLS 握手环节比拟容易了解,所以这里先用这个给大家展现 TLS 握手环节,如下图:
TLS 协定建设的详细流程:
首先,由客户端向主机动员加密通讯恳求,也就是ClientHello恳求。
在这一步,客户端关键向主机发送以下信息:
(1)客户端支持的 TLS 协定版本,如 TLS 1.2 版本。
(2)客户端消费的随机数(Client Random),前面用于生成「会话秘钥」条件之一。
(3)客户端支持的明码套件列表,如 RSA 加密算法。
主机收到客户端恳求后,向客户端收回照应,也就是SeverHello。主机回应的内容有如下内容:
(1)确认 TLS 协定版本,假设阅读器不支持,则封锁加密通讯。
(2)主机消费的随机数(Server Random),也是前面用于消费「会话秘钥」条件之一。
(3)确认的明码套件列表,如 RSA 加密算法。
(4)主机的数字证书。
客户端收到主机的回应之后,首先经过阅读器或许操作系统中的 CA 公钥,确认主机的数字证书的实在性。
假设证书没有疑问,客户端会从数字证书中取出主机的公钥,而后经常使用它加密报文,向主机发送如下信息:
(1)一个随机数(pre-master key)。该随机数会被主机公钥加密。
(2)加密通讯算法扭转通知,示意随后的信息都将用「会话秘钥」加密通讯。
(3)客户端握手完结通知,示意客户端的握手阶段曾经完结。这一项同时把之前一切内容的出现的数据做个摘要,用来供服务端校验。
上方第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
主机和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生老本次通讯的「会话秘钥」。
主机收到客户端的第三个随机数(pre-master key)之后,经过协商的加密算法,计算出本次通讯的「会话秘钥」。
而后,向客户端发送最后的信息:
(1)加密通讯算法扭转通知,示意随后的信息都将用「会话秘钥」加密通讯。
(2)主机握手完结通知,示意主机的握手阶段曾经完结。这一项同时把之前一切内容的出现的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段所有完结。接上去,客户端与主机进入加密通讯,就齐全是经常使用普通的 HTTP 协定,只不过用「会话秘钥」加密内容。
线程在操作系统中有一些特定的资源,包括:
栈中,关键保留了以下内容:
函数调用时,会启动以下压栈操作:
经过用mmap把该库间接映射到各个进程的地址空间中,虽然每个进程都以为自己地址空间中加载了该库,但实践上该库在内存中只要一份,mmap就这样很神奇和灵活链接库联动起来了。
在操作系统中,终止是指由配件或软件触发的一种事情,它会暂时停止正在口头的程序,并转而口头与终止相关的处置程序。终止可以是外部的,如除法失误或越界访问,也可以是外部的,如配件设施的输入/输入恳求或时钟终止。
终止的作用是提供一种机制来处置和照应各种事情,例如处置配件设施的输入/输入恳求、处置意外状况、启动时钟调度等。当出现终止时,操作系统会依据终止类型确定要口头的终止处置程序,并在处置完终止后恢还原来的程序口头。
终止处置程序可以口头一系列操作,如保留当行进程的高低文、处置终止事情、与设施启动通讯、调度其余进程等。经过经常使用终止,操作系统可以成功多义务处置、实时照应外部事情、提高系统的牢靠性和稳固性。
读者只会读取数据,不会修负数据,而写者即可以读也可以修负数据。
读者-写者的疑问形容:
接上去,提出几个处置打算来剖析剖析。
经常使用信号量的方式来尝试处置:
接上去看看代码的成功:
上方的这种成功,是读者优先的战略,由于只需有读者正在读的形态,起初的读者都可以间接进入,假设读者继续不时进入,则写者会处于饥饿形态。
那既然有读者优先战略,人造也有写者优先战略:
在打算一的基础上新增如下变量:
详细成功如下代码:
留意,这里rMutex的作用,开局有多个读者读数据,它们所有进入读者队列,此时来了一个写者,口头了P(rMutex)之后,后续的读者由于阻塞在rMutex上,都不能再进入读者队列,而写者来到,则可以所有进入写者队列,因此保障了写者优先。
同时,第一个写者口头了P(rMutex)之后,也不能马上开局写,必定等到一切进入读者队列的读者都口头完读操作,经过V(wDataMutex)唤醒写者的写操作。
既然读者优先战略和写者优先战略都会形成饥饿的现象,那么咱们就来成功一下偏心战略。
偏心战略:
详细代码成功:
看完代码不知你能否有这样的不懂,为什么加了一个信号量flag,就成功了偏心竞争?
对比打算一的读者优先战略,可以发现,读者优先中只需后续有读者抵达,读者就可以进入读者队列, 而写者必定期待,直到没有读者抵达。
没有读者抵达会造成读者队列为空,即rCount==0,此时写者才可以进入临界区口头写操作。
而这里flag的作用就是阻止读者的这种不凡权限(不凡权限是只需读者抵达,就可以进入读者队列)。
比如:开局来了一些读者读数据,它们所有进入读者队列,此时来了一个写者,口头P(falg)操作,使得后续来到的读者都阻塞在flag上,不能进入读者队列,这会使得读者队列逐渐为空,即rCount减为 0。
这个写者也不能立马开局写(由于此时读者队列不为空),会阻塞在信号量wDataMutex上,读者队列中的读者所有读取完结后,最后一个读者进程口头V(wDataMutex),唤醒刚才的写者,写者则继续开局启动写操作。
void*是一种通用的指针类型,被称为"无类型指针"。它可以用来示意指向任何类型的指针,由于void*指针没有指定特定的数据类型。
由于void*是无类型的,它不能间接启动解援用操作,也不能启动指针运算。在经常使用void*指针时,须要将其转换为详细的指针类型能力启动操作。
void*指针罕用于须要在不同类型之间启动通用操作的状况,例如在函数中传递恣意类型的指针参数或在灵活内存调配中经常使用。
可以经常使用类型转换将void*指针转化为int*指针。以下是将void*指针转化为int*指针的示例代码:
void* voidPtr = malloc(sizeof(int));// 调配内存并前往void*指针int* intPtr = (int*)voidPtr;// 将void*指针转化为int*指针// 如今可以经过intPtr指针访问int类型的数据*intPtr = 42;
在上述示例中,经常使用malloc函数调配了存储一个int类型数据所需的内存,并前往了一个void*指针。而后,经过将void*指针转换为int*指针,将其赋值给intPtr变量。如今,可以经过intPtr指针访问和操作int类型的数据。
三面一个八股没问,间接来两个场景代码题,比拟器重实操,太难啦。
本网站的文章部分内容可能来源于网络和网友发布,仅供大家学习与参考,如有侵权,请联系站长进行删除处理,不代表本网站立场,转载联系作者并注明出处:https://duobeib.com/diannaowangluoweixiu/6463.html