最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

深入講解Linux中斷子系統(tǒng)--Workqueue

2022-12-28 15:22 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

說(shuō)明:

  1. Kernel版本:4.14

  2. ARM64處理器,Contex-A53,雙核

  3. 使用工具:Source Insight 3.5, Visio

1. 概述

  • Workqueue工作隊(duì)列是利用內(nèi)核線程來(lái)異步執(zhí)行工作任務(wù)的通用機(jī)制;

  • Workqueue工作隊(duì)列可以用作中斷處理的Bottom-half機(jī)制,利用進(jìn)程上下文來(lái)執(zhí)行中斷處理中耗時(shí)的任務(wù),因此它允許睡眠,而SoftirqTasklet在處理任務(wù)時(shí)不能睡眠;

來(lái)一張概述圖:

圖片
  • 在中斷處理過(guò)程中,或者其他子系統(tǒng)中,調(diào)用workqueue的調(diào)度或入隊(duì)接口后,通過(guò)建立好的鏈接關(guān)系圖逐級(jí)找到合適的worker,最終完成工作任務(wù)的執(zhí)行;

2. 數(shù)據(jù)結(jié)構(gòu)

2.1 總覽

此處應(yīng)有圖:

圖片
  • 先看看關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):

    1. work_struct:工作隊(duì)列調(diào)度的最小單位,work item;

    2. workqueue_struct:工作隊(duì)列,work item都掛入到工作隊(duì)列中;

    3. workerwork item的處理者,每個(gè)worker對(duì)應(yīng)一個(gè)內(nèi)核線程;

    4. worker_poolworker池(內(nèi)核線程池),是一個(gè)共享資源池,提供不同的worker來(lái)對(duì)work item進(jìn)行處理;

    5. pool_workqueue:充當(dāng)橋梁紐帶的作用,用于連接workqueueworker_pool,建立鏈接關(guān)系;

下邊看看細(xì)節(jié)吧:


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個(gè)人覺(jué)得比較好的學(xué)習(xí)書(shū)籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書(shū)、實(shí)戰(zhàn)項(xiàng)目及代碼)? ?


2.2 work

struct work_struct用來(lái)描述work,初始化一個(gè)work并添加到工作隊(duì)列后,將會(huì)將其傳遞到合適的內(nèi)核線程來(lái)進(jìn)行處理,它是用于調(diào)度的最小單位。

關(guān)鍵字段描述如下:

圖片說(shuō)明下data字段:

圖片

2.3 workqueue

  • 內(nèi)核中工作隊(duì)列分為兩種:

    1. bound:綁定處理器的工作隊(duì)列,每個(gè)worker創(chuàng)建的內(nèi)核線程綁定到特定的CPU上運(yùn)行;

    2. unbound:不綁定處理器的工作隊(duì)列,創(chuàng)建的時(shí)候需要指定WQ_UNBOUND標(biāo)志,內(nèi)核線程可以在處理器間遷移;

  • 內(nèi)核默認(rèn)創(chuàng)建了一些工作隊(duì)列(用戶也可以創(chuàng)建):

    1. system_mq:如果work item執(zhí)行時(shí)間較短,使用本隊(duì)列,調(diào)用schedule[_delayed]_work[_on]()接口就是添加到本隊(duì)列中;

    2. system_highpri_mq:高優(yōu)先級(jí)工作隊(duì)列,以nice值-20來(lái)運(yùn)行;

    3. system_long_wq:如果work item執(zhí)行時(shí)間較長(zhǎng),使用本隊(duì)列;

    4. system_unbound_wq:該工作隊(duì)列的內(nèi)核線程不綁定到特定的處理器上;

    5. system_freezable_wq:該工作隊(duì)列用于在Suspend時(shí)可凍結(jié)的work item

    6. system_power_efficient_wq:該工作隊(duì)列用于節(jié)能目的而選擇犧牲性能的work item;

    7. system_freezable_power_efficient_wq:該工作隊(duì)列用于節(jié)能或Suspend時(shí)可凍結(jié)目的的work item

struct workqueue_struct關(guān)鍵字段介紹如下:

2.4 worker

  • 每個(gè)worker對(duì)應(yīng)一個(gè)內(nèi)核線程,用于對(duì)work item的處理;

  • worker根據(jù)工作狀態(tài),可以添加到worker_pool的空閑鏈表或忙碌列表中;

  • worker處于空閑狀態(tài)時(shí)并接收到工作處理請(qǐng)求,將喚醒內(nèi)核線程來(lái)處理;

  • 內(nèi)核線程是在每個(gè)worker_pool中由一個(gè)初始的空閑工作線程創(chuàng)建的,并根據(jù)需要?jiǎng)討B(tài)創(chuàng)建和銷毀;

