对poll休眠唤醒机制的理解
理解poll机制前,请先看下我的另一篇博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145225508
在上面这篇博文中,利用函数wait_event_interruptible()
使线程进入休眠状态,直到某个事件发生,线程才被唤醒并继续执行后面的代码。
但有时候我们希望线程进入休眠状态的时间是有限的,即线程等待某个事件的发生是有时间限制的,如果超过这个时间就不等了,然后线程被这个超时事件唤醒,进而去执行后面的代码,当然在后面的代码中,按等待的事件超时未发生的分支走。
举个具体的例子,你和别人约好某个时间在某个地点见面做某件事,你按时到达那里,结果你在那里左等不见人,右等不见人,这个时候你就是处于一种休眠状态中(因为对方未到你无法做事),但你总不能一直等下去吧,这个时候你就给自己设置个计时器,如果超过20分钟对方还未到,就不等了。如果20分钟内,对方到了,那按正常的安排走,如果超过20分钟对方还未到,就打车回家。
驱动程序的poll机制就能满足我们上面的这个需求,它会为等待事件发生的线程设置一个时间,在这个时间范围内,如果事件发生,那么它就返回一个值表示这个事件发生了,如果超过这个时间,相关的事件还没有发生,那么它也回返回一个值表示这个事件没有发生。
当然这个时间值可以设置为-1
,表示等待的时间为无穷大,这个时候poll机制就退化为上篇博文(https://blog.youkuaiyun.com/wenhao_ir/article/details/145225508)中的休眠唤醒机制。
Linux系统内核提供了函数poll()来实现poll机制在用户空间的部分,其地位与系统内核提供的函数open()、read()、write()、close()的地位一样。
当用户空间调用函数poll()后,在驱动程序中,文件操作结构体(file_operations)中成员poll对应的函数便会被调用,如下图所示:
本文通过一个不涉及具体的硬件操作的驱动程序来说明poll机制是怎么回事儿,也就是重心放在对其原理和结构的理解上。
poll机制的流程其实和自己最初的想象有很大区别
poll机制的实现我刚开始认为很简单,后来仔细研究后发现不是自己想象的那回事儿,比如下面这些:
①你以为程序一开始在驱动程序的操作函数poll_demo_poll()中运行到语句poll_wait(file, &wait_queue, wait);
时,它就会进入休眠状态,其实不是,第一次运行到到语句poll_wait(file, &wait_queue, wait);
时它是不会进入休眠状态的,第一次运行语句poll_wait(file, &wait_queue, wait);
时,这条语句不会有什么具体的行动。只有第二次运行语句poll_wait(file, &wait_queue, wait);
时,它才会将线程放入等待队列,从而让线程进入休眠状态。
②你以为被语句poll_wait(file, &wait_queue, wait);
挂入等待队列中的线程在被唤醒后函数poll_wait()
本身会提供判断要不要彻底被唤醒的判断机制,就像博文https://blog.youkuaiyun.com/wenhao_ir/article/details/145225508中的函数wait_event_interruptible
那样,本身提供了唤醒后判断要不要彻底被唤醒的条件【通过它的第3个参数condition
实现】,但很遗憾,它没有提供,它被唤醒后就会继续执行后面的代码,poll机制根据它后面代码提供的返回值判断要不要再次调用驱动程序的操作函数poll_demo_poll(),如果再次调用操作函数poll_demo_poll(),则线程才会再次进入休眠状。
③…更多自己想不到的内容请看我写的“详细分析与poll机制有关的代码流程”。
完整源码
驱动程序的源码“poll_demo_drv.c”
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
static int major;
static struct class *poll_demo_class;
static char *device_buffer;
static size_t buffer_size = 1024;
static size_t data_len = 0;
static wait_queue_head_t wait_queue;
static int data_ready = 0;
static int poll_demo_open(struct inode *inode, struct file *file)
{
// pr_info("poll_demo: Device opened\n");
return 0;
}
static int poll_demo_release(struct inode *inode, struct file *file)
{
// pr_info("poll_demo: Device closed\n");
return 0;
}
static ssize_t poll_demo_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
{
if (data_len == 0)
{
pr_info("poll_demo: No data to read\n");
return 0; // No data available
}
if (count > data_len)
count = data_len;
if (copy_to_user(buf, device_buffer, count))
return -EFAULT;
data_len = 0; // Data is consumed
data_ready = 0;
// pr_info("poll_demo: Read %zu bytes\n", count);
return count;
}
static ssize_t poll_demo_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
if (count > buffer_size)
count = buffer_size;
if (copy_from_user(device_buffer, buf, count))
return -EFAULT;
data_len = count;
data_ready = 1;
pr_info("poll_demo: Written %zu bytes\n", count);
wake_up_interruptible(&wait_queue);
return count;
}
static unsigned int poll_demo_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(file, &wait_queue, wait);
if (data_ready)
mask |= POLLIN | POLLRDNORM;
// pr_info("I am poll_demo_poll.\n");
return mask;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = poll_demo_open,
.release = poll_demo_release,
.read = poll_demo_read,
.write = poll_demo_write,
.poll = poll_demo_poll,
};
static int __init poll_demo_init(void)
{
major = register_chrdev(0, "poll_demo_major", &fops);