赞
踩
目录
3.2.1 wait_event_interruptible()函数解析
当应用程序发起读取数据(read)的时候,如果数据没有准备好,就会阻塞等待(进程休眠),如果与硬件的数据准备好了,就会产生硬件中断,在中断处理函数中唤醒休眠的进程,然后将准备好的数据拷贝至用户空间。
应用程序默认是以阻塞方式打开,所以不需要特别设置
open("/dev/mycdev",O_RDWR|O_NONBLOCK);
1、定义等待队列头
2、初始化等待队列头
3、如果数据没有准备好,就进行休眠
4、如果数据准备好了,就唤醒
- //1、定义等待队列头
- wait_queue_head_t wq;
-
- struct __wait_queue_head {
- spinlock_t lock;
- struct list_head task_list;
- };
- //2、初始化等待队列头
- init_waitqueue_head(&wq);
- //3.1、休眠操作,一个函数实现
- //如果数据没有准备好,就进行休眠
- wait_event(wq, condition); //进入不可中断的休眠态
- wait_event_interruptible(wq, condition) // 让进程进入可中断的休眠态
- //wq: 等待队列头
- //condition: 代表数据是否准备好,如果为真,代表数据准备好了,不需要休眠
- // 如果为假,代表数据没有准备好,进程需要休眠
- //3.2分步实现等待队列
- //等待队列项操作函数
- //定义并初始化一个等待队列项
- DECLARE_WAITQUEUE(name, tsk)
- //name:就是等待队列项的名字,
- //tsk:表示这个等待队列项属于哪个任务(进程),一般设置为current,
- //在Linux内核中 current相当于一个全局变量,表示当前进程。
- //因此宏DECLARE_WAITQUEUE就是给当前正在运行的进程创建并初始化了一个等待队列项。
-
- //等待队列项添加
- void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
- //q:等待队列项要加入的等待队列头。
- //wait:要加入的等待队列项。
-
- //等待队列项移除
- void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
- //q:要删除的等待队列项所处的等待队列头。
- //wait:要删除的等待队列项。
-
- struct __wait_queue {
- unsigned int flags;
- void *private;
- wait_queue_func_t func;
- struct list_head task_list;
- };
- //4、如果数据准备好了,就唤醒
- condition = 1; //设置为真,代表数据准备好了
- void wake_up(wait_queue_head_t *q)
- void wake_up_interruptible(wait_queue_head_t *q)
这个函数做了上述自己定义等待队列项,添加等待队列项的操作,所以稍微解析一下
- wait_event_interruptible(wq, condition)
- ->
- __wait_event_interruptible(wq, condition);
- ->
- ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, schedule())
- {
- //定义了一个等待队列项
- wait_queue_t __wait;
-
- //初始化了一个等待队列项
- init_wait_entry(&__wait, exclusive ? WQ_FLAG_EXCLUSIVE : 0);
-
- for (;;) {
- //将等待队列项放在队列头的队列尾
- //将task_struct->state = TASK_INTERRUPTIBLE
- prepare_to_wait_event(&wq, &__wait, state);
-
- //等待数据到来,退出循环
- if (condition)
- break;
- //传入的cmd为schedule(),即主动放弃CPU
- cmd;
- }
- //删除等待队列项
- //将进程状态设置为运行态
- finish_wait(&wq, &__wait);
- }
-
- typedef struct __wait_queue wait_queue_t;
-
- void init_wait_entry(wait_queue_t *wait, int flags)
- {
- wait->flags = flags;
- wait->private = current; //current是当前进程的结构体
- wait->func = autoremove_wake_function; //唤醒的函数
- INIT_LIST_HEAD(&wait->task_list); //初始化队列的指针
- }
-
- long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)
- {
- unsigned long flags;
- long ret = 0;
-
- spin_lock_irqsave(&q->lock, flags);
- //判断是否为信号引起的唤醒
- if (unlikely(signal_pending_state(state, current))) {
- list_del_init(&wait->task_list);
- ret = -ERESTARTSYS;
- } else {
- if (list_empty(&wait->task_list)) {
- if (wait->flags & WQ_FLAG_EXCLUSIVE)
- __add_wait_queue_tail(q, wait);
- else
- //将等待队列项加入等待队列
- __add_wait_queue(q, wait);
- }
- //设置传入状态TASK_INTERRUPTIBLE
- set_current_state(state);
- }
- spin_unlock_irqrestore(&q->lock, flags);
-
- return ret;
- }
-
- void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
- {
- unsigned long flags;
-
- //将进程状态设置为运行态
- __set_current_state(TASK_RUNNING);
-
- if (!list_empty_careful(&wait->task_list)) {
- spin_lock_irqsave(&q->lock, flags);
- //删除等待队列项
- list_del_init(&wait->task_list);
- spin_unlock_irqrestore(&q->lock, flags);
- }
- }
一般来说,都是在中断函数中去唤醒,但是写个中断又有点麻烦,所以这里采用的是另起一个进程,在write函数中去唤醒进程。
- //定义等待队列头
- wait_queue_head_t wq;
- int condition = 0;
-
- ssize_t mycdev_read(struct file *file, char __user * ubuf, size_t size, loff_t * offs)
- {
- ret = wait_event_interruptible(wq, condition);
-
- ret = copy_to_user(ubuf, kbuf, size);
-
- //将condition设置为假
- condition = 0;
-
- return size;
- }
-
- ssize_t mycdev_write(struct file *file, const char __user * ubuf, size_t size, loff_t * offs)
- {
- ret = copy_from_user(kbuf, ubuf, size);
-
- //唤醒
- condition = 1;
- wake_up_interruptible(&wq);
-
- return size;
- }
-
- const struct file_operations fops = {
- .read = mycdev_read,
- .write = mycdev_write,
- };
-
- static int __init mycdev_init(void)
- {
- //初始化等待队列头
- init_waitqueue_head(&wq);
- }
- int flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); /* 阻塞方式 */
1、阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。
2、同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO
3、非阻塞IO,它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。
4、阻塞IO模型(进程在内核状态下等待)使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。