歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux工作隊列實現機制

Linux工作隊列實現機制

日期:2017/2/28 15:59:21   编辑:Linux教程

工作項、工作隊列和工作者線程

把推後執行的任務叫做工作(work),描述它的數據結構為work_struct ,這些工作以隊列結構組織成工作隊列(workqueue),其數據結構為workqueue_struct ,而工作線程就是負責執行工作隊列中的工作。系統默認的工作者線程為events。

工作隊列(work queue)是另外一種將工作推後執行的形式。工作隊列可以把工作推後,交由一個內核線程去執行—這個下半部分總是會在進程上下文執行,但由於是內核線程,其不能訪問用戶空間。最重要特點的就是工作隊列允許重新調度甚至是睡眠。

通常,在工作隊列和軟中斷/tasklet中作出選擇非常容易。可使用以下規則:
如果推後執行的任務需要睡眠,那麼只能選擇工作隊列;
如果推後執行的任務需要延時指定的時間再觸發,那麼使用工作隊列,因為其可以利用timer延時;
如果推後執行的任務需要在一個tick之內處理,則使用軟中斷或tasklet,因為其可以搶占普通進程和內核線程;
如果推後執行的任務對延遲的時間沒有任何要求,則使用工作隊列,此時通常為無關緊要的任務。

實際上,工作隊列的本質就是將工作交給內核線程處理,因此其可以用內核線程替換。但是內核線程的創建和銷毀對編程者的要求較高,而工作隊列實現了內核線程的封裝,不易出錯,所以我們也推薦使用工作隊列。

工作隊列使用

相關文件:

kernel/include/linux/workqueue.h

Kernel/kernel/workqueue.c

工作隊列的創建

要使用工作隊列,需要先創建工作項,有兩種方式:

1)靜態創建:

DECLARE_WORK(name,function); 定義正常執行的工作項

DECLARE_DELAYED_WORK(name,function); 定義延後執行的工作項

2)動態創建,運行時創建:

通常在probe()函數中執行下面的操作來初始化工作項:

INIT_WORK(&work, new_ts_work);

INIT_DELAYED_WORK(&led_work,s0340_ledtime_scanf);

工作隊列待執行的函數原型是:

typedef void(*work_func_t)(structwork_struct *work);

這個函數會由一個工作者線程執行,因此,函數會運行在進程上下文中。默認情況下,允許響應中斷,並且不持有任何鎖。如果需要,函數可以睡眠。需要注意的是,盡管該函數運行在進程上下文中,但它不能訪問用戶空間,因為內核線程在用戶空間沒有相關的內存映射。通常在系統調用發生時,內核會代表用戶空間的進程運行,此時它才能訪問用戶空間,也只有在此時它才會映射用戶空間的內存。

創建了工作項之後,在適當的時候可以通過下面的兩種方式來提交工作項給工作者線程,通常我們使用的工作隊列和工作者線程都是系統初始化時候默認創建的。

工作隊列的調度運行

schedule_work(&work);

&work馬上就會被調度,一旦其所在的處理器上的工作者線程被喚醒,它就會被執行。

schedule_delayed_work(&delay_work,delay);

&delay_work指向的delay_work直到delay指定的時鐘節拍用完以後才會執行。

eg:

schedule_delayed_work(&kpd_backlight_work,msecs_to_jiffies(300));

默認工作隊列和工作者線程創建過程

系統默認的工作隊列名稱是:keventd_wq,默認的工作者線程叫:events/n,這裡的n是處理器的編號,每個處理器對應一個線程。比如,單處理器的系統只有events/0這樣一個線程。而雙處理器的系統就會多一個events/1線程。

默認的工作者線程會從多個地方得到被推後的工作。許多內核驅動程序都把它們的下半部交給默認的工作者線程去做。除非一個驅動程序或者子系統必須建立一個屬於它自己的內核線程,否則最好使用默認線程。不過並不存在什麼東西能夠阻止代碼創建屬於自己的工作者線程。如果你需要在工作者線程中執行大量的處理操作,這樣做或許會帶來好處。處理器密集型和性能要求嚴格的任務會因為擁有自己的工作者線程而獲得好處。

默認的工作隊列keventd_wq只有一個,但是其工作者線程在每一個cpu上都有。而標記為singlethread的工作者線程最存在於一個cpu上。

關於默認工作隊列keventd_wq和工作者線程events/n的建立在文件Kernel/kernel/workqueue.c中實現。

Start_kernel()-->rest_init(),該函數中創建了兩個內核線程kernel_init和kthreadd,這兩個線程都和本文描述的部分有關系,先說說kernel_init。

kernel_init()-->do_basic_setup()-->init_workqueues(),該函數中創建了上面提到的默認工作隊列和工作者線程。

init_workqueues()-->

-->hotcpu_notifier(workqueue_cpu_callback,0);

-->keventd_wq=create_workqueue("events");

注冊的cpu通知鏈cpu_chain上的回調函數是workqueue_cpu_callback(),raw_notifier_call_chain()函數用來調用cpu_chain上的所有回調函數。

這裡主要關注的是函數:create_workqueue("events");

@kernel/include/linux/workqueue.h

#define__create_workqueue(name,singlethread,freezeable,rt)/

__create_workqueue_key((name),(singlethread),(freezeable),(rt),/NULL,NULL)

#definecreate_workqueue(name)__create_workqueue((name),0,0,0)

#definecreate_rt_workqueue(name)__create_workqueue((name),0,0,1)

#definecreate_freezeable_workqueue(name)__create_workqueue((name),1,1,0)

#definecreate_singlethread_workqueue(name)__create_workqueue((name),1,0,0)

從宏__create_workqueue的參數可以看出,可以通過傳遞不同的參數:是否單cpu線程,是否可凍結,是否實時來創建不同類型的工作隊列和工作者線程。

work_struct工作項結構體定義:@kernel/include/linux/workqueue.h

工作隊列workqueue_struct結構體:@kernel/kernel/workqueue.c

Copyright © Linux教程網 All Rights Reserved