關(guān)鍵字段描述如下:

2.5 worker_pool

  • worker_pool是一個(gè)資源池,管理多個(gè)worker,也就是管理多個(gè)內(nèi)核線程;

  • 針對(duì)綁定類型的工作隊(duì)列,worker_pool是Per-CPU創(chuàng)建,每個(gè)CPU都有兩個(gè)worker_pool,對(duì)應(yīng)不同的優(yōu)先級(jí),nice值分別為0和-20;

  • 針對(duì)非綁定類型的工作隊(duì)列,worker_pool創(chuàng)建后會(huì)添加到unbound_pool_hash哈希表中;

  • worker_pool管理一個(gè)空閑鏈表和一個(gè)忙碌列表,其中忙碌列表由哈希管理;

關(guān)鍵字段描述如下:

2.6 pool_workqueue

  • pool_workqueue充當(dāng)紐帶的作用,用于將workqueueworker_pool關(guān)聯(lián)起來(lái);

關(guān)鍵字段描述如下:

2.7 小結(jié)

再來(lái)張圖,首尾呼應(yīng)一下:

圖片

3. 流程分析

3.1 workqueue子系統(tǒng)初始化

  • workqueue子系統(tǒng)的初始化分成兩步來(lái)完成的:workqueue_init_earlyworkqueue_init。

3.1.1 workqueue_init_early

圖片
  • workqueue子系統(tǒng)早期初始化函數(shù)完成的主要工作包括:

    1. 創(chuàng)建pool_workqueue的SLAB緩存,用于動(dòng)態(tài)分配struct pool_workqueue結(jié)構(gòu);

    2. 為每個(gè)CPU都分配兩個(gè)worker_pool,其中的nice值分別為0和HIGHPRI_NICE_LEVEL,并且為每個(gè)worker_poolworker_pool_idr中分配一個(gè)ID號(hào);

    3. 為unbound工作隊(duì)列創(chuàng)建默認(rèn)屬性,struct workqueue_attrs屬性,主要描述內(nèi)核線程的nice值,以及cpumask值,分別針對(duì)優(yōu)先級(jí)以及允許在哪些CPU上執(zhí)行;

    4. 為系統(tǒng)默認(rèn)創(chuàng)建幾個(gè)工作隊(duì)列,這幾個(gè)工作隊(duì)列的描述在上文的數(shù)據(jù)結(jié)構(gòu)部分提及過(guò),不再贅述;

從圖中可以看出創(chuàng)建工作隊(duì)列的接口為:alloc_workqueue,如下圖:

圖片
  • alloc_workqueue完成的主要工作包括:

    1. 首先當(dāng)然是要分配一個(gè)struct workqueue_struct的數(shù)據(jù)結(jié)構(gòu),并且對(duì)該結(jié)構(gòu)中的字段進(jìn)行初始化操作;

    2. 前文提到過(guò)workqueue最終需要和worker_pool關(guān)聯(lián)起來(lái),而這個(gè)紐帶就是pool_workqueuealloc_and_link_pwqs函數(shù)就是完成這個(gè)功能:1)如果工作隊(duì)列是綁定到CPU上的,則為每個(gè)CPU都分配pool_workqueue并且初始化,通過(guò)link_pwq將工作隊(duì)列與pool_workqueue建立連接;2)如果工作隊(duì)列不綁定到CPU上,則按內(nèi)存節(jié)點(diǎn)(NUMA,參考之前內(nèi)存管理的文章)來(lái)分配pool_workqueue,調(diào)用get_unbound_pool來(lái)實(shí)現(xiàn),它會(huì)根據(jù)wq屬性先去查找,如果沒(méi)有找到相同的就創(chuàng)建一個(gè)新的pool_workqueue,并且添加到unbound_pool_hash哈希表中,最后也會(huì)調(diào)用link_pwq來(lái)建立連接;

    3. 創(chuàng)建工作隊(duì)列時(shí),如果設(shè)置了WQ_MEM_RECLAIM標(biāo)志,則會(huì)新建rescuer worker,對(duì)應(yīng)rescuer_thread內(nèi)核線程。當(dāng)內(nèi)存緊張時(shí),新創(chuàng)建worker可能會(huì)失敗,這時(shí)候由rescuer來(lái)處理這種情況;

    4. 最終將新建好的工作隊(duì)列添加到全局鏈表workqueues中;

3.1.2 workqueue_init

workqueue子系統(tǒng)第二階段的初始化:

圖片
  • 主要完成的工作是給之前創(chuàng)建好的worker_pool,添加一個(gè)初始的worker;

  • create_worker函數(shù)中,創(chuàng)建的內(nèi)核線程名字為kworker/XX:YY或者kworker/uXX:YY,其中XX表示worker_pool的編號(hào),YY表示worker的編號(hào),u表示unbound;

