首页 > 其他分享 >内核睡眠机制和等待队列

内核睡眠机制和等待队列

时间:2024-03-24 20:33:40浏览次数:495  
标签:睡眠 队列 等待 up queue 内核 my wait

内核睡眠机制:

进程通过睡眠机制释放处理器,使其能够处理其他线程。处理器睡眠的原因可能在于感知数据可用性,或等待资源释放 内核调度器管理要运行的任务列表,这被称为运行队列。睡眠进程不再被调度,因为已将它们从运行队列中移除了。除非改变状态(唤醒),否则睡眠进程将永远不会被执行。进程一旦进入等待状态,就可以释放处理器,一定要确保有条件或其他进程会唤醒它。linux内核提供一组函数和数据结构来简化睡眠机制的实现  

等待队列:

等待队列实际上用于处理被阻塞的I/O,以等待特定条件成立,并感知数据或资源可用性 等待队列的数据结构如下:
struct __wait_queue {
    unsigned int flags;                // 用于存储等待队列的标志信息
    void *private;                     // 指向私有数据的指针,可以用于存储与等待队列相关的特定信息
    wait_queue_func_t func;            // 等待队列的回调函数指针,用于唤醒等待队列中的任务
    struct list_head task_list;        // 用于将等待队列项连接到等待队列头部的链表中
};
注意task_list字段,正如所看到的,它是一个链表。想要其入睡的每个进程都在该链表中排队(因此被称为等待队列)并进入睡眠状态,直到条件变为真。等待队列可被看做简单的进程链表和锁   处理等待队列,常用到的函数如下:
DECLARE_WAIT_QUEUE_HEAD(name)        // 静态声明

wait_queue_head_t my_wait_queue;    // 动态声明
init_waitqueue_head(&my_wait_queue);

int wait_event_interruptible(wait_queue_head_t q, CONDITION);    // 阻塞,如果条件为假,则阻塞等待队列中的当前任务(进程)

void wake_up_interruptible(wait_queue_head_t *q);            // 解除阻塞,如果上述条件为true,则唤醒在等待队列中休眠的进程
wait_event_interruptible不会持续轮询,而只是在被调用时评估条件。如果条件为假,则将进程进入TASK_INTERRUPTIBLE状态并从运行队列中删除。之后,当每次在队列中调用wake_up_interruptible时,都会重新检查条件。如果wake_up_interruptible运行时发现条件为真,则等待队列中的进程将被唤醒,并将其状态设置为TASK_RUNNING。进程按照它们进入睡眠时候的顺序被唤醒。要唤醒在等待队列中等待的所有进程,应该使用wake_up_interruptible_all。 如果调用了wake_up或wake_up_interruptible,并且条件为FLASE,则什么都不会发生。如果没有调用wake_up或者wake_up_interruptible,进程将永远不会被唤醒  

等待队列测试demo:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/wait.h>

static struct task_struct *my_thread;
DECLARE_WAIT_QUEUE_HEAD(my_queue);
int condition = 0;

// 线程函数
static int my_thread_func(void *data)
{
    while (!kthread_should_stop())
    {
        // 检查条件是否满足
        if (condition != 0)
        {
            printk(KERN_INFO "Condition is met. Thread wakes up.\n");
            break;
        }

        // 等待条件满足
        printk(KERN_INFO "Waiting for condition to be met...\n");
        wait_event_interruptible(my_queue, condition != 0);
    }

    printk(KERN_INFO "Thread exits.\n");
    return 0;
}

// 初始化模块
static int __init my_module_init(void)
{
    printk(KERN_INFO "Module initialized.\n");

    // 创建线程
    my_thread = kthread_run(my_thread_func, NULL, "my_thread");

    // 模拟条件变更
    msleep(5000);
    condition = 1;
    wake_up(&my_queue);

    return 0;
}

// 清理模块
static void __exit my_module_exit(void)
{
    // 停止线程
    if (my_thread)
    {
        kthread_stop(my_thread);
    }

    printk(KERN_INFO "Module exited.\n");
}

