Linux中I/O复用——poll函数详解及代码实现

本文详细介绍了Linux系统中的I/O复用机制,重点解析了poll函数的使用,包括其参数结构体pollfd的字段解释,如fd、events和revents。通过示例展示了如何设置待检测的事件以及如何获取发生的事件状态。同时,文章讨论了timeout参数的作用,即指定等待的超时时间。poll函数在成功检测到事件或超时时将返回相应信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux中I/O复用——poll函数

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

1、函数原型:


(1) 头文件:
  1. #include <poll.h>  

(2)函数体:
  1. int poll(struct pollfd *fds, nfds_t nfds, int timeout);  

2、函数参数

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

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

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

events/revents:要测试的条件由 events 成员指定,函数在相应的 revents 成员中返回该描述符的状态(每个描述符都有两个变量,一个为调用值,另一个为返回结果,从而避免值-结果参数)这两个成员中的每一个都由指定某个特定条件的一位或多位组合而成。下标列出指定 events 标志以及测试 revents 标志的一些常值。


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

(3)timeout: 指定等待的毫秒数

3、函数的返回值

成功时,poll() 返回结构体中 revents 域不为 0 的文件描述符个数;

如果在超时前没有任何事件发生,poll()返回 0;

失败时,poll() 返回 -1


二、函数示例代码

服务器端poll.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>

#define MAXFD 10

int create_socket();

int fds_init(struct pollfd *fds);
int fds_add(struct pollfd *fds,int fd);
int fds_del(struct pollfd *fds,int fd);

int main()
{
	int sockfd = create_socket();

	struct pollfd fds[MAXFD];
	fds_init(fds);

	fds_add(fds,sockfd);
	while(1)
	{
		int a = poll(fds,MAXFD,5000);
		if(a < 0)
		{
			perror("poll error");
			continue;
		}
		if(a > 0)
		{
			int i = 0;
			for(;i < MAXFD;i++)
			{
				if(fds[i].revents & POLLIN)
				{
					if(fds[i].fd == sockfd)
					{
						struct sockaddr caddr;
						int len = sizeof(caddr);
						int c = accept(sockfd,&caddr,&len);
						printf("accept(%d)\n",c);
						fds_add(fds,c);			
					}
					else
					{
						char buff[128] = {0};
						int n = recv(fds[i].fd,buff,127,0);
						if(n <= 0)
						{
							printf("one client over\n");
							close(fds[i].fd);
							fds_del(fds,fds[i].fd);	
						}
						else
						{
							printf("buff(%d) = %s\n",fds[i].fd,buff);
							send(fds[i].fd,"OK",2,0);
						}
					}
	
				}
			}	
		}
		
	}
	
	return 0;
}


int create_socket()
{	
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));
	assert(res != -1);

	listen(sockfd,5);

	return sockfd;
}

int fds_init(struct pollfd *fds)
{
	int i = 0;
	for(;i < MAXFD;i++)
	{
		fds[i].fd = -1;
		fds[i].events = 0;
	}
}

int fds_add(struct pollfd *fds,int fd)
{
	int i = 0;
	for(;i < MAXFD;i++)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = POLLIN;
			fds[i].revents = 0;
			break;
		}
	}
}

int fds_del(struct pollfd *fds,int fd)
{
	int i = 0;
	for(; i < MAXFD;i++)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			fds[i].events = 0;
			break;
		}
	}
}

客户端代码cli.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(6000);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res != -1);

	while(1)
	{
		printf("input :");
		char buff[128] = {0};
		fgets(buff,128,stdin);
		if(strncmp(buff,"end",3) == 0)
		{
			break;
		}
		
		send(sockfd,buff,strlen(buff),0);
		
		memset(buff,0,128);
		recv(sockfd,buff,127,0);
		printf("buff=%s\n",buff);
	}	
//	 close(sockfd);
}

运行结果:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值