IO多路转接
文章目录
IO多路转接selectpollepollepoll使用实例
用来监视文件描述符号。
函数:
select( ); 移植性好,太古老了。以事件为单位组织文件描述符的监视
poll( ); 以文件描述符为单位组织事件。
epoll( ); Linux方言,对于poll的优化。
select
SYNOPSIS
#include <sys/select.h>
#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
);
#include <sys/select.h>
int pselect(int nfds
, fd_set
*readfds
, fd_set
*writefds
,
fd_set
*exceptfds
, const struct timespec
*timeout
,
const sigset_t
*sigmask
);
RETURN VALUE
On success
, select() and
pselect() return the number of file d
escriptors contained in the three returned descriptor sets
(that
is
, the total number of bits that are set in readfds
, writefds
,
exceptfds
) which may be zero
if the timeout expires before anything
interesting happens
. On error
, -1 is returned
, and errno is set to
indicate the error
; the file descriptor sets are unmodified
, and timeout
becomes undefined
.
select 问题:监视文件描述符的集合和返回结果的集合为同一个变量,监视现场和监视结果放在同一个位置,对于大量多次次循环操作需要重复布置大量监视现场;nfds可能溢出。以事件为单位组织文件描述符,监视种类过于单一。
poll
NAME
poll
, ppoll
- wait
for some event on a file descriptor
SYNOPSIS
#include <poll.h>
int poll(struct pollfd
*fds
, nfds_t nfds
, int timeout
);
#define _GNU_SOURCE
#include <signal.h>
#include <poll.h>
int ppoll(struct pollfd
*fds
, nfds_t nfds
,
const struct timespec
*tmo_p
, const sigset_t
*sigmask
);
DESCRIPTION
poll() performs a similar task to
select(2): it waits
for one of a
set of file descriptors to become ready to perform I
/O
.
The set of file descriptors to be monitored is specified in the fds
argument
, which is an array of structures of the following form
:
struct pollfd
{
int fd
;
short events
;
short revents
;
};
POLLIN There is data to read
.
POLLPRI
There is some exceptional condition on the file descriptor
.
Possibilities include
:
* There is out
-of
-band data on a TCP socket
(see
tcp(7)).
* A pseudoterminal master in packet mode has seen a state
change on the slave
(see
ioctl_tty(2)).
* A cgroup
.events file has been modified
(see
cgroups(7)).
POLLOUT
Writing is now possible
, though a write larger that the
available space in a socket or pipe will still block
(unless
O_NONBLOCK is set
).
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
).
This bit is also set
for a file descriptor referring to the
write end of a pipe when the read end has been closed
.
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
).
When compiling with _XOPEN_SOURCE defined
, one also has the following
,
which convey no further information beyond the bits listed above
:
POLLRDNORM
Equivalent to POLLIN
.
POLLRDBAND
Priority band data can be read
(generally unused on Linux
).
epoll
NAME
epoll
- I
/O event notification facility
SYNOPSIS
#include <sys/epoll.h>
DESCRIPTION
The epoll API performs a similar task to
poll(2): monitoring multiple
file descriptors to see
if I
/O is possible on any of them
. The epoll
API can be used either as an edge
-triggered or a level
-triggered
interface and scales well to large numbers of watched file descriptors
.
The following system calls are provided to create and manage an epoll
instance
:
* epoll_create(2) creates a new epoll instance and returns a file
descriptor referring to that instance
. (The more recent
epoll_create1(2) extends the functionality of
epoll_create(2).)
* Interest in particular file descriptors is then registered via
epoll_ctl(2). The set of file descriptors currently registered
on an epoll instance is sometimes called an epoll set
.
* epoll_wait(2) waits
for I
/O events
, blocking the calling thread
if no events are currently available
.
epoll_create
NAME
epoll_create
, epoll_create1
- open an epoll file descriptor
SYNOPSIS
#include <sys/epoll.h>
int epoll_create(int size
);
int epoll_create1(int flags
);
DESCRIPTION
epoll_create() creates a new
epoll(7) instance
. Since Linux
2.6.8,
the size argument is ignored
, but must be greater than zero
; see NOTES
below
.
epoll_create() returns a file descriptor referring to the new epoll
instance
. This file descriptor is used
for all the subsequent calls
to the epoll interface
. When no longer required
, the file descriptor
returned by
epoll_create() should be closed by using
close(2). When
all file descriptors referring to an epoll instance have been closed
,
the kernel destroys the instance and releases the associated resources
for reuse
.
epoll_ctl
NAME
epoll_ctl
- control interface
for an epoll file descriptor
SYNOPSIS
#include <sys/epoll.h>
int epoll_ctl(int epfd
, int op
, int fd
, struct epoll_event
*event
);
EPOLL_CTL_ADD
Register the target file descriptor fd on the epoll instance
referred to by the file descriptor epfd and associate the event
event with the internal file linked to fd
.
EPOLL_CTL_MOD
Change the event event associated with the target file
descriptor fd
.
EPOLL_CTL_DEL
Remove
(deregister
) the target file descriptor fd from the
epoll instance referred to by epfd
. The event is ignored and
can be
NULL (but see BUGS below
).
epoll_wait
NAME
epoll_wait
, epoll_pwait
- wait
for an I
/O event on an epoll file descriptor
SYNOPSIS
#include <sys/epoll.h>
int epoll_wait(int epfd
, struct epoll_event
*events
,
int maxevents
, int timeout
);
int epoll_pwait(int epfd
, struct epoll_event
*events
,
int maxevents
, int timeout
,
const sigset_t
*sigmask
);
epoll使用实例
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.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 rel_fsm_st
{
int state
;
int sfd
;
int dfd
;
int len
;
int pos
;
char buf
[BUFSIZE
];
char *errstr
;
};
static void fsm_driver(struct rel_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
= "write()";
fsm
->state
= STATE_Ex
;
}
}
else{
fsm
->len
-= ret
;
fsm
->pos
+= 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
:
break;
default:
abort();
break;
}
}
void relay(int fd1
, int fd2
){
int fd1_save
, fd2_save
;
struct rel_fsm_st fsm12
, fsm21
;
int epfd
;
struct epoll_event ev
;
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_R
;
fsm21
.sfd
= fd2
;
fsm21
.dfd
= fd1
;
epfd
= epoll_create(10);
ev
.events
= 0;
ev
.data
.fd
= fd1
;
epoll_ctl(epfd
, EPOLL_CTL_ADD
, fd1
, &ev
);
ev
.events
= 0;
ev
.data
.fd
= fd2
;
epoll_ctl(epfd
, EPOLL_CTL_ADD
, fd2
, &ev
);
while (fsm12
.state
!= STATE_T
||fsm21
.state
!= STATE_T
){
ev
.events
= 0;
ev
.data
.fd
= fd1
;
if(fsm12
.state
== STATE_R
){
ev
.events
|= EPOLLIN
;
}
if(fsm21
.state
== STATE_W
){
ev
.events
|= EPOLLOUT
;
}
epoll_ctl(epfd
, EPOLL_CTL_MOD
, fd1
, &ev
);
ev
.events
= 0;
ev
.data
.fd
= fd2
;
if(fsm12
.state
== STATE_W
){
ev
.events
|= EPOLLOUT
;
}
if(fsm21
.state
== STATE_R
){
ev
.events
|= EPOLLIN
;
}
epoll_ctl(epfd
, EPOLL_CTL_MOD
, fd2
, &ev
);
if(fsm12
.state
< STATE_AUTO
|| fsm21
.state
< STATE_AUTO
){
while( epoll_wait(epfd
, &ev
, 1, -1) ){
if (errno
== EINTR
){
continue;
}
perror("epoll_wait:");
exit(1);
}
}
if( ev
.data
.fd
== fd1
&& ev
.events
& EPOLLIN \
|| ev
.data
.fd
== fd2
&& ev
.events
& EPOLLOUT\
|| fsm12
.state
> STATE_AUTO
)
fsm_driver(&fsm12
);
if( ev
.data
.fd
== fd2
&& ev
.events
& EPOLLIN \
|| ev
.data
.fd
== fd1
&& ev
.events
& EPOLLOUT\
|| fsm21
.state
> STATE_AUTO
)
fsm_driver(&fsm21
);
}
fcntl(fd1
, F_SETFL
, fd1_save
);
fcntl(fd2
, F_SETFL
, fd2_save
);
close(epfd
);
}
int main(int argc
, char *argv
[]){
int fd1
, fd2
;
fd1
= open(TTY1
, O_RDWR
);
write(fd1
, "TTY1\n", 5);
fd2
= open(TTY2
, O_RDWR
| O_NONBLOCK
);
write(fd2
, "TTY2\n", 5);
relay(fd1
, fd2
);
close(fd1
);
close(fd2
);
exit(0);
}