一、Linux阻塞IO
为什么要引入阻塞IO。因为在我们原来的实验应用app的编写测试中,发现我们使用的read,write等函数在while循环中使用。当运行app程序的时候发现,在终端中查看进程的状态,你会发现app程序的进程会塞满CPU,确实是这样,read,write会轮询查看管道符中的资源,或者放弃资源,这样子很不好。但是阻塞就不一样,阻塞当发现管道符中的没有数据可读写,就会将应用程序挂起。
1、阻塞IO的使用
为了实现阻塞IO,Linux内核机制提供等待队列实现阻塞的唤醒机制。
①定义等待队列头
等待队列头的成员可以看出来,是基于自旋锁与任务链表实现的。
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
②初始化等待头队列
使用初始化函数,或者也可以使用宏条件一次性完成定义及初始化
void init_waitqueue_head(wait_queue_head_t *q)
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
.task_list = { &(name).task_list, &(name).task_list } }
③定义等待队列项
每一个等待任务都会添加到这个队列中。因此要定义等待队列项的结构体。
struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
④初始化等待队列项
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } }
⑤添加到/移除队列中
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
⑥唤醒
唤醒队列的任务
void wake_up(wait_queue_head_t *q);
void wake_up_interruptible(wait_queue_head_t *q);
二、非阻塞IO
在open函数打开设备结点的时候,第二个参数中可以使用O_NONBLOCK参数对其进行说明,就会设置为非阻塞来轮询监控设备驱动文件。
应用程序使用非阻塞处理,可以采用select , poll ,epoll来处理轮询。
1.驱动中的poll
驱动中fops中提供poll接口与应用中的poll轮询机制进行对接。filp是对接的设备结点,wait是应用进程传进来的pollfd结构体,将该参数传给应用程序的poll_wait。
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait);
\