Linux驱动开发-11、设备阻塞访问-等待队列

Linux设备阻塞访问-等待队列

1、概念

a) 阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再执行

b) 被挂起的进程进入休眠状态,从调度器的运行队列移走,直到条件等待的条件被满足时进程再度被唤醒

2、需要阻塞访问的应用

a) 当应用程序使用read()或者write()等系统调用时,若设备的资源无法获取,而用户又希望等待到设备有数据自行读取或写入(阻塞方式访问数据)后才返回

b) 优点:阻塞时进程会进入休眠状态,将CPU使用权交个其他进程

 

3、阻塞访问技术-等待队列

a) 基础数据结构-队列

b) 与调度机制紧密结合

c) 能够实现内核中的异步事件通知机制

d) 等待队列可用来同步对系统资源的访问

 

 

4、等待队列基本操作

a) 定义等待队列头

wait_queue_head_t  xxx_q;

 

b) 初始化等待队列头

init_waitqueue_head(&xxx_q);

 

快捷操作:DECLARE_WAIT_QUEUE_HEAD(xxx_q):定义并初始化一个名为xxx_q的等待队列

c) 添加等待队列——将等待队列wait添加到等待队列头q指向的等待队列链表中

  void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

 

d) 删除等待队列——将等待队列wait从等待队列头q指向的等待队列链表中删除

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

 

e) 等待事件,condition 为布尔类型

wait_event(wq, condition);——进程睡眠等待,直到条件为真;不能被信号打断,既进 程进入TASK_UNINTERRUPTIBLE状态

 

wait_event_timeout(wq, condition, timeout);——等待条件为真既返回,超时也返回

 

wait_event_interruptible(wq, condition)——可被信号打断;既进程进入

TAKS_INTERRUPTIBLE状态

 

 

f) 唤醒队列

wake_up(wait_queue_t *q)---从等待队列q中唤醒状态为:

TASK_UNINTERRUPTIBLE
TASK_INTERRUPTIBLE

TASK_KILLABLE 的所有进程

 

wake_up_interruptible(wait_queue_t *q)--从等待队列q中唤醒

状态为TASK_INTERRUPTIBLE 的进程

 

 

5、程序设计流程

a) 定义等待队列头

b) 初始化等待队列

c) 在设备操作中进入等待队列,让进程睡眠,直到唤醒

d) 在某个条件成立时唤醒等待队列

 

按键驱动程序阻塞访问设计

/******************************************

author : hntea

date: 2016/3/16

function :

1.检测按键中断

2.在中断底半部交个tasklet处理

3.增加设备阻塞访问,当按键按下时唤醒等待队列

******************************************/

#include "key_hard.h"

 

/******************************************

定时器操作

******************************************/

struct timer_list key_timer;

static void key_do_timer(unsigned long data)

{

printk("I am key1_task -------runing timer work\n");

}

 

/******************************************

函数名: key_open

函数功能:文件操作

******************************************/

int  key_open(struct inode *inode, struct file *filp)

{

key_hardinit();

return 0;

}

/******************************************

函数名: key_read

函数功能:阻塞访问测试

******************************************/

wait_queue_head_t key_queue;

static int key_val = 0;

ssize_t key_read(struct file *filp, char __user *buf, size_t count, loff_t *lp)

{

int ret = 0;

/*等待直到有数据才读取数据*/

wait_event(key_queue,key_val);

/*将数据传递到用户空间*/

ret = copy_to_user(buf,&key_val,4);

key_val = 0; /*条件要记得清楚,要不下次读取就没有阻塞效果啦*/

return ret;

}

 

/******************************************

函数名:

函数功能:文件操作

******************************************/

struct file_operations key_fop =

{

.read = key_read,

.open = key_open,

};

 

struct miscdevice key=

{

.minor = DEV_MINIR ,

.name = "Key",

.fops = &key_fop,

};

 

/******************************************

中断底半部用任务实现

******************************************/

/*定义 task */

struct tasklet_struct key1_task;

struct tasklet_struct key2_task;

 

void key1_taskfun(unsigned long x)

{

int ret = 0;

/*3初始化定时器*/

init_timer(&key_timer);

key_timer.function = key_do_timer;

add_timer(&key_timer);

printk("I am key1_task ------- My argument is:%ld\r\n",x);

ret = mod_timer(&key_timer,  jiffies + HZ); /*1S后执行超时函数*/

}

void key2_taskfun(unsigned long x)

{

printk("I am key2_task ------- My argument is:%ld\r\n",x);

printk("I am key2_task ------- I try to wake up key_queue\n");

key_val = 4;

printk("Kernel->key_val is: %d\r\n",key_val);

wake_up(&key_queue);

}

/*关联结构体和服务函数*/

