您的位置:首页 > 理论基础 > 计算机网络

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

2013-10-14 23:28 435 查看
http://www.blogbus.com/eastsun-logs/7762285.html

摘自《UNIX NETWORK PROGRAMMING》chapter 6

p144





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

1.
wait to be ready

2.
copy data from kernel buffer to user buffer





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

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

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

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

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

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





# 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-band和the 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);

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: