模拟epoll的饥饿场景

news/2024/10/3 10:43:52

说明

一直听说epoll的饥饿场景,但是从未在实际环境中面对过,那么能不能模拟出来呢?实际的情况是怎样呢?

模拟步骤

  • 基于epoll写一个简单的tcp echo server,将每次read返回的字节数打印出来
  • 模拟一个客户端大量写入
  • 测试其他客户端能否正常返回

Server代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define MAX_EVENTS 1024
#define LISTEN_BACKLOG 10int epoll_fd;
void do_read(int fd);int main() {int server_fd, nfds, i;struct epoll_event event, events[MAX_EVENTS];struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd;// 创建 socketserver_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == -1) {perror("socket");return 1;}// 设置 socket 选项int opt = 1;if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {perror("setsockopt");close(server_fd);return 1;}// 绑定 socketmemset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(8080);if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind");close(server_fd);return 1;}// 监听 socketif (listen(server_fd, LISTEN_BACKLOG) == -1) {perror("listen");close(server_fd);return 1;}// 创建 epoll 实例epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");close(server_fd);return 1;}// 注册服务器 socketevent.events = EPOLLIN;event.data.fd = server_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {perror("epoll_ctl");close(server_fd);close(epoll_fd);return 1;}printf("Server listening on port 8080...\n");while (1) {// 等待事件就绪nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds == -1) {perror("epoll_wait");close(server_fd);close(epoll_fd);return 1;}// 处理就绪事件for (i = 0; i < nfds; i++) {if (events[i].data.fd == server_fd) {// 接受新连接client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);if (client_fd == -1) {perror("accept");continue;}if (fcntl(client_fd , F_SETFL, O_NONBLOCK) == -1) {perror("fcntl");close(client_fd);continue;}// 注册客户端 socketevent.events = EPOLLIN;event.data.fd = client_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {perror("epoll_ctl");close(client_fd);continue;}printf("New connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));} else {do_read(events[i].data.fd);}}}close(server_fd);close(epoll_fd);return 0;
}void do_read(int fd) {// 处理客户端数据char buf[1024];while(1) {ssize_t bytes_read = read(fd, buf, sizeof(buf));if (bytes_read == -1) {perror("read");close(fd);if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {perror("epoll_ctl");}break;} else if (bytes_read == 0) {printf("Client disconnected\n");close(fd);if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {perror("epoll_ctl");}break;} else {printf("Received data: %d\n", bytes_read);if (write(fd, buf, bytes_read) != bytes_read) {perror("write");close(fd);if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {perror("epoll_ctl");}break;}if (bytes_read < 1024) {break;}}}
}

模拟客户端

客户端1:大量写入客户端:

cat /dev/random 2>/dev/null | nc 127.0.0.1 8080 >/dev/null

客户端2:其他写入客户端,少量写入检查返回值

nc 127.0.0.1 8080

模拟结果

  • server端收到大量的数据,每次read返回1024个字节,句柄非常忙碌
    image
  • 客户端2往server发送的数据一直没有返回【处于饥饿状态】
    image
  • 一旦客户端1断开,客户端2就收到回复了
    image

结果分析

从代码中可以知道,read一直都有数据读取,一直在处理数据,导致其他句柄无法处理数据。也就是说,其实是我们的代码造成了所谓的饥饿,那么也可以从我们的代码层面上去解决这个问题,思路官方man page中已经提到了,将fd维护一个list,均匀的读写数据即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hjln.cn/news/44633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

浪潮服务器做 RAID 的方式

一次充实的装系统体验,非常感谢耐心的浪潮售后技术,没有他就没有这篇博文 由于按Ctrl + H 和 Ctrl + R 进不去 RAID,网上也没有合适的教程 于是使用工程师最有效的手段打电话 摇 人 首先把产品的序列号准备好浪潮服务器客服:400-860-0011关注微信公众号:浪潮信息专家服务 …

[形策/法规] 《促进和规范数据跨境流动规定》 [转]

国家互联网信息办公室令 第 16 号 《促进和规范数据跨境流动规定》已经2023年11月28日国家互联网信息办公室2023年第26次室务会议审议通过,现予公布,自公布之日起施行。主 任  庄荣文 2024年3月22日 第一条 为了保障数据安全,保护个人信息权益,促进数据依法有序自由流…

服务端和客户端 RESTful 接口上传 Excel 的 Python 代码

哈喽,大家好,我是木头左,物联网搬砖工一名,致力于为大家淘出更多好用的AI工具!背景 在现代软件开发中,RESTful API(Representational State Transfer Application Programming Interface)已经成为一种常用的架构风格。它提供了一种简单、易于理解和实现的方式来构建分布式…

基于卷积神经网络的花卉识别

前言 本文介绍卷积神经网络的入门案例,通过搭建和训练一个模型,来对几种常见的花朵进行识别分类;使用到TF的花朵数据集,它包含5类,即:“雏菊”,“蒲公英”,“玫瑰”,“向日葵”,“郁金香”;共 3670 张彩色图片;通过搭建和训练卷积神经网络模型,对图像进行分类,能…

[DP] DP优化总结

写在前面 $ DP $,是每个信息学竞赛选手所必会的算法,而 $ DP $ 中状态的转移又显得尤为关键。本文主要从状态的设计和转移入手,利用各种方法对朴素 $ DP $ 的时间复杂度和空间复杂度进行优化与处理,以达到满足题目要求的目的; 参考文献: 动态规划算法的优化技巧 毛子青 …

比特币区块检查

比特币区块儿在本地产生及接收到其他节点广播时,会进行详细的合法性检查,本文结合代码对主要检查的主要内容进行了分析!比特币采用Pow共识机制,即不断调整Nonce值,对区块头做双重SHA256哈希运算,使得结果满足给定数量前导0的哈希值的过程。其中前导0的个数,取决于挖矿难…

MYSQL——分组

MYSQL——分组 group by的含义:将查询结果按照1个或多个字段进分组,字段值相同的为组。 理解:按照表中数据的某个属性或多个属性将数据归类分成类,按照类别查询出来,这些分类就是分组查询。 group by可于单个字段分组,也可于多个字段分组。

【程序人生】公众号往期回顾如何设置

哈喽,大家好,我是木头左,AI改变生活!一、引言 在微信公众号中,往期回顾功能是一个非常实用的功能,它可以让用户轻松查看过去的文章,了解公众号的历史。本文将详细介绍如何设置公众号往期回顾功能,帮助大家更好地利用这个功能来展示自己的内容和品牌。 二、设置往期回顾…