高并发服务器架构笔记(1)——poll 和 epoll
来源:互联网 发布:淘宝微淘怎么做 编辑:程序博客网 时间:2024/06/10 21:18
signal(SIGPIPE, SIG_IGN); //忽略 SIGPIPE 信号
- 如果客户端关闭套接字
close(sockfd);
而服务器调用了一次write()
这时候服务器会产生一个 SIGPIPE 信号。
- 如果客户端关闭套接字
TIME_WAIT 状态对高并发服务器有不利影响。
- 应尽可能避免在服务器端出现 TIME_WAIT 状态,因为会耗费大量比必要的资源。
- 协议设计上,应尽可能让客户端先 close,这样就能把TIME_WAIT 状态分散到大量的客户端。
- 如果有个别不活跃的客户端,但又不 close,服务端也要有个机制来踢掉不活跃的客户端。
listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
- SOCK_NONBLOCK 非阻塞套接字
- SOCK_CLOEXEC 在 fork 之后 exec 的时候,关掉这个描述符,http://blog.csdn.net/chrisniu1984/article/details/7050663
取 vector 元素的首地址,
&(*v.begin());
poll
- poll 第三个参数为负数,表示 永远等待,不设超时
假如有一个std::vector v, 值为{1, 2, 3, 4, 5, 6},现在有一个 迭代器 it 指向 4 这个值,v.erase(it) 后, 值为{1, 2, 3, 5, 6},it 指向 4 原来的位置(现在的5),所以如果在循环中遍历要先 –it, 因为 for 的最后一步是 ++it,直接指向 6,而忽略 5 了。
如果 read 没有把 connfd 所对应的接收缓冲区的数据都读完,那么connfd 仍然是活跃的。
当我们 read 一个 socket 时(poll 情况下),如果如果一次没读完数据,那么这个数据可能是无效的,我们需要把数据存在缓冲区里,直至读完才使用数据。(poll 的电平触发机制不用
while(1)read()
可以直接read()
; 因为下次会继续触发 EPOLLIN 事件,与 epoll 的 ET 不同。)POLLOUT 事件的触发条件: connfd 的发送缓冲区不满(可以容纳数据)。
- 使用 poll 时,不能一接收到 connfd 就马上关注POLLOUT 事件,如果我们每有一个新连接就马上关注 POLLOUT 事件,这个 connfd 的缓冲区一直可写,但我们暂时不需要发送数据,那么 poll 会一直触发 POLLOUT 事件,造成busy-loop。
正确做法应该是添加一个应用层缓冲区。
以 echo server 为例
connfd POLLIN 事件到来read(connfd, ....);ret = write(connfd, buf, 10000);if(ret < 10000){ // 将未发完的数据添加到应用层缓冲区 OutBuffer // 继续关注connfd 的 POLLOUT 事件}// 到这里已经可以发送了//取出应用层缓冲区中的数据发送write(connfd, ...);如果应用层缓冲区中的数据发送完毕,取消关注 POLLOUT事件
总结:POLLOUT 需要写时才关注,数据全发出去才取消关注 POLLOUT
- poll 和 epoll 的 LT 使用几乎一样。
- accept(2) 返回 EMFILE 的处理。
- 表示描述符用完了
- 解决方法
- 调高进程文件描述符数目(治标不治本)
- 死等。当描述符到达上限时,新的需求要等到旧的释放套接字后再用被释放的套接字(效率低)。
- 退出程序。(小题大做,且不满足 7 * 24 小时不间断服务)
- 关闭监听套接字 listenfd。(不现实)
- 如果是 epoll,可改用 ET 模式。(后面会说)
- (推荐)一开始就准备一个空闲的文件描述符,再 accept( ), 拿到 socket 连接的文件描述符, 随后马上 close( ),这样就优雅的拒绝了与客户端的连接。最后再次打开这个空闲文件
int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);// ... while (1){ nready = poll(&*pollfds.begin(), pollfds.size(), -1) // ... if (pollfds[0].revens & POLLIN) { if (connfd == -1) { if (errno == EMFILE) { close(idlefd); idlefd = accept(listenfd, NULL, NULL); // 先接收再拒绝 close(idlefd); idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC); continue; } else { //..... } } }}
- 每次调用 poll 函数的时候,都需要把监听套接字与已连接的感兴趣的描述符数组拷贝到内核,效率比较低。
epoll
- 两个结构体
typedef union epoll_data{ void *ptr; int fd; uint32_t u32; uint64_t u64;}epoll_data_t;struct epoll_event{ uint32 events; /*需要监听的事件,例如 EPOLLIN */ epoll_data_t data; /*某个套接字,例如 listenfd */}
- 两种模式:LT 和 ET(默认为 LT)
- LT 和 poll 几乎一样,唯一不同的是:epoll 的 LT 返回结果时,不需要再所有套接字中遍历,因为
epoll_wait()
返回的都是活跃的套接字。
- LT 和 poll 几乎一样,唯一不同的是:epoll 的 LT 返回结果时,不需要再所有套接字中遍历,因为
epoll 能够管理的描述符的个数取决于系统资源。
epollfd = epoll_create();
- 这个函数返回一个epoll 描述符。
- epoll_ctl = (epollfd, EPOLL_CTL_ADD, listenfd, &event);
- EPOLL_CTL_ADD 表示添加关注。
- 把 listenfd 添加到 epollfd 所关注的事件。
- epoll_wait()
- 第一个参数就是上面创建的 epoll 描述符。
- 第二个参数是一个数组的首地址,用于存放返回的活跃事件()存放结果。
- 第三个参数是数组大小。
- 第四个是等待时间。
- 调用 epoll_wait( ) 的时候不需要从用户空间把感兴趣的描述符数组传递到内核空间,高效。(poll 需要)。
- 使用 ET 模式时如果 出现 EMFILE 错误,比较难处理。而在 LT 模式下,可以用先创建空描述符的方式处理,因为该描述符一直可读/写,那么下一次 epoll_wait() 时,LT 模式下还会触发。而 ET 不会再触发了。
- 使用 epoll 的一个要点就是掌握触发条件。
每一个套接字都有两个缓冲区,接收缓冲区和发送缓冲区。
LT 电平触发(在高电平时持续触发)
- EPOLIN (有数据来,可读)
- 内核中的 socket 接收缓冲区为空 低电平 不触发
- 内核中的 socket 接收缓冲区非空 高电平 触发
- EPOLLOUT(可往套接字写数据)
- 内核中的 socket 发送缓冲区为非满 高电平 触发
- 内核中的 socket 发送缓冲区为满 低电平 不触发
- EPOLIN (有数据来,可读)
ET 边沿触发(从低电平转化为高电平时触发,非持续)
- 低电平 -> 高电平 触发 (略,高低电平条件同上)
LT 新建连接后不能马上关注 EPOLLOUT 事件,否则 busy-loop。
ET 可以马上关注 EPOLLOUT 事件。
LT 一次没 read 完没关系,下次继续触发。
ET 必须一次接接收完, 否则下次不再触发,遗漏数据。
已连接套接字数量不大,并且这些套接字非常活跃, 如果使用 epoll 的话,那么系统的开销主要用于不停地调用 callback 函数。可能比 poll select 效率更低。(数量不多,遍历开销不高)。在处理高并发时 epoll 才有优势。
- 高并发服务器架构笔记(1)——poll 和 epoll
- 高并发服务器架构笔记(2)——面向对象编程风格
- 高并发服务器架构笔记(3)——muduo_base 源码分析
- 高并发服务器架构笔记(3)——muduo_base 源码分析
- 高并发服务器架构笔记(4)——muduo_net 源码分析
- (五十四)高并发服务器——多路IO转接机制poll模型
- 高并发服务器(基于epoll)
- Linux——高性能服务器编程——select&poll&epoll
- (五十五)高并发服务器——多路IO转接机制epoll模型
- Epoll实现服务器高并发
- epoll实现服务器高并发
- 高并发服务器架构
- poll&&epoll实现分析(一)——poll实现
- poll&&epoll实现分析(一)——poll实现
- poll&&epoll实现分析(一)——poll实现
- poll&&epoll实现分析(一)——poll实现
- poll&&epoll实现分析(一)—poll实现
- epoll——高并发的功臣
- Linux下zip和unzip解压缩文件命令
- Wireless tools for Linux介绍
- 音视频开发:码率、延时、花屏、卡顿
- 欢迎使用CSDN-markdown编辑器
- 文章标题
- 高并发服务器架构笔记(1)——poll 和 epoll
- OpenCV中的DFT和iDFT的详细代码及注释
- Eclipse 搭建Spring boot
- android view的绘制基础总结笔记-canvas
- 出海两个月,人都有点懵
- 欢呼声热烈 谷歌宣布 Kotlin 成 Android 开发一级语言
- mybatis 参数类型--不完整//与特殊符号# | $
- hive配置mysql连接,配置java连接用户名密码,配置自定义类验证用户名密码
- Effective java-并发 笔记