poll 引进: 补充 select()的不足。他俩的区别在于:
1 poll() 的 监视现场位置 和 监视结果位置分开存放,不需要向select()一样 每次返回都发生覆盖,不需要重复设置监视现场
2 poll() 所能监控的状态有7中,并且可以自己添加需要的其他状态位,比select()多
NAME
poll, ppoll - wait for some event on a file descriptor
以文件描述符为单位,组织事件,正好和 select()相反。
SYNOPSIS
#include <poll.h>
/* 功能与select()相似,等待目标文件描述符发生所需要的的变化,返回。
fds: pollfd 结构体数组的起始位置。
nfds:数组元素个数,即有多少个需要监视的文件描述符
timeout: 超时设置,单位是毫秒,即设置1000,为1秒。如果不设置超时,则函数阻塞等待
空:????
0: 以非阻塞方式 poll()
-1: 阻塞方式poll()
时间:超时设置
*/
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
返回值:
成功返回一个整数,表示有多少个事件已经发生,失败返回-1,并且设置 errno,如假错EINTR
ERRORS
EFAULT The array given as argument was not contained in the calling program's address space.
EINTR A signal occurred before any requested event; see signal(7). 假错,信号打断阻塞 poll
EINVAL The nfds value exceeds the RLIMIT_NOFILE value.
ENOMEM There was no space to allocate file descriptor tables.
//记录一个文件描述符相关内容,包括对文件描述符所关心的状态,和返回的状态。
// events 和 revents是两个位图,有7中状态,比select多,select只有3种状态,除了读写 都是异常
// events 和 revents是两个位图 可以添加我们需要的状态
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */ 所关心的状态
short revents; /* returned events */已经发生的事件,与所关心的事件分开存放!与select()不同
};
The bits that may be set/returned in events and revents are defined in <poll.h>:
POLLIN 是否可读
POLLPRI
There is urgent data to read (e.g., out-of-band data on TCP socket; pseudoterminal master in packet mode has seen state change in slave).
POLLOUT 是否可写
POLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE feature test macro must be defined (before including any header files) in order
to obtain this definition.
POLLERR
Error condition (only returned in revents; ignored in events).
POLLHUP
Hang up (only returned in revents; ignored in events). Note that when reading from a channel such as a pipe or a stream socket, this event merely indicates that the peer closed
its end of the channel. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed.
POLLNVAL
Invalid request: fd not open (only returned in revents; ignored in events).
实验:改进 状态机实现 用多路转接IO poll() 拷贝两个设备之间的数据
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
//#include <sys/select.h>
#include <poll.h>
#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE 1024
enum
{
STATE_R = 1,
STATE_W,
STATE_AUTO,
STATE_Ex,
STATE_T
};
struct fsm_st
{
int state;
int sfd;
int dfd;
char buf[BUFSIZE];
int len;
int pos;
char* errstr;
};
static void fsm_driver(struct fsm_st *fsm)
{
int ret;
switch(fsm->state)
{
case STATE_R:
fsm->len = read(fsm->sfd,fsm->buf,BUFSIZE);
if(fsm->len == 0)
fsm->state = STATE_T;
else if(fsm->len < 0)
{
if(errno == EAGAIN)
fsm->state = STATE_R;
else
{
fsm->errstr = "read()";
fsm->state = STATE_Ex;
}
}
else
{
fsm->pos = 0;
fsm->state = STATE_W;
}
break;
case STATE_W:
ret = write(fsm->dfd,fsm->buf+fsm->pos,fsm->len);
if(ret < 0)
{
if(errno == EAGAIN)
fsm->state = STATE_W;
else
{
fsm->errstr = "read()";
fsm->state = STATE_Ex;
}
}
else
{
fsm->pos += ret;
fsm->len -= ret;
if(fsm->len == 0)
fsm->state = STATE_R;
else
fsm->state = STATE_W;
}
break;
case STATE_Ex:
perror(fsm->errstr);
fsm->state = STATE_T;
break;
case STATE_T:
/* do something*/
break;
default:
abort();
break;
}
}
static void relay(int fd1,int fd2)
{
int fd1_save,fd2_save;
struct fsm_st fsm12,fsm21;
struct pollfd pfd[2];
fd1_save = fcntl(fd1,F_GETFL);
fcntl(fd1,F_SETFL,fd1_save|O_NONBLOCK);
fd2_save = fcntl(fd2,F_GETFL);
fcntl(fd2,F_SETFL,fd2_save|O_NONBLOCK);
fsm12.state = STATE_R;
fsm12.sfd = fd1;
fsm12.dfd = fd2;
fsm21.state = STATE_W;
fsm21.sfd = fd2;
fsm21.dfd = fd1;
//布置 监视现场
pfd[0].fd = fd1;
pfd[1].fd = fd1;
while(fsm12.state != STATE_T || fsm21.state != STATE_T)
{
//以文件描述符为单位组织事件,设置文件描述符所关心的状态,如状态机fsm12 为读状态时候,关心的文件描述符状态为读,只要文件可读,就 通知poll()返回,即只要目标文件中有内容可读,这里就是当TTY11 中有户数输入后,即为可读。
pfd[0].events = 0;
if(fsm12.state == STATE_R)
pfd[0].events |= POLLIN;
if(fsm21.state == STATE_W)
pfd[0].events |= POLLOUT;
pfd[1].events = 0;
if(fsm12.state == STATE_W)
pfd[1].events |= POLLOUT;
if(fsm21.state == STATE_R)
pfd[1].events |= POLLIN;
if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO)
{
//等待目标文件描述符发生状态变化,这里就是当TTY11 中有户数输入后,即为可读,则通知poll返回。
while(poll(pfd,2,-1) < 0)
{
if(errno == EINTR)
continue;
perror("poll()");
exit(1);
}
}
if(pfd[0].revents & POLLIN || pfd[1].revents & POLLOUT || fsm12.state > STATE_AUTO)
fsm_driver(&fsm12);
if(pfd[1].revents & POLLIN || pfd[0].revents & POLLOUT || fsm12.state > STATE_AUTO)
fsm_driver(&fsm21);
}
fcntl(fd1,F_SETFL,fd1_save);
fcntl(fd2,F_SETFL,fd2_save);
}
int main()
{
int fd1,fd2;
fd1 = open(TTY1,O_RDWR);
if(fd1 < 0)
{
perror("open()");
exit(1);
}
fd2 = open(TTY2,O_RDWR|O_NONBLOCK);
if(fd1 < 0)
{
perror("open()");
exit(1);
}
relay(fd1,fd2);
close(fd2);
close(fd1);
}