Linux 阻塞等待框架

在 Linux 設備驅動開發中,阻塞機制 是處理資源暫時不可用(如設備未準備好數據、緩沖區滿等)的核心手段。驅動程序可以將被阻塞的進程設置成休眠狀態,然后,在資源可用后,再將該進程喚醒

在 Linux 驅動開發中,休眠(等待)機制分為 簡單休眠(使用 wait_event系列宏)和 高級休眠(手動使用 prepare_to_wait/finish_wait),它們的核心區別在于封裝程度和靈活性。

其中,"簡單休眠"由"高級休眠"封裝而來,可以當做"高級休眠"的語法糖。

本質區別對比:
在這里插入圖片描述
如何選擇?

  • 優先使用簡單休眠,比如:只需要等待單一條件、不需要在等待過程中執行額外操作;
  • 當需要實現非標準的喚醒條件(如同時等待多個事件)、需要更精細的性能優化時必須使用高級休眠;
  • 現代內核更推薦使用 wait_event_interruptible 等封裝宏。

簡單休眠

最佳框架:

ssize_t scullpipe_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct scull_pipe *dev = filp->private_data;int ret;// 檢查參數if (count == 0)return 0;if (mutex_lock_interruptible(&dev->lock))return -ERESTARTSYS;// 如果緩沖區為空,進入休眠等待while (read_buffer_empty(dev)) {mutex_unlock(&dev->lock);  // 必須釋放鎖再休眠if (filp->f_flags & O_NONBLOCK)return -EAGAIN;ret = wait_event_interruptible(dev->inq, !read_buffer_empty(dev));if (ret == -ERESTARTSYS)  // 被信號打斷return -ERESTARTSYS;// 被喚醒后再次獲取鎖再判斷if (mutex_lock_interruptible(&dev->lock))return -ERESTARTSYS;}// 此時緩沖區有數據,可以讀取ret = copy_data_to_user(dev, buf, count);mutex_unlock(&dev->lock);return ret;
}

高級休眠

最佳框架:

