目錄
裝置阻塞IO的實作" >裝置阻塞IO的實作
1. 定義-初始化等待佇列頭" >1. 定義-初始化等待佇列頭
#2. 將本進程加入等待佇列" >#2. 將本進程加入等待佇列
模板" >模板
IO多路复用的实现" >IO多路复用的实现
模板" >模板
其他API" >其他API
首頁 系統教程 Linux 詳解Linux驅動技術(五) _設備阻塞/非阻塞讀寫

詳解Linux驅動技術(五) _設備阻塞/非阻塞讀寫

Feb 15, 2024 pm 04:00 PM
linux linux教程 linux系統 linux指令 shell腳本 typedef overflow 嵌入式linux linux入門 linux學習

在Linux驅動程式編寫過程中,裝置阻塞/非阻塞讀寫是一種非常重要的技術。它可以實現高效的資料傳輸和事件處理,提高系統的效能和響應速度。在本文中,我們將深入探討Linux驅動技術(五) _裝置阻塞/非阻塞讀寫的實作原理與相關技術。

详解Linux驱动技术(五) _设备阻塞/非阻塞读写

#等待佇列是核心中實現進程調度的一個十分重要的資料結構,其任務是維護一個鍊錶,鍊錶中每一個節點都是一個PCB(進程控制區塊),核心會將PCB掛在等待佇列中的所有行程都調度為睡眠狀態,直到某個喚醒的條件發生。應用層的阻塞IO與非阻塞IO的使用我已經在Linux I/O多路復用一文中討論過了,本文主要討論驅動中怎麼實現對設備IO的阻塞與非阻塞讀寫。顯然,實作這種與阻塞相關的機制要用到等待佇列機制。本文的核心原始碼使用的是3.14.0版本

裝置阻塞IO的實作

#當我們讀寫設備檔案的IO時,最終會回調驅動中對應的接口,而這些接口也會出現在讀寫設備進程的進程(內核)空間中,如果條件不滿足,介面函數使進程進入睡眠狀態,即使讀寫設備的使用者進程進入了睡眠,也就是我們常說的發生了阻塞。 In a word,讀寫裝置檔案阻塞的本質是驅動在驅動程式中實現對裝置檔案的阻塞,其讀寫的流程可概括如下:

1. 定義-初始化等待佇列頭

//定义等待队列头
wait_queue_head_t waitq_h;
//初始化,等待队列头
init_waitqueue_head(wait_queue_head_t *q);
 //或
//定义并初始化等待队列头
DECLARE_WAIT_QUEUE_HEAD(waitq_name);
登入後複製

在上面的幾個選擇中,最後一種會直接定義並初始化一個等待頭,但是如果在模組內使用全域變數傳參,用著並不方便,具體用哪種看需求。
我們可以追一下原始碼,看看上面這幾行都做了什麼:

//include/linux/wait.h 
 35 struct __wait_queue_head { 
 36         spinlock_t              lock;
 37         struct list_head        task_list;
 38 };
 39 typedef struct __wait_queue_head wait_queue_head_t;
登入後複製

#wait_queue_head_t
–36–>這個隊列用的自旋鎖定
–27–>將整個隊列」字串」在一起的紐帶

#然後我們看一下初始化的巨集:

 55 #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                           \
 56         .lock           = __SPIN_LOCK_UNLOCKED(name.lock),              \
 57         .task_list      = { &(name).task_list, &(name).task_list } }
 58 
 59 #define DECLARE_WAIT_QUEUE_HEAD(name) \
 60         wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
登入後複製

#DECLARE_WAIT_QUEUE_HEAD()
–60–>根據傳入的字串name,建立一個名為name的等待佇列頭
–57–>初始化上述task_list域,竟然沒有用核心標準的初始化宏,無語。 。 。

#2. 將本進程加入等待佇列

為等待佇列新增事件,即進程進入睡眠狀態直到condition為真才回傳。 **_interruptible的版本版本表示睡眠可中斷,_timeout**版本表示逾時版本,超時就會返回,這種命名規範在內核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);
登入後複製

這可是等待佇列的核心,我們來看看

wait_event
└── wait_event
└──
_wait_event
├── abort_exclusive_wait
├── finish_wait
├── prepare_to_wait_event
└── ___wait_is_interruptible

244 #define wait_event(wq, condition)                                       \
245 do {                                                                    \
246         if (condition)                                                  \
247                 break;                                                  \
248         __wait_event(wq, condition);                                    \ 
249 } while (0)
登入後複製

wait_event
–246–>如果condition为真,立即返回
–248–>否则调用__wait_event

194 #define ___wait_event(wq, condition, state, exclusive, ret, cmd)        \       
195 ({                                                                      \
206         for (;;) {                                                      \
207                 long __int = prepare_to_wait_event(&wq, &__wait, state);\
208                                                                         \  
209                 if (condition)                                          \       
210                         break;                                          \
212                 if (___wait_is_interruptible(state) && __int) {         \
213                         __ret = __int;                                  \
214                         if (exclusive) {                                \
215                                 abort_exclusive_wait(&wq, &__wait,      \
216                                                      state, NULL);      \
217                                 goto __out;                             \
218                         }                                               \
219                         break;                                          \
220                 }                                                       \
222                 cmd;                                                    \
223         }                                                               \
224         finish_wait(&wq, &__wait);                                      \
225 __out:  __ret;                                                          \
226 })
登入後複製

