内核的睡眠机制 wait_event wake_up

进程通过睡眠机制释放处理器,使其能够处理其他进程。处理器睡眠的原因可能在于感知数据的可用性,或等待资源释放

内核调度器管理要运行的任务列表,这被称作运行队列。Linux内核通过提供一组函数和数据结构来简化睡眠机制的实现

等待队列

        等待队列实际上用于处理 被阻塞的I/O, 以等待条件的成立,并感知数据或资源可用性。为了理解其工作方式,参考其在include/linux/wait.h中的结构。

struct  __wait_queue{
    unsigned  int flags;
#define  WQ_FLAG_EXCLUSIVE 0X01
    void  *private;
    wait_queue_func_t  func;
    struct  list_head  task_list;
};

其中task_list是一个链表,想要让其入睡的每个进程都在该链表中排队(因此被称作等待队列)并进入睡眠状态,直到条件变成真。等待队列可以被看作简单的进程链表和锁。

处理等待队列时,常用到的函数如下:

1.静态声明;

        DECLARE_WAIT_QUEUE_HEAD(name);

2.动态声明;

        wait_queue_head_t   my_wait_queue;

        init_waitqueue_head(&my_wait_queue);

3.阻塞 :如果condition为false,则阻塞等待队列中的当前任务(进程)

int  wait_event_interruptible(wait_queue_head_t  my_wait_queue,  condition);

4.解除阻塞:如果condition为true, 则唤醒在等待队列中休眠的队列。

void   wake_up_interruptible( wait_queue_head_t   my_wait_queue);

wait_event_interruptible()不会持续轮询,而只是在被调用时评估条件。如果条件为假,则进程将进入TASK_INTERRUPTIBLE状态并从运行队列中删除。之后,当每次在等待队列中调用wake_up_interruptible()时,都会重新检查条件。如果wake_up_interruptible运行时发现条件为真,则等待队列中的进程将被唤醒,并将其状态设置为TASK_RUNNING. 进程则按照它们进入睡眠的顺序唤醒。要唤醒在队列中等待的所有进程,应该使用wake_up_interruptible_all。

实际上,主要函数是wait_event    wake_up   和  wake_up_all。它们以独占(不可中断)等待的方式处理队列中的进程,因为它们不能被信号中断。它们只能用于关键任务。可中断函数是可选的,由于它们可以被信号中断,所以应该检查他们的返回值。非零值意味着睡眠被某种信号中断,驱动程序应该返回ERESTARTSYS。

如果调用了wake_up() 或者wake_up_interruptible(),并且条件依然是FALSE,则什么都不会发生。如果没有调用wake_up或者wake_up_interruptible,进程将永远不会被唤醒。

DEMO如下:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/workqueue.h>

static DECLARE_WAIT_QUEUE_HEAD(my_wq);
static int  condition = 0;

//声明一个工作队列
static  struct  work_struct  wrk;

static  void work_handler(struct work_struct *work){
    printk("Waitqueue  module handler %s\n",  __func__);
    msleep(5000);
    printk("Wake up the sleeping module\n");
    condition = 1;
    wake_up_interruptible(&my_wq);
}


static  int  __init  my_init(void){
    printk("Wait queue example");
    
    INIT_WORK(&wrk, work_handler);
    schedule_work(&wrk);
    
    printk("Going to sleep %s\n", __func__);
    wait_event_interruptible(my_wq, condition != 0);
    //如果没有进程唤醒my_wq这个任务将一直休眠,且是阻塞休眠
    pr_info("woken up by the work job\n");
    return 0;
}

void my_exit(void){
    printk("waitqueue example cleanup\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");


#makefile

obj-m += wait_queue.o
kernel_rel:=`uname -r`
linux_path=/lib/modules/`uname -r`/build

CURRENT_DIR=$(shell pwd)

all:
    make -C $(linux_path)  M=$(CURRENT_DIR)  modules

clean:
    rm -f *.o  *.cmd *.mod  *.ko

      (Makefile注意使用tab而不是空格)  在终端编译后,执行

sudo  insmod wait_queue.ko

       然后使用命令:  dmesg  就可以看到输出结果来,

在此demo中,当前进程(实际上是insmod)将在等待队列中进入睡眠状态5s,然后又工作队列将其唤醒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值