09. 休眠与唤醒

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

本节主要涉及以下内容:

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

1. 休眠简介

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

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

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

2. 休眠与唤醒相关操作

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

等待队列由等待队列头来管理,其定义在 <linux/wait.h> 头文件中,是一个 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 结果为假,则进入休眠,如果为真则继续运行;需要注意的是,该 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 唤醒。

3. 休眠唤醒实例

基于上面的休眠唤醒相关宏和函数,我们修改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!\n");
        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() 函数唤醒读取进程。

4. 休眠唤醒实例测试

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

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

读取数据进程开启

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

写入部分数据

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

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

写入足够数据

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

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

一次性写入足够的数据

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

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容