___wait_event
–206–>死循环的轮询
–209–>如果条件为真,跳出循环,执行finish_wait();进程被唤醒
–212–>如果进程睡眠的方式是interruptible的,那么当中断来的时候也会abort_exclusive_wait被唤醒
–222–>如果上面两条都不满足,就会回调传入的schedule(),即继续睡眠

模板

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)    //条件可以在中断处理函数中置位
        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多路复用的实现

对于普通的非阻塞IO,我们只需要在驱动中注册的read/write接口时不使用阻塞机制即可,这里我要讨论的是IO多路复用,即当驱动中的read/write并没有实现阻塞机制的时候,我们如何利用内核机制来在驱动中实现对IO多路复用的支持。下面这个就是我们要用的API

int poll(struct file *filep, poll_table *wait);
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)  
登入後複製

当应用层调用select/poll/epoll机制的时候,内核其实会遍历回调相关文件的驱动中的poll接口,通过每一个驱动的poll接口的返回值,来判断该文件IO是否有相应的事件发生,我们知道,这三种IO多路复用的机制的核心区别在于内核中管理监视文件的方式,分别是数组链表,但对于每一个驱动,回调的接口都是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

刚才我们讨论了如何使用等待队列实现阻塞IO,非阻塞IO,其实关于等待队列,内核还提供了很多其他API用以完成相关的操作,这里我们来认识一下

//在等待队列上睡眠
sleep_on(wait_queue_head_t *wqueue_h);
sleep_on_interruptible(wait_queue_head_t *wqueue_h);

//唤醒等待的进程
void wake_up(wait_queue_t *wqueue);
void wake_up_interruptible(wait_queue_t *wqueue);
登入後複製

总之,设备阻塞/非阻塞读写是Linux驱动程序编写过程中不可或缺的一部分。它可以实现高效的数据传输和事件处理,提高系统的性能和响应速度。希望本文能够帮助读者更好地理解Linux驱动技术(五) _设备阻塞/非阻塞读写的实现原理和相关技术。

以上是詳解Linux驅動技術(五) _設備阻塞/非阻塞讀寫的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

vscode需要什麼電腦配置 vscode需要什麼電腦配置 Apr 15, 2025 pm 09:48 PM

VS Code 系統要求:操作系統:Windows 10 及以上、macOS 10.12 及以上、Linux 發行版處理器:最低 1.6 GHz,推薦 2.0 GHz 及以上內存:最低 512 MB,推薦 4 GB 及以上存儲空間:最低 250 MB,推薦 1 GB 及以上其他要求:穩定網絡連接,Xorg/Wayland(Linux)

Linux體系結構:揭示5個基本組件 Linux體系結構:揭示5個基本組件 Apr 20, 2025 am 12:04 AM

Linux系統的五個基本組件是:1.內核,2.系統庫,3.系統實用程序,4.圖形用戶界面,5.應用程序。內核管理硬件資源,系統庫提供預編譯函數,系統實用程序用於系統管理,GUI提供可視化交互,應用程序利用這些組件實現功能。

vscode終端使用教程 vscode終端使用教程 Apr 15, 2025 pm 10:09 PM

vscode 內置終端是一個開發工具,允許在編輯器內運行命令和腳本,以簡化開發流程。如何使用 vscode 終端:通過快捷鍵 (Ctrl/Cmd ) 打開終端。輸入命令或運行腳本。使用熱鍵 (如 Ctrl L 清除終端)。更改工作目錄 (如 cd 命令)。高級功能包括調試模式、代碼片段自動補全和交互式命令歷史。

git怎麼查看倉庫地址 git怎麼查看倉庫地址 Apr 17, 2025 pm 01:54 PM

要查看 Git 倉庫地址,請執行以下步驟:1. 打開命令行並導航到倉庫目錄;2. 運行 "git remote -v" 命令;3. 查看輸出中的倉庫名稱及其相應的地址。

notepad怎麼運行java代碼 notepad怎麼運行java代碼 Apr 16, 2025 pm 07:39 PM

雖然 Notepad 無法直接運行 Java 代碼,但可以通過借助其他工具實現:使用命令行編譯器 (javac) 編譯代碼,生成字節碼文件 (filename.class)。使用 Java 解釋器 (java) 解釋字節碼,執行代碼並輸出結果。

vscode在哪寫代碼 vscode在哪寫代碼 Apr 15, 2025 pm 09:54 PM

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

Linux的主要目的是什麼? Linux的主要目的是什麼? Apr 16, 2025 am 12:19 AM

Linux的主要用途包括:1.服務器操作系統,2.嵌入式系統,3.桌面操作系統,4.開發和測試環境。 Linux在這些領域表現出色,提供了穩定性、安全性和高效的開發工具。

vscode終端命令不能用 vscode終端命令不能用 Apr 15, 2025 pm 10:03 PM

VS Code 終端命令無法使用的原因及解決辦法:未安裝必要的工具(Windows:WSL;macOS:Xcode 命令行工具)路徑配置錯誤(添加可執行文件到 PATH 環境變量中)權限問題(以管理員身份運行 VS Code)防火牆或代理限制(檢查設置,解除限制)終端設置不正確(啟用使用外部終端)VS Code 安裝損壞(重新安裝或更新)終端配置不兼容(嘗試不同的終端類型或命令)特定環境變量缺失(設置必要的環境變量)

See all articles