unix下的多路复用(select)

来源:互联网 发布:淘宝售后制度 编辑:程序博客网 时间:2024/06/16 12:24

 http://eastsun.blogbus.com/logs/7762285.html

 

对于常见的input操作,一般分为两个步骤:

1.  wait to be ready

2.  copy data from kernel buffer to user buffer

 

 

常见的I/O模型:(参见以上步骤)

1.  阻塞I/O                               用户进程执行12

2.  非阻塞I/O                           用户轮巡1,然后执行2

3.  多路复用selectpoll            用户调用select等待kernel返回,然后执行2

4.  信号驱动I/OSIGIO              用户设置信号处理函数(sigio)后,正常继续其他函数,当kernel返回SIGIO后,执行2

5.  异步信号I/O                        kernel执行12后通知用户进程(不常用)

 

 

# include <sys/select.h>

# include <sys/time.h>

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

 

 

作用:

1.  内核扫描maxfdp1个描述符(常规情况下,系统最大值FD_SETSIZE=1024,可修改)

2.  内核查看readset, writeset, exceptset集中的描述符是否准备好

3.  等待超过timeout时间而没有描述符准备好,select返回

 

 

struct timeval {

       long tv_sec;

       long tv_usec;

}

注:timeval == 0   马上返回

       timeval == NULL   永久阻塞

 

 

void FD_ZERO(fd_set *fdset);

void FD_SET(int fd, fd_set *fdset);

void FD_CLR(int fd, fd_set *fdset);

void FD_ISSET(int fd, fd_set *fdset);

 

 

注意,如循环调用select,多次检查同一描述符,必须在调用select之前重新设定初始值。select函数会在每次返回时,将没有ready的描述符所在的位清0

 

 

任何信号将使select()出错返回。而且BSD系统的select将不可能自动再启动

在标准select()中,返回后系统不会改变timeval的值,而linux系统例外。

 

 

exception condition

当前只支持  out-of-bandthe presence of control status information to be read from the master side of a pseudo terminal that has been put into packet mode.(???)

 

 

一般的系统实现中,将fd_set设置为整数队列,每个整数元素中的一位表示为一个描述符

 

 

FD_SETSIZE定义在<sys/select.h>中,如果要更改的话,必须重新编译内核

 

 

select()的常见错误:

1.  maxfdp1必须指定为最大描述符值+1

2.  每次select返回后,都会将fd_set中的初值清0,除非该描述符已经准备好。因此,如果要重新检查描述符,必须再次赋初值

 

 

select返回的描述符个数中,如果同一描述符同时为读、写准备好,则记数2

早期的SVR4版本只记录1次。(bug

 

 

 

 

select中准备好的意义:

为读准备好:

1.  socket接受缓存中的数据 >= SO_RCVLOWAT,默认情况下,SO_RCVLOWAT=1

2.  对端写关闭,read返回0

3.  listenfd中的complete queue 中,有entry

4.  socket出错。read返回-1

 

 

为写准备好:

1.  socket发送缓存中的数据 >= SO_SNDLOWAT,默认情况下,SO_SNDLOWAT=2048

2.  对端读关闭,kernel返回SIGPIPE

3.  socket出错,write返回-1

 

 

注意,当socket出错时,将在readset/writeset分别赋值

 

 

使用select应该注意:

由于同时处理单个描述符的读写,可能出现此描述符的写(或读)操作已全部完成,而相对的另一个读(或写)操作还没有完成。为了获得这些数据,进程必须调用shutdown()以进行半关闭

 

 

# include <sys/socket.h>

int shutdown(int sockfd, int howto);

其中,howto可以置为SHUT_RD/SHUT_WR/SHUTRDWR

 

 

区别于close()

1.  shutdown不查看描述符计数器。直接进行半关闭

2.  close只能进行全关闭,shutdown可以选择一端或两端

 

 

服务器处理客户机请求的原则:永远不要将服务器阻塞在一个客户连接中。(可能被dos攻击)

 

 

# include <sys/select.h>

# include <signal.h>

# include <time.h>

int pselect(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *except_set, const struct timespec *timeout, const sigset_t sigmask);