linux设备驱动程序-第六章2(阻塞IO和高级休眠)学习总结

本文总结了Linux设备驱动程序中关于阻塞IO和高级休眠的知识。介绍了进程休眠的基本原理,强调了在原子上下文中不应休眠,并通过wait_event宏展示了简单的休眠实现。此外,讨论了阻塞与非阻塞操作的区别,并详细解释了独占等待的概念,以提高系统性能。最后,给出了实际操作的步骤和相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言
在第三章中,我们已经讨论了如何实现驱动程序的read和write方法。现在谈论另一种重要问题:如何驱动程序无法立即满足要求,该如何响应?调用程序通常不会关心此类问题,程序员只会简单调用read和write,然后等待必要的工作结束后返回调用,因此,在这种情况下,我们驱动程序应该(默认)阻塞该进程,将其置入休眠状态直至请求可继续。
我么介绍过的函数可以满足许多驱动程序的休眠请求。但是在某些情况下,我们需要Linux的等待队列机制有更深的理解。复杂的锁定以及性能需求会强制驱动程序用底层的函数实现休眠。
二、阻塞IO和高级休眠
1、休眠的简单介绍
当一个进程被置入休眠时,它会被标记为一种特殊状态并从调度器的运行队列中移走。休眠中的进程会搁置在一边,等待将来的某个事件发生。
对linux设备驱动程序来讲,让一个进程进入休眠状态很容易,需牢记两个原则:
(1)永远不要在原子上下文中进入休眠。
(2)当我们被唤醒时,我们永远无法知道休眠了多长时间,或者休眠期间都发生了什么事情,因此必须检查以确保我们等待的条件真正为真。
2、简单休眠
linux内核中最简单的休眠方式为wait_event的宏,在实现休眠的同时,它也检查休眠条件。如下:

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

在 Linux 中, 一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中:


struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

唤醒休眠:


void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);//驱动中大部分情况使用这个

3、阻塞和非阻塞操作
显示的非阻塞IO由filp->f_flags的O_NONBLOCK标志决定。这个标志在默认时要被清除,因为等待数据的进程一般是休眠。
只有read、open和write文件操作受非阻塞标志的影响。
4、高级休眠
(1)手动休眠

/* ①创建并初始化一个等待队列。通常由宏定义完成:*/
DEFINE_WAIT(my_wait);
/*其中name 是等待队列入口项的名字. 也可以用下面两个步骤来做:*/
wait_queue_t my_wait;
init_wait(&my_wait);
/*在实现休眠的循环前放置 DEFINE_WAIT 通常更容易实现*/

/* ②添加等待队列入口到队列,并设置进程状态:*/
void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state); 
/*其中,queue 和 wait 分别地是等待队列头和进程入口。state 是进程的新状态:TASK_INTERRUPTIBLE(可中断休眠,推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠,不推荐)。*/

/* ③在调用pre_pare_to_wait之后,若进程仍有必要等待,则调用 schedule*/

schedule();

/* ④一旦schedule 返回,就到了清理时间*/
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
/*⑤代码测试其状态,并判断是否需要重新等待*/

(2)独占等待
当一个进程调用 wake_up 在等待队列上,所有的在这个队列上等待的进程被置为可运行的。 这在许多情况下是正确的做法。但有时,可能只有一个被唤醒的进程将成功获得需要的资源,而其余的将再次休眠。这时如果等待队列中的进程数目大,这可能严重降低系统性能。为此,内核开发者增加了一个“独占等待”选项。它与一个正常的睡眠有 2 个重要的不同:

①当等待队列入口设置了 WQ_FLAG_EXCLUSEVE 标志,它被添加到等待队列的尾部;否则,添加到头部。

②当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止唤醒.但内核仍然每次唤醒所有的非独占等待。
执行独占等待的进程每次只会被唤醒其中一个。但是,内核每次仍然会唤醒所有非独占等待进程。

void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);

三、实例
scull_pipe.h

#ifndef _SCULL_H_
#define _SCULL_H_
#include <linux/ioctl.h>

#define DEBUG

//#undef PDEBUG
#ifdef DEBUG
//#  ifdef __KERNEL__
#     define PDEBUG(fmt,args...) printk(KERN_DEBUG "scull:" fmt,## args)
//#  else
//#     define PDEBUG(fmt,args...) fprintf(stderr,fmt,## args)
//#  endif
#else
#  define PDEBUG(fmt,args...)   /*no printf*/
#endif

#ifndef SCULL_MAJOR
#define SCULL_MAJOR 0
#endif

#ifndef SCULL_P_NR_DEVS
#define SCULL_P_NR_DEVS 4
#endif

#ifndef SCULL_P_BUFFER
#define SCULL_P_BUFFER 4000
#endif

#define TYPE(minor)  ((minor)>>4 && 0xff)
#define NUM(minor)   ((minor) & 0xf)

extern int scull_major;   /*from main.c*/

extern int scull_p_buffer; /*for pipe.c*/

int scull_p_init(dev_t dev);
void scull_p_cleanup(void);
int scull_access_init(dev_t dev);
void scull_access_cleanup(void);

 /*  * Ioctl definitions
 *   */

/* Use 'k' as magic number */
#define SCULL_IOC_MAGIC  'k'
/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET    _IO(SCULL_IOC_MAGIC, 0)

/*
 *  * S means "Set" through a ptr,
 *  * T means "Tell" directly with the argument value
 *  * G means "Get": reply by setting through a pointer
 *  * Q means "Query": response is on the return value
 *  * X means "eXchange": switch G and S atomically
 *  * H means "sHift": switch T and Q atomically
 *  */
#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
#define SCULL_IOCGQSET    _IOR(SCULL_
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值