Linux内核驱动开发与等待队列机制实例

合集下载

Linux内核的等待队列

Linux内核的等待队列

Linux内核的等待队列Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。

在Linux2.4.21中,等待队列在源代码树include/linux/wait.h中,这是一个通过list_head连接的典型双循环链表,如下图所示。

在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。

等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。

由于我们只需要对队列进行添加和删除操作,并不会修改其中的对象(等待队列项),因此,我们只需要提供一把保护整个基础设施和所有对象的锁,这把锁保存在等待队列头中,为wq_lock_t类型。

在实现中,可以支持读写锁(rwlock)或自旋锁(spinlock)两种类型,通过一个宏定义来切换。

如果使用读写锁,将wq_lock_t定义为rwlock_t类型;如果是自旋锁,将wq_lock_t 定义为spinlock_t类型。

无论哪种情况,分别相应设置wq_read_lock、wq_read_unlock、wq_read_lock_irqsave、wq_read_unlock_irqrestore、wq_write_lock_irq、wq_write_unlock、wq_write_lock_irqsave和wq_write_unlock_irqrestore等宏。

等待队列头struct __wait_queue_head {wq_lock_t lock;struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;前面已经说过,等待队列的主体是进程,这反映在每个等待队列项中,是一个任务结构指针(struct task_struct * task)。

linux内核中等待队列的几种用法

linux内核中等待队列的几种用法

linux内核中等待队列的几种用法摘要:1.等待队列的基本概念与作用2.睡眠等待某个条件发生3.手工休眠方式一4.等待队列在驱动开发中的应用5.唤醒与检测条件正文:Linux内核中的等待队列(wait queue)是一种重要的同步机制,它在驱动开发中被广泛应用,主要用于实现阻塞式访问。

本文将详细介绍等待队列的几种用法,以帮助读者更好地理解和使用这一机制。

一、等待队列的基本概念与作用等待队列是Linux内核中一种用于同步进程间操作的数据结构。

它以队列为基础,与进程调度机制紧密结合,使进程能够在等待某个条件满足时进入阻塞状态。

当条件满足时,阻塞的进程将被唤醒。

等待队列在内核中还被用于实现信号量、互斥锁等机制。

二、睡眠等待某个条件发生睡眠等待某个条件发生是等待队列的一种常见用法。

在这种用法中,进程会在等待队列中睡眠,直到某个条件为真时被唤醒。

1.睡眠方式:- waitevent:进程阻塞等待,直到条件为真。

- waiteventinterruptible:进程阻塞等待,但允许中断,如硬件中断或信号。

2.唤醒方式:- wakeup:唤醒阻塞在等待队列中的进程。

唤醒时,需要检测条件是否为真。

如果条件仍为假,进程将继续睡眠。

三、手工休眠方式一1.建立并初始化一个等待队列项:```cwait_queue_head_t myqueue;INIT_LIST_HEAD(&myqueue);```2.添加进程到等待队列:```cadd_wait_queue(&myqueue, &wait);```3.唤醒进程:```cwake_up_interruptible(&myqueue);```四、等待队列在驱动开发中的应用在驱动开发中,等待队列用于实现异步事件通知机制。

当驱动程序等待某个事件时,可以将进程阻塞在等待队列上。

当事件发生时,驱动程序通过唤醒进程来通知等待的进程。

五、唤醒与检测条件在唤醒阻塞的进程时,需要确保条件已经满足。

wait_event_timeout用法(一)

wait_event_timeout用法(一)

wait_event_timeout用法(一)wait_event_timeout用法1. 简介wait_event_timeout是Linux内核中的一个函数,用于在等待某个事件的同时限定等待的时间。

2. 用法示例下面是wait_event_timeout的几种典型用法示例:•示例1:等待事件直到超时wait_event_timeout(wait_queue, condition, timeout);这个用法表示等待条件condition成立,直到超过指定的超时时间timeout。

•示例2:等待事件直到超时或者被唤醒wait_event_interruptible_timeout(wait_queue, condit ion, timeout);与示例1类似,但是如果在等待期间被接收到信号,函数将立即返回。

•示例3:循环等待事件直到超时while (!timeout) {timeout = wait_event_interruptible_timeout(wait_queu e, condition, timeout);}这个用法会进行循环等待,直到超时为止。

如果在等待期间被唤醒,将重新进入等待。

•示例4:检测返回值ret = wait_event_timeout(wait_queue, condition, tim eout);if (ret == 0) {// 等待超时} else {// 条件满足或者被唤醒}这个用法在等待结束后会检查返回值,如果返回0表示等待超时,否则表示条件满足或者被唤醒。

3. 参数说明wait_event_timeout函数的参数解释如下:•wait_queue:表示等待队列,用于等待事件的释放。

•condition:表示等待的条件,当条件成立时结束等待。

•timeout:表示等待的超时时间,以jiffies为单位,如果超过这个时间还没有等到条件满足,函数将返回。

4. 注意事项•在使用wait_event_timeout时,需要确保事件的释放和唤醒的正确逻辑,否则可能导致死锁或者长时间的等待。

wait_event_interruptible_exclusive 介绍

wait_event_interruptible_exclusive 介绍

wait_event_interruptible_exclusive 介绍在Linux内核中,wait_event_interruptible_exclusive是一个用于同步进程的函数。

它提供了一种让进程等待某个特定事件发生的机制,并且在等待期间可以被中断的能力。

本文将深入介绍wait_event_interruptible_exclusive的特性、用法以及实例。

wait_event_interruptible_exclusive函数的特性很明显:它是可中断的,而且支持独占式的等待。

这意味着在等待期间,如果进程收到了信号,它可以立即被唤醒。

另外,它还保证只有一个进程可以获得对某个共享资源的独占访问权限。

下面来看一下wait_event_interruptible_exclusive的用法。

它接受两个参数:等待队列头指针和条件表达式。

等待队列头是一个用于管理等待进程的数据结构,用于确保等待进程能够正确地被唤醒。

而条件表达式则是一个布尔表达式,用于判断等待的条件是否满足。

如果条件满足,则进程会立即返回;否则,进程会被阻塞,并加入到等待队列中。

在使用wait_event_interruptible_exclusive时,需要注意以下几点。

首先,它必须在拥有锁的情况下使用,以确保在进入等待状态之前条件表达式的值不会发生变化。

其次,它需要在等待之前检查信号是否已经到来,如果信号已经到来,则可以避免进入等待状态。

下面通过一个实例来进一步说明wait_event_interruptible_exclusive的使用。

```cDEFINE_WAIT(wait);while (!condition) {prepare_to_wait_exclusive(&wait_queue, &wait, TASK_INTERRUPTIBLE);if (signal_pending(current))break;schedule();}finish_wait(&wait_queue, &wait);```在上述代码中,condition是一个用于判断等待条件是否满足的布尔变量。

linux消息队列wait_queue实例

linux消息队列wait_queue实例

linux消息队列wait_queue实例在Linux内核中,`wait_queue`(等待队列)是一种用于实现进程或线程间同步的机制,通常与信号量一起使用。

它允许一个进程或线程等待某个条件成立,当条件满足时被唤醒。

下面是一个简单的Linux内核中`wait_queue`的示例,假设你正在编写一个内核模块,并希望使用等待队列来实现进程的等待和唤醒操作。

```c#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>#include<linux/kthread.h>#include<linux/delay.h>MODULE_LICENSE("GPL");MODULE_AUTHOR("Your Name");MODULE_DESCRIPTION("Wait Queue Example");//定义等待队列static DECLARE_WAIT_QUEUE_HEAD(my_wait_queue);//定义一个条件,当条件满足时唤醒等待队列中的进程static int condition=0;//定义一个内核线程static struct task_struct*my_thread;//内核线程的函数,模拟条件的变化static int my_thread_function(void*data){while(!kthread_should_stop()){//模拟一些条件的变化msleep(2000);condition=1;//唤醒等待队列中的进程wake_up(&my_wait_queue);}return0;}//模块加载时初始化内核线程static int__init wait_queue_example_init(void){printk(KERN_INFO"Wait Queue Example:Module Loaded\n");//创建内核线程my_thread=kthread_run(my_thread_function,NULL,"my_thread");return0;}//模块卸载时停止内核线程static void__exit wait_queue_example_exit(void){printk(KERN_INFO"Wait Queue Example:Module Unloaded\n");//停止内核线程kthread_stop(my_thread);}module_init(wait_queue_example_init);module_exit(wait_queue_example_exit);```在这个例子中,`DECLARE_WAIT_QUEUE_HEAD`定义了一个等待队列。

Linux kernel的wait queue机制

Linux kernel的wait queue机制

Linux kernel的wait queue机制1. 介绍当编写(Linux)驱动程序、模块或内核程序时,一些进程会等待或休眠一些事件。

Linux中有几种处理睡眠和醒来的方法,每种方法对应不同的需求,而w(ai)t queue便是其中一种。

每当进程必须等待一个事件(例如数据的到达或进程的终止)时,它都应该进入睡眠状态。

睡眠会导致进程暂停执行,从而释放处理器以供其他用途。

一段时间后,该过程将被唤醒,并在我们等待的事件到达时继续其工作。

等待队列是内核提供的一种机制,用于实现等待。

顾名思义,wait queue是等待事件的进程列表。

换句话说,当某个条件成立时,等待队列用于等待有人叫醒你。

它们必须小心使用,以确保没有竞争条件的存在。

实现wait queue的步骤如下:初始化等待队列排队(将任务置于睡眠状态,直到事件发生)唤醒排队的任务以下逐步介绍每个步骤的实现方式。

2. 初始化等待队列若使用wait queue功能,需要包含/linux/wait.h头文件。

可基于动态和静态两种方式实现等待队列的初始化。

静态方式:DECLARE_WAIT_QUEUE_HE(AD)(wq);因此,该线程正在等待该事件。

现在,我们将通过使用sudo cat/dev/etx_device读取驱动程序来发送事件现在检查dmesgDevice File Opened...Read FunctionEvent Came From Read Function - 1Waiting For Event...Device File Closed...我们从读取功能发送唤醒,因此它将打印读取计数,然后再次休眠。

现在通过sudo rmmod驱动程序从退出功能发送事件Event Came From Exit FunctionDevice Driver Remove...Done 现在条件是2。

因此,它将从线程返回并删除驱动程序。

Linux内核等待队列机制原理分析

Linux内核等待队列机制原理分析

内核等待队列机制原理分析1.等待队列数据结构等待队列由双向链表实现,其元素包括指向进程描述符的指针。

每个等待队列都有一个等待队列头(wait queue head),等待队列头是一个类型为wait_queque_head_t的数据结构:struct __wait_queue_head {spinlock_t lock;struct list_head task_list;};typedef struct __wait_queue_head wait_queue_head_t;其中,lock是用来防止并发访问,task_list字段是等待进程链表的头。

等待队列链表中的元素类型为wait_queue_t,我们可以称之为等待队列项:struct __wait_queue {unsigned int flags;#define WQ_FLAG_EXCLUSIVE 0x01void *private;wait_queue_func_t func;struct list_head task_list;};typedef struct __wait_queue wait_queue_t;每一个等待队列项代表一个睡眠进程,该进程等待某一事件的发生。

它的描述符地址通常放在private字段中。

Task_list字段中包含的是指针,由这个指针把一个元素链接到等待相同事件的进程链表中。

等待队列元素的func字段用来表示等待队列中睡眠进程应该用什么方式唤醒(互斥方式和非互斥方式)。

整个等待队列的结构如下图所示:下面看看等待队列的工作原理。

2.等待队列的睡眠过程使用等待队列前通常先定义一个等待队列头:static wait_queue_head_t wq ,然后调用wait_event_*函数将等待某条件condition的当前进程插入到等待队列wq中并睡眠,一直等到condition条件满足后,内核再将睡眠在等待队列wq上的某一进程或所有进程唤醒。

Linux等待队列(WaitQueue)

Linux等待队列(WaitQueue)

Linux等待队列(WaitQueue)1. Linux等待队列概述Linux内核的等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以⽤来同步对系统资源的访问、异步事件通知、跨进程通信等。

在Linux中,等待队列以循环链表为基础结构,包括两种数据结构:等待队列头(wait queue head)和等待队列元素(wait queue),整个等待队列由等待队列头进⾏管理。

下⽂将⽤内核源码(基于Linux kernel 5.2)对等待队列进⾏介绍,详细说明采⽤等待队列实现进程阻塞和唤醒的⽅法。

2. 等待队列头和等待队列元素等待队列以循环链表为基础结构,链表头和链表项分别为等待队列头和等待队列元素,分别⽤结构体 wait_queue_head_t 和 wait_queue_entry_t 描述(定义在 linux/wait.h )。

2.1 基本概念struct wait_queue_head {spinlock_t lock;struct list_head head;};typedef struct wait_queue_head wait_queue_head_t;typedef int (*wait_queue_func_t)(struct wait_queue_entry *wq_entry, unsigned mode, int flags, void *key);int default_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int flags, void *key);/* wait_queue_entry::flags */#define WQ_FLAG_EXCLUSIVE 0x01#define WQ_FLAG_WOKEN 0x02#define WQ_FLAG_BOOKMARK 0x04/** A single wait-queue entry structure:*/struct wait_queue_entry {unsigned int flags;void *private;wait_queue_func_t func;struct list_head entry;};typedef struct wait_queue_entry wait_queue_entry_t;等待队列头结构包括⼀个⾃旋锁和⼀个链表头。

linux内核中的workqueue_和work_使用方法__示例及解释说明

linux内核中的workqueue_和work_使用方法__示例及解释说明

linux内核中的workqueue 和work 使用方法示例及解释说明1. 引言1.1 概述Linux内核是操作系统的核心,工作队列(workqueue)和work是其重要的组成部分。

工作队列提供了一种异步机制,用于处理长时间运行的任务或者需要在后台执行的任务。

而work则是具体的任务对象,通过将任务封装为work对象,可以方便地在工作队列中进行调度和管理。

1.2 文章结构本文将详细介绍Linux内核中的工作队列(workqueue)和work的使用方法,并通过示例和解释说明来展示其具体应用。

文章分为五个部分:引言、Workqueue和Work基础知识、Workqueue使用方法、Work使用方法和示例说明以及结论与展望。

1.3 目的本文旨在帮助读者全面了解Linux内核中工作队列和work的概念以及它们的使用方法。

通过深入解析其原理和实践案例,读者可以掌握如何利用工作队列和work来进行高效地后台任务处理,并为未来的研究和应用提供思路和参考。

2. Workqueue和Work基础知识:2.1 Workqueue介绍:Workqueue是Linux内核中的一种机制,用于管理和执行工作任务。

它是一种异步处理的机制,可以在后台处理一些耗时的操作,而不会阻塞系统或其他任务的执行。

2.2 Work介绍:Work是由Workqueue管理的工作任务。

每个Work代表一个需要在后台执行的具体工作。

一个Work可以被认为是一段代码,在特定条件或事件发生时被调用执行。

2.3 Work之间的关系:Workqueue可以创建和管理多个Work任务。

当某个条件满足时,例如硬件中断发生或定时器超时,Workqueue会从任务队列中选择一个可用的Work,并将其分配给空闲的内核线程来运行,以完成相应的工作。

在这个过程中,多个Work之间不存在直接依赖关系。

每个Work都是独立地被分配、执行和管理。

它们并行运行,并且不需要等待其他Work的完成。

linux 等待队列用法

linux 等待队列用法

linux 等待队列用法在Linux内核中,等待队列是一种用于进程或线程等待某个条件成立的数据结构。

当进程或线程需要等待某个资源或事件时,可以将自己加入到等待队列中,并进入睡眠状态,直到被唤醒并继续执行。

等待队列的使用通常与进程调度和同步有关。

以下是一些常用的等待队列用法:1.等待资源:当进程需要等待某个资源可用时,可以将自己加入到等待队列中,并在资源可用时被唤醒。

例如,进程可能需要等待文件描述符可用或等待某个锁被释放。

2.等待事件:等待队列可以用于等待某个事件发生。

例如,进程可能需要等待某个I/O操作完成或等待某个定时器超时。

3.进程同步:等待队列可以用于实现进程间的同步。

例如,当多个进程需要访问共享资源时,可以将它们放入等待队列中,并在资源可用时按照一定的顺序唤醒它们。

在使用等待队列时,需要遵循一些规则和注意事项:1.避免死锁:在使用等待队列时,需要特别注意避免死锁。

死锁是指多个进程互相等待对方释放资源,导致它们都无法继续执行的情况。

为了防止死锁,可以采用一些避免策略,例如先入先出(FIFO)调度算法。

2.避免饥饿:饥饿是指等待时间过长的进程无法获得足够的资源,导致其一直处于等待状态的情况。

为了避免饥饿,可以采用一些调度算法,例如轮转法(Round Robin)。

3.避免忙等待:忙等待是指进程在等待资源时一直处于运行状态,而不是进入睡眠状态。

这会导致CPU资源的浪费。

为了避免忙等待,可以使用等待队列将进程放入睡眠状态,直到资源可用或事件发生。

总之,等待队列是Linux内核中用于进程调度和同步的重要工具之一。

正确地使用等待队列可以有效地提高系统的性能和稳定性。

linux内核中等待队列的几种用法

linux内核中等待队列的几种用法

linux内核中等待队列的几种用法【实用版】目录1.介绍等待队列的概念和作用2.说明等待队列在 Linux 内核中的应用3.详述等待队列的四种用法4.总结等待队列的重要性和应用场景正文一、等待队列的概念和作用等待队列(wait queue)是一种数据结构,它在 Linux 内核中用于实现异步事件通知机制。

等待队列可以让进程在等待某个条件发生时进入阻塞状态,从而实现对系统资源的同步访问。

当条件满足时,另一个进程可以通过唤醒(wakeup)操作唤醒阻塞的进程,从而实现进程间的同步。

二、等待队列在 Linux 内核中的应用在 Linux 内核中,等待队列作为一种基本的功能单位,与进程调度机制紧密结合。

它主要用于实现以下功能:1.阻塞式访问:通过等待队列,进程可以在访问某个资源时进入阻塞状态,直到资源可用。

2.异步事件通知:等待队列可以实现内核中异步事件的通知,如硬件设备驱动的回调函数等。

3.信号量实现:信号量是 Linux 内核中用于实现进程同步的一种机制,其实现依赖于等待队列。

三、等待队列的四种用法1.睡眠等待某个条件发生:通过 waitevent 和waiteventinterruptible 函数实现睡眠等待,进程会根据条件是否满足来决定是否唤醒。

2.手工休眠方式一:建立并初始化一个等待队列项,通过 definewait 函数实现。

唤醒方式为 wakeup 函数,需要检测条件是否满足。

3.手工休眠方式二:使用 waitqueueheadt 结构体定义等待队列头,然后使用 initwaitqueuehead 函数初始化。

唤醒方式为 wakeup 函数。

4.添加/移除等待队列:使用addwaitqueue和removewaitqueue函数实现等待队列的添加和移除。

四、总结等待队列在 Linux 内核中具有重要作用,主要用于实现进程间的同步和异步事件通知。

Linux驱动总结3-unlocked_ioctl和堵塞(waitqueue)读写函数的实现

Linux驱动总结3-unlocked_ioctl和堵塞(waitqueue)读写函数的实现

Linux驱动总结3-unlocked_ioctl和堵塞(waitqueue)读写函数的实现学习了驱动程序的设计,感觉在学习驱动的同时学习linux内核,也是很不错的过程哦,做了几个实验,该做一些总结,只有不停的作总结才能印象深刻。

我的平台是虚拟机,fedora14,内核版本为2.6.38.1.其中较之前的版本存在较大的差别,具体的实现已经在上一次总结中给出了。

今天主要总结的是ioctl和堵塞读写函数的实现。

一、ioctl函数的实现首先说明在2.6.36以后ioctl函数已经不再存在了,而是用unlocked_ioctl和compat_ioctl两个函数实现以前版本的ioctl函数。

同时在参数方面也发生了一定程度的改变,去除了原来ioctl中的struct inode参数,同时改变了返回值。

但是驱动设计过程中存在的问题变化并不是很大,同样在应用程序设计中我们还是采用ioctl实现访问,而并不是unlocked_ioctl函数,因此我们还可以称之为ioctl函数的实现。

ioctl函数的实现主要是用来实现具体的硬件控制,采用相应的命令控制硬件的具体操作,这样就能使得硬件的操作不再是单调的读写操作。

使得硬件的使用更加的方便。

ioctl函数实现主要包括两个部分,首先是命令的定义,然后才是ioctl函数的实现,命令的定义是采用一定的规则。

ioctl的命令主要用于应用程序通过该命令操作具体的硬件设备,实现具体的操作,在驱动中主要是对命令进行解析,通过switch-case 语句实现不同命令的控制,进而实现不同的硬件操作。

ioctl函数的命令定义方法:int (*unlocked_ioctl)(struct file*filp,unsigned int cmd,unsigned long arg)虽然其中没有指针的参数,但是通常采用arg传递指针参数。

cmd是一个命令。

每一个命令由一个整形数据构成(32bits),将一个命令分成四部分,每一部分实现具体的配置,设备类型(幻数)8bits,方向2bits,序号8bits,数据大小13/14bits。

linux 等待队列用法 -回复

linux 等待队列用法 -回复

linux 等待队列用法-回复Linux 等待队列用法在Linux 操作系统中,等待队列(Wait Queue)是一种常用的数据结构,用于实现进程间的同步和通信。

当一个进程需要等待某个事件发生或者某个资源可用时,它可以将自己加入等待队列,并进入睡眠状态,直到满足等待条件后被唤醒。

本文将详细介绍Linux 等待队列的用法和实现原理,让读者更好地理解和应用这一重要的操作系统概念。

1. 等待队列的基本概念和作用等待队列是一种链表结构,用于存放需要等待同一个事件或资源的进程。

当一个进程需要等待某个条件满足时,它会把自己放入等待队列,并进入睡眠状态,等待被唤醒。

等待队列的作用在于帮助进程实现阻塞、睡眠和唤醒操作,使得进程能够在需要等待某个条件满足时能够有效地等待,避免了占用CPU 资源。

2. Linux 等待队列的数据结构和设计Linux 等待队列的主要数据结构包括等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。

2.1 等待队列头(wait_queue_head_t)等待队列头是一个结构体,用于表示一个等待队列,它包含两部分数据:等待队列中的等待项列表和唤醒函数指针。

等待队列头的定义如下:typedef struct __wait_queue_head {spinlock_t lock;struct list_head task_list;wait_queue_proc_t procs;} wait_queue_head_t;其中,lock 是一个自旋锁,用于保护等待队列的访问;task_list 是一个链表结构,用于保存等待队列中的等待项;procs 是一个指向唤醒函数的指针,用于在唤醒等待队列中的进程时执行相应的操作。

2.2 等待队列项(wait_queue_t)等待队列项用于表示等待队列中的一个等待项,它包含了等待的条件和等待的进程。

等待队列项的定义如下:struct __wait_queue {unsigned int flags;void *private;wait_queue_func_t func;struct list_head task_list;};typedef __wait_queue wait_queue_t;其中,flags 用于表示等待的条件;private 是一个指针,用于保存等待过程中的私有数据;func 是唤醒函数,用于在满足等待条件时唤醒相应的进程;task_list 是链表结构,用于保存等待队列中的所有等待项。

Linux内核中工作队列(work_queue)的操作

Linux内核中工作队列(work_queue)的操作
// 保存线程指针
cwq->thread = p;
return p;
}
static int worker_thread(void *__cwq)
{
struct cpu_workqueue_struct *cwq = __cwq;
// 声明一个等待队列
DECLARE_WAITQUEUE(wait, current);
// 参数初始化定义, 而该宏用在程式之中对工作结构赋值
#define INIT_WORK(_work, _func, _data) \
do { \
INIT_LIST_HEAD(&(_work)->entry); \
(_work)->pending = 0; \
/*
* The externally visible workqueue abstraction is an array of
* per-CPU workqueues:
*/
// 工作队列结构
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
int cpu)
{
// 每个CPU的工作队列
struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu);
struct task_struct *p;
spin_lock_init(&cwq->lock);
{
...
keventd_wq = create_workqueue("events");

linux 等待队列用法

linux 等待队列用法

linux 等待队列用法在Linux内核中,等待队列是一种用于进程或线程等待某个条件成立的数据结构。

当进程或线程需要等待某个事件发生时,它们会被加入到等待队列中,并在条件满足时被唤醒。

等待队列的用法通常与进程调度和同步机制相关。

以下是等待队列的基本用法:定义等待队列:在内核中定义一个等待队列结构体,通常包括一个队列头和一个队列项数组。

C:DEFINE_WAIT(wait); // 定义一个等待队列项wait_queue_head_t wait_queue_head; // 定义等待队列头初始化等待队列:使用init_waitqueue_head()函数初始化等待队列头。

C:init_waitqueue_head(&wait_queue_head); // 初始化等待队列头将进程或线程加入等待队列:使用add_wait_queue()或add_wait_queue_exclusive()函数将进程或线程加入到等待队列中。

C:add_wait_queue(&wait_queue_head, &wait); // 将进程或线程加入等待队列唤醒进程或线程:当事件发生时,使用wake_up()函数唤醒等待队列中的进程或线程。

C:wake_up(&wait_queue_head); // 唤醒等待队列中的进程或线程清理等待队列项:当进程或线程被唤醒并继续执行后,需要使用finish_wait()函数清理等待队列项。

C:finish_wait(&wait_queue_head, &wait); // 清理等待队列项使用条件变量:在等待队列中,可以使用条件变量来让进程或线程在满足某个条件时自动唤醒。

条件变量通常与自旋锁一起使用,以确保条件变量的原子性访问。

注意避免忙等待:为了避免忙等待,可以在等待队列中使用计时器来让进程或线程在一定时间后自动唤醒,或者使用信号量或其他同步机制来控制等待进程或线程的执行。

linux内核中的等待队列分析

linux内核中的等待队列分析

Company
Logo
等待队列头
等待队列头结构体 //yoursource/include/linux/wait.h 50struct __wait_queue_head { 51 52 53}; 54typedef struct __wait_queue_head wait_queue_head_t; spinlock_t lock; struct list_head task_list;
3
Company
Logo
阻塞和非阻塞 ( 续 )
用户空间通过 read() 和 write() 等统一的系统调用来访问 设备文件。而这些系统调用函数最终则会调用设备驱动中的 XXX_read() 和 XXX_write() 函数。因此,如果在设备驱 动中实现了阻塞功能,当访问设备的进程不能及时获得设备 资源时,设备驱动中的操作函数就会将该进程阻塞到资源可 访问为止。 此时, XXX_read() 和 XXX_write() 等函数也就不会立即 返回, read() 和 write() 等系统调用也就不会立即返回。 整个阻塞 - 唤醒的过程用户是无法感知到的。从用户的角度 来看,他们会认为直接就可以对此设备进行操作。 非阻塞 I/O 则与之相反。 4
10
Company
Logo
add_wait_queue 函数源码分析
yoursource/kernel/wait.c 22void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) 23{ 24 unsigned long flags; 25 26 wait->flags &= ~WQ_FLAG_EXCLUSIVE;// 非互斥进程 27 spin_lock_irqsave(&q->lock, flags);// 关中断并保存状态字 28 __add_wait_queue(q, wait); 29 spin_unlock_irqrestore(&q->lock, flags); 30} yoursource/include/linux/wait.h 122static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) 123{ // 非互斥进程被添加在链表头结点之后 124 list_add(&new->task_list, &head->task_list); 125}

Linux内核中等待队列的几种用法

Linux内核中等待队列的几种用法

Linux内核中等待队列的⼏种⽤法Linux内核⾥的等待队列机制在做驱动开发时⽤的⾮常多,多⽤来实现阻塞式访问,下⾯简单总结了等待队列的四种⽤法,希望对读者有所帮助。

1. 睡眠等待某个条件发⽣(条件为假时睡眠):睡眠⽅式:wait_event, wait_event_interruptible唤醒⽅式:wake_up (唤醒时要检测条件是否为真,如果还为假则继续睡眠,唤醒前⼀定要把条件变为真)2. ⼿⼯休眠⽅式⼀:1)建⽴并初始化⼀个等待队列项DEFINE_WAIT(my_wait) <==> wait_queue_t my_wait; init_wait(&my_wait);2)将等待队列项添加到等待队列头中,并设置进程的状态prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state)3)调⽤schedule(),告诉内核调度别的进程运⾏4)schedule返回,完成后续清理⼯作finish_wait()3. ⼿⼯休眠⽅式⼆:1)建⽴并初始化⼀个等待队列项:DEFINE_WAIT(my_wait) <==> wait_queue_t my_wait; init_wait(&my_wait);2)将等待队列项添加到等待队列头中:add_wait_queue3)设置进程状态__set_current_status(TASK_INTERRUPTIBLE);4)schedule()5)将等待队列项从等待队列中移除remove_wait_queue()其实,这种休眠⽅式相当于把⼿⼯休眠⽅式⼀中的第⼆步prepare_to_wait拆成两步做了,即prepare_to_wait <====>add_wait_queue + __set_current_status,其他都是⼀样的。

