色yeye在线视频观看_亚洲人亚洲精品成人网站_一级毛片免费播放_91精品一区二区中文字幕_一区二区三区日本视频_成人性生交大免费看

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > Linux中的阻塞機制

Linux中的阻塞機制 時間:2018-09-29      來源:未知

 我們知道在字符設備驅動中,應用層調(diào)用read、write等系統(tǒng)調(diào)用終會調(diào)到驅動中對應的接口。 可以當應用層調(diào)用read要去讀硬件的數(shù)據(jù)時,硬件的數(shù)據(jù)未準備好,那我們該怎么做?

一種辦法是直接返回并報錯,但是這樣應用層要獲得數(shù)據(jù)需要不斷的調(diào)用read去訪問硬件,進程的上下文在用戶空間和內(nèi)核空間不停的切換,耗費了CPU的資源,降低了系統(tǒng)效率。那么有沒有更好的辦法呢?  答案是有的,在這種情況下我們就可以利用Linux的阻塞機制,實現(xiàn)阻塞訪問。

一、阻塞和非阻塞

阻塞操作是指在執(zhí)行設備操作時若不能獲得資源則掛起進程,直到滿足可操作的條件后再進行操作。被掛起的進程進入休眠狀態(tài),被從調(diào)度器的運行隊列移走,直到等待的條件被滿足。而非阻塞操作的進程在不能進行設備操作時并不掛起,它或者放棄,或者不停地查詢,直至可以進行操作為止。

二、等待隊列

在 Linux 驅動程序中,可以使用等待隊列(wait queue)來實現(xiàn)阻塞進程的喚醒。wait queue 很早就作為一個基本的功能單位出現(xiàn)在 Linux 內(nèi)核里了,它以隊列為基礎數(shù)據(jù)結構,與進程調(diào)度機制緊密結合,能夠用于實現(xiàn)內(nèi)核中的異步事件通知機制。等待隊列可以用來同步對系統(tǒng)資源的訪問,信號量在內(nèi)核中也依賴等待隊列來實現(xiàn)。

希望等待特定事件的進程把自己放進合適的等待隊列,并放棄控制權。因此,等待隊列是一組睡眠的進程,當某一條件變?yōu)檎鏁r,由內(nèi)核喚醒他們。

等待隊列是一個具有頭節(jié)點的雙向循環(huán)鏈表,把所有睡眠的進程連接起來,每個節(jié)點元素都有進程相關的信息

等待隊列示意圖1

 

等待隊列示意圖2

1,等待隊列頭

每個等待隊列都有一個等待隊列頭,驅動注意操作等待隊列頭來實現(xiàn)阻塞的功能,二等待隊列項的內(nèi)容不需要關心,因為等待隊列是由中斷處理程序和主要內(nèi)核函數(shù)修改的,其雙向鏈表必須進行保護,防止多進程同時進行訪問修改,造成不可預知的后果,所以定義了lock來鎖住鏈表操作的區(qū)域

等待隊列頭結構體的定義:內(nèi)核使用等待隊列頭來掛起一個進程,也使用等待隊列頭來喚醒進程

struct __wait_queue_head {

  spinlock_t  lock;          //自旋鎖變量,用于在對等待隊列頭          

  struct list_head task_list;  // 指向等待隊列的list_head

}; 

typedef struct __wait_queue_head wait_queue_head_t;

操作函數(shù)

#include <linux/sched.h>

#include <linux/wait.h>

1).定義“等待隊列頭”

wait _ queue _ head _ t my _ queue;

2) .初始化“等待隊列頭”。

void init_waitqueue_head(wait_queue_head_t *);

而下面的 DECLARE_WAIT_QUEUE_HEAD()宏可以作為定義并初始化等待隊列頭的“快捷方式”。

DECLARE_WAIT_QUEUE_HEAD(name);

3).條件等待/休眠函數(shù) 一邊休眠等待條件

//當cond條件是false(0)則休眠(不可中斷版,不推薦使用)

