《UNIX网络编程》笔记 - select和poll

栏目: 服务器 · 发布时间: 4年前

内容简介:IO复用:让进程等待一系列IO条件而不是一个IO条件通过定义:

IO复用:让进程等待一系列IO条件而不是一个IO条件

通过 selectpoll 函数我们可以同时监听多个描述符,在描述符就绪时进行对应的操作。

select

定义:

//maxfdpl: 待测试的描述符个数
//返回就绪描述符的个数,若超时则为0, 若出错则为-1
int select(int maxfdpl, 
    fd_set *readset, 
    fd_set *writeset, 
    fd_set *exceptset, 
    struct timeval *timeout);
 
//超时选项    
//NULL:wait forever;0:don't wait
struct timeval {
	long    tv_sec;         /* seconds */
	long    tv_usec;        /* and microseconds */
};

//每个fds_bit的每一位对应一个描述符
typedef struct fd_set {
	int     fds_bits[FD_SETSIZE/sizeof(int)/NBBY]; /* NBBY=bits in a byte ; usually 8*/
} fd_set;

#define FD_SETSIZE  1024        /* fd_set中描述符的总数 */
void FD_ZERO(fd_set *fdset);            /* clear all bits in fdset */
void FD_SET(int fd, fd_set *fdset);     /* turn on the bit for fd in fdset */
void FD_CLR(int fd, fd_set *fdset);     /* turn off the bit for fd in fdset */
void FD_ISSET(int fd, fd_set *fdset);   /* is the bit for fd on in fdset ? */
复制代码

select的使用方法:

int fds[FD_SETSIZE]; 保存当前所有描述符
    fd_set rset, wset, eset; //定义读、写、异常对应的fd_set
    //初始化fd_set,非常重要且不能省略,因为如果不初始化可能会影响FD_ISSET的调用结果
    FD_ZERO(&rset); 
    FD_ZERO(&wset);
    FD_ZERO(&eset);
    for (;;) {
        //在循环中调用select
        select(FD_SETSIZE, &rset, &wset, &eset, NULL);
        //遍历当前所有的fd,处理就绪的fd
        for (int i = 0; i < FD_SETSIZE; i++)
        {
            if (FD_ISSET(fds[i], &rset))
            {
                //handle read
            }
            if (FD_ISSET(fds[i], &wset))
            {
                //handle write
            }
            if (FD_ISSET(fds[i], &eset))
            {
                //handle exception
            }
        }
    }
复制代码

fd_set的限制

在很早之前就看到网上的介绍说select在描述符个数上是有限制的,现在终于知道这个限制是从哪来的了,这实际上跟fd_set的实现机制有关。

fd_set 中使用int数组中的各个位来保存多个描述符的状态,这个数组称为 描述符集 ,比如数组的第一个数有32位,那么第一个数的每一位就表示第0~31个描述符的状态,这样一来当我们调用 FD_ISSET 来判断某一个描述符状态时,我们只需要找到其对应的位判断其是0或者1就行了;同理当我们需要设置某个描述符状态时,只需要设置对应的位的状态即可。而 fd_set 中数组的大小是通过 FD_SETSIZE 这个值算出来的, FD_SETSIZE 是一个宏定义,通常它的默认值比较小,在我的mac上查看其默认值是1024,也就是说在我的mac上select能够支持的最大的描述符数量是1024个。当然 FD_SETSIZE 也可以重新定义,但如果要调整需要重新编译内核。

描述符读就绪条件

  1. 接收缓冲区数据字节数大于低水位(默认是1),这时读取操作返回大于0
  2. 读半关闭,也就是对端发来了FIN,这时返回0,也就是EOF
  3. 当前套接字是监听套接字,而且已完成连接数不为0,这时可以进行accept操作
  4. 描述符上有套接字错误需要处理

描述符写就绪条件

  1. 发送缓冲区数据字节数大于低水位(通常为2048);
  2. 套接字已连接或不需要连接(UDP)
  3. 写半关闭,这时如果再写会收到 SIGPIPE 信号
  4. 使用非阻塞式 connect 的套接字已建立连接或者 connect 失败
  5. 描述符上有套接字错误需要处理

shutdown&close

有两个函数可以关闭套接字: shutdownclose ,它们的区别如下:

  1. close 会将引用计数减1,当计数为0时关闭套接字; shutdown 可以直接触发关闭。
  2. close 会终止读和写两个方向; shutdown 可以通过参数 howto 指定关闭某个方向
