关于epoll_wait的个人理解的一些记忆

本文探讨了在Linux高性能服务器编程中,如何使用epoll的边沿触发非阻塞I/O模式避免水平触发可能导致的数据堆积问题。分析了边沿触发在阻塞I/O情况下可能遇到的‘饥渴状态’,并解释了read函数返回值为0的两种情况:客户端主动关闭连接和读到文件末尾。最后给出了在处理边沿触发非阻塞I/O时的代码示例,包括检查客户端断开连接的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该篇博客类似做笔记的行为,记录自己关于学习IO多路复用过程中的一些个人理解,不喜勿喷,谢谢!!~

epoll采用的模式为边沿触发非阻塞I/O

水平/边沿触发:指的是epoll_wait()的触发行为
阻塞/非阻塞I/O:指的是文件描述符的读写(read)行为

  • 使用水平触发非阻塞I/O:如果客户端发送10字节,服务端每次只能读5字节,水平触发就会导致缓冲区不断堆积。如果客户端发送5000字节,这5000字节首部有一个50字节长度的标识,服务器每次读取50字节,可以通过判断标识来决定后面的要不要,如果是水平触发,后面虽然客户端不再继续发送数据,但是epoll_wait还是会被触发为文件描述符可读,每次都进行数据读取。如果使用边沿触发就不会出现这种情况了。
  • 使用边沿触发阻塞I/O:假如设置的readn是要一次读取500字节,但是客户端只发送了200字节过来,此时read就会发生阻塞,直到客户端再次发送数据过来,也就是说需要epoll_wait再次调用成功返回监听的文件描述符可读。但是此时已经阻塞在了read函数,即使客户端发送了数据,也无法触发epoll_wait,而是一直处于阻塞状态(饥渴状态)。

Linux高性能服务器编程P157页原话是:每个使用ET模式的文件描述符都应该是非阻塞的。如果文件描述符是阻塞的,那么读或写操作将会因为没有后续的事件而一直处于阻塞状态(饥渴状态)。

read函数返回值为0的两种情况:

  • 客户端主动关闭了连接
  • read读到了文件末尾

在使用epoll_wait的边沿触发非阻塞I/O模型设计程序时

一般代码如下:

while (1) {
	res = epoll_wait(epfd, res_event, max_event, -1);
	...
	if (res_event[0].data.fd == connfd) {// 该文件描述符是可读的文件描述符
		while ((n = read(confd, buf, MAXLINE / 2)) > 0) { // 直到读到文件末尾才会推出
			write(STDOUT_FILENO, buf, n);
	}
}

个人认为还需要先进行一个客户端主动断开的判断

改进代码如下:

while (1) {
	res = epoll_wait(epfd, res_event, max_event, -1);
	...
	if (res_event[0].data.fd == connfd) {// 该文件描述符是可读的文件描述符
		if ((n = read(confd, buf, MAXLINE / 2)) == 0) { // 客户端主动断开连接了
			res = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
			if (res == -1)
				perr_exit("epoll_ctl error");
			close(sockfd);
		}
		else if (n < 0) {
			perror("read n < 0 error: ");
			res = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
			close(sockfd);
		}
		else { // 文件描述符有数据可以读
			while ((n = read(confd, buf, MAXLINE / 2)) > 0) { // 当n=0时表示的读到文件末尾,而不是对端关闭连接
				write(STDOUT_FILENO, buf, n);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值