使用select的多路传输
poll()系统调用最早是作为Unix书中System V部分被引入的,BSD 通过相似的办法引入seletct()系统调用,解决相同的问题。
定义:
/* 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);
Three independent sets of file descriptors are watched. Those listed in readfds will be watched to see if characters become avail‐able for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file), those in writefds will be watched to see if a write will not block, and those in exceptfds will be watched for exceptions. On exit, the sets are modified in place to indicate which file descriptors actually changed status. Each of the three file descriptor sets may be specified as NULL if no file descriptors are to be watched for the corresponding class of events.
Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.
nfds is the highest-numbered file descriptor in any of the three sets, plus 1.
The timeout argument specifies the interval that select() should block waiting for a file descriptor to become ready. This interval will be rounded up to the system clock granularity, and kernel scheduling delays mean that the blocking interval may overrun by a small amount. If both fields of the timeval structure are zero, then select() returns immediately. (This is useful for polling.) If timeout is NULL (no timeout), select() can block indefinitely.
The timeout The time structures involved are defined in <sys/time.h> and look like
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
and
struct timespec {
long tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
select()调用返回这三个fd_set中所有被设置的项目总数,如果调用超时则返回0,如果有错误发生则返回-1。
用select可以实现的延时操作:
#include <sys/select.h>
#include <sys/stdlib.h>
int usecsleep(int usecs) {
strcut timeval tv;
tv.tv_sec = 0;
tv.tv_usec = usecs;
return select(0, NULL, NULL, NULL, &tv);
}
这样就实现了短于1秒的延时,
usecsleep(500000)引起一个半秒的暂停。
最后再用select解决”Linux高级文件操作 -2”中的多路输入问题:
/* mpx-select.c - Displays input from two pipes using \codefn{select()} */
#include <fcntl.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main(void) {
int fds[2];
char buf[4096];
int i, rc, maxfd;
fd_set watchset; /* fds to read from */
fd_set inset; /* updated by select() */
/* open both pipes */
if ((fds[0] = open("p1", O_RDONLY | O_NONBLOCK)) < 0) {
perror("open p1");
return 1;
}
if ((fds[1] = open("p2", O_RDONLY | O_NONBLOCK)) < 0) {
perror("open p2");
return 1;
}
/* start off reading from both file descriptors */
FD_ZERO(&watchset);
FD_SET(fds[0], &watchset);
FD_SET(fds[1], &watchset);
/* find the maximum file descriptor */
maxfd = fds[0] > fds[1] ? fds[0] : fds[1];
/* while we're watching one of fds[0] or fds[1] */
while (FD_ISSET(fds[0], &watchset) ||
FD_ISSET(fds[1], &watchset)) {
/* we copy watchset here because select() updates it */
inset = watchset;
if (select(maxfd + 1, &inset, NULL, NULL, NULL) < 0) {
perror("select");
return 1;
}
/* check to see which file descriptors are ready to be
read from */
for (i = 0; i < 2; i++) {
if (FD_ISSET(fds[i], &inset)) {
/* fds[i] is ready for reading, go ahead... */
rc = read(fds[i], buf, sizeof(buf) - 1);
if (rc < 0) {
perror("read");
return 1;
} else if (!rc) {
/* this pipe has been closed, don't try
to read from it again */
close(fds[i]);
FD_CLR(fds[i], &watchset);
} else {
buf[rc] = '\0';
printf("read: %s", buf);
}
}
}
}
return 0;
}