linux wait函数头文件_《Linux设备驱动程序》(九)——休眠与唤醒

本节是本书中《高级字符驱动程序操作》章节的第二节内容。本节主要涉及到的是进程睡眠和唤醒相关的内容。

07b78036-a5a4-4ed0-b515-765c0205e75c

本节主要涉及以下内容:

  • 休眠的简单介绍
  • 休眠与唤醒相关的操作函数

休眠简介

休眠是指进程被标记为一种特殊状态,并从调度器的运行队列中移走。休眠的进程会被搁置在一边,等待将来的某个事件发生,修改这种特殊状态,之后才会在任意CPU上调度并运行该进程。

让进程进入休眠状态是很容易的,但让进程安全进入休眠,需要记住以下规则:

  • 永远不要在原子上下文中进入休眠:即不能在拥有自旋锁时休眠;拥有信号量的时候可以休眠,但要保证拥有的信号量不会阻塞唤醒我们的进程。
  • 当进程被唤醒时,永远无法知道休眠了多长时间,或者休眠期间发生了什么事情:唤醒之后必须检查以确保我们等待的条件为帧。

休眠与唤醒相关操作

休眠的进程如果要被唤醒,必须让唤醒的进程能够找到休眠的进程。在Linux中,维护了一个称为等待队列的数据结构,它是一个进程链表,其中包含了等待某个特定事件的所有进程。

等待队列由等待队列头来管理,其定义在头文件中,是一个wait_queue_head_t结构体。可以通过以下方式进行定义并初始化一个等待队列头:

// 静态定义并初始化DECLARE_WAIT_QUEUE_HEAD(name);// 动态定义并初始化wait_queue_head_t my_queue;init_waitqueue_head(&my_queue);

当进程需要某个条件为真时才能继续运行,如果条件不满足,需要进入到休眠状态。这种情况可以使用wait_event宏及其它的几个变种来实现:

wait_event(queue, condition)wait_event_interruptible(queue, condition)wait_event_timeout(queue, condition, timeout)wait_event_interruptible_timeout(queue, condition, timeout)

其中:

  • queue是上面说的等待队列头,其传递的是值不是指针。
  • condition是一个布尔表达式,如果condition结果Wie假,则进入休眠,如果为真则继续运行;需要注意的是,该condition表达式可能会被多次运算,因此需要注意该表达式不要产生副作用。
  • timeout是超时的时间,以jiffy表示,超过给定的时间后,无论condition的结果如何,这个宏都会返回,进程继续运行。

有休眠自然有对应的唤醒方法,主要是以下两个:

void wake_up(wait_queue_head_t *queue);void wake_up_interruptible(wait_queue_head_t *queue);

wake_up会唤醒给定queue上的所有进程,而wake_up_interruptible只会唤醒queue中的可中断的进程。通常约定wait_event使用wake_up唤醒,wait_event_interruptible使用wake_up_interruptible唤醒。

休眠唤醒实例

基于上面的休眠唤醒相关宏和函数,我们修改scull_lock设备,将其从信号量控制数据打印更改为休眠唤醒的方式。

设备设计如下:设备名称命名为scull_sleep,当读取进程在数据量小于20的时候会休眠,每次写入数据的时候唤醒读取进程;唤醒读取进程后,如果数据量未达到20,则继续休眠,否则读取进程读取数据并返回,然后清空数据。

完整代码位于:https://gitee.com/Quehehe/LinuxDeviceDriver/tree/master/scull_sleep


读取数据的接口函数部分代码如下:

ssize_t scull_read (struct file *filp, char __user *buf, size_t size, loff_t *loff){   ......    ret = wait_event_interruptible(dev->read_wait_queue,         dev->data_length >= DATA_SIZE_LIMIT); /* 等待数据达到指定的数据量 */    if (ret != 0) { /* 休眠被中断 */        printk(KERN_ALERT "wait for data error!");        return 0;    }    ......    dev->data_length = 0; /* 清空数据 */    return size;}

使用宏wait_event_interruptible()来实现进程在不满足条件的情况下睡眠,条件为数据长度dev->data_length大于等于DATA_SIZE_LIMIT。

读取完成后,将dev->data_length清0,即进行了数据清除操作。


写入数据的接口函数部分代码如下:

ssize_t scull_write (struct file *filp, const char __user *buf, size_t size, loff_t *loff){    ......    dev->data_length += size;    wake_up_interruptible(&dev->read_wait_queue); /* 唤醒读取数据进程 */    return size;}

写入数据的时候不会重新写入,而是在之前的基础上进行累加,并且写入完成后即调用wake_up_interruptible()函数唤醒读取进程。

休眠唤醒实例测试

在项目根目录下执行make,在scull_sleep目录中生成scull_sleep.ko模块,将其加载到系统中。

使用root权限执行scull_sleep目录下的cat_scull_sleep.sh脚本,开启读取数据的进程,此时由于数据为空,进程会睡眠,不打印任何信息:

79a5d9954ae240a38c92c85fb2368815

读取数据进程开启

往/dev/scull_sleep0节点中写入数据:

6af183b215aa4a31aee3cd61621ae26f

写入部分数据

可以看到读取数据进程仍然没有打印任何信息,而不像之前的信号量那样,写入后直接打印数据,这是因为数据量不够我们设定的值,读取线程被唤醒后又再次进入到睡眠状态。

我们再次写入数据,达到我们设定的数据长度20后,结果如下:

5fe49dc3abd34ea98c3075ae71e53531

写入足够数据

当我们写入的数据累计足够后,会打印出所有的数据。

当我们一次性写入足够的数据后,也会直接打印出来:

9ac0ed2894474d0dbac4b77a371f53d8

一次性写入足够的数据

因此,驱动代码按照我们的预期运行。

全部代码位于:https://gitee.com/Quehehe/LinuxDeviceDriver

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值