<网络编程>IO复用

IO复用是一种机制,一个进程可以监听多个描述符,一旦某个描述符就绪(读就绪和写就绪),能够同志程序进行相应的读写操作。

目前支持I/O复用的系统调用有select,poll,pselect,epoll,本质上这些I/O复用技术是同步I/O技术。在读写事件就绪后需要进程自己负责进行读写,即读写过程是进程阻塞的。

与多进程和多线程相比,I/O复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销

同步I/O操作导致请求进程阻塞,直到I/O操作完成。异步I/O不导致请求进程阻塞。

1、select:允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或者多个事件发生或者经历一段时间后才唤醒它。

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfdpl, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
  • 参数
    • maxfdpl:指定待测试的描述符个数,值为待测试的最大描述符加1
    • readset:读描述符集合
    • writeset:写描述符集合
    • exceptset:异常描述符集合。
    • timeout:内核等待任意描述符就绪的超时时间,超时函数返回0.永远等待下去,值为0;等待固定一段时间;立即返回。即timeval中的时间设置为0.
struct timeval{
    long tv_sec;
    long tv_usec;
};
  • 描述集说明:
    • select使用的描述符集,通常是一个整数数组,其中每个整数的一位对应一个描述符。
    • 如果对三个描述符集中的某个不感兴趣,可以设置为空指针,如果都设置为空指针,会得到一个比sleep更加精确的定时器
    • <sys/select.h>中定义的FD_SETSIZE常值是数据类型fd_set中的描述符总数,其值通常是1024,不过很少用到这么多描述符,maxfdp1参数迫使我们计算

描述符就绪条件

  • 套接字可读
    • 该连接的读半部关闭,对这样的套接字读操作将不会阻塞并返回0
    • 该套接字是一个监听套接字,且已完成的连接数不为0
    • 套接字上有一个错误,对其读不会阻塞并返回-1,同时把errno设为确切错误条件。
    • 该套接字接收缓存区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小。
  • 套接字可写
    • 该套接字发送缓存区中的数据字节数大于等于套接字发送缓冲区低水位标记的当前大小。
    • 该连接写半部关闭,对这样的套接字写将产生SIGPIPE信号
    • 使用非阻塞式connect的套接字已建立连接,或者connect以失败告终
    • 套接字上有一个错误,对其写将不会阻塞并返回-1,同时把errno设为确切错误条件

2、pselect

#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
int select(int maxfdpl, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout, const sigset_t *signmask);
  • 参数
    • maxfdp1:指定待测试的描述符个数,值为待测试的最大描述符加1(参数名的由来)
    • readset:读描述符集
    • writeset:写描述符集
    • exceptset:异常描述符集。目前支持的异常条件只有两个
    • timeout:(告知)内核等待任意描述符就绪的超时时间,超时函数返回0
    • sigmask:该参数运行程序先禁止递交某些信号,再测试由这些当前被禁止信号的信号处理函数设置的全局变量,然后调用pselect,告诉它重新设置信号掩码
  • 相对于select的变化:
    • 使用timespec结构,时间精度更高。
    • 增加了第6个参数:一个指向信号掩码的指针。该参数运行程序先禁止递交某些信号,再测试由这些当前被禁止信号的信号处理函数设置的全局变量,然后调用pselect,告诉它重新设置信号掩码

3、poll

#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
  • 参数
    • fdarray:指向pollfd数组的指针,每个pollfd结构包含了描述符及其相应事件。
