2009年(41)
分类: LINUX
2009-04-14 14:20:39
在网络编程中I/O复用非常有用,具体的原因就不用说了,相信用过的人都有体会~
1、I/O模型
在Unix中,有5中基本的IO模型,它们是:
·blocking I/O
最普通的I/O模型,也是默认的I/O模型。它会一直阻塞调用者直到读/写成功、被信号中断或错误发生。
·nonblocking I/O
通过调用一些函数,可以将blocking I/O设置为nonblocking模式。nonblocking模式下,进行I/O操作不会被阻塞,无论成功与否都会返回,返回值和errno表明了操作状态。
·I/O multiplexing (select/poll)
对于同时处理多个I/O的进程(线程)来说,这很有用。它相当于同时“监视”多个I/O,在满足一定的条件后返回,向调用者“反馈”情况。
·signal driven I/O (SIGIO)
有事件发生时,同时信号的方式通知“对此感兴趣”的对象。
·asynchronous I/O
执行I/O操作后立即返回,但不代表实际的操作已经进行,什么时候执行实际操作由底层决定。实际操作执行完毕后底层会通知相应的对象。
前面4种模型和最后一种的一个显著区别是:
前面4种模型不会主动把数据从内核复制到用户进程,只有当用户进程使用系统调用读取数据时才会复制过来;而使用异步I/O时,当底层通知调用者操作完成时,已经将数据从内核复制到了相应的用户进程。
2、select
提到unix下的I/O复用,就不得不提select。虽然还有一个poll,但是select的使用远远要比poll广泛,POSIX对select的支持也要多于poll。select有一个更为强大的版本pselect,而poll则没有(后面出现了epoll,主要是为了网络而设计的)。
使用select时,需要注意几点:
·是它的3个fd_set是value-resule类型,调用select的时候,代表的是感兴趣的事件,而返回 的时候被修改为已经发生的事件。使用select常犯的一个错误就是,select返回后再次调用没有 重新设置3个fd_set。
·timeval指针参数是为了实现超时而用的,它可以为NULL、0(tv_sec和tv_usec为0)和大于 0。为NULL代表一直阻塞知道有事件发生,0表示无论有没有事件发生都立即返回,大于0表示阻塞 的最大时间。
·timeval指针参数使用const限制符,也就是说它返回的时候不会向其他一些具有定时功能的函数 一样,将其修改为“剩下的时间”。
·操作fd_set用FD_ZERO、FD_SET、FD_CLR和FD_ISSET宏。
2.1 什么时候socket为“可读”
·socket接收缓存中的字节数大于socket接收缓存的“low-water”值(对于TCP和UDP默认值一般 为1,可通过SO_RCVLOWAT选项进行设置)。
·读端被关闭,如收到了FIN包。此时read将立即返回,返回值为0,表示遇到EOF。
·socket是监听端口,且到该端口连接完成的端口数不为0,即accept可以立即返回。
·socket有错误发生,此时read将立即返回,返回值为-1,errno为发生的错误。错误还可以通过 getsockopt采用SO_ERROR获得。
2.2 什么时候socket为“可写”
·socket发送缓冲中可用空间大于socket发送缓存的“low-water”值(对于TCP和UDP默认值一般 为2048,可通过SO_SNDLOWAT选项进行设置)。
·写端关闭(比如收到RST),此时write会返回EPIPE。
·socket以nonblocking方式使用的connect连接成功或失败。
·socket有错误发生,“可读”。
由上面可知,当一个socket发生错误时,select认为是可读写的。
2.3 增强版select---pselect
从表面上看,有2个不同:timeval结构变成timespec结构;增加了一个参数const sigset_t *sigmask。
(未完待续)