linux工作隊列

来源:http://www.cnblogs.com/oceanding/archive/2017/09/26/7595738.html
-Advertisement-
Play Games

轉載http://bgutech.blog.163.com/blog/static/18261124320116181119889/ 1. 什麼是workqueue Linux中的Workqueue機制就是為了簡化內核線程的創建。通過調用workqueue的介面就能創建內核線程。並且可以根據當前系統 ...


轉載http://bgutech.blog.163.com/blog/static/18261124320116181119889/

1. 什麼是workqueue
       Linux中的Workqueue機制就是為了簡化內核線程的創建。通過調用workqueue的介面就能創建內核線程。並且可以根據當前系統CPU的個數創建線程的數量,使得線程處理的事務能夠並行化。workqueue是內核中實現簡單而有效的機制,他顯然簡化了內核daemon的創建,方便了用戶的編程.

      工作隊列(workqueue)是另外一種將工作推後執行的形式.工作隊列可以把工作推後,交由一個內核線程去執行,也就是說,這個下半部分可以在進程上下文中執行。最重要的就是工作隊列允許被重新調度甚至是睡眠。

2. 數據結構
     我們把推後執行的任務叫做工作(work),描述它的數據結構為work_struct:

 

  1. struct work_struct {  
  2.     atomic_long_t data;       /*工作處理函數func的參數*/  
  3. #define WORK_STRUCT_PENDING 0        /* T if work item pending execution */  
  4. #define WORK_STRUCT_STATIC 1        /* static initializer (debugobjects) */  
  5. #define WORK_STRUCT_FLAG_MASK (3UL)  
  6. #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)  
  7.     struct list_head entry;        /*連接工作的指針*/  
  8.     work_func_t func;              /*工作處理函數*/  
  9. #ifdef CONFIG_LOCKDEP  
  10.     struct lockdep_map lockdep_map;  
  11. #endif  
  12. };  

 

      這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct:

[cpp] view plain copy  
  1. struct workqueue_struct {  
  2.  struct cpu_workqueue_struct *cpu_wq;  
  3.  struct list_head list;  
  4.  const char *name;   /*workqueue name*/  
  5.  int singlethread;   /*是不是單線程 - 單線程我們首選第一個CPU -0表示採用預設的工作者線程event*/  
  6.  int freezeable;  /* Freeze threads during suspend */  
  7.  int rt;  
  8. };   


     如果是多線程,Linux根據當前系統CPU的個數創建cpu_workqueue_struct 其結構體就是:

[cpp] view plain copy  
  1. truct cpu_workqueue_struct {  
  2.  spinlock_t lock;/*因為工作者線程需要頻繁的處理連接到其上的工作,所以需要枷鎖保護*/  
  3.  struct list_head worklist;  
  4.  wait_queue_head_t more_work;  
  5.  struct work_struct *current_work; /*當前的work*/  
  6.  struct workqueue_struct *wq;   /*所屬的workqueue*/  
  7.  struct task_struct *thread; /*任務的上下文*/  
  8. } ____cacheline_aligned;  

       在該結構主要維護了一個任務隊列,以及內核線程需要睡眠的等待隊列,另外還維護了一個任務上下文,即task_struct。
       三者之間的關係如下:

 

3. 創建工作
3.1 創建工作queue
a. create_singlethread_workqueue(name)
        該函數的實現機制如下圖所示,函數返回一個類型為struct workqueue_struct的指針變數,該指針變數所指向的記憶體地址在函數內部調用kzalloc動態生成。所以driver在不再使用該work queue的情況下調用:

        void destroy_workqueue(struct workqueue_struct *wq)來釋放此處的記憶體地址。

 

        圖中的cwq是一per-CPU類型的地址空間。對於create_singlethread_workqueue而言,即使是對於多CPU系統,內核也只負責創建一個worker_thread內核進程。該內核進程被創建之後,會先定義一個圖中的wait節點,然後在一迴圈體中檢查cwq中的worklist,如果該隊列為空,那麼就會把wait節點加入到cwq中的more_work中,然後休眠在該等待隊列中。

        Driver調用queue_work(struct workqueue_struct *wq, struct work_struct *work)向wq中加入工作節點。work會依次加在cwq->worklist所指向的鏈表中。queue_work向cwq->worklist中加入一個work節點,同時會調用wake_up來喚醒休眠在cwq->more_work上的worker_thread進程。wake_up會先調用wait節點上的autoremove_wake_function函數,然後將wait節點從cwq->more_work中移走。

        worker_thread再次被調度,開始處理cwq->worklist中的所有work節點...當所有work節點處理完畢,worker_thread重新將wait節點加入到cwq->more_work,然後再次休眠在該等待隊列中直到Driver調用queue_work...

