溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Linux怎么實(shí)現(xiàn)設(shè)備阻塞/非阻塞讀寫

發(fā)布時(shí)間:2021-12-24 11:59:20 來源:億速云 閱讀:337 作者:iii 欄目:系統(tǒng)運(yùn)維

本篇內(nèi)容介紹了“Linux怎么實(shí)現(xiàn)設(shè)備阻塞/非阻塞讀寫”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

當(dāng)我們讀寫設(shè)備文件的IO時(shí),最終會(huì)回調(diào)驅(qū)動(dòng)中相應(yīng)的接口,而這些接口也會(huì)出現(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ì)列頭

//定義等待隊(duì)列頭  wait_queue_head_t waitq_h;//初始化,等待隊(duì)列頭  init_waitqueue_head(wait_queue_head_t *q); //或//定義并初始化等待隊(duì)列頭  DECLARE_WAIT_QUEUE_HEAD(waitq_name);

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

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

//include/linux/wait.h    struct __wait_queue_head {            spinlock_t              lock;            struct list_head        task_list;    };   typedef struct __wait_queue_head wait_queue_head_t;

wait_queue_head_t

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

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

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

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                            .lock        = __SPIN_LOCK_UNLOCKED(name.lock),               .task_list   = { &(name).task_list, &(name).task_list } }  define DECLARE_WAIT_QUEUE_HEAD(name) \     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í)就會(huì)返回,這種命名規(guī)范在內(nèi)核API中隨處可見。

void wait_event(wait_queue_head_t *waitq_h,int condition); void wait_event_interruptible(wait_queue_head_t *waitq_h,int condition); void wait_event_timeout(wait_queue_head_t *waitq_h,int condition); 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

 #define wait_event(wq, condition)                                 do {                                                                      if (condition)                                                           break;                                                    __wait_event(wq, condition);                               } while (0)

wait_event

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

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

#define ___wait_event(wq,condition,state, exclusive, ret, cmd) \   ({                                                            \     for (;;) {                                                 \        long __int = prepare_to_wait_event(&wq, &__wait, state);\                                                                \         if (condition)                                          \                   break;                                              \        if (___wait_is_interruptible(state) && __int) {         \            __ret = __int;                                      \        if (exclusive) {                                        \            abort_exclusive_wait(&wq, &__wait,                  \            state, NULL);                                       \            goto __out;                                         \        }                                                       \        break;                                                  \     }                                                          \     cmd;                                                       \   }                                                            \   finish_wait(&wq, &__wait);                                   \   __out:  __ret;                                               \  })

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

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

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

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

模板

struct wait_queue_head_t xj_waitq_h; static ssize_t demo_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) {    if(!condition)    //條件可以在中斷處理函數(shù)中置位         wait_event_interruptible(&xj_waitq_h,condition); } static file_operations fops = {     .read = demo_read, }; static __init demo_init(void){     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

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í)會(huì)遍歷回調(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。

模板

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

其他API

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

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

“Linux怎么實(shí)現(xiàn)設(shè)備阻塞/非阻塞讀寫”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI