前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于I/O模型,和select、poll、epoll的区别

关于I/O模型,和select、poll、epoll的区别

作者头像
呱牛笔记
发布2023-05-02 15:14:50
3920
发布2023-05-02 15:14:50
举报
文章被收录于专栏:呱牛笔记

I/O模型主要包括:阻塞IO、非阻塞IO、I/O 多路复用、异步I/O和信号I/O;

Socket创建的时候默认是阻塞的,如何将Socket设置为非阻塞的?

第一种:直接在创建socket时设置

int sockfd= socket(AF_INET,SOCK_STREAM | SOCK_NONOBLOCK,0);

第二种,fcntl系统调用函数是用来控制文件描述符常用的属性和行为,通过此函数可以修改socket属性;

#include <fcntl.h>

int fcntl(int fd,int cmd,...)

int flags = fcntl(sockfd,F_GETFL,0);

fcntl(sockfd,F_SETFL,flags | SOCK_NONOBLOCK);

阻塞和非阻塞的理解

阻塞和非阻塞能应用于所有文件描述符。所谓阻塞方式的意思是指,当试图对该文件描述符进行读写时,如果当时没有东西可读,或者暂时不可写,程序就进入等待状态,直到有东西可读或者可写为止;而对于非阻塞状态,如果没有东西可读,或者不可写,读写函数马上返回,而不会等待,O_NONBLOCK 的标志打开文件/Socket/FIFO句柄,如果连续做 read 操作而没有数据可读,此时程序不会阻塞起来等待数据准备就绪返回,read 函数会返回一个错误 EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。

IO Multiplexing - IO多路复用

I/O复用是最常使用的I/O通知机制,指的是,应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序,Linux常用的I/O复用函数有select、poll和epoll;但I/O复用函数本身是阻塞的,它们能提高程序效率的原因在于它们具有同时监听多个I/O事件的能力;

SIGIO信号,即信号驱动IO,也可以用来报告I/O事件,但某个目标文件描述符上有事件发生时,SIGIO信号的信号处理函数就将被触发,我们也就可以在该信号处理函数中对目标文件描述符执行非阻塞I/O操作了。

Asynchronous IO - 异步IO,用户直接对I/O执行读写操作,这些操作告诉内核用户读写缓冲区的位置,以及I/O操作完成之后内核通知应用程序的方式,异步I/O的读写操作总是立即返回,而不论I/O是否是阻塞的,因为真正的读写操作已经由内核接管。也就是说,同步I/O模型要求用户代码自行执行I/O操作(将数据从内核缓冲区读入用户缓冲区,或是将数据从用户缓冲区写入内核缓冲区),而异步I/O机制则由内核来在后台执行I/O操作。同步I/O向应用程序通知的是I/O就绪事件,而异步I/O向应用程序通知的是I/O完成时间。

Linux环境中,aio.h头文件中定义的函数提供了对异步I/O的支持。

进程文件描述符上限和系统文件描述符上限

进程文件描述符上限user limit中nofile的soft limit,实际上这是单个用户的文件描述符上限

[root@ff353cc400a7 ~]# ulimit -a

open files                      (-n) 1048576

[root@ff353cc400a7 ~]# ps -ef|grep nginx

root       401     1  0 May06 ?        00:00:00 nginx: master process /usr/sbin/nginx

www-data   402   401  0 May06 ?        00:00:00 nginx: worker process

www-data   403   401  0 May06 ?        00:00:00 nginx: worker process

www-data   404   401  0 May06 ?        00:00:00 nginx: worker process

www-data   405   401  0 May06 ?        00:00:00 nginx: worker process

[root@ff353cc400a7 ~]# cat /proc/401/limits

Max processes             unlimited            unlimited            processes 

Max open files            1048576              1048576              files     

查看nginx进程实际打开的文件句柄数!

[root@ff353cc400a7 ~]# ll /proc/401/fd | wc -l

22

进程文件描述符上限修改:

1、临时修改,重启恢复:ulimit -n 2048

2、修改linux系统参数。vi /etc/security/limits.conf 添加

*  soft  nofile  65536

*  hard  nofile  65536

