面试总结

深信服面试

1. 代码覆盖率统计方法

  • 在gcc编译代码时增加参数gcov

  • 使用gtest写ut用例,构造不同的参数,争取跑到函数中每个分支

  • 使用gcov还原代码执行过程,生成代码执行分析文件.html

2. 对于函数中难以测试的分支,如何单元测试

  • 使用gdb在运行时改变参数值,使各个分支都能执行到

3. 自旋锁、互斥锁、读写锁实现原理

4. 如何使用互斥锁api实现读写锁

5. c++一些实现原理

6. 访问百度过程中使用到哪些协议

应用层协议

  • tcp三次握手建立连接

  • tls 4次握手确定加密算法、随机数、hash算法

  • 接下来就用加密算法对数据加密后,再在客户端、服务端通信

传输层

  • tcp协议

网络层

  • ip协议

链路层

  • 以太网协议

7. 以太网协议头组成、长度、mtu

  • 协议头: 目的mac,源mac,帧类型

  • 长度:14bytes

  • mtu: 1500bytes

8. tcp滑动窗口原理

9. send函数返回值在阻塞、非阻塞情况下区别

tcp协议本身可靠,并不等于应用程序用tcp发送数据就一定可靠;send发送的大小,并不代表对端recv到多少数据

  • 使用sysctl -a | grep net.ipv4.tcp_wmem可以查看系统默认的发送缓存大小,net.ipv4_tcp_wmem = 4096(最少字节数) 16384(默认值,会被net.core.wmwm_default覆盖) 81920(最大字节数,会被net.core.wmem_max覆盖)

阻塞模式

send函数是将应用程序请求发送的数据拷贝到发送缓存中

  • 如果发送缓存大小比请求发送大小要大,那么send函数立即返回,同时向网络中发送数据

  • 否则会等待接收端对之前发送数据的确认,以便腾出缓存空间容纳新的待发送数据再返回;否则阻塞

非阻塞模式

  • send函数仅仅是将数据拷贝到协议栈的缓存区中,返回成功拷贝的大小;

  • 如果缓存区可用空间为0,则返回-1,同时置errno为EAGAIN;

example

如果在某些条件下,发送端看似发送了10k数据,但是只发送了2k到对端缓存中,还有8k在本机缓存中;此时,接收端收到2k数据;假如接收端调用了recv函数获取了1k的数据在处理,这时发生了以下几种情况

发送端认为send完了10k数据,关闭了socket

  • 发送端进入fin_wait1,并发送fin报文给对端,缓存中8k数据并不清除,依然会发给对端

  • 接收端依然在recv,则会收到余下的8k数据;然后得到一个fin报文,走4次挥手

发送端再次调用send发送8k数据

  • 如果发送端缓存空间为20k,则足够装的下;send函数将数据做拷贝后,并立即返回8192;

  • 如果发送缓存空间为10k,则装不下新的8k数据(10-8=2<8);则send函数会返回2048,这时应用层必须阻塞(通过select等待下一次可写信号),否则此时再调用send会得到-1,errno=EAGAIN

接收端在处理完1k后,关闭socket

  • 接收端作为主动关闭者,连接将处于fin_wait1的半关闭;

  • 发送端会收到socket可读信号,但是在recv时,数据长度为0,这时应该调用close函数关闭socket

    1. 发送端如果仅检测该socket是否可写,会得到可写信号,但继续send将返回-1,同时errno=ECONNRESET

    2. 发送端没有使用select机制,如果send发生在RST标志收到之前,则send工作正常

    3. 发送端没有使用select机制,如果send发生在RST标志收到之后,应用程序会收到SIGPIPE信号,如果忽略该信号,send=-1,errno=EPIPE

交换机或路由器网络断开

  • 接收端处理完1k后,继续从接收缓存区读余下1k数据,然后表现为无数据可读;这时最好设定一个select等待的最大时间,如果超时无数据则认为socket不可用

  • 发送端会不断将余下数据发送到网络上,但始终得不到确认,发送缓存可用空间持续为0,需要应用层程序处理;当然也可以利用tcp本身超时机制来处理

    1. net.ipv4.tcp_keepalive_intvl

    2. net.ipv4.tcp_keepalive_probes

    3. net.ipv4.tcp_keepalive_time

10. listen第二个参数的含义

11. 3次握手4次挥手过程以及为什么是3次,4次

为什么3次握手

为防止已失效的连接请求报文段突然又传送到服务端,因而产生错误

  • client发出的第一个连接请求报文段并没有丢失,只是滞留在网络上,已致延误到连接释放以后的某个时间才到server

  • 但server收到此失效连接请求报文后,误认为是一个新的连接请求,于是向client发出确认报文

  • 如果没有第三个ack,则这个连接就建立起来了,浪费了server端资源

为什么4次挥手

client,server都需要和对方说,我已经没有数据要发送了;再加上tcp的全双工机制

12. 什么情况下会出现timewait过多,如何解决

timewait存在理由

可靠的实现tcp全双工连接的终止

  • 在进行关闭连接四次挥手协议时,最后的ack由主动关闭端发出,如果这个最终的ack丢失,服务器将重发最终的fin,客户端必须维护状态信息允许它重发最终的ack;

  • 如果不维持timewait状态信息,那么客户端将响应rst报文

允许老的重复分节在网络中消逝

  • lost duplicate: tcp分节可能由于路由器异常而迷途,在迷途期间,tcp发送端可能因ack超时重发这个分节,迷途的分节在路由器修复后也会被发送到目的地

  • incarnation(连接的化身): 关闭一个tcp连接后,马上重新建立起一个相同IP:port的连接,前一个连接的迷途分节在连接终止后出现,从而被误解为属于新的化身

  • 为避免这种情况,tcp不允许处于time_wait状态的连接启动一个新的化身,time_wait状态持续2msl可以保证当成功建立一个tcp连接时,来自连接先前化身的重复分组已经在网络中消逝

大量time_wait造成的影响

在高并短连接tcp服务器上,当服务器处理玩请求后立刻主动正常关闭连接,这是会出现大量socket处于time_wait状态

  • 高并发可以让服务器在段时间范围内同时占用大量端口,从而影响新建连接的能力

  • 短连接表示”业务处理+传输数据的时间 « timewait超时的时间”的连接

如何尽量处理timewait过多(打开系统timewait重用、快速回收)

编辑内核文件/etc/sysctl.conf,加入以下内容

  • net.ipv4.tcp_syncookies = 1: 开启syn cookies, 当出现syn等待队列溢出时,启用cookies处理,可防止少量syn攻击,默认为0,表示关闭

  • net.ipv4.tcp_tw_reuse = 1: 开启重用,允许timewait sockets重新用于新的tcp连接,默认关闭

  • net.ipv4.tcp_tw_recycle = 1: 开启tcp连接中timewait sockets的快速回收,默认关闭

  • net.ipv4.tcp_fin_timeout = 1: 修改系统默认的timeout时间

执行/sbin/sysctl -p 让配置生效

13.客户端收到乱序报文,会怎样返回报文