linux内核等待队列详解

linux内核等待队列详解

linux内核等待队列详解等待队列⽤于使得进程等待某⼀特定事件的发⽣,⽆需频繁的轮询,进程在等待周期中睡眠,当时间发⽣后由内核⾃动唤醒。

1 数据结构1.1 等待队列头等待队列结构如下,因为每个等待队列都可以再中断时被修改,因此,在操作等待队列之前必须获得⼀个⾃旋锁。

定义位于:linux-3.10.73\include\linux\wait.h1struct __wait_queue_head {2 spinlock_t lock;3struct list_head task_list;4 };5 typedef struct __wait_queue_head wait_queue_head_t;1.2 等待队列等待队列是通过task_list双链表来实现,其数据成员是以下数据结构:1 typedef struct __wait_queue wait_queue_t;23struct __wait_queue {4 unsigned int flags;5#define WQ_FLAG_EXCLUSIVE 0x016void *private;7 wait_queue_func_t func;8struct list_head task_list;9 };关系如下:等待队列使⽤分两步:(1)为了使得等待进程在⼀个等待队列中睡眠,需要调⽤函数wait_event()函数。

进程进⼊睡眠,将控制权释放给调度器。

(2)在内核中另⼀处,调⽤wake_up()函数唤醒等待队列中的睡眠进程。

注:使⽤wait_event()函数使得进程睡眠;⽽在内核另⼀处有⼀个对应的wake_up()函数被调⽤。

2 等待队列的初始化有两种⽅法初始化队列,分为动态初始化和静态初始化。

2.1 静态初始化1#define DEFINE_WAIT_FUNC(name, function) \2 wait_queue_t name = { \3 .private = current, \4 .func = function, \5 .task_list = LIST_HEAD_INIT((name).task_list), \6 }78#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)2.2 动态初始化1static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)2 {3 q->flags = 0;4 q->private = p;5 q->func = default_wake_function;6 }其中函数autoremove_wake_function()是⽤来唤醒进程的,该函数不经调⽤default_wake_function(),还将所属等待队列成员从等待队列删除。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

相信很多写程序的人都写过socket 的程序。

当我们open 一个socket 之后,接着去读取这个socket,如果此时没有任何资料可供读取,那read 就会block 住。

(这是没有加上O_NONBLOCK 的情形),直到有资料可读取才会传回来。

在Linux kernel 里有一个数据结构可以帮助我们做到这样的功能。

这个数据结构就是这里要跟各位介绍的wait queue。

在kernel 里,wait_queue 的应用很广,举凡device driver semaphore 等方面都会使用到wait_queue 来implement。

所以,它算是kernel 里蛮基本的一个数据结构。

接下来,我要跟各位介绍一下wait_queue 的用法,以及用一个例子来说明如何使用wait_queue。

最后,我会带各位去trace 一下wait_queue 的原始程序代码,看看wait_queue 是如何做到的。

我想有件事要先提及的是Linux 在user space 跟在kernel space 上的差异。

我们知道Linux 是multi-tasking 的环境,同时可以有很多人执行很多的程序。

这是从user 的观点来看的。

如果就kernel 的观点来看,是没有所谓的multi-tasking 的。

在kernel 里,只有single-thread。

也就是说,如果你的kernel code 正在执行,那系统里只有那部分在执行。

不会有另一部分的kernel code 也在运作。

当然,这是指single processor 的情况下,如果是SMP 的话,那我就不清楚了。

我想很多人都在Windows 3.1 下写过程序,在那种环境下写程序,每一个程序都必须适当的将CPU 让给别的程序使用。

如果有个程序里面有一个while (1);的话,那保证系统就停在那里了。

这种的多任务叫做non-preemptive。

它多任务的特性是由各个程序相互合作而造成的。

在Linux 的user space 下,则是所谓的preemptive,各个process 喜欢执行什么就执行什么,就算你在你的程序里加上while(1); 这一行也不会影响系统的运作。

反正时间到了,系统自动就会将你的程序停住,让别的程序去执行。

这是在user space 的情况下,在kernel 这方面,就跟Windows 3.1 程序是一样的。

在kernel 里,你必须适当的将CPU 的执行权释放出来。

如果你在kernel里加入while(1); 这一行。

那系统就会跟Windows 3.1 一样。

卡在那里。

当然啦,我是没试过这样去改kernel,有兴趣的人可以去试试看,如果有不同的结果,请记得告诉我。

假设我们在kernel 里产生一个buffer,user 可以经由read,write 等system call 来读取或写资料到这个buffer 里。

如果有一个user 写资料到buffer 时,此时buffer 已经满了。

那请问你要如何去处理这种情形呢? 第一种,传给user 一个错误讯息,说buffer 已经满了,不能再写入。

第二种,将user 的要求block 住,等有人将buffer 内容读走,留出空位时,再让user 写入资料。

但问题来了,你要怎么将user 的要求block 住。

难道你要用while ( is_full );write_to_buffer;这样的程序代码吗? 想想看,如果你这样做会发生什么事? 第一,kernel会一直在这个while 里执行。

第二个,如果kernel 一直在这个while 里执行,表示它没有办法去maintain系统的运作。

那此时系统就相当于当掉了。

在这里is_full 是一个变量,当然,你可以让is_full 是一个function,在这个function里会去做别的事让kernel 可以运作,那系统就不会当。

这是一个方式。

但是,如果我们使用wait_queue 的话,那程序看起来会比较漂亮,而且也比较让人了解,如下所示:struct wait_queue *wq = NULL; /* global variable */while ( is_full ) {interruptible_sleep_on( &wq );}write_to_buffer();interruptible_sleep_on( &wq ) 是用来将目前的process,也就是要求写资料到buffer 的process放到wq 这个wait_queue 里。

在interruptible_sleep_on 里,则是最后会呼叫schedule() 来做schedule 的动作,也就是去找另一个process 来执行以维持系统的运作。

当执行完interruptible_sleep_on 之后,要求write 的process 就会被block 住。

那什么时候会恢复执行呢? 这个process 之所以会被block 住是因为buffer 的空间满了,无法写入。

但是如果有人将buffer 的资料读取掉,则buffer 就有空间可以让人写入。

所以,有关于叫醒process 的动作应该是在read buffer 这方面的程序代码做的。

extern struct wait_queue *wq;if ( !is_empty ) {read_from_buffer();wake_up_interruptible( &wq );}....以上的程序代码应该要放在read buffer 这部分的程序代码里,当buffer 有多余的空间时,我们就呼叫wake_up_interruptible( &wq ) 来将挂在wq 上的所有process 叫醒。

请记得,我是说将wq 上的所有process 叫醒,所以,如果如果有10个process 挂在wq 上的话,那这10 个都会被叫醒。

之后,至于谁先执行。

则是要看schedule 是怎么做的。

就是因为这10 个都会被叫醒。

如果 A 先执行,而且万一很不凑巧的,A 又把buffer 写满了,那其它9 个process 要怎么办呢? 所以在write buffer 的部分,需要用一个while 来检查buffer 目前是否满了.如果是的话,那就继续挂在wq 上面.上面所谈的就是wait_queue 的用法。

很简单不是吗? 接下来,我会再介绍一下wait_queue 提供那些function 让我们使用。

让我再重申一次。

wait_queue 应设为global variable,比方叫wq,只要任何的process 想将自己挂在上面,就可以直接叫呼叫sleep_on 等function。

要将wq 上的process 叫醒。

只要呼叫wake_up 等function 就可以了.就我所知,wait_queue 提供4个function 可以使用,两个是用来将process 加到wait_queue 的:sleep_on( struct wait_queue **wq );interruptible_sleep_on( struct wait_queue **wq );另外两个则是将process从wait_queue上叫醒的。

wake_up( struct wait_queue **wq );wake_up_interruptible( struct wait_queue **wq );我现在来解释一下为什么会有两组。

有interruptible 的那一组是这样子的。

当我们去read 一个没有资料可供读取的socket 时,process 会block 在那里。

如果我们此时按下Ctrl+C,那read() 就会传回EINTR。

像这种的block IO 就是使用interruptible_sleep_on() 做到的。

也就是说,如果你是用interruptible_sleep_on() 来将process 放到wait_queue 时,如果有人送一个signal 给这个process,那它就会自动从wait_queue 中醒来。

但是如果你是用sleep_on() 把process 放到wq 中的话,那不管你送任何的signal 给它,它还是不会理你的。

除非你是使用wake_up() 将它叫醒。

sleep 有两组。

wake_up 也有两组。

wake_up_interruptible() 会将wq 中使用interruptible_sleep_on() 的process 叫醒。

至于wake_up() 则是会将wq 中所有的process 叫醒。

包括使用interruptible_sleep_on() 的process。

在使用wait_queue 之前有一点需要特别的小心,呼叫interruptible_sleep_on() 以及sleep_on() 的function 必须要是reentrant。

简单的说,reentrant 的意思是说此function不会改变任何的global variable,或者是不会depend on 任何的global variable,或者是在呼叫interruptible_sleep_on() 或sleep_on() 之后不会depend on 任何的global variable。

因为当此function 呼叫sleep_on() 时,目前的process 会被暂停执行。

可能另一个process 又会呼叫此function。

若之前的process 将某些information 存在global variable,等它恢复执行时要使用,结果第二行程进来了,又把这个global variable 改掉了。

等第一个process 恢复执行时,放在global variable 中的information 都变了。

产生的结果恐怕就不是我们所能想象了。

其实,从process 执行指令到此function 中所呼叫的function 都应该是要reentrant 的。

不然,很有可能还是会有上述的情形发生.由于wait_queue 是kernel 所提供的,所以,这个例子必须要放到kernel 里去执行。

我使用的这个例子是一个简单的driver。

它会maintain 一个buffer,大小是8192 bytes。

提供read跟write 的功能。

当buffer 中没有资料时,read() 会马上传回,也就是不做block IO。

而当write buffer 时,如果呼叫write() 时,空间已满或写入的资料比buffer 大时,就会被block 住,直到有人将buffer 里的资料读出来为止。

相关文档
最新文档