面经分享--百度开发一面
一、tcp三次握手是怎么样的
当客户端向服务器发送一个syn请求报文之后,客户端的状态从closed状态变为syn_sent状态,服务器收到这个请求之后,校验这个请求是否合法,是否有效,之后会返回一个ack+syn的报文,状态变为syn_rcvd状态,之后客户端收到了也返回一个ack给服务器表示已收到建立连接请求,之后双方的状态都变为established状态。
具体过程:
1.发送第一个syn:客户端随机初始化序号,将此序号置于TCP首部的序号字段中,同时把syn标志位置为1,代表这个是syn的报文,发送给服务器之后,进入syn_sent状态
2.返回ack+syn:服务器也会随机初始化自己的序号,将此序号填入TCP首部的序号字段中,之后将TCP首部的确认应答号填入client_syn+1,之后将syn和ack位都置为1,然后返回给客户端,进入syn_rcvd状态
3.最后一个ack:将TCP首部ack标志位置为1,将确认应答号字段填入server_isn+1,之后进入established状态
二、TCP是通过什么保证可靠传输的
1.超时重传:当双方任何一方发送数据之后,对方都会返回一个ack表示收到了,如果长时间没有返回ack,就会进行重传,但是为了避免重传次数过多,设置了梯度增加的机制,比如2s没返回就传1次,之后就4s,8s,16s等,多次都没有返回ack,就不在传输,认为对方出现了问题,断开连接
2.序列号:TCP给每一段数据都增加了序列号这样的东西,当服务器给客户端传输1-1000的数据的时候,那么这个ACK返回就表示下一个该传1001开始的数据,这样就避免了重复传输,以及数据丢失找不到原因的情况
3.确认应答:与超时重传共同使用
4.连接管理:通过三次握手,四次挥手确保能够建立起连接。
5.流量控制:接收端处理的能力是有限的,所以发送方传输数据的时候会根据对方返回的ack来知道对方的缓冲区还有多大的空间,这样就使得发送方能够发送对方能处理的数据大小,这样使得不会一次发送很多数据导致接收端出现宕机等情况
6.拥塞控制:拥塞控制就是当网络拥堵严重时,发送端减少数据发送。拥塞控制是通过发送端维护一个拥塞窗口来实现的。
三、为什么time_wait要等2MSL
MSL是报文最长生存时间,也就是说一条报文在网络上最长生存时间,因为TCP是传输层的协议,它的下一层是网络层,依靠的是IP协议,IP协议里面就有一个TTL字段,也就是表示路由数量,每经过一次路由,这个数就-1,直到为0,TTL的值一般是64,LINUX将MSL设置为了30s,认为30s肯定会经过64次路由,如果没有,那么这个报文就消失在网络中了,为什么TIME_WAIT要等2个MSL,就是为了保证数据一来一回的情况,一个向另一个发送了数据,另一个又得返回数据,两次都消耗到了MSL的时间,比较极端的情况。至少允许报文丢失一次。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。
比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 FIN 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
四、进程和线程的区别
定义:进程是操作系统分配资源的最小单位,进程是操作系统执行调度的最小单位
多进程之间不会相互干扰,通过共享资源,管道pipe等方式沟通,而线程主要是通过共享资源进行沟通
系统会给每个进程分配一定的内存空间,但是除了CPU之外,不会给线程分配内存.
进程中某个线程如果崩溃了,可能会导致整个进程都崩溃。而进程中的子进程崩溃,并不会影响其他进程。
五、一个进程中有什么资源
进程控制块:进程ID,进程状态,程序计数器,CPU寄存器值,进程优先级等关键信息
内存资源:存储程序指令的代码块,存储全局和静态变量的数据段,用于动态分配内存的堆,函数调用,局部变量存储的栈
CPU时间片:操作系统通过调度算法为进程分配CPU的使用权,IO操作的文件描述符等
同时,进程会涉及同步与通信资源,比如用于避免资源竞争的信号量、互斥锁,以及用于进程间数据传递的消息队列。进程间的关系信息也属于进程资源。
六、同一个线程有哪些数据段可以共享
1.代码块:存储进程执行指令的区域,所有线程执行的都是进程的同一套代码
2.数据段:进程中的全局变量和静态变量,所有线程都可以进行使用
3.堆区域:线程在执行过程中动态分配的堆内存,并非线程私有,其他线程只要持有对应的内存地址,就能访问和操作该堆内存中的数据,因此堆也属于共享数据段。
七、进程和线程之间的通信方式有哪些
进程:管道pipe,信号signal,套接字socket
线程:
互斥锁:互斥量(mutex)从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。
条件变量:主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使“条件成立”。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。
自旋锁:旋锁通过 CPU 提供的 CAS 函数(Compare And Swap),在「用户态」完成加锁和解锁操作,不会主动产生线程上下文切换,所以相比互斥锁来说,会快一些,开销也小一些。一般加锁的过程,包含两个步骤:第一步,查看锁的状态,如果锁是空闲的,则执行第二步;第二步,将锁设置为当前线程持有;使用自旋锁的时候,当发生多线程竞争锁的情况,加锁失败的线程会「忙等待」,直到它拿到锁。
信号量:通常信号量表示资源的数量,对应的变量是一个整型(sem)变量
八、分布式锁有哪些实现方式
在分布式系统中,分布式锁主要用于解决多节点并发问题,如避免重复执行任务、保证数据一致性),核心是确保 “同一时间只有一个节点能持有锁”。
1.基于数据库实现:给数据加唯一索引,成功插入到数据库中就代表获取到了锁,删除操作就代表释放了锁,防止了重复获取锁。再通过悲观锁锁住查询记录,但是没有处理死锁,锁超时等问题
2.基于Redis的分布式锁使用:基础就是set key value nx ex,键不存在就插入,并设置一定的过期时间,为了避免锁到的误删(一个节点持有锁的期间超时了,其他节点获取到新锁之后,原节点完成任务误删新锁),会给value设置唯一标识UUID,释放时会用LUA脚本先判断value是否匹配,再删除,还可以使用Redisson这样封装好的框架
3.zookeeper和看门狗的使用:
九、布隆过滤器用来解决什么问题
布隆过滤器的数据结构是位图数组,不是1就是0,初始值都是0。当我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。当然,布隆过滤器也是将大集合映射到小鸡和中,所以,查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。
十、算法:最长公共子序列
LCR 095. 最长公共子序列 - 力扣(LeetCode)