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;
}