工作队列线程
工作隊列線程
工作隊列是一個內核對象,專門用來處理先進先出管理器管理的工作項成員。每一個工作項都是通過調用它指定的函數來處理的。工作隊列的典型應用是在中斷函數或者高優先級線程中把一些非緊急任務分給低優先級線程處理,這樣非緊急任務就不會對時間敏感的任務造成影響了
工作隊列線程相關概念
我們可以定義任意數量的工作隊列。每個工作隊列都通過它的內存地址引用。
一個工作隊列有一下幾個主要屬性:
①隊列中的工作項都是被添加進去、但是還沒被處理的
②隊列中的工作項是通過一個線程處理的,這個線程的優先級是可配置的,我們可以把它配置成協作式線程或者搶占式線程
工作隊列在使用之前需要進行初始化。初始化時把工作隊列設置為空并且派生了一個工作隊列線程。
工作項生活周期
我們可以定義任意數量的工作項。每一個工作項通過他的內存地址來引用。 一個工作項有以下幾個主要屬性: ①一個處理函數,當工作線程處理工作項成員時這個函數就會被調用。這個函數有一個參數,這個參數是工作項的內存地址。 ②一個掛起標志,內核使用這個標志表示當前的一個工作項是工作隊列的一個成員 ③一個隊列鏈表,內核通過這個隊列鏈表把工作隊列中所有掛起項鏈接起來一個工作項在使用前需要進行初始化,初始化時會記錄工作項的處理函數并標記成非掛起狀態 一個工作項可以在中斷處理函數或者線程中提交到工作隊列中,新提交的工作項會被追加到工作隊列結尾。一旦工作隊列線程處理完之前隊列中的工作項后,線程將會從隊列中移除一個掛起的工作項并調用工作項的處理函數。取決于工作隊列線程的優先級和隊列中其他成員的工作請求,一個工作項可能會被立即處理或者是在隊列中等待一段時間。 工作隊列的處理函數可以調用所有內核APIs。然而,我們需要小心操作被阻塞,因為工作隊列在處理完當前處理函數之前是不能處理隊列中其它工作項的(所以如果調用了阻塞是接口,那么工作隊列中其它的隊列項將會被阻塞不能被處理) 工作隊列處理函數有一個函數參數,如果不使用可以忽略它。如果執行處理函數時請求一些額外的信息,工作項可以嵌入在一個大的數據構體中。處理函數可以通過這個參數值計算出大的數據構體地址,這樣就可以訪問一些需要的額外信息了 當有一個工作需要執行時可以先初始化一個工作項然后提交到指定的工作隊列中。如果一個中斷服務函數或者一個線程嘗試提交一個已經掛起的工作項,是不會對這個工作項產生影響的,這個工作項會保持在當前工作隊列中的位置,并且只會被執行一次 在工作項不是掛起狀態時,可以重新提交處理函數的參數到工作隊列中。這允許處理函數在工作執行階段不會對工作隊列中其他工作成員的處理造成過度延遲 *********重要注釋********* 直到工作項被工作隊列線程處理之前,一個掛起的工作項不能被告警(alert),這意味著一個工作項在掛起的時候不能被重新初始化。因此,工作項處理函數執行它的工作時需要的一些額外信息在處理函數執行完之前不可以被告警(alert)
延時工作
一個中斷服務函數或者線程需要一個能在延時一個指定時間段后再被調用的工作項,而不是立即執行??梢酝ㄟ^提交一個延時工作項到工作隊列中實現 一個延時工作項和標準工作項相比,有如下一些額外的屬性: ①一個延時時間,這個延時時間指定的是延時工作項在提交到工作隊列前需要延時的時間長度②一個工作隊列標示符,表示當前工作項需要被提交到哪個工作隊列中
雖然談事工作項和標準工作項操作時使用了不同的內核APIs,但是他們的初始化和提交到工作隊列的操作是很相似的。當發出一個提交請求時內核會啟動一個超市機制,當指定的時間到達時會被觸發。當超時出現時內核會提交一個延時工作項到它指定的工作隊列中、這個工作項在被處理之前會一直以掛起的狀態保留在工作隊列中 一個中斷服務函數或者線程可以在延時工作項超時之前關閉這個工作項任務。這樣,這個工作項超時任務會被異步終止并且不會執行指定的工作
如果關閉一個超時時間已經達到的延時工作項是不會對它產生任何影響的。一個工作項會維持在原來的工作隊列中的掛起狀態、除非工作項已經被移除并且被工作線程處理完畢。因此,一旦延時工作項的超時已經到達,那么這個工作項總是會被處理且不可以被取消
系統工作隊列
內核定義了一個叫做系統工作隊列的工作隊列,這個工作隊列可以被需要工作隊列的應用程序或者內核使用。系統工作隊列是可選功能,并且只有在應用程序使用它的時候才存在 *********重要注釋*********只有在一些新的工作項不能提交到系統工作隊列中時才需要定義一些額外的工作隊列,因為一共新的工作隊列會有很多內存開銷。只有在一個新的工作項和系統工作項中已經存在的工作項不可共存、有著不可接受的影響時才會定義新的工作隊列。例如,如果一個新的工作項因為執行了阻塞操作引起系統工作隊列延時了一個不可接受的時間時,就會定義新的工作隊列
工作隊列的實現
定義一個工作隊列
一個工作隊列通過 struct k_work_q 變量來定義。工作隊列通過它的線程定義一個堆棧區域然后調用 k_work_q_start()來初始化。定義堆棧區域時需要使用K_THREAD_STACK_DEFINE宏定義,確保能正確設置堆棧內存空間 下面的代碼定義并初始化了一個工作隊列: #define MY_STACK_SIZE 512 #define MY_PRIORITY 5K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);struct k_work_q my_work_q;k_work_q_start(&my_work_q, my_stack_area,K_THREAD_STACK_SIZEOF(my_stack_area), MY_PRIORITY);提交一個工作項
一個工作項通過首先需要通過struct k_work變量來定義,然后調用k_work_init()接口來初始化 一個已經初始化的工作項可以通過調用k_work_submit()提交到系統工作隊列中,或者調用k_work_submit_to_queue()接口提交到指定的工作隊列中 下面的代碼演示了如何通過中斷服務函數把打印錯誤消息函數移交到系統工作隊列中。如果中斷服務函數嘗試重新提交一個仍然掛起的工作項,這個工作項是不會被改變的并且相關的錯誤信息也不會被打印 struct device_info {struct k_work work;char name[16] } my_device;void my_isr(void *arg) {...if (error detected) {k_work_submit(&my_device.work);}... }void print_error(struct k_work *item) {struct device_info *the_device =CONTAINER_OF(item, struct device_info, work);printk("Got error on device %s\n", the_device->name); }/* initialize name info for a device */ strcpy(my_device.name, "FOO_dev");/* initialize work item for printing device's error messages */ k_work_init(&my_device.work, print_error);/* install my_isr() as interrupt handler for the device (not shown) */ ...提交一個延時工作項
一個延時工作項通過struct k_delayed_work變量來定義,然后通過k_delayed_work_init()接口初始化這個工作項 一個初始化的工作項可以通過調用k_delayed_work_submit()提交到系統工作隊列中,或者調用k_delayed_work_submit_to_queue()提交到指定的工作隊列中。一個延時工作項可以在提交到工作隊列之前的延時期間通過調用k_delayed_work_cancel()接口取消提交請求推薦用法
使用系統工作隊列可以從中斷處理函數中推遲一些和中斷相關的復雜處理到協作式線程中。這樣可以及時處理和中斷相關操作的同時響應后續到來的中斷,并且還不需要應用程序定義額外的線程來處理(而是通過系統工作隊列線程來處理)配置選項
相關的配置選項: CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE CONFIG_SYSTEM_WORKQUEUE_PRIORITYAPIs
k_work_q_start() k_work_init() k_work_submit() k_work_submit_to_queue() k_delayed_work_init() k_delayed_work_submit() k_delayed_work_submit_to_queue() k_delayed_work_cancel() k_work_pending()總結
- 上一篇: 内核时钟
- 下一篇: 树莓派上搭建svn服务器