workqueue子系統(tǒng)初始化完成后,基本就已經(jīng)將數(shù)據(jù)結(jié)構(gòu)的關(guān)聯(lián)建立好了,當(dāng)有work來(lái)進(jìn)行調(diào)度的時(shí)候,就可以進(jìn)行處理了。

3.2 work調(diào)度

3.2.1 schedule_work

schedule_work接口為例進(jìn)行分析:

圖片
  • schedule_work默認(rèn)是將work添加到系統(tǒng)的system_work工作隊(duì)列中;

  • queue_work_on接口中的操作判斷要添加work的標(biāo)志位,如果已經(jīng)置位了WORK_STRUCT_PENDING_BIT,表明已經(jīng)添加到了隊(duì)列中等待執(zhí)行了,否則,需要調(diào)用__queue_work來(lái)進(jìn)行添加。注意了,這個(gè)操作是在關(guān)中斷的情況下進(jìn)行的,因?yàn)楣ぷ麝?duì)列使用WORK_STRUCT_PENDING_BIT位來(lái)同步work的插入和刪除操作,設(shè)置了這個(gè)比特后,然后才能執(zhí)行work,這個(gè)過(guò)程可能被中斷或搶占打斷;

  • workqueue的標(biāo)志位設(shè)置了__WQ_DRAINING,表明工作隊(duì)列正在銷毀,所有的work都要處理完,此時(shí)不允許再將work添加到隊(duì)列中,有一種特殊情況:銷毀過(guò)程中,執(zhí)行work時(shí)又觸發(fā)了新的work,也就是所謂的chained work

  • 判斷workqueue的類型,如果是bound類型,根據(jù)CPU來(lái)獲取pool_workqueue,如果是unbound類型,通過(guò)node號(hào)來(lái)獲取pool_workqueue;

  • get_work_pool獲取上一次執(zhí)行workworker_pool,如果本次執(zhí)行的worker_pool與上次執(zhí)行的worker_pool不一致,且通過(guò)find_worker_executing_work判斷work正在某個(gè)worker_pool中的worker中執(zhí)行,考慮到緩存熱度,放到該worker執(zhí)行是更合理的選擇,進(jìn)而根據(jù)該worker獲取到pool_workqueue;

  • 判斷pool_workqueue活躍的work數(shù)量,少于最大限值則將work加入到pool->worklist中,否則加入到pwq->delayed_works鏈表中,如果__need_more_worker判斷沒(méi)有worker在執(zhí)行,則喚醒worker內(nèi)核線程執(zhí)行;

  • 總結(jié):

    1. schedule_work完成的工作是將work添加到對(duì)應(yīng)的鏈表中,而在添加的過(guò)程中,首先是需要確定pool_workqueue;

    2. pool_workqueue對(duì)應(yīng)一個(gè)worker_pool,因此確定了pool_workqueue也就確定了worker_pool,進(jìn)而可以將work添加到工作鏈表中;

    3. pool_workqueue的確定分為三種情況:1)bound類型的工作隊(duì)列,直接根據(jù)CPU號(hào)獲?。?)unbound類型的工作隊(duì)列,根據(jù)node號(hào)獲取,針對(duì)unbound類型工作隊(duì)列,pool_workqueue的釋放是異步執(zhí)行的,需要判斷refcnt的計(jì)數(shù)值,因此在獲取pool_workqueue時(shí)可能要多次retry;3)根據(jù)緩存熱度,優(yōu)先選擇正在被執(zhí)行的worker_pool;

3.2.2 worker_thread

work添加到工作隊(duì)列后,最終的執(zhí)行在worker_thread函數(shù)中:

