【Linux系统编程】IO多路复用之poll

00. 目录

01. 概述

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

02. poll函数

#include <poll.h>

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
    监视并等待多个文件描述符的属性变化
参数说明:
    pollfd -- 数组的地址
    nfds: 数组的最大长度, 数组中最后一个使用的元素下标+1
        § 内核会轮询检测fd数组的每个文件描述符
    timeout:
        § -1: 永久阻塞
        § 0: 调用完成立即返回
        § >0: 等待的时长毫秒
返回值: 
     成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;
     如果在超时前没有任何事件发生,poll()返回 0;
         
     失败时,poll() 返回 -1,并设置 errno 为下列值之一:
         EBADF:一个或多个结构体中指定的文件描述符无效。
         EFAULT:fds 指针指向的地址超出进程的地址空间。
         EINTR:请求的事件之前产生一个信号,调用可以重新发起。
         EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。
         ENOMEM:可用内存不足,无法完成请求。
​
struct pollfd {
    int   fd;         /* 文件描述符 */
    short events;     /* 等待的事件 */
    short revents;    /* 实际发生的事件 */
};

03. 程序示例

#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
 
	int ret;
	int fd;
	struct pollfd fds[2]; // 监视文件描述符结构体,2 个元素
	
	ret = mkfifo("test_fifo", 0666); // 创建有名管道
	if(ret != 0){
		perror("mkfifo:");
	}
	
	fd = open("test_fifo", O_RDWR); // 读写方式打开管道
	if(fd < 0){
		perror("open fifo");
		return -1;
	}
	
	ret = 0;
	
	fds[0].fd = 0;	 // 标准输入
	fds[1].fd = fd;	 // 有名管道
		
	fds[0].events = POLLIN;	// 普通或优先级带数据可读
	fds[1].events = POLLIN; // 普通或优先级带数据可读
	
	while(1){
	
		// 监视并等待多个文件(标准输入,有名管道)描述符的属性变化(是否可读)
		// 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
		ret = poll(fds, 2, -1);
		//ret = poll(&fd, 2, 1000);
 
		if(ret == -1){ // 出错
			perror("poll()");
		}else if(ret > 0){ // 准备就绪的文件描述符
		
			char buf[100] = {0};
			if( ( fds[0].revents & POLLIN ) ==  POLLIN ){ // 标准输入
				read(0, buf, sizeof(buf));
				printf("stdin buf = %s\n", buf);
				
			}else if( ( fds[1].revents & POLLIN ) ==  POLLIN ){ // 有名管道
				read(fd, buf, sizeof(buf));
				printf("fifo buf = %s\n", buf);
			}
			
		}else if(0 == ret){ // 超时
			printf("time out\n");
		}
	
	}
	
	return 0;
}

04. poll优缺点

select和poll对比, poll的优点如下

优点:

1).理论上支持无数个文件描述符

2).省去了内核检查位图的繁琐操作

缺点:

1).每次调用都会出现一次从用户空间到内核空间的拷贝

2).每次返回都会出现一次从内核空间到用户空间的拷贝

3).返回后需要用户依次扫描fds数组,因此会做很多没必要的检查

因此,poll函数会比select的效率稍微高一点

05. 附录

发布了639 篇原创文章 · 获赞 2326 · 访问量 75万+

猜你喜欢

转载自blog.csdn.net/dengjin20104042056/article/details/102991412