14.LINUX驱动之阻塞IO

350 阅读2分钟

一、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);

\