1. gzyueqian
      13352868059

      linux培訓機構講解linux運行的阻塞機制

      更新時間: 2019-11-15 15:58:12來源: 粵嵌教育瀏覽量:9376

             在想要學習linux技術的學員面前,難學習的時候就是要學習linux知識里面的那些運行機制吧。不管是linux運行的阻塞機制還是運行的非阻塞機制都是學員比較難懂的。那么linux運行的阻塞機制是怎樣的呢?下面粵嵌科技的講師就給大家講解下:
        一、阻塞和非阻塞

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

        二、等待隊列

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

        希望等待特定事件的進程把自己放進合適的等待隊列,并放棄控制權。因此,等待隊列是一組睡眠的進程,當某一條件變為真時,由內核喚醒他們。

        1,等待隊列頭

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

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

        struct __wait_queue_head {

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

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

        };

        typedef struct __wait_queue_head wait_queue_head_t;

        操作函數

        #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).條件等待/休眠函數 一邊休眠等待條件

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

        void wait_event(wait_queue_head_t wq, int cond);

        上面程序的執行過程:

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

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

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

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

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

        使用舉例: 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,查看內核源碼的用法,如果被中斷或者信號打斷,直接返回錯誤。

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

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

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

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

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

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

        //返回值:打斷:負數,值是錯誤碼,成功0 返回值需要做判斷

        int wait_event_interruptible(wait_queue_head_t wq, int condition);

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

        //打斷:負數,值是錯誤碼; 超時:0; 條件滿足:>0

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

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

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

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

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

        如: counter = count;

        wake_up_interruptible(&wq);

        2,等待隊列項

        定義等待對列:

        struct __wait_queue {

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

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

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

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

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

        };

        typedef struct __wait_queue wait_queue_t;

        1) 定義一個等待隊列

        wait_queue_t wait;

        2) 初始化等待隊列

        內核中定義的接口如下:

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

        {

        q->flags = 0;

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

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

        }

        使用范例:

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

        }

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

        三、函數 sleep_on的實現

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

        void sleep_on(wait_queue_head_t *wq)

        {

        wait_queue_t wait; //定義等待隊列

        init_waitqueue_entry(&wait, current);

        //初始化等待隊列

        current->state = TASK_UNINTERRUPTIBALE; //設置進程狀態

        add_wait_queue(wq,&wait);

        //加入等待隊列

        schedule();

        //調度,當前進程進入睡眠

        remove_wait_queue(wq,&wait);

        //醒后從等待隊列中移除

        }

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

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

        以上就是粵嵌科技的小編給大家整理的關于linux培訓機構講解linux運行的阻塞機制的內容,如果說你是有基礎的學員的話肯定是可以看得懂小編說的內容,但是如果說你是零基礎的學員的話估計就很難看的懂了。如果說你想要學習linux技術的話,那么粵嵌科技的小編建議大家可以到我們粵嵌科技的linux培訓班進行實地考察下,來和我們的講師進行面對面的交流和互動。也可以點擊我們文章下面的獲取試聽資格按鈕來獲取我們的linux培訓的免費課程試聽資格,來免費體驗我們的linux課程并深入的了解我們粵嵌科技。

      免費預約試聽課

      亚洲另类欧美综合久久图片区_亚洲中文字幕日产无码2020_欧美日本一区二区三区桃色视频_亚洲AⅤ天堂一区二区三区

      
      

      1. 欧美日韩中文精品在线 | 在线观看午夜看片免费 | 亚洲高清性爱在线视频 | 亚洲综合色自拍一区 | 一本久久a久久精品vr综合 | 亚洲的天堂在线中文字幕 |