module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
在上述示例中,我们首先定义了一个全局等待队列头部 my_queue,和一个条件变量 condition。然后,我们创建了一个内核线程 my_thread,该线程在等待队列上等待条件的满足。线程函数 my_thread_func 中通过循环检查条件是否满足。若条件已满足,则打印消息并跳出循环。否则,使用 wait_event_interruptible() 函数等待条件变量的改变,将该线程阻塞,并设置为可中断等待,以便能够响应内核的信号。在模块初始化函数 my_module_init 中,我们首先创建了 my_thread,然后休眠一段时间,模拟条件的变更。在条件变更后,我们设置 condition 为非零值,并使用 wake_up() 函数唤醒等待在 my_queue 上的线程。最后,在模块退出函数 my_module_exit 中,我们停止了线程,并输出相应的消息。    

标签:睡眠,队列,等待,up,queue,内核,my,wait
From: https://www.cnblogs.com/lethe1203/p/18092974

相关文章

  • 一篇文章搞懂并设计循环队列
    目录1.为什么使用循环队列2.循环队列组成为什么要只使用size-1个空间存储?3.循环队列的元素进出3.1队尾加入元素3.2队头删除元素3.3取出队头元素3.4取出队尾元素1.为什么使用循环队列“假溢出”——》出队列会空出存储空间,无法再次利用如图:索引为0和1的空......
  • 在linux中无需修改内核驱动就能操作GPIO口的示例
    一、首先编写一个脚本文件init.sh#!/bin/bashecho2>/sys/class/gpio/exportsleep1echo3>/sys/class/gpio/exportsleep1echoout>/sys/class/gpio/gpio3/directionecho1>/sys/class/gpio/gpio3/value这段代码是在Linux系统中使用shell脚本语言编写的。让......
  • Spark重温笔记(三):Spark在企业中为什么能这么强?——持久化、Checkpoint机制、共享变量与
    Spark学习笔记前言:今天是温习Spark的第3天啦!主要梳理了Spark核心数据结构:RDD(弹性分布式数据集),包括RDD持久化,checkpoint机制,spark两种共享变量以及spark内核调度原理,希望对大家有帮助!Tips:"分享是快乐的源泉......
  • JAVAEE——多线程的设计模式,生产消费模型,阻塞队列
    文章目录多线程设计模式什么是设计模式单例模式饿汉模式懒汉模式线程安全问题懒汉模式就一定安全吗?锁引发的效率问题jvm的优化引起的安全问题阻塞队列阻塞队列是什么?生产消费者模型阻塞队列实现消费生产者模型可能遇到的异常多线程设计模式什么是设计模式首先我......
  • Golang:无锁队列实现
    无锁队列实现无锁队列实现参考论文适用场景并发条件下,需要使用到队列的情况,都可以使用无锁队列技术要点使用atomic.loadpointer来实现实时的指针相等判断使用CAS来替代同步状态下的p=p.next额外判断tail是不是正确的tail,并且不断地尝试推后他示例代码pack......
  • Python数据结构实验 队列的实现
    一、实验目的1.掌握用Python定义队列的顺序存储结构和链式存储结构,以便在实际背景下灵活运用;2.掌握队列的特点,即先进先出的原则;3.掌握队列的基本操作实现方法。二、实验环境1.Windows操作系统的计算机2.Python3.7环境平台和PyCharm编辑器三、实验说明 1.实现队列的顺序存......
  • 在Linux中,如何查看内核版本?内核版本信息包含什么?
    在Linux中查看内核版本有多种方法,下面列举了几种常用且详细的命令:方法一:uname命令仅查看内核版本:uname-r这个命令会打印出当前系统运行的内核版本号,例如:4.15.0-72-generic。查看详细系统信息:uname-a这个命令会输出所有与内核相关的详细信息,包括内核名称、主机名、......
  • 设备驱动-15.内核kmalloc/vmalloc及CMA内存介绍
    1kmalloc/vmalloc区别函数位置特性大小限制kmalloc物理内存映射区域物理地址虚拟地址均连续不能超过128Kkzalloc物理内存映射区域物理地址虚拟地址均连续不能超过128Kvmalloc虚拟内存映射区域虚拟地址连续,物理地址不一定连续无限制vzalloc虚拟内......
  • 【消息队列开发】 实现 VirtualHostTests 类——测试虚拟主机操作
    文章目录......
  • 【模板】单调队列 滑动窗口最值
    LuoguP1886滑动队列/单调队列有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。 以求最小值为例f[i]表示以i结尾的窗口中的最小值f[i]=min(a[j]),i-k+1<=j<=i暴力算法O(n^2)......