poll不同于select使用三个位图表示fdset的方式,poll使用一个pollfd的指针来实现。

因为select的实现和poll的实现非常类似,知识描述fd集合的方式有所不同,poll使用的是pollfd而不是select中的fd_set结构,其他类似,所以poll的实现相比于select实现简单。

首先我们看下Linux下的poll()是如何描述的

一、poll()函数

头文件

#include <poll.h>

函数原型

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

参数说明

fds

存放需要被检测状态的套接字描述符,与select不同(select在调用之后会清空这个数组),每当调用这个数组,系统不会清空这个数组,而是存放revents状态变化描述符变量,这样才做起来很方便。

nfds

用于标记数组fd中struct pollfd结构元素的总数量

timeout

poll函数调用阻塞时间,单位是毫秒(ms)

INFTIM     永远等待

0               立即返回,不阻塞进程

>0             等待指定数目的毫秒数

二、struct pollfd结构

poll函数的第一个参数是一个数组,里面存放的是struct pollfd结构,struct pollfd结构如下

           struct pollfd {
               int   fd;                 /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

fd             文件描述符

events     请求的事件

revents    返回的事件

events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。

poll函数通过在revents中设置标志POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。

如果fd小于0, 则events字段被忽略,而revents被置为0.

标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。


常量                                   说明

                                                            POLLIN                       普通或优先级带数据可读  

                                                            POLLRDNORM          普通数据可读

                                                            POLLRDBAND           优先级带数据可读

                                                            POLLPRL                   高优先级带数据可读

                                                            POLLOUT                  普通数据可写

                                                            POLLWRNORM         普通数据可写

                                                            POLLWRBAND          优先级带数据可写

                                                            POLLERR                  发生错误

                                                            POLLHUP                  发生挂起

                                                            POLLNVAL                描述字不是一个打开的文件

poll函数的事件标志符值

注意:

       1)后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。

  2)第二个参数nfds:要监视的描述符的数目。

  3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待时间。如果  它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。

相比于select机制,poll机制采用链表来进行文件描述符的存储,因此它并没有最大连接数的限制。

但同样存在一些缺点

  1、大量的 fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。

  2、 poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

三、poll实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
int main(int argc, char* argv[])
{
	struct pollfd fds[2];//文件描述符
	fds[0].fd = 0;
	fds[0].events = POLLIN;//对读事件感兴趣
	fds[0].revents = 0;//输出型

	fds[1].fd = 1;
	fds[1].events = POLLOUT;//对写事件感兴趣
	fds[1].revents = 0;

	char buf[1024];
	int done = 0;
	int i = 0;

	int timeout = 1000;//1000ms 1秒

	while (!done)
	{
		int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout);
		switch (ret)
		{
		case -1:
			perror("poll");
			exit(2);
			break;
		case 0:
			printf("timeout...");
		default:
                        //有事件就绪,要判断哪个文件描述符的事件所以要遍历fds
                       for (i = 0; i < sizeof(fds) / sizeof(fds[0]); ++i)
			{
                                //是否可读 fds[i]关心的是读,读事件发生 
                               if (fds[i].fd == 0 && (fds[i].revents&POLLIN))
				{
					memset(buf, '\0', sizeof(buf));
					ssize_t _s = read(0, buf, sizeof(buf) - 1);
					if (_s > 0)
					{
						buf[_s - 1] = '\0';
						if (strncmp(buf, "quit", 4) == 0)
						{
							close(fds[i].fd);
							exit(0);
						}
						printf("echo#.%s\n", buf);
					}
				}
				if (fds[i].fd == 1 && (fds[1].revents&POLLOUT))
				{
					memset(buf, '\0', sizeof(buf));
					strcpy(buf, "Hello!");
					printf("echo#.%s\n", buf);
					sleep(3);
				}
			}
			break;
		}
	}
	return 0;
}

实现效果


Logo

更多推荐