linux复习——I/O复用

I/O复用
linux下实现I/O复用的系统调用主要有select,poll和epoll.
一、select系统调用
用途:在一段指定时间内,监听用户感兴趣的文件描述符上的可读可写和异常等事件。
1.1 select API
#include<sys/select.h>
int select( int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval * timeout );
  1. nfds参数指定被监听的文件描述符的总数,select监听所有文件描述符的最大值+1(文件描述符是从0开始计数的)。
  2. readfds,writefds,exceptfds分别指向可读、可写、异常等事件对应的文件描述符的集合。应用程序调用select函数时,通过这3个参数传入自己感兴趣的文件描述符。select调用返回时,内核将修改他们来通知应用程序哪些文件描述符已经就绪。三个参数的是通过fd_set结构体仅包含了一个整型数组,该数组的每个元素的每一位标记一个文件描述符。fd_set能容纳的文件描述符数量由FD_SETSIZE指定,这就限定了select能同时处理文件描述符的总量。
  3. timeout参数用来设置select函数的超时时间。timeval结构类型的指针:采用指针参数是因为内核将修改它来告诉应用程序select等待了多久。但是timeout在调用失败是值是不确定的。
select提供微妙级的定时方式。给timeout变量tv_sec成员和tv_usecc成员都传递0,则select将立即返回。如果传递null,select一直阻塞,直到某个文件描述符就绪。 select成功时返回就绪文件描述符的总数。如果在超时时间内没有任何文件描述符就绪,select将返回0,select失败时返回-1并设置errno.如果在select等待期间,程序接收到信号,则select立即返回-1.并设置errno为EINTR。
1.2文件描述符就绪的条件
  1.2.1以下情况socket是可读的:
  1. 无堵塞的读该socket:socket内核接收缓存区中字节数>=最低水位标记(SO_RCVLOWAT),此时读操作返回的字节数大于0.
  2. socket通信的对方关闭了连接,此时读操作返回0;
  3. 监听socket上有新的连接请求;
  4. socket上有未处理的错误(使用getsockopt来读取和清除该错误)
    1.2.2以下情况socket是可写的:
    1. socket内核发送缓存区中的可用字节数>=最低水位标记,此时无阻塞的写该socket,写的操作返回的字节数大于0;
    2. socket的写操作被关闭。(对于写操作被关闭的socket,执行写操作将触发SIGPIPE信号)
    3. socket使用非阻塞connect连接成功或者失败之后。
    4. socket上有未处理的错误
    1.2.3异常情况:
    1. socket上接收到带外数据
    二、poll系统调用
    poll系统调用和select类似,也是在指定时间内轮询一定数量的文件描述符,用来测试其中是否有绪者。
    #include<poll.h>
    int poll( struct pollfd *fds,nfds_t nfds,int timeout );
    参数解析:
    1、fds参数,pollfd结构体类型的数组,指定所有文件描述符上发生的可读、可写和异常等事件
    struct pollfd
    {
        int fd;//文件描述符
        short events;//注册事件,告诉poll监听fd上哪些事件,一系列事件的按位或
        short revents;//实际发生的事件,有内核填充
    };
    通常,应用程序需要根据recv调用的返回值来区分socket上接收到的是有效数据还是对方关闭连接的请求,并作出相应的处理。
    2、nfds参数指定被监听事件集合fds的大小。
    3、timeout参数指定poll的超时值,单位是毫秒。当timeout=-1时,poll调用将永远阻塞,直到某个事件发生;当timeout=0时,poll调用将立即返回。
    poll系统调用的返回值的含义与select相同。
    三、epoll的系统调用
    epoll需要一个额外的文件描述符来唯一识别内核中的事件表(存放用户关心的文件描述符上的事件)。
    1、创建额外的文件描述符
    int epoll_create( int size );
    1. 参数size不起作用,只是给内核一个提示,告诉事件表的大小。
    2. 返回值是文件描述符,用作其他epoll系统调用的第一个参数,用来指定要访问的内核事件表。
    2、用来操作epoll的内核事件表
    int epoll_ctl( int epfd,int op,int fd,struct epoll_event *event);
    参数说明:
    1、fd:要操作的文件描述符
    2、op:指定操作类型。
         操作类型的分类:
    1.      EPOLL_CTL_ADD,往事件表中注册fd上的事件。
    2.      EPOLL_CTL_MOD,修改fd上的注册事件
    3.      EPOLL_CTL_DEL,删除fd上的注册事件
    3、event:指定事件。结构:struct epoll_event
                           {
                              _uint32_t events;//描述事件类型
                              epoll_data_t data;};//存储用户数据
            data的定义:typedef union epoll_data
                        {
                             void *ptr;//用来指定和fd相关的用户数据
                             int fd;//指定事件所从属的目标文件描述符
                             uint32_t u32;
                              uint64_t u64;}epoll_data_t;
    注:由于epoll_data_t是一个联合体,故不能同时使用ptr和fd.如果要实现将文件描述符和用户数据关联起来,以实现数据的快速访问。
    解决方法:放弃使用epoll_data_t中的fd成员,而在ptr指向的用户数据中包含fd.
    3、epoll_wait函数:一段超时时间内等待一组文件描述符的事件。epoll系列调用的主要接口是epoll_wait函数。
    int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
    参数解释:
    1. maxevents:指定最多监听多少事件,必须大于0;
    2. events:只用于输出epoll_wait检测到的就绪事件。
    注:epoll_wait如果检测到事件,就将所有就绪的事件从内核事件表中复制到它的第二个参数events指定的数组中。

猜你喜欢

转载自blog.csdn.net/minld/article/details/76100719