【Linux】poll()方法

在这里插入图片描述

博客主页:PannLZ
系列专栏:《Linux系统之路》
欢迎关注:点赞收藏✍️留言

文章目录

      • poll方法
        • poll步骤


poll方法

如果需要实现被动等待(在感知字符设备时不浪费CPU周期),则必须实现poll()函数,每当用户空间程序在与设备关联的文件上执行系统调用select()poll()时都会调用poll()函数:

unsigned int (*poll) (struct file *, struct poll_table_struct *);

这个方法核心的内核函数是poll_wait(),它定义在中,这个头文件应该包含在驱动代码中:

void poll_wait(struct file * filp,wait_queue_head_t * wait_address,poll_table *p)

poll_wait()根据注册到struct poll_table结构(作为第三个参数传递)中的事件,把与struct file结构(作为第一个参数)关联的设备添加到可以唤醒进程的列表(由第二个参数struct wait_queue_head_t结构指定,进程在其中处于睡眠状态)中。

用户进程可以运行poll()select()或者epoll()系统调用把需要等待的一组文件添加到等待队列上,以了解是否有相关的设备准备就绪。之后,内核将会调用与每个设备文件相关的驱动程序的poll入口。每个驱动程序的poll方法再调用poll_wait,为需要接收内核通知的进程注册事件,在这些事件发生之前把进程置于睡眠状态,并把驱动程序注册为可以唤醒进程的驱动程序。通常的方法是根据select()(或poll())系统调用支持的事件,为每个事件类型使用一个等待队列(一方面是考虑可读性,另一方面是考虑可写性,最后是考虑需要时的异常处理)。

如果有需要读取的数据(此时调用select或poll),则返回**(* poll)文件操作的返回值必须是POLLIN |POLLRDNORM**;如果设备是可写的(此时也调用select或poll),则返回POLLOUT | POLLWRNORM;如果没有新数据且设备尚不可写,则返回0

如果驱动程序没有定义这个方法,则设备将被视为总是可读可写的,poll()或select()系统调用立即返回。

poll步骤
//当实现poll函数时,read或write方法可能会改变。
//(1)为每个需要实现被动等待的事件类型(读、写、异常)声明等待队列,当无数据可读或设备不可写时,把任务放入该队列:
static DECLARE_WAIT_QUEUE_HEAD(my_wq);
static DECLARE_WAIT_QUEUE_HEAD(my_rq);

//(2)像这样实现poll函数:
#include 
static unsigned int eep_poll(struct file *file,poll_table *wait)
{
		unsigned int reval_mask = 0;
		poll_wait(file, &my_wq, wait);
		poll_wait(file, &my_rq, wait);
		if (new-data-is-ready)
				reval_mask |= (POLLIN | POLLRDNORM);
		if (ready_to_be_written)
				reval_mask |= (POLLOUT | POLLWRNORM);
		return reval_mask;
}3)当有新数据或者是设备可写入时,通知等待队列:
wake_up_interruptible(&my_rq); /* 准备读取 */
wake_up_interruptible(&my_wq); /* 准备写入 */

通知可读事件可以采用以下两种方法:

  • 在驱动程序的write()方法中通知,这意味着写入的数据可以读回;
  • 在IRQ处理程序中通知,这意味着外部设备发送的数据可以读回。

通知可写事件可以采用以下两种方法:

  • 在驱动程序的read()方法中通知,这意味着缓冲区是空的,可以被重新填充;
  • 在IRQ处理程序中通知,这意味着设备已经完成数据发送,准备好再次接收数据。

当用户需要读取时,如果有数据,数据会立即发送到进程,;如果没有数据可用,进程会进入睡眠状态,等待数据到达。

以下代码在指定的字符设备上执行select(),以便感知数据是否可用:

#include 
#include 
#include 
#include 
#include 
#define NUMBER_OF_BYTE 100
#define CHAR_DEVICE "/dev/packt_char"
char data[NUMBER_OF_BYTE];
int main(int argc, char **argv)
{
		int fd, retval;
		ssize_t read_count;
		fd_set readfds;
		fd = open(CHAR_DEVICE, O_RDONLY);
		if(fd < 0)
		/* 打印一条消息并退出*/
				[...]
		while(1) {
				FD_ZERO(&readfds);
				FD_SET(fd, &readfds);
/*
* 只需要通知读取事件,而不需要超时
* 这个调用将使进程进入休眠状态,直到通知它自己
注册的事件为止
*/
		ret = select(fd + 1, &readfds, NULL,NULL, NULL);
		/* 从这一行开始,进程已经得到通知 */
		if (ret == -1) {
				fprintf(stderr, "select call on %s:an error ocurred",CHAR_DEVICE);
				break;
		}
/*
* 文件描述符现在已经准备好了
* 假设我们只对一个文件感兴趣
*/
		if (FD_ISSET(fd, &readfds)) {
				read_count = read(fd, data,
				NUMBER_OF_BYTE);
				if (read_count < 0 )
				/* 发生一个错误,处理这个问题*/
						[...]
				if (read_count != NUMBER_OF_BYTE)
				/* 读取的比需要的字节还少 */
						[...] /* 处理 */
				else
				/* 处理读取的数据*/
				[...]
		}
}
		close(fd);
		return EXIT_SUCCESS;
}

你可能感兴趣的:(Linux系统之路,linux,运维,服务器,linux内核,内核开发,驱动开发)