DECLARE_TASKLET(key1_task,key1_taskfun,1);

DECLARE_TASKLET(key2_task,key2_taskfun,2);

 

/******************************************

函数功能:中断服务函数入口

******************************************/

static irqreturn_t key1_interrupt (int irq, void *dev_id)

{

  /*1、顶部,硬件操作*/

  

  /*2、底部,交个工作队列来处理*/

    tasklet_schedule(&key1_task);

  return 0;

}

static irqreturn_t key2_interrupt (int irq, void *dev_id)

{

  /*1、顶部,硬件操作*/

  

  /*2、底部,交个工作队列来处理*/

    tasklet_schedule(&key2_task);

  return 0;

}

 

 

/**********************************

函数名:

函数功能:初始化函数

**********************************/

static int keyInit(void)

{

int ret = 0;

/*1、注册设备文件*/

ret = misc_register(&key);

/*2、注册中断号*/

ret = request_irq(IRQ_EINT0,key1_interrupt,IRQF_TRIGGER_FALLING, "key1", 0);

printk("ret val is:%d\n",ret);

ret = request_irq(IRQ_EINT1,key2_interrupt,IRQF_TRIGGER_FALLING, "key2", 0);

printk("ret val is:%d\n",ret);

/*3、初始化等待队列*/

init_waitqueue_head(&key_queue);

return 0;

}

static void keyExit(void)

{

int ret = 0;

/*注销中断*/

free_irq(IRQ_EINT0, 0);

free_irq(IRQ_EINT1,0);

/*注销混杂设备*/

ret = misc_deregister(&key);

/*注销设备*/

}

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Hntea");

 

module_init(keyInit);

module_exit(keyExit);

 

驱动测试

#include<stdio.h>

#include<fcntl.h>

#include<unistd.h>

#define PATH  "/dev/Key"

int main(void)

{

int ret = 0;

int fd = 0;

int key_val=0;

if( (fd = open(PATH,O_RDWR)) < 0 )

printf("UserSpace ----> No such file!\n");

ret = read(fd,&key_val,4);

 

printf("\r\nUserSpace ---->key_val  is: %d\n",key_val);

close(fd);

return ret;

}

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要让字符设备驱动支持阻塞与非阻塞IO模型,需要在驱动程序中使用select/poll机制,同时使用文件操作的O_NONBLOCK标志。下面以read操作为例,简要说明如下: 1. 在设备驱动程序中,使用`file_operations`结构体中的`read`函数实现读操作,同时在该函数中添加对阻塞和非阻塞IO模型的支持。 2. 对于阻塞IO模型,可以直接在`read`函数中调用`wait_event_interruptible`函数使进程进入睡眠状态,等待数据就绪后再唤醒进程。当然在等待数据就绪的过程中,如果进程接收到了信号,则需要立即返回`-ERESTARTSYS`。 3. 对于非阻塞IO模型,需要在`read`函数中使用`O_NONBLOCK`标志进行判断。如果该标志被设置,则可以直接调用`poll_wait`函数等待数据就绪,如果数据没有准备好,则直接返回`-EAGAIN`。 4. 在`poll`函数中,需要添加对于设备文件的监控,以便在数据就绪时通知进程。这可以通过在驱动程序中添加`poll`函数来实现。在该函数中,需要使用`poll_wait`函数将当前进程添加到等待队列中,并在数据就绪时唤醒进程。 下面是一个简单的代码示例: ```c static DECLARE_WAIT_QUEUE_HEAD(read_wait_queue); static unsigned int mydevice_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; struct mydevice_data *data = file->private_data; poll_wait(file, &read_wait_queue, wait); if (data->data_ready) { mask |= POLLIN | POLLRDNORM; } return mask; } static ssize_t mydevice_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct mydevice_data *data = file->private_data; ssize_t ret = 0; if (file->f_flags & O_NONBLOCK) { /* 非阻塞IO模型 */ if (!data->data_ready) { return -EAGAIN; } } else { /* 阻塞IO模型 */ wait_event_interruptible(read_wait_queue, data->data_ready); if (signal_pending(current)) { return -ERESTARTSYS; } } /* 读取数据 */ if (copy_to_user(buf, data->buffer, data->size)) { ret = -EFAULT; } else { ret = data->size; data->data_ready = 0; } return ret; } ``` 在上面的代码示例中,`mydevice_read`函数是`read`操作对应的函数,`mydevice_poll`函数是`poll`操作对应的函数。其中,`wait_event_interruptible`函数用于阻塞进程,等待数据就绪;`poll_wait`函数用于将进程添加到等待队列中,等待数据就绪时唤醒进程。同时,根据文件操作的O_NONBLOCK标志,判断当前使用的是阻塞IO模型还是非阻塞IO模型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值