linux内核的休眠

84 篇文章 0 订阅

linux内核的简单休眠

本次学习资料,来自linux设备驱动程序第七章节高级字符串设备驱动第149页

什么时候会发生睡眠

当我们去对linux中一个不可读的描述符进行read调用,或者对一个缓冲区已经满了的套接字进行write的时候都会发生睡眠,也就是我们说的IO阻塞,linux进程会陷入休眠状态。

关于休眠的简单介绍

一个进程发生休眠会发生什么

操作系统会把这个进程标记为休眠状态,从调度器的运行队列移走,只有发生一些情况,进程状态不再是休眠状态,才会被cpu调度。

如何让一个进程安全的进入休眠?

1.第一个原则就是,内核程序中,原子操作的代码段永远不要有休眠!!

为什么这么说呢,如果自旋锁,sqlock或者RCU进入睡眠之后,都是不可中断的,会导致其他线程进入很长时间等待,甚至说死锁

信号量可以进入睡眠,但是注意会发生中断!!

要尽可能缩小临界区的执行时间。

2.另一个原则是我们被唤醒之后,不知道发生了什么,以及睡眠了多久,所以醒来后我们要判断我们等待的条件是否为真。

linux中的简单休眠

内核内部维护了一个等待队列的数据链表,,其中包含了某个特定事件的所有进程

linux中通过一个等待队列的头来管理,等待队列头是一个wait_queue_head_t结构体。定义在<linux/wait.h>中,通过静态方法定义并初始化一个等待队列的头:

#define __WAITQUEUE_INITIALIZER(name, tsk) {                                    \
        .private        = tsk,                                                  \
        .func           = default_wake_function,                                \
        .entry          = { NULL, NULL } }

#define DECLARE_WAITQUEUE(name, tsk)                                            \
        struct wait_queue_entry name = __WAITQUEUE_INITIALIZER(name, tsk)

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                                   \
        .lock           = __SPIN_LOCK_UNLOCKED(name.lock),                      \
        .head           = { &(name).head, &(name).head } }

#define DECLARE_WAIT_QUEUE_HEAD(name) \
        struct wait_queue_head name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

当然也可以通过动态方法去初始化

#define init_waitqueue_head(wq_head)                                            \
        do {                                                                    \
                static struct lock_class_key __key;                             \
                                                                                \
                __init_waitqueue_head((wq_head), #wq_head, &__key);             \
        } while (0)

通过调用代码__wait_event_interruptible 把当前进程加入到了等待队列中,一直等待唤醒,具体见内核实现代码,当前线程的信息保存在current这个宏定义里

current:

* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_GENERIC_CURRENT_H
#define __ASM_GENERIC_CURRENT_H

#include <linux/thread_info.h>

#define get_current() (current_thread_info()->task)
#define current get_current()

#endif /* __ASM_GENERIC_CURRENT_H */

代码实现:

#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)           \
({                                                                              \
        __label__ __out;                                                        \
        struct wait_queue_entry __wq_entry;                                     \
        long __ret = ret;       /* explicit shadow */                           \
                                                                                \
        init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);        \
        for (;;) {                                                              \
                long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
                                                                                \
                if (condition)                                                  \
                        break;                                                  \
                                                                                \
                if (___wait_is_interruptible(state) && __int) {                 \
                        __ret = __int;                                          \
                        goto __out;                                             \
                }                                                               \
                                                                                \
                cmd;                                                            \
        }                                                                       \
        finish_wait(&wq_head, &__wq_entry);                                     \
__out:  __ret;                                                                  \
})

关键内核驱动代码

写一个最简单的实践demo,不确定任何的原子性

引入

#include <linux/wait.h>

初始化等待队列

static int flag = 0;
static DECLARE_WAIT_QUEUE_HEAD(wq);

修改read函数

ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
        wait_event_interruptible(wq, flag != 0);
        flag = 0;
        return count;
}

修改write函数

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,
                loff_t *f_pos)
{

        flag = 1;
        wake_up_interruptible(&wq);
        return count;
}

编译 装载驱动

实验代码

阻塞读
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>

int main ()
{
    int fd = open("/dev/scull1", O_RDWR);
    std::cout << fd << std::endl;
    char data[6] = "haha";
    char newData[6];
    bzero(newData, 6);
    int res = read(fd, newData, sizeof(data));
    std::cout << newData << std::endl;
    return 0;
}
写唤醒
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>

int main ()
{
    int fd = open("/dev/scull1", O_RDWR);
    std::cout << fd << std::endl;
    char data[6] = "haha";
    int res = write(fd, data, sizeof(data));
    std::cout << strerror(errno) << std::endl;
    return 0;
}
编译
g++ test.cpp -o test_read
g++ test_write.cpp -o test_write
执行
sudo ./test_read

发生了阻塞

写入
sudo ./test_write

结束阻塞

很像一个描述符了,flag标志不可读,write写入后flag可读,结束睡眠

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值