Linux系统编程59 高级IO - IO 多路转接之 poll() 监视文件描述符读,写等状态

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);
	

}

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/114675348