自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux驅(qū)動(dòng)技術(shù)(五) _設(shè)備阻塞/非阻塞讀寫

系統(tǒng) Linux 系統(tǒng)運(yùn)維
等待隊(duì)列是內(nèi)核中實(shí)現(xiàn)進(jìn)程調(diào)度的一個(gè)十分重要的數(shù)據(jù)結(jié)構(gòu),其任務(wù)是維護(hù)一個(gè)鏈表,鏈表中每一個(gè)節(jié)點(diǎn)都是一個(gè)PCB(進(jìn)程控制塊),內(nèi)核會將PCB掛在等待隊(duì)列中的所有進(jìn)程都調(diào)度為睡眠狀態(tài),直到某個(gè)喚醒的條件發(fā)生。應(yīng)用層的阻塞IO與非阻塞IO的使用我已經(jīng)在Linux I/O多路復(fù)用一文中討論過了,本文主要討論驅(qū)動(dòng)中怎么實(shí)現(xiàn)對設(shè)備IO的阻塞與非阻塞讀寫。顯然,實(shí)現(xiàn)這種與阻塞相關(guān)的機(jī)制要用到等待隊(duì)列機(jī)制。

[[184472]]

等待隊(duì)列是內(nèi)核中實(shí)現(xiàn)進(jìn)程調(diào)度的一個(gè)十分重要的數(shù)據(jù)結(jié)構(gòu),其任務(wù)是維護(hù)一個(gè)鏈表,鏈表中每一個(gè)節(jié)點(diǎn)都是一個(gè)PCB(進(jìn)程控制塊),內(nèi)核會將PCB掛在等待隊(duì)列中的所有進(jìn)程都調(diào)度為睡眠狀態(tài),直到某個(gè)喚醒的條件發(fā)生。應(yīng)用層的阻塞IO與非阻塞IO的使用我已經(jīng)在Linux I/O多路復(fù)用一文中討論過了,本文主要討論驅(qū)動(dòng)中怎么實(shí)現(xiàn)對設(shè)備IO的阻塞與非阻塞讀寫。顯然,實(shí)現(xiàn)這種與阻塞相關(guān)的機(jī)制要用到等待隊(duì)列機(jī)制。本文的內(nèi)核源碼使用的是3.14.0版本

設(shè)備阻塞IO的實(shí)現(xiàn)

當(dāng)我們讀寫設(shè)備文件的IO時(shí),最終會回調(diào)驅(qū)動(dòng)中相應(yīng)的接口,而這些接口也會出現(xiàn)在讀寫設(shè)備進(jìn)程的進(jìn)程(內(nèi)核)空間中,如果條件不滿足,接口函數(shù)使進(jìn)程進(jìn)入睡眠狀態(tài),即使讀寫設(shè)備的用戶進(jìn)程進(jìn)入了睡眠,也就是我們常說的發(fā)生了阻塞。In a word,讀寫設(shè)備文件阻塞的本質(zhì)是驅(qū)動(dòng)在驅(qū)動(dòng)中實(shí)現(xiàn)對設(shè)備文件的阻塞,其讀寫的流程可概括如下:

1. 定義-初始化等待隊(duì)列頭

  1. //定義等待隊(duì)列頭 
  2.  
  3. wait_queue_head_t waitq_h;//初始化,等待隊(duì)列頭 
  4.  
  5. init_waitqueue_head(wait_queue_head_t *q); //或//定義并初始化等待隊(duì)列頭 
  6.  
  7. DECLARE_WAIT_QUEUE_HEAD(waitq_name); 

上面的幾條選擇中,***一種會直接定義并初始化一個(gè)等待頭,但是如果在模塊內(nèi)使用全局變量傳參,用著并不方便,具體用哪種看需求。

我們可以追一下源碼,看一下上面這幾行都干了什么:

  1. //include/linux/wait.h  
  2.   struct __wait_queue_head {  
  3.           spinlock_t              lock;  
  4.           struct list_head        task_list;  
  5.   }; 
  6.   typedef struct __wait_queue_head wait_queue_head_t; 

 