圖片
  • 在創(chuàng)建worker時(shí),創(chuàng)建內(nèi)核線程,執(zhí)行函數(shù)為worker_thread;

  • worker_thread在開(kāi)始執(zhí)行時(shí),設(shè)置標(biāo)志位PF_WQ_WORKER,調(diào)度器在進(jìn)行調(diào)度處理時(shí)會(huì)對(duì)task進(jìn)行判斷,針對(duì)workerqueue worker有特殊處理;

  • worker對(duì)應(yīng)的內(nèi)核線程,在沒(méi)有處理work的時(shí)候是睡眠狀態(tài),當(dāng)被喚醒的時(shí)候,跳轉(zhuǎn)到woke_up開(kāi)始執(zhí)行;

  • woke_up之后,如果此時(shí)worker是需要銷毀的,那就進(jìn)行清理工作并返回。否則,離開(kāi)IDLE狀態(tài),并進(jìn)入recheck模塊執(zhí)行;

  • recheck部分,首先判斷是否需要更多的worker來(lái)處理,如果沒(méi)有任務(wù)處理,跳轉(zhuǎn)到sleep地方進(jìn)行睡眠。有任務(wù)需要處理時(shí),會(huì)判斷是否有空閑內(nèi)核線程以及是否需要?jiǎng)討B(tài)創(chuàng)建,再清除掉worker的標(biāo)志位,然后遍歷工作鏈表,對(duì)鏈表中的每個(gè)節(jié)點(diǎn)調(diào)用process_one_worker來(lái)處理;

  • sleep部分比較好理解,沒(méi)有任務(wù)處理時(shí),worker進(jìn)入空閑狀態(tài),并將當(dāng)前的內(nèi)核線程設(shè)置成睡眠狀態(tài),讓出CPU;

  • 總結(jié):

    1. 管理worker_pool的內(nèi)核線程池時(shí),如果有PENDING狀態(tài)的work,并且發(fā)現(xiàn)沒(méi)有正在運(yùn)行的工作線程(worker_pool->nr_running == 0),喚醒空閑狀態(tài)的內(nèi)核線程,或者動(dòng)態(tài)創(chuàng)建內(nèi)核線程;

    2. 如果work已經(jīng)在同一個(gè)worker_pool的其他worker中執(zhí)行,不再對(duì)該work進(jìn)行處理;

work的執(zhí)行函數(shù)為process_one_worker

圖片
  • work可能在同一個(gè)CPU上不同的worker中運(yùn)行,直接退出;

  • 調(diào)用worker->current_func(),完成最終work的回調(diào)函數(shù)執(zhí)行;

3.3 worker動(dòng)態(tài)管理

3.3.1 worker狀態(tài)機(jī)變換

圖片
  • worker_pool通過(guò)nr_running字段來(lái)在不同的狀態(tài)機(jī)之間進(jìn)行切換;

  • worker_pool中有work需要處理時(shí),需要至少保證有一個(gè)運(yùn)行狀態(tài)的worker,當(dāng)nr_running大于1時(shí),將多余的worker進(jìn)入IDLE狀態(tài),沒(méi)有work需要處理時(shí),所有的worker都會(huì)進(jìn)入IDLE狀態(tài);

  • 執(zhí)行work時(shí),如果回調(diào)函數(shù)阻塞運(yùn)行,那么會(huì)讓worker進(jìn)入睡眠狀態(tài),此時(shí)調(diào)度器會(huì)進(jìn)行判斷是否需要喚醒另一個(gè)worker;

  • IDLE狀態(tài)的worker都存放在idle_list鏈表中,如果空閑時(shí)間超過(guò)了300秒,則會(huì)將其進(jìn)行銷毀;

  1. Running->Suspend

圖片
  • 當(dāng)worker進(jìn)入睡眠狀態(tài)時(shí),如果該worker_pool沒(méi)有其他的worker處于運(yùn)行狀態(tài),那么是需要喚醒一個(gè)空閑的worker來(lái)維持并發(fā)處理的能力;

  1. Suspend->Running

圖片
  • 睡眠狀態(tài)可以通過(guò)wake_up_worker來(lái)進(jìn)行喚醒處理,最終判斷如果該worker不在運(yùn)行狀態(tài),則增加worker_poolnr_running值;

3.3.2 worker的動(dòng)態(tài)添加和刪除

  1. 動(dòng)態(tài)刪除

圖片
  • worker_pool初始化時(shí),注冊(cè)了timer的回調(diào)函數(shù),用于定時(shí)對(duì)空閑鏈表上的worker進(jìn)行處理,如果worker太多,且空閑時(shí)間太長(zhǎng),超過(guò)了5分鐘,那么就直接進(jìn)行銷毀處理了;

  1. 動(dòng)態(tài)添加

圖片
  • 內(nèi)核線程執(zhí)行worker_thread函數(shù)時(shí),如果沒(méi)有空閑的worker,會(huì)調(diào)用manage_workers接口來(lái)創(chuàng)建更多的worker來(lái)處理工作;


原文作者:LoyenWang



深入講解Linux中斷子系統(tǒng)--Workqueue的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
青阳县| 怀来县| 千阳县| 通许县| 德州市| 德清县| 安泽县| 都兰县| 宣威市| 洛隆县| 昆明市| 新兴县| 亳州市| 乌拉特中旗| 灵川县| 青田县| 高台县| 祁阳县| 鸡西市| 呼图壁县| 宜城市| 东城区| 始兴县| 静乐县| 崇信县| 修武县| 黔东| 景德镇市| 吐鲁番市| 章丘市| 惠州市| 富川| 响水县| 平武县| 宣武区| 托里县| 永和县| 兰溪市| 广州市| 三河市| 安庆市|