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;

}

 

 

 

發佈了35 篇原創文章 · 獲贊 10 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章