linux内核唤醒源,linux内核驱动休眠和唤醒机制(select系统调用的内核驱动poll实现)...

https://blog.csdn.net/yikai2009/article/details/8653842

阻塞与非阻塞的概念:

阻塞IO: 当数据不可读或不可写,进程休眠,直到得到数据可读或可写时才返回。阻塞效率高,实时性比较好。

非阻塞IO:不管数据是否可读可写,都马上返回。

应用程序是否能实现阻塞或非阻塞是取决于驱动程序。实际驱动中应该把阻塞和非阻塞这种选择权交给应用程序来选择。要实现这个效果 ,就必须让驱动程序知道应用程序的选择。这个信息是通过 file 结构来传递的。

struct file 结构中有成员:

unsigned int         f_flags;

存放的就是 open(path,flag);

中的flag参数。

驱动中每个接口函数都有一个file指针,通过这个参数就能取得应用程序打开方式(阻塞或非阻塞)

等待队列

内核对这种阻塞提供等待队列机制来实现,这样可以改善实时性问题。

等待队列头数据结构

内核使用这个结构来给进程一个休眠的地方。

定义如下:Wait.h (include\linux)

struct __wait_queue_head {

spinlock_t lock;

struct list_head task_list;

};

typedef struct __wait_queue_head wait_queue_head_t;

1.等待队列的睡眠

wait_event(wq, condition) ;建立不可以杀进程(信号不能唤醒,效果和msleep相同)。

wait_event_interruptible(wq, condition)    ;它可以被被信号唤醒。休眠过程中,进程可以接收信号,收到后不管条件如何,直接返回。

wait_event_timeout(wq, condition, timeout)    ;休眠期间效果和 wait_event ,但是有一个超时时间 ,时间到,不管条件如何,直接返回。

wait_event_interruptible_timeout(wq, condition, timeout);休眠期间效果和 wait_event_interruptible相同,区别是有超时功能。时间 到,不管条件如何,直接返回。

wq 是变量

2.等待队列的唤醒

通过调用 wake_up* 函数(表面上是宏)唤醒进程

wake_up(wq)  能用于唤醒各种方式进入休眠的进程,只唤醒队列上的一个进程。

wake_up_all(wq)效果和wake_up相同,只是能唤醒队列上所有的进程。

wake_up_interruptible(wq)   只能用于唤醒一个 使用wait_event_interruptible*休眠的进程。

wake_up_interruptible_all(wq)能唤醒队列所有 使用wait_event_interruptible*休眠的进程。

wq 是指针。

以上函数并不是说一调用,就一定可以把进程唤醒,其实它会先去检测进程休眠的条件,是否变成真了,如果为真才把它唤醒。

3. 等待队列 API

1)定义一个等待不队列头变量 ,并且 初始化。有两种方法:

方法1:动态定义:

wait_queue_head_t  wq;   //全局变量

在模块初始化函数中调用:

init_waitqueue_head(&wq);   //安装模块时候执行了初始化

方法2:静态初始化

DECLARE_WAIT_QUEUE_HEAD(wq)   ;    //这句等效于上面两句。

DECLARE_WAIT_QUEUE_HEAD(name) 表示定义一个 名字为name 的等待队列头变量,并且进行初始化。

2)在需要休眠的地方调用 wait_event*类宏让进程休眠

示例:

在按键驱动代码基础上修改read函数的休眠代码

/* 按键驱动read接口 */

static int buttons_read(struct file *filp,

char __user *buff,

size_t count,

loff_t *offp)

{

unsigned long err;

/* count ==0 ,则返回0,不是错误 */

if(!count) {

return 0;

}

/* 没有按键动作 */

if ( press==0 ) {

if ( filp->f_flags & O_NONBLOCK) {

return -1;

}

else { /* 用户以阻塞方式打开 */

wait_event_interruptible(wq, press);

}

}

/*清按键动作标记*/

press = 0;

/* 修正大小 */

count = min(sizeof(key_values), count);

/* 复制按键缓冲数据给用户空间 */

err = copy_to_user((void __user *)buff, (const void *)(&key_values), count);

if(err) {

return -EFAULT;

}

return count;

}

3)在需要唤醒的地方(一般是要中断程序中)把 休眠条件设置真,然后调用 wake_up* 类宏唤醒进程。

接上一步修改 中断服务程序:

在原来的 press = 1 ;

后面添加  wake_up_interruptible 代码,如下。

press = 1;  //设置按键动作标志位

// wake_up(&wq) ; //唤醒进程

wake_up_interruptible(&wq) ; //唤醒进程

现在要解决的问题是给应用程序提供一个接口,告诉它是否有按键动作发生了。这个接口就是驱动 中文件操作结构中的 poll 接口。

linux 驱动 poll 接口---------- 这个接口是用来告诉应用程序本设备是否可以读,或是否可写。

unsigned int    xxxx_poll(  struct file *file,struct poll_table_struct *wait)

poll 接口模板 :

1) 定义一个等待队列头,并且 初始化

2)按照以下的代码模板实现poll接口。

//静态定义名字是button_waitq的等待队列头, 并且对它进行初始化

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 按键驱动poll接口 */

static unsigned int buttons_poll( struct file *file,struct poll_table_struct *wait)

{

unsigned int mask = 0;

poll_wait(file, &button_waitq, wait); //把等待队列加到查询表中,固定方式

//press 非0表示有按键动作

if (press)

mask |= POLLIN | POLLRDNORM;

//如存在一个变量wr_flag表示设备可以写,则是mask |= POLLOUT| POLLWRNORM

/* if(wr_flag)

mask |= POLLOUT| POLLWRNORM;

*/

return mask;

}

poll 的系统调用接口 select()函数

参考:

标签:队列,休眠,内核,wq,linux,驱动,poll,唤醒,wait

来源: https://blog.csdn.net/u010299133/article/details/100065869

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值