struct pollfd{
    int   fd;       //监视的描述符
    short events;   //该描述符上监视的事件
    short revents;  //该描述符上发生的事件
};
  • nfds:pollfd数组的元素个数(即监视的描述符总数.
  • timeout:内核等待任意描述符就绪的超时时间,超时函数返回0.
    • INFTIM(一个负值):永远等待下去
    • >0:等待一段固定时间
    • 0:立即返回(轮询)

如果不再关心某个特定描述符,可以把与之对应的pollfd结构的fd成员设置成一个负值。poll函数将忽略这样的pollfd结构的events成员,返回时将其revents成员的值置为0

4、epoll

#include <sys/epoll.h>
int epoll_create(int size);
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);
  • epoll_create():创建一个epoll句柄,它会占用一个fd,用完epoll后,必须将其关闭。
  • 参数:
    • size:告诉内核监听描述符的数量,并不是监听数量的最大值,是对内核初始分配内部数据结构的一个建议
    • 返回值:创建的epoll句柄
  • epoll_ctl():对描述符fd进行op操作。
    • 参数:epfd:epoll_create得到的epoll句柄。
    • op:操作
      • EPOLL_CTL_ADD:注册新的fdepfd
      • EPOLL_CTL_DEL:从epfd中删除一个fd
      • EPOLL_CTL_MOD:修改已注册fd的监听事件
    • fd:操作的描述符
    • event:告知内核需要监听的时间。
      • EPOLLIN:对应的描述符可读
      • EPOLLOUT:对应的描述符可写
      • EPOLLPRI:对应的描述符有紧急数据可读(带外数据)
      • EPOLLERR:对应的描述符发生错误
      • EPOLLHUP:对应的描述符被挂断
      • EPOLLET:将epoll设为边缘触发模式(默认为水平(LT)触发模式
      • EPOLLONESHOT:只监听一次事件,监听完后,如果需要再次监听,需再次将描述符加入到epoll队列
  • epoll_wait():等待epoll句柄上的I/O事件,最多返回maxevents个事件
    • 参数:
      • epfd:epoll_create得到的epoll句柄。
      • events:从内核得到事件的集合
      • maxevents:返回事件的最大数量(不能大于创建epoll句柄时的size参数)
      • timeout:超时参数

工作模式:

  • LT水平触发模式:
    • 应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次向应用程序通知此事件
  • 边缘触发(ET)模式
    • 应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不再向应用程序通知此事件

select,poll,epoll比较

  1. 时间复杂度
  • select--O(n):采取无差别轮询,找出能读的数据流以及写数据流
  • poll--O(n):将用户传入的数组拷贝到内核空间,然后查询每个描述符状态,无最大连接限制
  • epoll—O(1):哪个流发生了怎么样的I/O事件通知

优缺点:

  • select
    • 单个进程可监视的fd数量受限。
    • 对socket的扫描是线性的,即采用轮询方式
    • 需要维护一个用来存储大量fd的数组结构,使得用户空间和内核空间在传递该结构时,复制开销大
  • poll
    • 与select类似,将用户传入的数组拷贝到内核空间,然后查询每个fd的状态,如果设备就绪,则在设备等待队列中加入一次并继续遍历,若遍历完该数组没有发现就绪设备,则挂起该进程直到设备就绪或者主动超时。
    • 没有最大连接限制(基于链表存储)
    • 大量的fd数组被整体复制到用户态和内核态,而不管它们是否有意义
    • 水平触发模式,如果这次报告的fd没有被处理,那么下次poll仍会报告
  • epoll
    • LT模式:只要fd可读,每次epoll_wait都会返回它的事件,提醒用户操作
    • ET模式:只会提醒一次,除非有新数据流入,所以在ET模式下,读一个fd,一定要读光所有的buffer
    • epoll使用事件的就绪通知方式,通过epoll_ctl注册fd,一旦fd就绪,内核就会采用类似于回调函数机制来激活fd。
    • 无最大并发连接限制
    • 效率提升(非轮询模式),只在意活跃的fd,而与连接总数无关。
    • 内存拷贝利用mmap文件映射内存,加速与内核空间消息传递。

原文地址:https://www.cnblogs.com/tingweichen/p/10720908.html

时间: 2024-10-13 07:46:48

<网络编程>IO复用的相关文章

epoll实现IO复用,TCP通信

函数原型: 函数说明:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它. 参数说明: fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符: 每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便:特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率:这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述

IO复用之——poll

一. 关于poll 对于IO复用模型,其优点无疑是免去了对一个个IO事件就绪的等待,转而代之的是同时对多个IO数据的检测,当检测等待的事件中至少有一个就绪的时候,就会返回告诉用户进程"已经有数据准备好了,快看看是哪个赶紧处理",而对于IO复用的实现,除了可以用select函数,另外一个函数仍然支持这种复用IO模型,就是poll函数: 二. poll函数的用法 虽然同样是对多个IO事件进行检测等待,但poll和select多少还是有些不同的: 函数参数中, 先来说nfds,这个是和sel

《深入理解计算机系统》Tiny服务器4——epoll类型IO复用版Tiny

前几篇博客分别讲了基于多进程.select类型的IO复用.poll类型的IO复用以及多线程版本的Tiny服务器模型,并给出了主要的代码.至于剩下的epoll类型的IO复用版,本来打算草草带过,毕竟和其他两种IO复用模型差不太多.但今天在看Michael Kerrisk的<Linux/UNIX系统编程手册>时,看到了一章专门用来讲解epoll函数,及其IO复用模型.于是,自己也就动手把Tiny改版了一下.感兴趣的同学可以参考上述手册的下册1113页,有对于epoll比较详细的讲解. 前边针对IO

IO复用

IO复用简单介绍 IO复用使得程序能同一时候监听多个文件描写叙述符.这对提高程序的性能至关重要.通常.网络程序在下列情况下须要使用IO复用技术: client程序要同一时候处理多个socket. client程序要同一时候处理用户输入和网络连接. TCPserver同一时候处理监听socket和连接socket. server要同一时候处理TCP请求和UDP请求. 须要指出的是.IO复用尽管能同一时候监听多个文件描写叙述符,但它本身是堵塞的.而且当多个文件描写叙述符同一时候就绪时,假设不採取额外

Libevent的IO复用技术和定时事件原理

Libevent 是一个用C语言编写的.轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相当精炼.易读:跨平台,支持 Windows. Linux. *BSD 和 Mac Os:支持多种 I/O 多路复用技术, epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O,定时器和信号等事件:注册事件优先级. 1 Libevent中的epoll Libevent重

Linux中的IO复用接口简介(文件监视?)

I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux中,提供了select.poll.epoll三类接口来实现I/O复用. select函数接口 select中主要就是一个select函数,用于监听指定事件的发生,原型如下: 12345 #include<sys/select.h>#include<sys/time.h>int sele

imx6中iomux IO复用

IOMUX Controller (IOMUXC) IO多路复用控制器1.overviewThe IOMUX Controller (IOMUXC), together with the IOMUX, enables the IC to share one pad to several functional blocks.IOMUXC和IOMUX使IMX6能够多个功能模块公用一个pad. 共用是通过对pad的输入/输出信号的多路复用技术来实现的.每一个模块需要一个特定的pad设置, 且对于每一个

IO复用之——select

一. select 前面提到Linux下的五种IO模型中有一个是IO复用模型,这种IO模型是可以调用一个特殊的函数同时监听多个IO事件,当多个IO事件中有至少一个就绪的时候,被调用的函数就会返回通知用户进程来处理已经ready事件的数据,这样通过同时等待IO事件来代替单一等待一个IO窗口数据的方式,可以大大提高系统的等待数据的效率:而接下来,就要讨论在Linux系统中提供的一个用来进行IO多路等待的函数--select: 二. select函数的用法 首先在使用select之前,要分清在IO事件

IO复用_select函数

select函数: #include <sys/select.h> #include <time.h> #include <sys/types.h> #include <unistd.h> int select(int nfds, fd_set*readfds, fd_set*writefds, fd_set*exceptfds, struct timeval*timeout); 参数含义: nfds:一个整型变量,它比所有文件描述符集合中的文件描述符的最大

Linux网络编程-IO复用技术

IO复用是Linux中的IO模型之一,IO复用就是进程预先告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程进程处理,从而不会在单个IO上阻塞了.Linux中,提供了select.poll.epoll三种接口函数来实现IO复用. 1.select函数 #include <sys/select.h> #include <sys/time.h> int select(int nfds, fd_set *readfds, fd_set *writef