b. create_workqueue

 

 

 

 

       相對於create_singlethread_workqueue, create_workqueue同樣會分配一個wq的工作隊列,但是不同之處在於,對於多CPU系統而言,對每一個CPU,都會為之創建一個per-CPU的cwq結構,對應每一個cwq,都會生成一個新的worker_thread進程。但是當用queue_work向cwq上提交work節點時,是哪個CPU調用該函數,那麼便向該CPU對應的cwq上的worklist上增加work節點。

c.小結
       當用戶調用workqueue的初始化介面create_workqueue或者create_singlethread_workqueue對workqueue隊列進行初始化時,內核就開始為用戶分配一個workqueue對象,並且將其鏈到一個全局的workqueue隊列中。然後Linux根據當前CPU的情況,為workqueue對象分配與CPU個數相同的cpu_workqueue_struct對象,每個cpu_workqueue_struct對象都會存在一條任務隊列。緊接著,Linux為每個cpu_workqueue_struct對象分配一個內核thread,即內核daemon去處理每個隊列中的任務。至此,用戶調用初始化介面將workqueue初始化完畢,返回workqueue的指針。

        workqueue初始化完畢之後,將任務運行的上下文環境構建起來了,但是具體還沒有可執行的任務,所以,需要定義具體的work_struct對象。然後將work_struct加入到任務隊列中,Linux會喚醒daemon去處理任務。

       上述描述的workqueue內核實現原理可以描述如下:

 

 

 

3.2  創建工作
       要使用工作隊列,首先要做的是創建一些需要推後完成的工作。可以通過DECLARE_WORK在編譯時靜態地建該結構:
       DECLARE_WORK(name,void (*func) (void *), void *data);
      這樣就會靜態地創建一個名為name,待執行函數為func,參數為data的work_struct結構。
      同樣,也可以在運行時通過指針創建一個工作:
      INIT_WORK(structwork_struct *work, woid(*func) (void *), void *data);

4. 調度
a. schedule_work

       在大多數情況下, 並不需要自己建立工作隊列,而是只定義工作, 將工作結構掛接到內核預定義的事件工作隊列中調度, 在kernel/workqueue.c中定義了一個靜態全局量的工作隊列static struct workqueue_struct *keventd_wq;預設的工作者線程叫做events/n,這裡n是處理器的編號,每個處理器對應一個線程。比如,單處理器的系統只有events/0這樣一個線程。而雙處理器的系統就會多一個events/1線程。
       調度工作結構, 將工作結構添加到全局的事件工作隊列keventd_wq,調用了queue_work通用模塊。對外屏蔽了keventd_wq的介面,用戶無需知道此參數,相當於使用了預設參數。keventd_wq由內核自己維護,創建,銷毀。這樣work馬上就會被調度,一旦其所在的處理器上的工作者線程被喚醒,它就會被執行。

b. schedule_delayed_work(&work,delay);
      有時候並不希望工作馬上就被執行,而是希望它經過一段延遲以後再執行。在這種情況下,同時也可以利用timer來進行延時調度,到期後才由預設的定時器回調函數進行工作註冊。延遲delay後,被定時器喚醒,將work添加到工作隊列wq中。

      工作隊列是沒有優先順序的,基本按照FIFO的方式進行處理。

 5. 示例

[cpp] view plain copy  
  1. #include <linux/module.h>  
  2. #include <linux/init.h>  
  3. #include <linux/workqueue.h>  
  4.   
  5. static struct workqueue_struct *queue=NULL;  
  6. static struct work_struct   work;  
  7.   
  8. staticvoid work_handler(struct work_struct *data)  
  9. {  
  10.        printk(KERN_ALERT"work handler function.\n");  
  11. }  
  12.   
  13. static int __init test_init(void)  
  14. {  
  15.       queue=create_singlethread_workqueue("hello world");/*創建一個單線程的工作隊列*/  
  16.       if (!queue)  
  17.             goto err;  
  18.   
  19.        INIT_WORK(&work,work_handler);  
  20.        schedule_work(&work);  
  21.   
  22.       return0;  
  23. err:  
  24.       return-1;  
  25. }  
  26.   
  27. static   void __exit test_exit(void)  
  28. {  
  29.        destroy_workqueue(queue);  
  30. }  
  31. MODULE_LICENSE("GPL");  
  32. module_init(test_init);  
  33. module_exit(test_exit);  