wait_queue_head_t

--36-->這個(gè)隊(duì)列用的自旋鎖

--27-->將整個(gè)隊(duì)列"串"在一起的紐帶

然后我們看一下初始化的宏:

  1. #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                     
  2.        .lock        = __SPIN_LOCK_UNLOCKED(name.lock),       
  3.         .task_list   = { &(name).task_list, &(name).task_list } } 
  4.  
  5. define DECLARE_WAIT_QUEUE_HEAD(name) \ 
  6.     wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name

 

DECLARE_WAIT_QUEUE_HEAD()

--60-->根據(jù)傳入的字符串name,創(chuàng)建一個(gè)名為name的等待隊(duì)列頭

--57-->初始化上述task_list域,竟然沒有用內(nèi)核標(biāo)準(zhǔn)的初始化宏,無語。。。

2. 將本進(jìn)程添加到等待隊(duì)列

為等待隊(duì)列添加事件,即進(jìn)程進(jìn)入睡眠狀態(tài)直到condition為真才返回。**_interruptible的版本版本表示睡眠可中斷,_timeout**版本表示超時(shí)版本,超時(shí)就會返回,這種命名規(guī)范在內(nèi)核API中隨處可見。

  1. void wait_event(wait_queue_head_t *waitq_h,int condition); 
  2. void wait_event_interruptible(wait_queue_head_t *waitq_h,int condition); 
  3. void wait_event_timeout(wait_queue_head_t *waitq_h,int condition); 
  4. void wait_event_interruptible_timeout(wait_queue_head_t *waitq_h,int condition); 

這可是等待隊(duì)列的核心,我們來看一下

wait_event

└── wait_event

└── _wait_event

├── abort_exclusive_wait

├── finish_wait

├── prepare_to_wait_event

└── ___wait_is_interruptible

  1.  #define wait_event(wq, condition)                                
  2.  do {                                                             
  3.          if (condition)                                           
  4.                 break;                                           
  5.          __wait_event(wq, condition);                              
  6.  } while (0) 

 

wait_event

--246-->如果condition為真,立即返回

--248-->否則調(diào)用__wait_event

 

  1. #define ___wait_event(wq,condition,state, exclusive, ret, cmd) \  
  2.  ({                                                            \ 
  3.     for (;;) {                                                 \ 
  4.        long __int = prepare_to_wait_event(&wq, &__wait, state);\ 
  5.                                                                \  
  6.        if (condition)                                          \        
  7.      break;                                              \ 
  8.        if (___wait_is_interruptible(state) && __int) {         \ 
  9.            __ret = __int;                                      \ 
  10.        if (exclusive) {                                        \ 
  11.            abort_exclusive_wait(&wq, &__wait,                  \ 
  12.            state, NULL);                                       \ 
  13.            goto __out;                                         \ 
  14.        }                                                       \ 
  15.        break;                                                  \ 
  16.     }                                                          \ 
  17.     cmd;                                                       \ 
  18.   }                                                            \ 
  19.   finish_wait(&wq, &__wait);                                   \ 
  20.   __out:  __ret;                                               \ 
  21.  }) 

 

--206-->死循環(huán)的輪詢

--209-->如果條件為真,跳出循環(huán),執(zhí)行finish_wait();進(jìn)程被喚醒

--212-->如果進(jìn)程睡眠的方式是interruptible的,那么當(dāng)中斷來的時(shí)候也會abort_exclusive_wait被喚醒

--222-->如果上面兩條都不滿足,就會回調(diào)傳入的schedule(),即繼續(xù)睡眠

