linux 字符驱动阻塞型 等待队列
2019獨角獸企業重金招聘Python工程師標準>>>
內核等待隊列
等待隊列
在linux驅動程序設計中,可以使用等待隊列來實現進程的阻塞,等待隊列可看作保存進程的容器,在阻塞進程時,將進程放入等待隊列,當喚醒進程時,從等待隊列中取出進程。
linux2.6內核提供了如下關于等待隊列的操作:
1.定義等待隊列
wait_queue_head_t??my_queue
2.初始化等待隊列
init_waitqueue_head(&my_queue)
3.定義并初始化等待隊列(直接代替第1、2兩個操作)
DECLARE_WAIT_QUEUE_HEAD(my_queue)
4.有條件睡眠
——wait_event(queue,?condition)
當condition(一個布爾表達式)為真時,立即返回;否則讓進程進入TASK_UNINTERRUPTIBLE模式的睡眠,并掛載queue參數所指定的等待隊列上
——wait_event_interruptible(queue,?condition)
當condition(一個布爾表達式)為真時,立即返回;否則讓進程進入TASK_INTERRUPTIBLE的睡眠,并掛載queue參數所指定的等待隊列上
——int?wait_event_killable(wait_queue_t?queue,?condition)
當condition(一個布爾表達式)為真時,立即返回;否則讓進程進入TASK_KILLABLE的睡眠,并掛載queue參數所指定的等待隊列上(TASK_UNINTERRUPTIBLE在中斷和給它發送信號的時候都不會被喚醒;TASK_KILLABLE也如此,不過在發送kill信號的時候能被喚醒)
5.無條件睡眠(老版本,不建議使用)
——sleep_on(wait_queue_head_t?*q)
讓進程進入不可中斷的睡眠,并把它放入等待隊列q
——interruptible_sleep_on(wait_queue_head_t?*q)
讓進程進入可中斷的睡眠,并把它放入等待隊列q
6.從等待隊列中喚醒進程
——wake_up(wait_queue?_t?*q)
從等待隊列q中喚醒狀態為TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有進程(喚醒:將進程從睡眠狀態改為TASK_RUNNING狀態,不過只是就緒狀態而非執行態)
——wake_up_interruptible(wait_queue_t?*q)
從等待隊列q中喚醒狀態為TASK_INTERRUPTIBLE的進程
阻塞型字符設備驅動
1、阻塞型字符設備驅動的功能?
????? 當一個設備無法立刻滿足用戶的讀寫請求時。例如:調用read時沒有數據可讀, 但以后可能會有;或者一個進程試圖向設備寫入數據,但是設備暫時沒有準備好接收數據。應用程序通常不關心這種問題,應用程序只是調用 read 或 write 并得到返回值。驅動程序應當(缺省地)阻塞進程,使它進入睡眠,直到請求可以得到滿足。?
2、阻塞方式?
????? 1)在阻塞型驅動程序中,Read實現方式如下:如果進程調用read,但設備沒有數據或數據不足,進程阻塞。當新數據到達后,喚醒被阻塞進程。?
????? 2)在阻塞型驅動程序中,Write實現方式如下:如果進程調用了write,但設備沒有足夠的空間供其寫入數據,進程阻塞。當設備中的數據被讀走后,緩沖區中空出部分空間,則喚醒進程。?
3、非阻塞方式?
????? 阻塞方式是文件讀寫操作的默認方式,但應用程序員可通過使用O_NONBLOCK標志來人為的設置讀寫操作為非阻塞方式(該標志定義在<linux/fcntl.h>中,在打開文件時指定)。?
????? 如果設置了O_NONBLOCK標志,read和write的行為是不同的。如果進程在沒有數據就緒時調用了read,或者在緩沖區沒有空間時調用了write,系統只是簡單地返回-EAGAIN,而不會阻塞進程。?
例子
在.c中添加
bool have_data = false;
在頭文件.h中添加 等待隊列
讀函數添加 檢查是否有數據 并添加到等待隊列
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)?
{?
? unsigned long p =? *ppos;?
? unsigned int count = size;?
? int ret = 0;?
? struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*/?
? /*判斷讀位置是否有效*/?
? if (p >= MEMDEV_SIZE)?
??? return 0;?
? if (count > MEMDEV_SIZE - p)?
??? count = MEMDEV_SIZE - p;?
?? ??
while (!have_data) /*?沒有數據可讀 ,考慮為什么不用if,而用while。因為wait_event_interruptible函數,除了收到數據會跳出外,中斷也會跳出,中斷喚醒等待隊列,如果使用if就會往下運行但此時并沒有數據可讀會出錯,故次用while和interruptible配合的原因?*/?
{
??????????/*判斷用戶是否設置了非阻塞方式*/?
??????? if (filp->f_flags & O_NONBLOCK)?
??????????? return -EAGAIN; /*設置了非阻塞方式*/?
??????? /*?當設置了阻塞方式*/?
?? ??? wait_event_interruptible(dev->inq,have_data);/**當have_data為真時,立即返回,否則讓進程進入TASK_KILL的睡眠 并掛在dev->inq隊列上*/
}?
? /*讀數據到用戶空間*/?
? if (copy_to_user(buf, (void*)(dev->data + p), count))?
? {?
??? ret =? - EFAULT;?
? }?
? else?
? {?
??? *ppos += count;?
??? ret = count;?
? ??
??? printk(KERN_INFO "read %d bytes(s) from %d\n", count, p);?
? }?
??
? have_data = false; /* 表明不再有數據可讀 */?
? return ret;?
}?
寫函數添加 有數據標志,并移出等待隊列
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)?
{?
? unsigned long p =? *ppos;?
? unsigned int count = size;?
? int ret = 0;?
? struct mem_dev *dev = filp->private_data; /*獲得設備結構體指針*/?
??
? /*分析和獲取有效的寫長度*/?
? if (p >= MEMDEV_SIZE)?
??? return 0;?
? if (count > MEMDEV_SIZE - p)?
??? count = MEMDEV_SIZE - p;?
?? ??
? /*從用戶空間寫數據*/?
? if (copy_from_user(dev->data + p, buf, count))?
??? ret =? - EFAULT;?
? else?
? {?
??? *ppos += count;?
??? ret = count;?
?? ??
??? printk(KERN_INFO "written %d bytes(s) from %d\n", count, p);?
? }?
??
? have_data = true; /* 有新的數據可讀 */?
?? ??
??? /* 喚醒讀進程*/?
??? wake_up(&(dev->inq));?
? return ret;?
}?
添加等待隊列初始化函數
static int memdev_init(void)?
{?
? int result;?
? int i;?
? dev_t devno = MKDEV(mem_major, 0);?
? /* 靜態申請設備號*/?
? if (mem_major)?
??? result = register_chrdev_region(devno, 2, "memdev");?
? else? /* 動態分配設備號 */?
? {?
??? result = alloc_chrdev_region(&devno, 0, 2, "memdev");?
??? mem_major = MAJOR(devno);?
? } ??
??
? if (result < 0)?
??? return result;?
? /*初始化cdev結構*/?
? cdev_init(&cdev, &mem_fops);?
? cdev.owner = THIS_MODULE;?
? cdev.ops = &mem_fops;?
??
? /* 注冊字符設備*/?
? cdev_add(&cdev, MKDEV(mem_major, 0), MEMDEV_NR_DEVS);?
? ??
? /* 為設備描述結構分配內存*/?
? mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev), GFP_KERNEL);?
? if (!mem_devp)??? /*申請失敗*/?
? {?
??? result =? - ENOMEM;?
??? goto fail_malloc;?
? }?
? memset(mem_devp, 0, sizeof(struct mem_dev));?
??
? /*為設備分配內存*/?
? for (i=0; i < MEMDEV_NR_DEVS; i++)?
? {?
??????? mem_devp[i].size = MEMDEV_SIZE;?
??????? mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);?
??????? memset(mem_devp[i].data, 0, MEMDEV_SIZE);?
??
? ?? ??/*初始化等待隊列*/?
??? ?init_waitqueue_head(&(mem_devp[i].inq));?
? }?
? ??
? return 0;?
? fail_malloc:?
? unregister_chrdev_region(devno, 1);?
??
? return result;?
}?
測試函數
寫函數
轉載于:https://my.oschina.net/u/274829/blog/285758
總結
以上是生活随笔為你收集整理的linux 字符驱动阻塞型 等待队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【自己动手写神经网络】小白入门连载(二)
- 下一篇: [转]CDN(内容分发网络)技术原理