(一)阻塞和非阻塞

阻塞:执行设备操作时,若不能获得资源,则挂起进程进入休眠直到满足可操作的条件后再操作。
非阻塞:进程在不能进行设备操作时,并不挂起,它要么放弃,要么不停地查询,直至可以进行操作为止。

(二)为什么学习等待队列

在讲解等待队列的作用之前先来看一下内核的休眠机制:

正在运行的进程让出CPU,休眠的进程会被内核搁置在一边,只有当内核再次把休眠的进程唤醒,
进程才会重新在CPU运行,这是内核中的进程调度
一个CPU在同一时间只能有一个进程在运行,内核将所有的进程按一定的算法将CPU轮流的给每个
进程使用,而休眠就是进程没有被运行时的一种形式。在休眠下,进程不占用CPU,等待被唤醒。

当一个进程休眠时,其他进程为了能够唤醒休眠的进程,它必须知道休眠的进程在哪里,出于这样的原因,需要有一个称为等待队列的结构体。等待队列是一个存放着等待某个特定事件进程链表。所以等待队列相当于休眠进程的链表,需要手动进行操作

它的作用主要是:实现中断处理、进程同步及延时进程

(三)等待队列的相关接口

等待队列头数据结构:

struct __wait_queue_head {spinlock_t lock; //自旋锁机制struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
wait_queue_head_t:即为等待队列头的数据类型

初始化等待队列头:

静态定义等待队列头并初始化:
#define DECLARE_WAIT_QUEUE_HEAD(name) \wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)动态定以并初始化:#define init_waitqueue_head(q)              \do {                       \static struct lock_class_key __key;    \\__init_waitqueue_head((q), #q, &__key);   \} while (0)

休眠等待队列wait_event或wait_event_interruptible:

/*** wait_event - sleep until a condition gets true* @wq: the waitqueue to wait on* @condition: a C expression for the event to wait for** The process is put to sleep (TASK_UNINTERRUPTIBLE) until the* @condition evaluates to true. The @condition is checked each time* the waitqueue @wq is woken up.** wake_up() has to be called after changing any variable that could* change the result of the wait condition.*/
#define wait_event(wq, condition)                   \
do {                                    \if (condition) //判断条件是否满足,如果满足则退出等待                         \break;                         \__wait_event(wq, condition);   //如果不满足,则进入__wait_event宏             \
} while (0)#define __wait_event(wq, condition)                  \
do {                                    \DEFINE_WAIT(__wait);                       \/*定义并且初始化等待队列项,后面我们会将这个等待队列项加入我们的等待队列当中,同时在初始化的过程中,会定义func函数的调用函数autoremove_wake_function函数,该函数会调用default_wake_function函数。*/                                       \for (;;) {                         \prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);   \/*调用prepare_to_wait函数,将等待项加入等待队列当中,并将进程状态置为不可中断TASK_UNINTERRUPTIBLE;*/            if (condition)      //继续判断条件是否满足                            \break;                     \schedule();     //如果不满足,则交出CPU的控制权,使当前进程进入休眠状态                   \}                              \finish_wait(&wq, &__wait);/**如果condition满足,即没有进入休眠状态,跳出了上面的for循环,便会将该等待队列进程设置为可运行状态,并从其所在的等待队列头中删除    */                   \
} while (0)#define wait_event_interruptible(wq, condition)              \
({  /**如果condition为false,那么__wait_event_interruptible将会被执行*/  \int __ret = 0;
\if (!(condition))                      \__wait_event_interruptible(wq, condition, __ret);  \__ret;                             \
})

唤醒等待队列节点wake_up_interruptible或wake_up:

#define wake_up(x)           __wake_up(x, TASK_NORMAL, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key);
/*** __wake_up - wake up threads blocked on a waitqueue.* @q: the waitqueue* @mode: which threads* @nr_exclusive: how many wake-one or wake-many threads to wake up* @key: is directly passed to the wakeup function** It may be assumed that this function implies a write memory barrier before* changing the task state if and only if any tasks are woken up.*//**定义wake_up函数宏,同时其需要一个wait_queue_head_t的结构体指针,在该宏中调用__wake_up方法。*/
#define wake_up(x)          __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_nr(x, nr)       __wake_up(x, TASK_NORMAL, nr, NULL)
#define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL)

加interruptible接口标识唤醒可中断

#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
#define wake_up_interruptible_sync(x)   __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

(四)如何实现等待队列

1、创建等待队列头

//1.静态定义并初始化
#define DECLARE_WAIT_QUEUE_HEAD(name) \wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)//2.动态定以并初始化:wait_queue_head_t name;init_waitqueue_head(q)

默认情况下会把当前进程作为等待任务放到等待队列中
2、在需要休眠的地方调用休眠操作

wait_event 、 wait_event_timeout  wait_event_interruptible(首选)

3、在满条件的地方唤醒等待队列

wake_up 、wake_up_interruptible

(五)实例代码

chrdev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/uaccess.h>#define CDEVCOUNT 5
#define CDEVNAME "cdevdevice"
#define INODENAME "mycdev"
int count=0;
dev_t dev=0;
int keyflag;//等待队列第二个参数
char keyvalue[4]={-1,-1,-1,-1};
struct cdev * cdev =NULL;
struct class * cdevclass =NULL;
wait_queue_head_t  waithead;struct Key
{unsigned int gpios;char * name;int num;unsigned int irq;};struct Key key[]={{EXYNOS4_GPX3(2),"K1",0},{EXYNOS4_GPX3(3),"K2",1},{EXYNOS4_GPX3(4),"K3",2},{EXYNOS4_GPX3(5),"K4",3},};
irqreturn_t key_handler(int irq, void * dev)
{struct Key * tmp =(struct Key *)dev;int value[4];value[tmp->num]= gpio_get_value(tmp->gpios);gpio_set_value(EXYNOS4X12_GPM4(tmp->num),value[tmp->num]);keyflag = 1;keyvalue[tmp->num] = value[tmp->num];wake_up_interruptible(&waithead);printk("key%d value is %d\n",tmp->num,value[tmp->num]);printk("the current cpu is %d\n",smp_processor_id());return IRQ_HANDLED;}int cdev_open (struct inode *node, struct file *file)
{printk("cdev_open is install\n");return 0;
}
ssize_t cdev_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{int ret =0;if((fp->f_flags & O_NONBLOCK)== O_NONBLOCK)//非阻塞{if(!keyflag)return -EAGAIN;}else//阻塞{wait_event_interruptible(waithead,keyflag);}keyflag =0;ret = copy_to_user(buf,keyvalue,4);if(ret <0)return -EFAULT;printk("cdev_read is install\n");return 0;
}
ssize_t cdev_write (struct file *fp, const char __user * buf, size_t size, loff_t *offset)
{printk("cdev_write is install\n");return 0;
}
int cdev_release (struct inode *node, struct file *fp)
{printk("cdev_release is install\n");return 0;
}
struct file_operations fop={.open=cdev_open,.read=cdev_read,.write=cdev_write,.release=cdev_release,
};void mycdev_add(void)
{//1.申请设备号--动态int ret =alloc_chrdev_region(&dev,0, CDEVCOUNT, CDEVNAME);if(ret)return ;//初始化cdev结构体cdev = cdev_alloc();if(!cdev){goto out;}cdev_init(cdev,&fop);//添加字符设备到系统中ret =cdev_add(cdev,dev, CDEVCOUNT);if(ret){goto out1;}//创建设备类cdevclass = class_create(THIS_MODULE, INODENAME);if(IS_ERR(cdevclass)){goto out2;}for (count=0;count<CDEVCOUNT;count++)device_create(cdevclass, NULL, dev+count, NULL, "mydevice%d",count);out:unregister_chrdev_region(dev,CDEVCOUNT);   return ;
out1:unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;
out2:cdev_del(cdev);unregister_chrdev_region(dev,CDEVCOUNT);kfree(cdev);return ;}static int __init  dev_module_init(void)
{int ret=0,i=0;unsigned long flags= IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING|IRQF_SHARED;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);ret =request_irq(key[i].irq, key_handler,flags,key[i].name,(void *) &key[i]);}init_waitqueue_head(&waithead);//创建等待队列头mycdev_add();printk("this is dev_module_init \n");return 0;
}static void __exit dev_module_cleanup(void)
{int i=0;for(i=0;i<4;i++){key[i].irq = gpio_to_irq(key[i].gpios);free_irq(key[i].irq,(void *)&key[i]);}for(count=0;count<CDEVCOUNT;count++){device_destroy(cdevclass, dev+count);}class_destroy(cdevclass);cdev_del(cdev);unregister_chrdev_region(dev, CDEVCOUNT);kfree(cdev);printk("this is dev_module_cleanup\n");
}module_init(dev_module_init);
module_exit(dev_module_cleanup);
MODULE_LICENSE("GPL");