模板

  1. struct wait_queue_head_t xj_waitq_h; 
  2. static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) 
  3. {    if(!condition)    //條件可以在中斷處理函數(shù)中置位 
  4.         wait_event_interruptible(&xj_waitq_h,condition); 
  5. static file_operations fops = { 
  6.     .read = demo_read, 
  7. }; 
  8. static __init demo_init(void){ 
  9.     init_waitqueue_head(&xj_waitq_h); 

 

IO多路復(fù)用的實(shí)現(xiàn)

對于普通的非阻塞IO,我們只需要在驅(qū)動(dòng)中注冊的read/write接口時(shí)不使用阻塞機(jī)制即可,這里我要討論的是IO多路復(fù)用,即當(dāng)驅(qū)動(dòng)中的read/write并沒有實(shí)現(xiàn)阻塞機(jī)制的時(shí)候,我們?nèi)绾卫脙?nèi)核機(jī)制來在驅(qū)動(dòng)中實(shí)現(xiàn)對IO多路復(fù)用的支持。下面這個(gè)就是我們要用的API

  1. int poll(struct file *filep, poll_table *wait);void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)   

當(dāng)應(yīng)用層調(diào)用select/poll/epoll機(jī)制的時(shí)候,內(nèi)核其實(shí)會遍歷回調(diào)相關(guān)文件的驅(qū)動(dòng)中的poll接口,通過每一個(gè)驅(qū)動(dòng)的poll接口的返回值,來判斷該文件IO是否有相應(yīng)的事件發(fā)生,我們知道,這三種IO多路復(fù)用的機(jī)制的核心區(qū)別在于內(nèi)核中管理監(jiān)視文件的方式,分別是位,數(shù)組,鏈表,但對于每一個(gè)驅(qū)動(dòng),回調(diào)的接口都是poll。

模板

  1. struct wait_queue_head_t waitq_h;static unsigned int demo_poll(struct file *filp, struct poll_table_struct *pts){    unsigned int mask = 0; 
  2.     poll_wait(filp, &wwaitq_h, pts);    if(counter){ 
  3.         mask = (POLLIN | POLLRDNORM); 
  4.     }    return mask; 
  5. }static struct file_operations fops = { 
  6.     .owner  = THIS_MODULE, 
  7.     .poll   = demo_poll, 
  8. };static __init demo_init(void){ 
  9.     init_waitqueue_head(&xj_waitq_h); 

 

其他API

剛才我們討論了如何使用等待隊(duì)列實(shí)現(xiàn)阻塞IO,非阻塞IO,其實(shí)關(guān)于等待隊(duì)列,內(nèi)核還提供了很多其他API用以完成相關(guān)的操作,這里我們來認(rèn)識一下 

  1. //在等待隊(duì)列上睡眠
  2. sleep_on(wait_queue_head_t *wqueue_h); 
  3. sleep_on_interruptible(wait_queue_head_t *wqueue_h);//喚醒等待的進(jìn)程 
  4.  
  5. void wake_up(wait_queue_t *wqueue); 
  6. void wake_up_interruptible(wait_queue_t *wqueue); 

 

責(zé)任編輯:龐桂玉 來源: 嵌入式Linux中文站
相關(guān)推薦

2021-06-04 18:14:15

阻塞非阻塞tcp

2019-07-23 11:01:57

Python同步異步

2012-10-10 10:00:27

同步異步開發(fā)Java

2012-02-22 21:15:41

unixIO阻塞

2018-03-28 08:52:53

阻塞非阻塞I

2023-03-15 08:39:07

遠(yuǎn)程服務(wù)調(diào)用

2023-12-06 07:28:47

阻塞IO異步IO

2024-09-23 17:15:28

Python并發(fā)并行

2015-07-03 10:12:04

編程同步非阻塞

2011-12-07 17:17:02

JavaNIO

2020-05-08 10:34:30

Spring非阻塞編程

2024-06-19 10:26:36

非阻塞IO客戶端

2011-12-08 10:12:34

JavaNIO

2021-02-04 10:50:11

網(wǎng)絡(luò)安全非阻塞模Winsock編程

2021-02-27 16:08:17

Java異步非阻塞

2025-02-17 13:23:34

Python同步阻塞MySQL

2018-01-11 08:24:45

服務(wù)器模型詳解

2022-06-22 08:16:29

異步非阻塞框架

2023-05-05 09:48:14

LinuxIO模型

2023-12-13 09:45:49

模型程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號