高级I/O
1, I/O多路转接select 函数
/* According to POSIX.1-2001 */
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set); //清除其中的一位
int FD_ISSET(int fd, fd_set *set); //看其中的一位是否被设定
void FD_SET(int fd, fd_set *set); //设定其中的一位
void FD_ZERO(fd_set *set); //清零
说明:
该函数提供了一种在单个进程中监视多个文件描述符的方法。可以对三种类型的描述符集进行监视:可读(第2个参数:readfds)、可写(第3个参数:writefds)、处于异常状态(第4个参数:exceptfds)的描述符。 从第2个参数起,参数都可以为空(NULL),当文件描述符集为空时,表示不监视其描述符的状态;如果是时间值为空,表示永远阻塞,直到描述符准备好。
nfds 是三个文件描述符号中最大的描述符+1。这样就会在一定的范围内搜索需要检测的描述符,否则,将会在所有可选的fd_set中搜索。
*select 函数的使用
fd_set rset, wset;
int maxfd;
FD_ZERO(&rset); //不管定义哪一个集合都必须要先清零
FD_ZERO(&wset);
//设置读集
FD_SET(0, &rset);
FD_SET(3, &rset);
//设置写集
FD_SET(1, &wset);
FD_SET(2, &wset);
maxfd = 3 + 1; //必须是FD_SET中最大的描述符 + 1
if (select(maxfd, &rset, &wset, NULL, NULL) > 0) {
if (FD_ISSET(0, &rset)) { //看是哪一个准备好了读
...
}
if (FD_ISSET(1, &wset)) { //看是哪一个准备好了写
...
}
}
*一般的使用模式
...
maxfd = fromfd1;
if (fromfd2 > maxfd)
maxfd = fromfd2;
for ( ; ; ) {
FD_ZERO(&rset);
FD_SET(fromfd1, &rset);
FD_SET(fromfd2, &rset);
if (((num = select(maxfd+1, &rset, NULL, NULL, NULL)) == -1) &&
(errno == EINTR)) //若是被信号中断,自启动,linux下是自启动的可以不判断
continue;
if (num == -1) //error happened
return ntotal;
if (FD_ISSET(fromfd1, &rset)) {
nread = readwrite(fromfd1, tofd1);
if (nread <= 0)
break;
ntotal += nread;
}
if (FD_ISSET(fromfd2, &rset)) {
nread = readwrite(fromfd2, tofd2);
if (nread <= 0)
break;
ntotal += nread;
...
*关于select的返回值
-1 出错
0 没有描述符准备好,并超时
n>0 返回已准备好的描述符的数量,该值是三个描述符中已准备好的描述符之和,
若一个描述既准备好读,又准备好了写,那么返回2。
*在什么样的情况下描述符准备好?
.对于读集合(readfds) 中的一个描述符的读操作read不会阻塞,则此描述符准备好。
.对于写集合(writefds) 中的一个描述符的写操作write不会阻塞,则此描述符准备好。
.对于写集合(exceptfds) 中的一个描述符有一个未决异常状态,则此描述符准备好。
2, pselect 函数
man pselect
3, poll函数
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE
#include <poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *timeout, const sigset_t *sigmask);
结构说明:
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
*poll函数说明
参数
fds是一个pollfd数组,说明关心的描述符和它的状态。
nfds 给出要监视的描述符的数目
timeout 是毫秒表示的时间值,是poll没有接受到事件的等待时间。
返回值
0 超时
-1 错误
成功 返回拥有事件的描述符的数目。
与select函数不同,poll函数不时按照文件描述符的类型来组织信息的,而是通过pollfd数组来组织信息。每个数组元素指定一个描述符编号以及一个对其关心的状态。
*poll函数的使用
void
monitorpoll(int fd[], int numfds)
{
char buf[BUFSIZE];
int bytesread;
int i;
int numnow = 0;
int numready;
struct pollfd *pollfd;
for (i = 0; i < numfds; i++)
if (fd[i] >= 0)
numnow++;
pollfd = (void *)calloc(numfds, sizeof(struct pollfd));
if (pollfd == NULL)
return ;
for (i = 0; i < numfds; i++) {
(pollfd + i)->fd = *(fd + i);
(pollfd + i)->events = POLLRDNORM;
}
while(numnow > 0) {
numready = poll(pollfd, numfds, -1);
if ((numready == -1 ) && (errno == EINTR)) //信号中断重启
continue;
else if (numready == -1) //错误发生
break;
for (i = 0; i < numfds && numready > 0; i++) {
if ((pollfd + i)->revents) { //事件发生
if ((pollfd + i)->revents & (POLLRDNORM | POLLIN)) { //可读
bytesread = r_read(fd[i], buf, BUFSIZE);
numready --;
if (bytesread > 0)
docmd(buf, bytesread);
else
bytesread = -1;
}
} else if ((pollfd + i)->revents & (POLLERR | POLLHUP)) //错误发生
bytesread = -1;
else
bytesread = 0;
if (bytesread == -1) {
r_clos(fd[i]);
(pollfd + i)->fd = -1;
numnow --;
}
}
}
for (i = 0; i < numfds; i++)
r_close(fd[i]);
free(pollfd);
}
*poll比select函数的优点
.select函数每次使用时都要重新设置文件描述符集,而poll函数为输入和返回值设置了独立的变量,不需要每次调用后重置掩码。
.poll的时间timeout的范围受限,但它跟容易使用。
.与select函数不同,poll把错误当成返回的事件来处理。
.与select函数不一样,poll不使用max_fd参数。