void wait_event(wait_queue_head_t wq, int cond); 

上面程序的執(zhí)行過程:

1.用當前的進程描述塊(PCB)初始化一個wait_queue描述的等待任務。

2.在等待隊列鎖資源的保護下,將等待任務加入等待隊列。

3.判斷等待條件是否滿足,如果滿足,那么將等待任務從隊列中移出,退出函數(shù)。

4.如果條件不滿足,那么任務調(diào)度,將CPU資源交與其它任務。

5.當睡眠任務被喚醒之后,需要重復(2)、(3)步驟,如果確認條件滿足,退出等待事件函數(shù)。

使用舉例: flag可以是一個條件表達式

static wait_queue_head_t wq;

init_waitqueue_head(&wq);//初始化等待隊列頭

//if(!flag){ 

while(!flag){ //條件不滿足

if(wait_event_interruptible(wq,flag)) //如果是被其他信號喚醒則返回錯誤

return -ERESTARTSYS;

}

使用 while 而不使用if的原因是:wait_event_interruptible

可以被中斷及信號打斷,使用while(1),可以避免被打斷的情況。

注:其實可以不用加while,查看內(nèi)核源碼的用法,如果被中斷或者信號打斷,直接返回錯誤。

flag = 1; //先設置條件,再喚醒

wake_up(&wq); //條件滿足時喚醒等待隊列頭上所有的進程

//當cond條件是false(0)則休眠(超時版,timeout是超時值,單位是計數(shù)值)

//超時返回值為0 ,被喚醒大于0 需判斷返回值

int wait_event_timeout(wait_queue_head_t wq, int condition, unsigend long timeout);

//當cond條件是false則休眠(可中斷版)

//返回值:打斷:負數(shù),絕對值是錯誤碼,成功0 返回值需要做判斷

int wait_event_interruptible(wait_queue_head_t wq, int condition);

//當cond條件是false則休眠(可超時中斷版)

//打斷:負數(shù),絕對值是錯誤碼; 超時:0; 條件滿足:>0

int wait_event_interruptible_timeout(wait_queue_head_t wq, int condition, unsigend long timeout);

4).喚醒函數(shù) 另一邊條件成熟時喚醒

void wake_up(wait_queue_head_t *) //能喚醒所以狀態(tài)的進程

void wake_up_interruptible(wait_queue_head_t *) //只適用于interruptible,配對使用

注意:喚醒函數(shù)當條件滿足時,一定要先設置條件condition,再喚醒調(diào)用喚醒函數(shù)。因為等待睡眠函數(shù)返回后會首先檢查condition是否滿足,若不滿足會繼續(xù)睡

如: counter = count;

wake_up_interruptible(&wq);

2,等待隊列項

定義等待對列:

struct __wait_queue {

     unsigned int flags;  //prepare_to_wait()里有對flags的操作,查看以得出其含義

          #define WQ_FLAG_EXCLUSIVE        0x01 //一個常數(shù),在prepare_to_wait()用于修改flags的值

                                                                                             

          void * private           //通常指向當前任務控制塊

          wait_queue_func_t func;     //喚醒阻塞任務的函數(shù) ,決定了喚醒的方式

   struct list_head task_list;    // 阻塞任務鏈表

};

typedef struct __wait_queue          wait_queue_t;

1) 定義一個等待隊列

 wait_queue_t wait;

 

2) 初始化等待隊列

內(nèi)核中定義的接口如下:

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)

{

  q->flags = 0;

  q->private = p;          //私有數(shù)據(jù)指針,一般指向當前任務控制塊

  q->func = default_wake_function;  //使用默認的喚醒函數(shù)

}

 

使用范例:

init_waitqueue_entry(&wait, current); 

 

3) 添加/ 等待隊列。

void fastcall add _ wait _ queue(wait _ queue _ head _ t *q, wait _ queue _ t *wait);

add_wait_queue()用于將等待隊列 wait 添加到等待隊列頭 q 指向的等待隊列鏈表

 