系统文件描述符上限, 文件描述符一定会占用资源,在有限的硬件条件下,比方内存等系统资源,文件描述符必定会有上限,但远大于进程文件描述符大小:

[root@ff353cc400a7 ~]# cat /proc/sys/fs/file-max

782683 

select、poll和epoll的区别与关键差异

select监听文件句柄的个数,主要受限sys/select.h头文件中 FD_SETSIZE 的大小,一般来说是1024,只有重新编译内核才能调整,这就限定了select函数中的文件描述符上限;

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

poll监听文件句柄的个数,则是进程可打开的文件描述符大小;

int poll(struct pollfd *fds, unsigned int nfds, int timeout);

pollfd 结构包含了要监视的 event 和发生的 event,不在使用 select “参数-值”的传递方式。poll 方式为每个需要监听的文件描述符构建一个类型为 pollfd 的对象并填充监听的事件,poll 返回后,检查 revents 字段判断是否就绪。poll 不会修改 pollfds 数组,所以不需要每次都重新传入 pollfds,但是每次处理完需要重制 revents 值。

struct pollfd {

    int fd;  /* file descriptor */

    short events; /* requested events to watch */

    short revents; /* returned events witnessed */

}

同时,pollfd 并没有最大数量限制(但是数量过大后性能也是会下降)。和 select 函数一样,poll 返回后,需要轮询 pollfd 来获取就绪的描述符。

select 和 poll 都需要在返回后,通过遍历文件描述符来获取已经就绪的 socket。事实上,同时连接的大量客户端在某一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,效率也会线性下降。

epoll监听文件句柄的个数,则是系统文件描述符上限,句柄上限是系统最大可以打开文件的数目,这个通常可以达到百万量级;

// 创建一个 epoll 的句柄,size 用来告诉内核这个监听的数目一共有多大,并不是限制了 epoll 所能监听的描述符的最大个数,只是对内核初始分配内部数据结构的一个建议

int epoll_create(int size); 

// 向 epfd 里添加、移除文件描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 

// 等待事件发生

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epfd: 是 epoll_create() 的返回值。

op: 表示 op 操作,用三个宏来表示,

添加 EPOLL_CTL_ADD,

删除 EPOLL_CTL_DEL,

修改 EPOLL_CTL_MOD。

分别添加,删除和修改对 fd 的监听事件。

fd:是需要监听的 fd(文件描述符)

epoll_event:是告诉内核需要监听什么事件,struct epoll_event 的结构如下:

struct epoll_event {

__uint32_t events; / Epoll events /

epoll_data_t data; / User data variable /

};

epoll的ET模式和LT模式理解;

epoll是一种的较为高效的多路复用模型,高效主要体现在哪些方面?并发性能更强,处理大量句柄能力没有内核句柄列表拷贝的过程,内核提供了更快的性能处理。

epoll的ET模式下,正确的读写方式是:

读: 只要可读, 就一直读,直到返回0,或者 errno = EAGAIN

写:只要可写, 就一直写,直到数据发送完,或者 errno = EAGAIN

思考:epoll为什么只能使用非阻塞的Socket?

epoll 对文件描述符的操作有两种模式:LT(水平触发 level trigger) 和 ET(边缘触发 edge trigger)。LT 模式为默认模式,LT 模式与 ET 模式的区别如下:

LT模式:当 epoll_wait 检测到描述符时间发生并将此事件通知应用程序,应用程序可以不立即处理该时间。下次调用 epoll_wait 时,会再次相应应用程序并通知此事件。

ET模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知次事件。

一种是在单独的线程或进程里解析数据,另外一种解决方法就是设置EPOLLONESHOT属性!

代码语言:javascript
复制
void Eepoll::ResetOneShot(intepollfd,SOCKET fd,bool bOne)
{
         epoll_eventevent;
         event.data.fd= fd;
         event.events= EPOLLIN | EPOLLET ;

         if(bOne)
         {
                   event.events |=EPOLLONESHOT;
         }
         if(-1 == epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event))
         {
                   perror("resetoneshotepoll_ctl error!");
         }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/05/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 阻塞和非阻塞的理解
  • 进程文件描述符上限和系统文件描述符上限
  • select、poll和epoll的区别与关键差异
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档