计算机网络--linux下poll函数详解

poll函数概述

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

头文件:
#include <poll.h>
函数体:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:

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

参数:

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

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

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:可用内存不足,无法完成请求。

poll示例举例

用poll实现udp同时收发

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
 
int main(int argc,char *argv[])
{
	int udpfd = 0;
	int ret = 0;
	struct pollfd fds[2];//监测文件描述结构体数组:2个
	struct sockaddr_in saddr;
	struct sockaddr_in caddr;
 
	bzero(&saddr,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port   = htons(8000);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bzero(&caddr,sizeof(caddr));
	caddr.sin_family  = AF_INET;
	caddr.sin_port    = htons(8000);
	
	//创建套接字
	if( (udpfd = socket(AF_INET,SOCK_DGRAM, 0)) < 0)
	{
		perror("socket error");
		exit(-1);
	}
	
	//套接字端口绑字
	if(bind(udpfd, (struct sockaddr*)&saddr, sizeof(saddr)) != 0)
	{
		perror("bind error");
		close(udpfd);		
		exit(-1);
	}
 
	printf("input: \"sayto 192.168.220.X\" to sendmsg to somebody\033[32m\n");	
	fds[0].fd = 0;		//标准输入描述符
	fds[1].fd = udpfd;	//udp描述符
	
	fds[0].events = POLLIN; // 普通或优先级带数据可读  
    fds[1].events = POLLIN; // 普通或优先级带数据可读
	
	while(1)
	{	
		// 监视并等待多个文件(标准输入,udp套接字)描述符的属性变化(是否可读)  
        // 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时  
        ret = poll(fds, 2, -1); 
		
		write(1,"UdpQQ:",6);
		
        if(ret == -1){ // 出错  
            perror("poll()");  
        }
		else if(ret > 0){ // 准备就绪的文件描述符  
            char buf[100] = {0};  
            if( ( fds[0].revents & POLLIN ) ==  POLLIN ){ // 标准输入  
                
				fgets(buf, sizeof(buf), stdin);
				buf[strlen(buf) - 1] = '\0';
				if(strncmp(buf, "sayto", 5) == 0)
				{
					char ipbuf[16] = "";
					inet_pton(AF_INET, buf+6, &caddr.sin_addr);//给addr套接字地址再赋值.
					printf("\rsay to %s\n",inet_ntop(AF_INET,&caddr.sin_addr,ipbuf,sizeof(ipbuf)));
					continue;
				}
				else if(strcmp(buf, "exit")==0)
				{
					close(udpfd);
					exit(0);
				}
				sendto(udpfd, buf, strlen(buf),0,(struct sockaddr*)&caddr, sizeof(caddr));  
                  
            }
			else if( ( fds[1].revents & POLLIN ) ==  POLLIN ){ //udp套接字  
                struct sockaddr_in addr;
				char ipbuf[INET_ADDRSTRLEN] = "";
				socklen_t addrlen = sizeof(addr);
				
				bzero(&addr,sizeof(addr));
				
				recvfrom(udpfd, buf, 100, 0, (struct sockaddr*)&addr, &addrlen);
				printf("\r\033[31m[%s]:\033[32m%s\n",inet_ntop(AF_INET,&addr.sin_addr,ipbuf,sizeof(ipbuf)),buf);  
            }  
              
        }
		else if(0 == ret){ // 超时  
            printf("time out\n");  
        }  
	}
	
	return 0;
}
 

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`poll_wait` 函数是 Linux 内核中用于实现轮询等待的一个重要函数,其主要作用是将当前进程挂起,等待某个特定的事件发生,例如数据从 IO 设备上到达或者设备错误等。当事件发生时,`poll_wait` 将会唤醒等待的进程,使其可以继续执行。 下面详细介绍一下 `poll_wait` 函数的使用方法。 ## 语法 ```c void poll_wait(struct file *filp, wait_queue_head_t *queue, struct wait_queue_entry *wait); ``` ## 参数 - `filp`:待等待的文件指针; - `queue`:等待队列的头指针; - `wait`:等待队列的条目指针。 ## 功能 将当前进程加入到等待队列中,等待某个特定的事件发生。 ## 注意事项 1. `poll_wait` 函数只能在进程的上下文中使用,不能在中断上下文中使用。 2. 在调用 `poll_wait` 函数之前,必须先调用 `init_waitqueue_head` 函数对等待队列进行初始化。 3. 在等待队列中,每个等待事件都必须使用唯一的等待队列条目。 ## 示例 下面是一个示例程序,演示了如何使用 `poll_wait` 函数: ```c DEFINE_WAIT(wait); init_waitqueue_head(&wait_queue_head); // 等待事件发生 poll_wait(file, &wait_queue_head, &wait); // 判断事件是否已经发生 if (event_occurred) { // 唤醒等待队列中的进程 wake_up_interruptible(&wait_queue_head); } ``` 在上面的示例程序中,首先使用 `DEFINE_WAIT` 宏定义了一个等待队列条目,然后使用 `init_waitqueue_head` 函数对等待队列进行初始化。接着,使用 `poll_wait` 函数将当前进程加入到等待队列中,等待事件发生。当事件发生时,使用 `wake_up_interruptible` 函数唤醒等待队列中的进程,使其可以继续执行。 总之,`poll_wait` 函数是 Linux 内核中实现轮询等待的一个重要函数,它可以方便地实现进程挂起和等待事件发生的功能,是 Linux 内核中非常常用的一个函数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值