Linux阻塞和非阻塞IO

文章目录

  • 前言
  • 一、什么是阻塞、非阻塞?
  • 二、阻塞等待队列
    • 1.等待队列头
    • 2.等待队列项
    • 3.将队列项添加/移除等待队列头
    • 4.唤醒等待
    • 5.等待事件
  • 三、非阻塞轮询
    • 1.select
    • 2.poll
    • 3.epoll
  • 四、Linux驱动下的poll
  • 总结


前言

阻塞和非阻塞IO是linux驱动开发中很常见的设备访问模式,编写驱动时一定要考虑到。这里的IO并不是指GPIO,而是Input/Output,也就是输入输出。


一、什么是阻塞、非阻塞?

阻塞:应用程序调用 read 函数从设备中读取数据,当设备不可用或数据未准备好的时候就会进入到休眠态。等设备可用的时候就会从休眠态唤醒,然后从设备中读取数据返回给应用程序。
非阻塞:应用程序使用非阻塞访问方式从设备读取数据,当设备不可用或数据未准备好的时候会立即向应用程序返回一个错误码,表示数据读取失败。应用程序会再次重新读取数据,这样一直往复循环,直到数据读取成功。

二、阻塞等待队列

阻塞访问最大的好处就是设备不可操作时进入休眠,可以把cpu资源让出来;可操作时就必须唤醒。
一般在中断函数中完成唤醒工作,Linux提供了等待队列来实现阻塞的唤醒,必须创建并初始化一个等待队列头。
Linux阻塞和非阻塞IO_第1张图片

1.等待队列头

结构体定义内核源码目录 include/linux/wait.h :

struct wait_queue_head {
   
	spinlock_t lock;
	struct list_head head;
};
typedef struct wait_queue_head wait_queue_head_t;

wait_queue_head_t  my_queue;

使用 init_waitqueue_head 初始化等待队列头,是个宏定义

#define init_waitqueue_head(wq_head) \
do {
      \
	static struct lock_class_key __key; \
	\
	__init_waitqueue_head((wq_head), #wq_head, &__key); \
} while (0)

init_waitqueue_head(&my_queue);

也可以使用宏 DECLARE_WAIT_QUEUE_HEAD 来一次性完成等待队列头的定义和初始化

2.等待队列项

每个访问设备的进程都是一个队列项,当设备不可用的时候就要添加进等待队列里
结构体 wait_queue_entry_t 表示等待队列项。

typedef struct wait_queue_entry wait_queue_entry_t;

struct wait_queue_entry {
   
	unsigned int flags;
	void *private;
	wait_queue_func_tfunc;
	struct list_head entry;
};

使用宏 DECLARE_WAITQUEUE 定义并初始化一个等待队列项,宏的内容如下:

#define DECLARE_WAITQUEUE(name, tsk) \
struct wait_queue_entry name = __WAITQUEUE_INITIALIZER(name, tsk)

name 就是等待队列项的名字, tsk 表示这个等待队列项属于哪个任务(进程),一般设置为current , 在 Linux 内 核 中 current 相 当 于 一 个 全 局 变 量 , 表 示 当 前 进 程 。 因 此 宏DECLARE_WAITQUEUE 就是给当前正在运行的进程创建并初始化了一个等待队列项。

3.将队列项添加/移除等待队列头

设备不可访问时就添加,可访问就移除;只有添加进等待队列头以后进程才能进入休眠态:

void add_wa

你可能感兴趣的:(linux,arm开发,c语言,嵌入式硬件,物联网)