轉自:https://blog.csdn.net/cc289123557/article/details/52551176linux
版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/l289123557/article/details/52551176
workqueue納入中斷子系統是因爲和中斷處理有密切關係,寫博客重要在於整理本身的思緒,寫的時候會把一些不懂的細節問題暴露出來,這樣會把問題看的更透徹,workqueue的代碼在文件kernel/workqueue.c中,大約5K+行,本文基於linux 4.6.3編寫,因爲能力有限,本文介紹不全面或者理解有誤之處還請見諒,歡迎指正一塊兒探討。多線程
文章系列
1.linux工做隊列 - workqueue總覽
2.linux工做隊列 - workqueue_struct建立
3.linux工做隊列 - 把work_struct加入工做隊列
4.linux工做隊列 - work_struct被調用過程併發
1.介紹
1.1工做隊列介紹
有許多狀況下須要異步執行進程上下文,在linux中工做隊列(wq)是處理這種狀況最經常使用的機制。在處理中斷中,對於中斷下半部的處理最經常使用的就是wq。框架
1.2CMWQ
在原有的wq實現中,一個多線程wq(MT wq)有一個per CPU worker線程,一個單線程wq有一個系統範圍的worker線程。多線程wq(MT wq)的數量保持和cpu的個數一致,隨着近年來cpu core個數的增長,多線程wq(MT wq)的增長已經使得系統在32k PID的控件接近飽和。異步
雖然MT wq消耗了大量資源,可是提供的併發執行水平仍是得不到知足。即便MT有不多的服務可是ST wq和MT wq仍是有限制。每一個wq有本身單獨的worker pool,MTwq智能在per CPU執行,而STwq能夠在整個系統執行。函數
CMWQ(Concurrency Managed Workqueue)是對原有wq的一個從新實現,主要解決如下問題:
a. API要可以兼容之前的wq實現
b. 使用per-CPU的worker pool要可以被全部的wq所共享,以便提供更好的併發靈活性,以避免浪費資源
c. 對於使用者要隱藏細節.net
2.workqueue設計框架
工做隊列的總體設計框架以下圖,這裏有多個結構體,他們之間的關係下面說明線程
work_struct結構體表明的是一個任務,它指向一個待異步執行的函數,無論驅動仍是子系統何時要執行這個函數,都必須把work加入到一個workqueue。設計
worker結構體表明一個工做者線程(worker thread),它主要一個接一個的執行掛入到隊列中的work,若是沒有work了,那麼工做者線程就掛起,這些工做者線程被worker-pool管理。blog
對於驅動和子系統的開發人員來講,接觸到的只有work,而背後的處理機制是管理worker-pool和處理掛入的work,這就是CMWQ設計的不一樣。
worker-pool結構體用來管理worker,對於每一種worker pool都分兩種狀況:一種是處理普通work,另外一種是處理高優先級的work。
workqueue_struct結構體表明的是工做隊列,工做隊列分unbound workqueue和bound workqueue。bound workqueue就是綁定到cpu上的,掛入到此隊列中的work只會在相對應的cpu上運行。unbound workqueue不綁定到特定的cpu,並且後臺線程池的數量也是動態的,具體workqueue關聯到哪一個worker pool,這是由workqueue_attrs決定的。
3.workqueue用法
工做隊列從字面意義上很好理解,雖然背後的實現機制有點複雜,可是使用的時候仍是簡單方便的,對於使用者來講,咱們只要定義一個work,而後把work加入到workqueue。若是當前沒有咱們須要的workqueue,那麼咱們須要本身建立一個。
下文各個API的具體說明見include/linux/workqueue.h
3.1workqueue_struct建立與銷燬
//建立workqueue_struct
#define alloc_ordered_workqueue(fmt, flags, args...) \
alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
//銷燬workqueue_struct
extern void destroy_workqueue(struct workqueue_struct *wq);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
由上能夠看出workqueue的建立根據flag的不一樣會建立不一樣種類的workqueue,但最終都會調用到接口alloc_workqueue,具體workqueue建立的過程及代碼分析見文章linux工做隊列 - workqueue_struct建立。
3.2初始化work_struct
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), 0)
#define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), 1)
#define INIT_DELAYED_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, 0)
#define INIT_DELAYED_WORK_ONSTACK(_work, _func) \
__INIT_DELAYED_WORK_ONSTACK(_work, _func, 0)
#define INIT_DEFERRABLE_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, TIMER_DEFERRABLE)
#define INIT_DEFERRABLE_WORK_ONSTACK(_work, _func) \ __INIT_DELAYED_WORK_ONSTACK(_work, _func, TIMER_DEFERRABLE)123456789101112131415163.3把work_struct加入工做隊列extern bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work);extern bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);extern bool mod_delayed_work_on(int cpu, struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);123456具體代碼分析見linux工做隊列 - 把work_struct加入工做隊列————————————————版權聲明:本文爲CSDN博主「鴨蛋西紅柿」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。原文連接:https://blog.csdn.net/cc289123557/article/details/52551176