int close(int fd);
int shutdown(int fd, int howto);

/*
 * howto arguments for shutdown(2), specified by Posix.1g.
 */
#define SHUT_RD         0               /* shut down the reading side */
#define SHUT_WR         1               /* shut down the writing side */
#define SHUT_RDWR       2               /* shut down both sides */

复制代码

poll

pollselect 的功能类似,也支持IO复用,但是 poll 没有使用描述符集,而是使用 pollfd 这种结构来表示描述符的状态。

//nfds:array的长度,受进程能打开的最大文件数限制
//返回就绪描述符的个数,若超时则为0, 若出错则为-1
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

struct pollfd {
	int     fd;         /* descriptor to check */
	short   events;     /* events of intrests on fd */
	short   revents;    /* events that occurred on fd */
};
复制代码

poll 的使用方法:

struct pollfd pollfds[OPEN_MAX]; //定义pollfd数组,将需要监听的描述符保存起来
    for (;;)
    {
        //在循环中调用poll
        poll(pollfds, OPEN_MAX, INFTIM);
        for (int i = 0; i < OPEN_MAX; i++)
        {
            //遍历pollfd数组处理就绪的描述符
            struct pollfd pfd = pollfds[i];
            if (pfd.revents & POLLIN) {
                //handle read
            }
            if (pfd.revents & POLLOUT) {
                //handle write
            }
        }
    }
复制代码

poll 识别的数据类型:普通(normal)、优先级带(priority band)、高优先级(high priority); 这些术语来自基于流的实现。(没太明白,先标记下)

events常量列举:

常量 出现在events 出现在revents 说明
POLLIN y y 普通或优先级带数据可读
POLLRDNORM y y 普通数据可读
POLLRDBAND y y 优先级带数据可读
POLLPRI y y 高优先级带数据可读
POLLOUT y y 普通数据可写
POLLWRNORM y y 普通数据可写
POLLWRBAND y y 优先级带数据可写
POLLERR n y 发生错误
POLLHUP n y 发生挂起
POLLNVAL n y 描述符不是一个打开的文件

poll的就绪条件:

  • 所有正规TCP数据和所有UDP数据视为普通数据
  • TCP带外数据视为优先级带数据
  • 当TCP读半关闭时(收到对端传来的FIN),也视为普通数据,随后的读操作将返回0
  • TCP连接存在错误既可以视为普通数据,也可以视为错误( POLLERR )。随后的读操作将返回-1,并设置全局的 errno 变量。
  • 监听套接字上有新的连接既可以视为普通数据,也可以视为优先级数据。
  • 非阻塞式的 connect 的完成视为使对应的套接字可写。

总结

selectpoll 都支持IO复用,其思路都是调用函数监听多个描述符,当有描述符就绪或者超时的时候函数调用就会返回,对应的描述符集合状态也会改变,这时候再遍历描述符集合,处理其中就绪的部分即可。

这种方式在需要监听的描述符比较小,或者是每次就绪的描述符很多的情况下比较有效;但当描述符很多而且每次只有少数描述符就绪时,效率就比较低了。后面出现的 epoll 就避免了这种线性遍历的问题。

另外 select 还受 FD_SETSIZE 的限制,只能处理较少的描述符,而 poll 则没有这个限制。 poll 监听的集合大小只受进程能打开的文件数量( RLIMIT_NOFILE )的限制。


以上所述就是小编给大家介绍的《《UNIX网络编程》笔记 - select和poll》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

移动互联网商规28条

移动互联网商规28条

王吉斌、彭盾、程成 / 机械工业出版社 / 2014-6 / 49.00

每一次信息技术革命都会颠覆很多行业现有的商业模式和市场规则,当前这场移动互联网变革的波及面之广和蔓延速度之快,完全超出我们的想象。行业的边界被打破并互相融合,在此之前,我们只面临来自同行业的竞争,但是今天,我们不知道竞争对手会来自哪里。也许今天我们还是行业的巨人,但是明天就会被踩在脚下,当我们的体温犹热时,新的巨人已经崛起。诺基亚等传统科技巨头的衰退告诉我们,企业在一个时代的优势,到了另外一个新时......一起来看看 《移动互联网商规28条》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

html转js在线工具
html转js在线工具

html转js在线工具