chr_app.c

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>char status[]={-1,-1,-1,-1};
char key[]={-1,-1,-1,-1};
int main(int argc, char *argv[])
{int i=0,fd= open(argv[1],O_RDWR);if(fd== -1){perror("open");return -1;}while(1){if(read(fd,status,4)<0){printf("按键状态未改变\n");sleep(1);continue;}for(i=0;i<4;i++){if(status[i] != key[i] ){printf("key%d is %s\n",i,status[i]?"up":"down");status[i]=key[i];}}}close(fd);return 0;
}

Makefile

CFLAG =-C
TARGET = chrdev
TARGET1 = chr_app
KERNEL = /mydriver/linux-3.5
obj-m += $(TARGET).oall:make $(CFLAG)  $(KERNEL) M=$(PWD)arm-linux-gcc -o $(TARGET1) $(TARGET1).c
clean:make $(CFLAG)  $(KERNEL) M=$(PWD) clean

本文章仅供学习交流用禁止用作商业用途,文中内容来水枂编辑,如需转载请告知,谢谢合作

微信公众号:zhjj0729

微博:文艺to青年

(十)Linux之等待队列相关推荐

  1. linux 等待进程,Linux 进程等待队列

    Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制. 在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待 ...

  2. Linux 进程等待队列

    Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制. 在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待 ...

  3. linux之等待队列

    等待队列本质上是一双向链表,由等待队列头和队列节点构成. 当运行的线程要获得某一个资源二暂不可得时,线程有时候需要等待,此时它可以进入睡眠状态,内核为此生成一个新的等待队列节点将睡眠的线程挂载到等待队 ...

  4. linux驱动---等待队列、工作队列、Tasklets

    概述: 等待队列.工作队列.Tasklet都是linux驱动很重要的API,下面主要从用法上来讲述如何使用API. 应用场景: 等待队列(waitqueue) linux驱动中,阻塞一般就是用等待队列 ...

  5. 二十.Linux开发之根文件系统构建及过程详解

    老规矩 有道云笔记地址: 详情看这里链接,记录太多,就不一一排版了. http://note.youdao.com/noteshare?id=15b6e982c2e66d0f47b1c787a49f4 ...

  6. 【Linux】【开发环境】【RHEL】开发环境搭建系列之十——Linux主机环境下挂载samba服务器

    在Windows下访问samba服务器比较容易,但是在Linux系统下访问samba服务器操作就略复杂了,本文将Linux服务器下访问samba服务器的步骤做一整理,供各位参考. 1.安装必要的软件 ...

  7. (十)linux内核时钟

    目录 一.什么是内核时钟 二.HZ 三.jiffies 四.linux中的延时函数 五.内核的动态定时器 六.附录 一.什么是内核时钟 1.内核时钟 操作系统的内核都需要一个系统时钟才可以工作,这个系 ...

  8. Linux基础命令(二十)Linux中的磁盘管理(后)

    一.开机自动挂载 需求1:由于mount挂载后,每次关机都会umount,想让开机自动挂载! 前提:存在已经格式化好了的分区! 策略1:vim /etc/fstab /dev/vdb1 /mnt xf ...

  9. 芯灵思Sinlinx A64开发板 Linux内核等待队列p

    阻塞:阻塞调用是指调用结果返回之前,当前进程程会被挂起(休眠).函数只有在得到结果之后才会返回.默认情况下,文件都是以这种方式打开. 非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前进程程,而会立 ...