4)移除等待隊列。

void fastcall remove _ wait _ queue(wait _ queue _ head _ t *q, wait _ queue _ t *wait);

remove_wait_queue()用于將等待隊列 wait 從附屬的等待隊列頭 q 指向的等待隊列鏈表中移除。

 

5)判斷等待隊列是否為空。

static inline int waitqueue_active(wait_queue_head_t *q)

{

  return ! list_empty(&q->task_list);

}

  判斷等待對列頭是否為空,當一個進程訪問設備而得不到資源時就會被放入等待隊列頭指向的等待隊列中。

三、函數(shù) sleep_on的實現(xiàn)

Sleep()函數(shù)相信大家早已耳熟能詳了,可是內(nèi)部究竟是怎么實現(xiàn)的呢?讓我們一起來揭開它的面紗

 

void sleep_on(wait_queue_head_t *wq)

{

wait_queue_t wait; //定義等待隊列

init_waitqueue_entry(&wait, current);  //初始化等待隊列

current->state = TASK_UNINTERRUPTIBALE; //設置進程狀態(tài)

add_wait_queue(wq,&wait);  //加入等待隊列

schedule();  //調(diào)度,當前進程進入睡眠

remove_wait_queue(wq,&wait);  //醒后從等待隊列中移除

}

可以發(fā)現(xiàn),程序之所以能睡眠,是因為他改變了自己的狀態(tài),并執(zhí)行調(diào)度,放棄了占用CPU。但是我們要喚醒進程,必須要找到它,怎么找到它呢,關鍵就在于進程在睡眠前我們把它加入了等待對應,只要找到等待隊列我們就能找到掛起的進程并喚醒它。

相信進過前面的介紹大家應該對等待隊列和linux中的阻塞機制有更深的了解了吧。

上一篇:掌握Android對話框

下一篇:TCP、IP涉及到的一些基本概念

熱點文章推薦
華清學員就業(yè)榜單
高薪學員經(jīng)驗分享
熱點新聞推薦
前臺專線:010-82525158 企業(yè)培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,京ICP備16055225號-5京公海網(wǎng)安備11010802025203號

回到頂部

主站蜘蛛池模板: 人人澡人人澡人人澡澡 | 日韩精品一卡2卡3卡4卡新区视频 | 一区一区三区四区产品动漫 | 亚洲VA在线VA天堂VA欧美VA | 免费mmmxxx日本96 | 一色一伦一区二区三区 | 亚洲AV成人无码一二三在线观看 | 麻豆精产一二三产区 | 日本舌吻交缠舌头视频网站 | 久久久久日韩精品久久久男男 | 国产裸体裸美女无遮挡网站 | 国产精品一区二区av日韩在线 | 丰满的年轻搜子在线观看 | 精品人妻无码专区中文字幕 | 国语高清videossex | 亚洲精品久久久久久中文传媒 | 三级全黄的视频在线观看 | AAAAA级少妇高潮大片免费看 | 国内精品久久人妻朋友 | 久久天天躁夜夜躁狠狠85麻豆 | 人妻熟妇乱又伦精品视频无广告 | 浪潮色诱AV久久久久久久 | 免费欧洲毛片A级视频老妇女 | 女邻居的大乳中文字幕 | 99久久精品国产免费 | 无翼乌18禁全肉肉无遮挡彩色 | 亚洲AV无码一区东京热 | 大又大粗又爽又黄少妇毛片 | 乌克兰18极品XX00喷水 | 久久国产人妻无码一区 | 性少妇tubevⅰdeos高清 | 国产乱色精品成人免费视频 | 免费看荫蒂添的好舒服视频 | 精品高朝久久久久9999 | 2018国产大陆天天弄 | 亚洲色婷婷综合久久 | 国产亚洲视频在线观看播放 | 午夜福利免费a片在线观看无码 | 风韵丰满熟妇啪啪区老老熟女百度 | gogo大胆无码免费视频列表 | 亚洲国产精品成人午夜在线观看 |