高级IO poll函数

转载与:https://blog.csdn.net/skypeng57/article/details/82743681

1 poll函数概述

select() 和 poll() 系统调用的本质一样,poll() 的机制与 select() 类似,与 select() 在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是 poll() 没有最大文件描述符数量的限制(但是数量过大后性能也是会下降)。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

1.1 poll()函数介绍

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:监视并等待多个文件描述符的属性变化

参数:

fds指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件

struct pollfd{
	int fd;		//文件描述符
	short events;	//等待的事件
	short revents;	//实际发生的事件
};

fds结构体参数说明:

fd:每一个 pollfd 结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示 poll() 监视多个文件描述符。

events:指定监测fd的事件(输入、输出、错误),每一个事件有多个取值,如下:

revents:revents 域是文件描述符的操作结果事件,内核在调用返回时设置这个域。events 域中请求的任何事件都可能在 revents 域中返回.

注意:每个结构体的 events 域是由用户来设置,告诉内核我们关注的是什么,而 revents 域是返回时内核设置的,以说明对该描述符发生了什么事件

nfds:用来指定第一个参数数组元素个数

timeout:指定等待的毫秒数,无论 I/O 是否准备好,poll() 都会返回.

返回值:

成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0;

失败时,poll() 返回 -1,并设置 errno 为下列值之一:

  • EBADF:一个或多个结构体中指定的文件描述符无效。
  • EFAULT:fds 指针指向的地址超出进程的地址空间。
  • EINTR:请求的事件之前产生一个信号,调用可以重新发起。
  • EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。
  • ENOMEM:可用内存不足,无法完成请求。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>

#define BUFSIZE	64
#define TTY1	"/dev/tty8"
#define TTY2	"/dev/tty9"

enum {
	STATE_R,  //读
	STATE_W,  //写
	STATE_EX,  //异常
	STATE_T    //终止
};
// 抽象状态机类型
struct relay_st {
	int rfd;  //读文件的描述符
	int wfd;  //读文件的描述符
	int state;  //状态
	char buf[BUFSIZE];  
	int readcnt;  //读到的字节个数
	char *errmsg;
	int pos;  
};

// 状态机推动
int relay_drive(struct relay_st *r)
{
	int n;

	switch(r->state) {
		case STATE_R:
			r->readcnt = read(r->rfd, r->buf, BUFSIZE);  //读
			if (r->readcnt == -1) {
				if (errno != EAGAIN) {
					// 文件为空--》假错
					r->errmsg = "read()";  //错误函数
					r->state = STATE_EX;  //状态改变	
				}
			} else if (r->readcnt == 0) {
				r->state = STATE_T;
			} else {
				r->pos = 0;
				r->state = STATE_W;	
			}
			break;
		case STATE_W:  
			n = write(r->wfd, r->buf+r->pos, r->readcnt);  //写
			if (n == -1) {
				if (errno != EAGAIN) {
					r->state = STATE_EX;
					r->errmsg = "write()";
				}
			} else if (n < r->readcnt) {
				r->pos += n;	
				r->readcnt -= n;
			} else {
				r->state = STATE_R;
			}
			break;
		case STATE_EX:
			fprintf(stderr, "%s:%s\n", r->errmsg, strerror(errno));  //错误输出
			r->state = STATE_T;
			break;
		case STATE_T:
			break;
	}

	return 0;
}

struct relay_st *relay_create(int rfd, int wfd)
{
	struct relay_st *me;
	int rfd_save, wfd_save;

	me = malloc(sizeof(*me));
	if (NULL == me)
		return NULL;

	// 确保rfd, wfd都是非阻塞io
	rfd_save = fcntl(rfd, F_GETFL);
	fcntl(rfd, F_SETFL, rfd_save | O_NONBLOCK);
	wfd_save = fcntl(wfd, F_GETFL);
	fcntl(wfd, F_SETFL, wfd_save | O_NONBLOCK);

	me->rfd = rfd;
	me->wfd = wfd;
	me->state = STATE_R;
	memset(me->buf, '\0', BUFSIZE);
	me->pos = 0;
	me->errmsg = NULL;
	me->readcnt = 0;

	return me;
}


int main(void)
{
	int fd1, fd2;
	struct relay_st *fd12, *fd21;
	struct pollfd pfd[2];

	fd1 = open(TTY1, O_RDWR);
	if (-1 == fd1) {
		perror("open()");
		exit(1);
	}

	fd2 = open(TTY2, O_RDWR | O_NONBLOCK);
	// if error

	fd12 = relay_create(fd1, fd2);
	fd21 = relay_create(fd2, fd1);

	pfd[0].fd = fd1;
	pfd[1].fd = fd2;

	while (1) {
		if (fd12->state == STATE_T && fd21->state == STATE_T)
			break;
		if (fd12->state == STATE_EX) {
			relay_drive(fd12);
			continue;  //如果异常,推动状态机,重新判断条件,下面的不判断
		}

		if (fd21->state == STATE_EX) {
			relay_drive(fd21);
			continue;
		}

		pfd[0].events = 0;  //初始化
		pfd[1].events = 0;

		if (fd12->state == STATE_R) {
			pfd[0].events |= POLLIN;  //赋读状态
		} else if (fd12->state == STATE_W)
			pfd[1].events |= POLLOUT;  //赋写状态

		if (fd21->state == STATE_R)
			pfd[1].events |= POLLIN;
		else if (fd21->state == STATE_W)
			pfd[0].events |= POLLOUT;

		while (poll(pfd, 2, -1) < 0) {
			if (errno == EINTR)  //假错
				continue;
			perror("select()");
			goto ERROR;
		}

		if (pfd[0].revents & POLLIN || pfd[1].revents & POLLOUT)
			relay_drive(fd12);
		if (pfd[0].revents & POLLOUT || pfd[1].revents & POLLIN)
			relay_drive(fd21);
	}

	free(fd12);
	free(fd21);

	close(fd1);
	close(fd2);

	exit(0);
ERROR:
	free(fd12);
	free(fd21);

	close(fd1);
	close(fd2);
	exit(1);
}






posted @ 2021-04-07 21:49  小浩small  阅读(152)  评论(0)    收藏  举报