webserver面试题

协议栈相关

  1. 为什么listenfd要设置为非阻塞

    • 客户端和服务端建立tcp连接时,connect()会先于accep()函数返回。
    • 当一个连接到来的时候,监听套接字可读,此时,我们稍微等一段时间之后再调用accept()。就在这段时间内,客户端设置linger选项(l_onoff = 1, l_linger = 0),然后调用了close(),那么客户端将不经过四次挥手过程,通过发送RST报文断开连接。服务端接收到RST报文,系统会将排队的这个未完成连接直接删除,此时就相当于没有任何的连接请求到来, 而接着调用的accept()将会被阻塞,直到另外的新连接到来时才会返回。这是与IO多路复用的思想相违背的(系统不阻塞在某个具体的IO操作上,而是阻塞在select、poll、epoll这些IO复用上的)。
    • 上述这种情况下,如果监听套接字为非阻塞的,accept()不会阻塞住,立即返回-1,同时errno = EWOULDBLOCK。
  2. setsocketopt()中so_linger的作用

    SO_LINGER选项用来设置延迟关闭的时间,等待套接字发送缓冲区中的数据发送完成。
    没有设置该选项时,在调用close()后,在发送完FIN后会立即进行一些清理工作并返回。如果设置了SO_LINGER选项,并且等待时间为正值,则在清理之前会等待一段时间。
    以调用close()主动关闭为例,在发送完FIN包后,会进入FIN_WAIT_1状态。如果没有延迟关闭(即设置SO_LINGER选项),在调用tcp_send_fin()发送FIN后会立即调用sock_orphan()将sock结构从进程上下文中分离。分离后,用户层进程不会再接收到套接字的读写事件,也不知道套接字发送缓冲区中的数据是否被对端接收。如果设置了SO_LINGER选项,并且等待时间为大于0的值,会等待套接字的状态从FIN_WAIT_1迁移到FIN_WAIT_2状态。我们知道套接字进入FIN_WAIT_2状态是在发送的FIN包被确认后,而FIN包肯定是在发送缓冲区中的最后一个字节,所以FIN包的确认就表明发送缓冲区中的数据已经全部被接收。当然,如果等待超过SO_LINGER选项设置的时间后,还是没有收到FIN的确认,则继续进行正常的清理工作,Linux下也没有返回错误。从这里看来,SO_LINGER选项的作用是等待发送缓冲区中的数据发送完成,但是并不保证发送缓冲区中的数据一定被对端接收(对端宕机或线路问题),只是说会等待一段时间让这个过程完成。如果在等待的这段时间里接收到了带数据的包,还是会给对端发送RST包,并且会reset掉套接字,因为此时已经关闭了接收通道。
    在使用这个选项来延迟关闭连接的时候有三个地方需要注意:

    1. 进程会睡眠,直到状态不为FIN_WAIT_1、CLOSING、LAST_ACK(也就是接收到对FIN的ACK包),或者等待超时
    2. 在等待的过程中如果接收到带数据的包还是会发送RST包
    3. 消耗更多的额外资源, TCP协议是一个通用的传输层协议,不关心上层具体的业务,如果要延迟关闭连接,最好是结合自己的业务和场景自己来管理,不要依赖这个选项。nginx的延迟关闭就是自己来管理的,觉得要比直接使用SO_LINGER选项好一些,并且不会导致进程阻塞。 ngxin在发送错误信息后,会等待一段时间,让用户把所有的数据都发送完。超过等待时间后,会直接关闭连接。通过lingering_close,nginx可以保持更好的客户端兼容性,避免客户端被reset掉。
    4. SO_LINGER还有一个作用就是用来减少TIME_WAIT套接字的数量。在设置SO_LINGER选项时,指定等待时间为0,此时调用主动关闭时不会发送FIN来结束连接,而是直接将连接设置为CLOSE状态,清除套接字中的发送和接收缓冲区,直接对对端发送RST包。

操作系统相关

  1. 中断
  • 中断概念:内核为了处理硬件发送的中断请求,停止对当前进程的处理,转而使用内核中的中断处理程序来处理请求
  • 中断过程:

linux

  • 端口占用如何处理:
    • lsof -i:端口号 来查看占用端口的进程号以及对应的文件描述符
    • netstat -tunlp(tcp/udp/no resolve/listen/pid)| grep 端口号
    • kill -9 pid

webserver面试题
http://example.com/2023/12/10/webserver-log/
作者
李凯华
发布于
2023年12月10日
许可协议