最新文章

  1. WSS页面定制系列(2)---定制单个列表的表单页面
  2. PDF批量替换文字器免费版
  3. python3精要(26)-map
  4. java练习_Java基础笔试练习(一)
  5. 【转】syslog服务和syslogd守护进程
  6. 【剑指offer】旋转数组的最小数字
  7. OpenShift 4 - 用 Operator 创建 Jenkins 环境
  8. VirtualBox 桥接模式
  9. 想要改变自己,请先突破这3个自我限制
  10. Bom及Bom对象的详细介绍
  11. WPS Office Pro v10.8.2.6726 绿色便携专业增强版
  12. linux 安装vim 8.2(支持python3)
  13. 过去一年对我帮助最大的三本书
  14. qq华夏服务器状态,最国产!QQ华夏199组服务器皆“国名”
  15. [LOJ]#6515. 「雅礼集训 2018 Day10」贪玩蓝月
  16. DNS是什么意思有什么作用了
  17. 安装python包的时候文件夹权限报错:InvalidArchiveError(“Error with archive D:\\anaconda\\pkgs\\cudnn-8.4.1.50-h)
  18. 如何使用Java实现类似Windows域登录
  19. js在浏览器中对cookie进行增删改查
  20. SpringBoot 三大开发工具,你都用过么?

热门文章

  1. linux死锁的例子,操作系统教程—Linux实例分析 孟庆昌 第8章 死锁new.ppt
  2. java radio 不可选_在Java Swing中取消选择RadioButtons
  3. js json数据传递传递、json数据解析
  4. 前后端敏感数据加密方案及实现_01
  5. linux shell脚本关闭指定端口号的进程
  6. 工作流实战_14_flowable_已办任务列表查询
  7. VBA SQL查询-记录集转数组
  8. Vue 过渡效果的组件
  9. python循环迭代_Python中循环迭代的重做
  10. 快速运行python虚拟环境_快速入门Python 最新最流行的pipenv虚拟环境