ssize_t mydev_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{struct my_device *dev = filp->private_data;DEFINE_WAIT_FUNC(wait, default_wake_function);ssize_t ret = 0;if (mutex_lock_interruptible(&dev->lock))return -ERESTARTSYS;while (!data_available(dev)){mutex_unlock(&dev->lock);/* 非阻塞模式 */if (filp->f_flags & O_NONBLOCK) return -EAGAIN;if (prepare_to_wait_event(&dev->inq, &wait, TASK_INTERRUPTIBLE))return -ERESTARTSYS;  		// 信號/* 在當前進程掛入等待隊列后,再次確認條件*/if (!data_available(dev))schedule();if (signal_pending(current)) {ret = -ERESTARTSYS;  		// 信號break;}if (mutex_lock_interruptible(&dev->lock)){ret = -ERESTARTSYS;break;}}finish_wait(&dev->inq, &wait);if(ret)return ret;// 現在有鎖,并且數據有效,下面進行讀操作count = min(count, xxx);if (copy_to_user(buf, dev->rp, count) ){mutex_unlock(&dev->lock);return -EFAULT;}mutex_unlock(&dev->lock);return count;
}
  1. DEFINE_WAIT_FUNC(wait, default_wake_function) :定義一個 等待隊列項 (wait_queue_entry),并初始化其喚醒回調函數
    當前進程創建一個“代表自己”的等待隊列項 ( wait ),并指定當條件滿足(通常是資源可用)時如何喚醒自己(通過 default_wake_function 函數)。
    // 源碼 (include/linux/wait.h):
    #define DEFINE_WAIT_FUNC(name, function)              \wait_queue_entry_t name = {                       \.private    = current,                        \.func      = function,                        \.entry     = LIST_HEAD_INIT((name).entry),    \}
    
  2. signal_pending(current):檢查當前進程 (current) 是否有待處理的信號。實現可中斷的睡眠 (TASK_INTERRUPTIBLE)。避免進程在需要響應信號(如 Ctrl+C)時被無謂地阻塞。
    • 返回非零值( true )表示有掛起的信號需要處理。此時,進程不應繼續睡眠,而應退出等待循環并返回 -ERESTARTSYS 錯誤碼給用戶空間(讓用戶空間有機會處理信號)。
    • 通常在調用 schedule() 讓出 CPU 之前檢查,避免信號丟失導致多余阻塞。這也符合現代 Linux 關于"信號優先響應"的設計原則。
  3. prepare_to_wait_event(&dev->inq, &wait, state):準備當前進程進入睡眠狀態。核心任務是將等待項 (&wait) 添加到設備的等待隊列頭 (&dev->inq),并設置當前進程的狀態 (根據參數 state,通常是 TASK_INTERRUPTIBLE, 表示可被信號喚醒的睡眠)。
    正式將當前進程“注冊”到設備的等待隊列 (參數 dev->inq),并標記為睡眠狀態,為安全讓出 CPU 做準備。
    • 它將等待項 (wait) 安全地(加鎖)添加到等待隊列 ()。
    // 簡化邏輯,kernel/sched/wait.c
    void prepare_to_wait_event(wait_queue_head_t *wq_head, wait_queue_entry_t *wait, int state)
    {unsigned long flags;wait->flags &= ~WQ_FLAG_EXCLUSIVE; 			// 通常是非獨占等待spin_lock_irqsave(&wq_head->lock, flags); 	// 獲取隊列鎖if (list_empty(&wait->entry)) 				// 防止重復添加__add_wait_queue(wq_head, wait); 		// 將等待項加入隊列尾部set_current_state(state); 		// 設置當前進程狀態 (TASK_INTERRUPTIBLE/TASK_UNINTERRUPTIBLE)spin_unlock_irqrestore(&wq_head->lock, flags); // 釋放隊列鎖
    }
    
  4. schedule():主動讓出 CPU,觸發進程調度。當前進程進入睡眠狀態,內核選擇另一個就緒進程運行。
    • 調用 schedule() 時,進程狀態必須已經是 TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE(由 prepare_to_wait_event 設置)。調度器根據此狀態將進程移出運行隊列。
    • 進程在此處“睡著”。
    • 當進程被喚醒時(通常是因為等待的條件滿足或有信號到達),執行會從 schedule() 調用之后的下一條語句繼續。
  5. finish_wait(&dev->inq, &wait):清理等待狀態。將等待項從等待隊列中移除,并將當前進程狀態設置回 TASK_RUNNING。
    // 簡化邏輯,kernel/sched/wait.c
    void finish_wait(wait_queue_head_t *wq_head, wait_queue_entry_t *wait)
    {unsigned long flags;set_current_state(TASK_RUNNING); 			// 重要:恢復為可運行狀態if (!list_empty_careful(&wait->entry)) { 	// 如果還在隊列里spin_lock_irqsave(&wq_head->lock, flags);list_del_init(&wait->entry); 			// 從等待隊列中移除spin_unlock_irqrestore(&wq_head->lock, flags);}
    }
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89667.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89667.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89667.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PCIe RAS學習專題(3):AER內核處理流程梳理

目錄 一、AER內核處理整體流程梳理 二、AER代碼重要部分梳理 1、AER初始化階段 2、中斷上半部 aer_irq 3、中斷下半部 aer_isr 3.1、aer_isr_one_error 3.2、find_source_device 3.3、aer_process_err_devices 3.4、handle_error_source 3.5、pcie_do_recovery 整體邏…

?HAProxy負載均衡集群概述

前言: 在現代分布式系統中,負載均衡和高可用性是保障服務穩定性和性能的關鍵技術。HAProxy 作為一款高性能的 TCP/HTTP 負載均衡器,憑借其輕量級、高并發處理能力和靈活的配置機制,成為構建高可用架構的核心組件之一。通過智能的流…

ELN:生物醫藥科研的數字化引擎——衍因科技引領高效創新

在生物醫藥研究領域,實驗數據的準確記錄與管理是科研成敗的關鍵。想象一個場景:某頂尖醫學院實驗室,研究員小張正為一項抗癌藥物實驗焦頭爛額。紙質記錄本中,數據混亂、協作困難,導致實驗重復率高達20%。引入衍因科技的…

暑假---作業2

學習目標&#xff1a;xss-1abs 1-8關python美現自動化布爾自注的2、代碼進行優化(二分查找)學習內容&#xff1a;1.xss-1abs 1-8關1<h2 align"center">歡迎用戶test</h2>2 <script> alert (1)</script&gt<center> <form action&…

【Tensor數據轉換】——深度學習.Torch框架

目錄 1 Tensor與Numpy 1.1 張量轉Numpy 1.2 Numpy轉張量 1 Tensor與Numpy 1.1 張量轉Numpy 調用numpy()方法可以把Tensor轉換為Numpy&#xff0c;此時內存是共享的。 使用copy()方法可以避免內存共享 import torch import numpy as np# tensor轉numpy:numpy() def test0…

基于Tranformer的NLP實戰(5):BERT實戰-基于Pytorch Lightning的文本分類模型

文本分類作為自然語言處理中的基礎任務&#xff0c;能夠幫助我們將海量醫學摘要自動歸類到具體疾病領域中。本文將基于NVIDIA NeMo框架&#xff0c;構建一個用于醫學疾病摘要分類的深度學習應用&#xff0c;支持將摘要劃分為三類&#xff1a;癌癥類疾病、神經系統疾病及障礙、以…

14-鏈路聚合

鏈路聚合技術 一 鏈路聚合概述鏈路聚合定義鏈路聚合是把多條物理鏈路聚合在一起&#xff0c;形成一條邏輯鏈路。應用在交換機、路由器、服務器間鏈路。分為三層鏈路聚合和二層鏈路聚合。二 鏈路聚合的作用 1. 鏈路聚合模式靜態聚合模式 端口不與對端設備交互信息。選擇參考端口…

學習C++、QT---28(QT庫中使用QShortcut類對快捷鍵創建和使用的講解)

每日一言 所有的努力&#xff0c;都是為了讓未來的自己感謝現在的你。 QShortcut 我們的記事本肯定要有通過快捷鍵對字體的放大和縮小進行控制的功能啊&#xff0c;那么我們這邊就這個問題我們需要先學習一下QShortCut 我們這個類就是專門做快捷鍵的 老樣子我們剛開始學習這個…

Web Worker:讓前端飛起來的隱形引擎

目錄 Web Worker&#xff1a;讓前端飛起來的隱形引擎 一、什么是 Web Worker&#xff1f; 1、為什么需要 web worker 2、什么是 web worker 二、基本使用方法 1、創建一個 Worker 文件&#xff08;worker.js&#xff09; 2、主線程引入并使用 三、實戰案例&#xff1a;…

關于在VScode中使用git的一些步驟常用命令及其常見問題:

輸入 gitee用戶 gitee綁定郵箱git config --global user.name "automated-piggy-senior" git config --global user.email "1323280131qq.com"克隆遠程庫到本地 git clone https://gitee.com/automated-piggy-senior/20250717-test.git常見問題1&#xff1…

LeafletJS 性能優化:處理大數據量地圖

引言 LeafletJS 作為一個輕量、靈活的 JavaScript 地圖庫&#xff0c;以其高效的渲染能力和模塊化設計深受開發者喜愛。然而&#xff0c;當處理大數據量&#xff08;如數千個標記、復雜的 GeoJSON 數據或高分辨率瓦片&#xff09;時&#xff0c;LeafletJS 的性能可能面臨挑戰&…

LLM(Large Language Model)大規模語言模型淺析

參考: https://zhuanlan.zhihu.com/p/7046080918 LLM(Large Language Model)大規模語言模型,是指具有大規模參數和復雜計算結構的機器學習模型。大模型里常說的多少B, B 是 Billion 的縮寫&#xff0c;表示 十億,如DeepSeek滿血版 671B(6710億參數); 大模型本質上是一個使用海量…

【后端】配置SqlSugar ORM框架并添加倉儲

目錄 1.添加包 2.配置連接字符串 3.配置SqlSugar 3.1.添加基礎類 3.2.添加方法 3.2.1.讀取配置方法 3.2.2.枚舉擴展方法 3.3.添加管理類&#xff08;重要&#xff09; 4.配置倉儲 4.1.倉儲接口添加 5.注冊 6.使用 該文檔是配置SqlSugar多租戶和加倉儲教程。使用 S…

全國高等院校計算機基礎教育研究會2025學術年會在西寧成功舉辦 ——高原論道啟新程,數智融合育英才

7 月16日至18日&#xff0c;全國高等院校計算機基礎教育研究會2025學術年會在青海西寧隆重召開。大會以“數智融合&#xff0c;創新計算機教育”為主題&#xff0c;匯聚人工智能領域頂尖專家學者、高校校長、產業翹楚及一線教師300 多人&#xff0c;共商人工智能時代計算機基礎…

AppTrace:重新定義免填邀請碼,解鎖用戶裂變新高度

??在移動互聯網時代&#xff0c;?用戶裂變是App增長的核心引擎&#xff0c;而邀請機制則是裂變的關鍵驅動力。然而&#xff0c;傳統的邀請碼機制——依賴用戶手動輸入、記憶復雜字符——已經成為用戶體驗的絆腳石&#xff0c;導致轉化率下降、運營成本上升。?AppTrace? 作…

神經網絡常見激活函數 13-Softplus函數

文章目錄Softplus函數導函數函數和導函數圖像優缺點PyTorch 中的 Softplus 函數TensorFlow 中的 Softplus 函數Softplus 函數導函數 Softplus函數 Softplus?(x)ln?(1ex)\begin{aligned} \operatorname{Softplus}(x) & \ln \bigl(1 e^{\,x}\bigr) \end{aligned} Softplu…

深度理解 KVM:Linux 內核系統學習的重要角度

&#x1f4d6; 推薦閱讀&#xff1a;《Yocto項目實戰教程:高效定制嵌入式Linux系統》 &#x1f3a5; 更多學習視頻請關注 B 站&#xff1a;嵌入式Jerry 深度理解 KVM&#xff1a;Linux 內核系統學習的重要角度 作者&#xff1a;嵌入式 Jerry 一、為什么開發者需要學習 KVM&…

閉包的定義和應用場景

一、閉包是什么&#xff1f; 閉包是指函數可以“記住”并訪問它定義時的詞法作用域&#xff0c;即使這個函數在其作用域鏈之外執行。 簡單說&#xff1a;函數 A 在函數 B 中被定義&#xff0c;并在函數 B 外部被調用&#xff0c;它依然能訪問函數 B 中的變量&#xff0c;這就是…

北京-4年功能測試2年空窗-報培訓班學測開-第五十四天

今天交付的成果是&#xff0c;初版簡歷雖然只寫了項目部分&#xff0c;但用了一整天&#xff0c;期間聯系了前司組長&#xff0c;拿到了性能測試報告。然后再看壓測腳本&#xff0c;突然能看懂了&#xff0c;對服務端日志也能看懂些了&#xff0c;還找到了客戶端日志怎么說呢&a…

算法訓練營day24 回溯算法③ 93.復原IP地址 、78.子集、 90.子集II

今天繼續回溯算法的專題&#xff0c;第三篇博客&#xff01; 93.復原IP地址 輸入&#xff1a;s "25525511135" 輸出&#xff1a;["255.255.11.135","255.255.111.35"] 切割字符串為4段&#xff0c;當進行到第四段的時候對第四段字符串進行判斷…