序號


介面函數


說明


1


create_workqueue


用於創建一個workqueue隊列,為系統中的每個CPU都創建一個內核線程。輸入參數:


@name:workqueue的名稱


2


create_singlethread_workqueue


用於創建workqueue,只創建一個內核線程。輸入參數:


@name:workqueue名稱


3


destroy_workqueue


釋放workqueue隊列。輸入參數:


@ workqueue_struct:需要釋放的workqueue隊列指針


4


schedule_work


調度執行一個具體的任務,執行的任務將會被掛入Linux系統提供的workqueue——keventd_wq輸入參數:


@ work_struct:具體任務對象指針


5


schedule_delayed_work


延遲一定時間去執行一個具體的任務,功能與schedule_work類似,多了一個延遲時間,輸入參數:


@work_struct:具體任務對象指針


@delay:延遲時間


6


queue_work


調度執行一個指定workqueue中的任務。輸入參數:


@ workqueue_struct:指定的workqueue指針


@work_struct:具體任務對象指針


7


queue_delayed_work


延遲調度執行一個指定workqueue中的任務,功能與queue_work類似,輸入參數多了一個delay。

轉自:http://bgutech.blog.163.com/blog/static/18261124320116181119889/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文為mariadb官方手冊:Identifier Names的譯文。 原文:https://mariadb.com/kb/en/library/identifier-names/我提交到MariaDB官方手冊的譯文:https://mariadb.com/kb/zh-cn/library/iden ...
  • sql事務對錶的鎖及With(NoLock),With(ReadPast)對錶的查詢結果對比 ...
  • Sentinel(哨兵)是Redis 的高可用性解決方案:由一個或多個Sentinel 實例 組成的Sentinel 系統可以監視任意多個主伺服器,以及這些主伺服器屬下的所有從伺服器,併在被監視的主伺服器進入下線狀態時,自動將下線主伺服器屬下的某個從伺服器升級為新的主伺服器。 ...
  • 一、Linux概述 Linux是一款全球性的免費的開源的操作系統平臺,其特點是實現了多任務多用戶處理,主要是依賴內核kernel shell,且占用資源少 (最小配置只要4Mb記憶體就能運行)。 百度百科概述 Linux是一套免費使用和自由傳播的類Unix操作系統,是一個基於POSIX和UNIX的多用 ...
  • 原文發表於cu:2017-03-27 參考文檔: 本文涉及keepalived的安裝,簡單配置,為haproxy做高可用。 一.環境準備 1. 操作系統 CentOS-7-x86_64-Everything-1511 2. Keepalived版本 截至2017-03-22,keepalived版本 ...
  • 嵌入式系統在我們的日常生活中已經隨處可見,已經貫穿在我們生活的方方面面,今天筆者就來淺談一下對嵌入式的認識。 一、什麼是嵌入式系統 在清華大學出版社出版的《嵌入式系統原理與設計》當中,對嵌入式系統是這樣進行定義的:嵌入式系統是以應用為中心,以電腦技術為基礎,採用可裁剪軟硬體,適用於對功能、可靠性、 ...
  • 網路配置 網路參數設置: 1)命令行設置,可以立即生效,但是電腦重啟後所有的設置會失效; 2)修改系統配置文件,不會馬上生效,但是可以永久保存設置; 命令行設置網路參數1》網路介面參數 ifconfig命令 ifconfig eth0 192.168.10.10 netmask 255.255.2 ...
  • 一、預設許可權 每一個終端都有一個 umask 屬性,是用來確定新建文件或目錄的預設許可權的許可權“掩碼”(mask 有“掩碼”的含義,至於 u,後面說)。 Linux 中一般有預設的許可權掩碼,使用命令 umask 用以查看或設置: 一般地,普通用戶(uid 為 500 以上)的許可權掩碼預設為 0002, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...