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

epoll VS poll

poll() 可以理解为 我们在用户态 创建并维护一个结构体数组,而 epoll() 则是相当于,poll 中的结构体数组被放在内核态,内核为我们维护该数组,内核为我们提供一些方法(系统调用)来管理这个数组。

NAME
       epoll_create, epoll_create1 - open an epoll file descriptor

SYNOPSIS
       #include <sys/epoll.h>
//创建一个epoll的句柄,当创建好epoll句柄后,它就是会占用一个fd值,使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽
       int epoll_create(int size);

RETURN VALUE
       On success, these system calls return a nonnegative file descriptor.  On error, -1 is returned, and errno is set to indicate the error.




SYNOPSIS
       #include <sys/epoll.h>
//对 epfd实例当中的 fd文件描述符 进行 op(添加,删除,更改)epoll_event 动作行为
//epoll的事件注册函数
       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

DESCRIPTION
       This  system call performs control operations on the epoll(7) instance referred to by the file descriptor epfd.  It requests that the operation op be performed for the target file descriptor,

op:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

           typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;      /* 感兴趣的事件,Epoll events  位图 */
               epoll_data_t data;        /* User data variable */
           };
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里



SYNOPSIS
       #include <sys/epoll.h>
//等待事件的产生
       int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

实验:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
//#include <sys/select.h>
//#include <poll.h>
#include <sys/epoll.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 epoll_event ev;
	int epfd;

	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;	
	
//创建一个epoll的句柄
	epfd = epoll_creat(10);
	if(epfd < 0)
	{
		perror("epoll_creat()");
		exit(1);
	}
	
//添加需要监视的文件描述符
	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)	
	{
	//针对文件描述符fd1  布置监视现场
		ev.data.fd = fd1;
		ev.events = 0;

		if(fsm12.state == STATE_R)
			ev.events |= EPOLLIN;
		if(fsm21.state == STATE_W)
			ev.events |= EPOLLOUT;
		epoll_ctl(epfd,EPOLL_CTL_MOD,fd1,&ev);
	

		//针对文件描述符fd2  布置监视现场
		ev.data.fd = fd2;
		ev.events = 0;
		
		if(fsm12.state == STATE_W)
			ev.events |= EPOLLOUT;
		if(fsm21.state == STATE_R)
			ev.events |= EPOLLIN;
	
		
		if(fsm12.state < STATE_AUTO || fsm21.state < STATE_AUTO)
		{
			//监视fd1 fd2
			while(epoll_wait(epfd,&ev,1,-1) < 0)	
			{
				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 == fd1 && ev.events & EPOLLOUT|| ev.data.fd == fd2 && ev.events & EPOLLIN || fsm12.state > STATE_AUTO)
		fsm_driver(&fsm21);
	}

	fcntl(fd1,F_SETFL,fd1_save);
	fcntl(fd2,F_SETFL,fd2_save);
	
	close(epfd);
}

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/114686177