一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Linux中的工作隊列

 wu_wade 2015-03-03
 

工作隊列一般用來做滯后的工作,比如在中斷里面要做很多事,但是比較耗時,這時就可以把耗時的工作放到工作隊列。說白了就是系統(tǒng)延時調(diào)度的一個自定義函數(shù)。

 

工作隊列是實現(xiàn)延遲的新機(jī)制,從 2.5 版本 Linux 內(nèi)核開始提供該功能。不同于微線程一步到位的延遲方法,工作隊列采用通用的延遲機(jī)制, 工作隊列的處理程序函數(shù)能夠休眠(這在微線程模式下無法實現(xiàn))。 工作隊列可以有比微線程更高的時延,并為任務(wù)延遲提供功能更豐富的 API。 從前,延遲功能通過 keventd 對任務(wù)排隊來實現(xiàn), 但是現(xiàn)在由內(nèi)核工作線程 events/X 來管理。

 

工作隊列提供一個通用的辦法將任務(wù)延遲到 bottom halves 處于核心的是工作隊列(結(jié)構(gòu)體 workqueue_struct), 任務(wù)被安排到該結(jié)構(gòu)體當(dāng)中。 任務(wù)由結(jié)構(gòu)體 work_struct 來說明, 用來鑒別哪些任務(wù)被延遲以及使用哪個延遲函數(shù)(參見 3)。 events/X 內(nèi)核線程(每 CPU 一個)從工作隊列中抽取任務(wù)并激活一個 bottom-half 處理程序(由處理程序函數(shù)在結(jié)構(gòu)體 work_struct 中指定)。

 

 

3. 工作隊列背后的處理過程

 

Linux中的工作隊列 - Tanatseng - Tanatseng Blog

 

 

由于 work_struct 中指出了要采用的處理程序函數(shù), 因此可以利用工作隊列來為不同的處理程序進(jìn)行任務(wù)排隊。 現(xiàn)在,讓我們看一下能夠用于工作隊列的 API 函數(shù)。

 

工作隊列 API

 

工作隊列 API 比微線程稍復(fù)雜,主要是因為它支持很多選項。 我們首先探討一下工作隊列,然后再看一下任務(wù)和變體。

 

通過 3 可以回想工作隊列的核心結(jié)構(gòu)體是隊列本身。 該結(jié)構(gòu)體用于將任務(wù)安排出 top half ,進(jìn)入 bottom half ,從而延遲它的執(zhí)行。 工作隊列通過宏調(diào)用生成 create_workqueue,返回一個 workqueue_struct 參考值。當(dāng)用完一個工作隊列,可以通過調(diào)用函數(shù) destroy_workqueue 來去掉它(如果需要):

 

struct workqueue_struct *create_workqueue( name );

struct workqueue_struct *create_singlethread_workqueue(const char *name);

void destroy_workqueue( struct workqueue_struct * );

 

一個工作隊列必須明確的在使用前創(chuàng)建,若使用 create_workqueue, 就得到一個工作隊列它在系統(tǒng)的每個處理器上有一個專用的線程。在很多情況下,過多線程對系統(tǒng)性能有影響,如果單個線程就足夠則使用 create_singlethread_workqueue 來創(chuàng)建工作隊列。

通過工作隊列與之通信的任務(wù)可以由結(jié)構(gòu)體 work_struct 來定義。 通常,該結(jié)構(gòu)體是用來進(jìn)行任務(wù)定義的結(jié)構(gòu)體的第一個元素(后面有相關(guān)例子)。 工作隊列 API 提供三個函數(shù)來初始化任務(wù)(通過一個事先分配的緩存); 參見 清單 6。 INIT_WORK 提供必需的初始化數(shù)據(jù)以及處理程序函數(shù)的配置(由用戶傳遞進(jìn)來)。 如果開發(fā)人員需要在任務(wù)被排入工作隊列之前發(fā)生延遲,可以使用宏 INIT_DELAYED_WORK INIT_DELAYED_WORK_DEFERRABLE。

 

清單 6. 任務(wù)初始化宏

                           

INIT_WORK( work, func );

INIT_DELAYED_WORK( work, func );

INIT_DELAYED_WORK_DEFERRABLE( work, func );

 

INIT_* 做更加全面的初始化結(jié)構(gòu)的工作,在第一次建立結(jié)構(gòu)時使用。

任務(wù)結(jié)構(gòu)體的初始化完成后,接下來要將任務(wù)安排進(jìn)工作隊列。 可采用多種方法來完成這一操作(參見 清單 7)。 首先,利用 queue_work 簡單地將任務(wù)安排進(jìn)工作隊列(這將任務(wù)綁定到當(dāng)前的 CPU)。 或者,可以通過 queue_work_on 來指定處理程序在哪個 CPU 上運行。 兩個附加的函數(shù)為延遲任務(wù)提供相同的功能(其結(jié)構(gòu)體裝入結(jié)構(gòu)體 work_struct 之中,并有一個 計時器用于任務(wù)延遲 )。

 

清單 7. 工作隊列函數(shù)

                           

int queue_work( struct workqueue_struct *wq, struct work_struct *work );

int queue_work_on( int cpu, struct workqueue_struct *wq, struct work_struct *work );

int queue_delayed_work( struct workqueue_struct *wq,

                     struct delayed_work *dwork, unsigned long delay );

int queue_delayed_work_on( int cpu, struct workqueue_struct *wq,

                     struct delayed_work *dwork, unsigned long delay );

 

每個都添加work到給定的workqueue。如果使用 queue_delay_work, 則實際的工作至少要經(jīng)過指定的 jiffies 才會被執(zhí)行。 這些函數(shù)若返回 1 則工作被成功加入到隊列; 若為0,則意味著這個 work 已經(jīng)在隊列中等待,不能再次加入。

可以使用全局的內(nèi)核全局工作隊列,利用 4 個函數(shù)來為工作隊列定位。 這些函數(shù)(見 清單 8)模擬 清單 7,只是不需要定義工作隊列結(jié)構(gòu)體。

 

清單 8. 內(nèi)核全局工作隊列函數(shù)

                           

int schedule_work( struct work_struct *work );

int schedule_work_on( int cpu, struct work_struct *work );

int scheduled_delayed_work( struct delayed_work *dwork, unsigned long delay );

int scheduled_delayed_work_on(

              int cpu, struct delayed_work *dwork, unsigned long delay );

 

還有一些幫助函數(shù)用于清理或取消工作隊列中的任務(wù)。想清理特定的任務(wù)項目并阻塞任務(wù), 直到任務(wù)完成為止, 可以調(diào)用 flush_work 來實現(xiàn)。 指定工作隊列中的所有任務(wù)能夠通過調(diào)用 flush_workqueue 來完成。 這兩種情形下,調(diào)用者阻塞直到操作完成為止。 為了清理內(nèi)核全局工作隊列,可調(diào)用 flush_scheduled_work。

 

int flush_work( struct work_struct *work );

int flush_workqueue( struct workqueue_struct *wq );

void flush_scheduled_work( void );

 

flush_workqueue 返回后, 沒有在這個調(diào)用前提交的函數(shù)在系統(tǒng)中任何地方運行。

還沒有在處理程序當(dāng)中執(zhí)行的任務(wù)可以被取消。 調(diào)用 cancel_work_sync 將會終止隊列中的任務(wù)或者阻塞任務(wù)直到回調(diào)結(jié)束(如果處理程序已經(jīng)在處理該任務(wù))。 如果任務(wù)被延遲,可以調(diào)用 cancel_delayed_work_sync。

 

int cancel_work_sync( struct work_struct *work );

int cancel_delayed_work_sync( struct delayed_work *dwork );

 

最后,可以通過調(diào)用 work_pending 或者 delayed_work_pending 來確定任務(wù)項目是否在進(jìn)行中。

 

work_pending( work );

delayed_work_pending( work );

 

這就是工作隊列 API 的核心。在 ./kernel/workqueue.c 中能夠找到工作隊列 API 的實現(xiàn)方法, API ./include/linux/workqueue.h 中定義。 下面我們看一個工作隊列 API 的簡單例子。

 

工作隊列簡單例子

 

下面的例子說明了幾個核心的工作隊列 API 函數(shù)。 如同微線程的例子一樣,為方便起見,可將這個例子部署在內(nèi)核模塊上下文。

 

首先,看一下將用于實現(xiàn) bottom half 的任務(wù)結(jié)構(gòu)體和處理程序函數(shù)(參見 清單 9)。 首先您將注意到工作隊列結(jié)構(gòu)體參考的定義 my_wq)以及 my_work_t 的定義。 my_work_t 類型定義的頭部包括結(jié)構(gòu)體 work_struct 和一個代表任務(wù)項目的整數(shù)。 處理程序(回調(diào)函數(shù))將 work_struct 指針引用改為 my_work_t 類型。 發(fā)送出任務(wù)項目(來自結(jié)構(gòu)體的整數(shù))之后,任務(wù)指針將被釋放。

 

清單 9. 任務(wù)結(jié)構(gòu)體和 bottom-half 處理程序

                           

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/workqueue.h>

 

MODULE_LICENSE("GPL");

 

static struct workqueue_struct *my_wq;

 

typedef struct {

  struct work_struct my_work;

  int    x;

} my_work_t;

 

my_work_t *work, *work2;

 

static void my_wq_function( struct work_struct *work)

{

  my_work_t *my_work = (my_work_t *)work;

  printk( "my_work.x %d\n", my_work->x );

  kfree( (void *)work );

  return;

}

 

清單 10 init_module 函數(shù), 該函數(shù)從使用 create_workqueue API 函數(shù)生成工作隊列開始。 成功生成工作隊列之后,創(chuàng)建兩個任務(wù)項目(通過 kmalloc 來分配)。 利用 INIT_WORK 來初始化每個任務(wù)項目,任務(wù)定義完成, 接著通過調(diào)用 queue_work 將任務(wù)安排到工作隊列中。 top-half 進(jìn)程(在此處模擬)完成。如同清單 10 中所示,任務(wù)有時會晚些被處理程序處理。

 

 

清單 10. 工作隊列和任務(wù)創(chuàng)建

                           

int init_module( void )

{

  int ret;

 

  my_wq = create_workqueue("my_queue");

  if (my_wq) {

 

   /* Queue some work (item 1) */

    work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);

    if (work) {

      INIT_WORK( (struct work_struct *)work, my_wq_function );

      work->x = 1;

      ret = queue_work( my_wq, (struct work_struct *)work );

    }

 

    /* Queue some additional work (item 2) */

    work2 = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);

    if (work2) {

      INIT_WORK( (struct work_struct *)work2, my_wq_function );

      work2->x = 2;

      ret = queue_work( my_wq, (struct work_struct *)work2 );

    }

  }

 

  return 0;

}

 

最終的元素在 清單 11 中展示。 在模塊清理過程中,會清理一些特別的工作隊列(它們將保持阻塞狀態(tài)直到處理程序完成對任務(wù)的處理), 然后銷毀工作隊列。

 

清單 11. 工作隊列清理和銷毀

                           

void cleanup_module( void )

{

  flush_workqueue( my_wq );

  destroy_workqueue( my_wq );

  return;

}

 

參考:

內(nèi)核的工作隊列使用方法

 

工作隊列一般用來做滯后的工作,比如在中斷里面要做很多事,但是比較耗時,這時就可以把耗時的工作放到工作隊列。說白了就是系統(tǒng)延時調(diào)度的一個自定義函數(shù)。

 

1、定義struct work_struct irq_queue;

 

2、初始化INIT_WORK(&irq_queue,do_irq_queuework);

 

3、調(diào)用方法:schedule_work(&rq_queue);

 

注,調(diào)用完畢后系統(tǒng)會釋放此函數(shù),所以如果想再次執(zhí)行的話,就再次調(diào)用schedule_work()即可。

 

另外,內(nèi)核必須掛載文件系統(tǒng)才可以使用工作隊列。我的理解是:工作隊列也屬于調(diào)度,如果內(nèi)核掛了,他就不調(diào)度了,當(dāng)然就不能用工作隊列了。

 

工作隊列接口

  工作隊列接口是在2.5的開發(fā)過程中引入的,用于取代任務(wù)隊列接口(用于調(diào)度內(nèi)核任務(wù))。每個工作隊列有一個專門的線程,

所有來自運行隊列的任務(wù)在進(jìn)程的上下文中運行(這樣它們可以休眠)。驅(qū)動程序可以創(chuàng)建并使用它們自己的工作隊列,或者使用內(nèi)核的一個工作隊列。

工作隊列用以下方式創(chuàng)建:

  struct workqueue_struct *create_workqueue(const char *name); 在這里 name 是工作隊列的名字。

 

  工作隊列任務(wù)可以在編譯時或者運行時創(chuàng)建。任務(wù)需要封裝為一個叫做 work_struct 的結(jié)構(gòu)體。在編譯期初始化一個工作隊列任務(wù)時要用到:

  DECLARE_WORK(name, void (*function)(void *), void *data); 在這里 name work_struct 的名字,function 是當(dāng)任務(wù)被調(diào)度時調(diào)用的函數(shù),data 是指向那個函數(shù)的指針。

 

  在運行期初始化一個工作隊列時要用到:

  INIT_WORK(struct work_struct *work, void (*function)(void *), void *data);

 

    用下面的函數(shù)調(diào)用來把一個作業(yè)(一個類型為work_struct 結(jié)構(gòu)的工作隊列作業(yè)/任務(wù))加入到工作隊列中:

  int queue_work(struct workqueue_struct *queue, struct work_struct *work);

    int queue_delayed_work(struct workqueue_struct *queue, struct work_struct *work, unsigned long delay);

  在queue_delay_work()中指定delay,是為了保證至少在經(jīng)過一段給定的最小延遲時間以后,工作隊列中的任務(wù)才可以真正執(zhí)行。

 

  工作隊列中的任務(wù)由相關(guān)的工作線程執(zhí)行,可能是在一個無法預(yù)期的時間(取決于負(fù)載,中斷等等),或者是在一段延遲以后。任何一個在工作隊列中等待了無限長的時間也沒有運行的任務(wù)可以用下面的方法取消:

  int cancel_delayed_work(struct work_struct *work);

 

    如果當(dāng)一個取消操作的調(diào)用返回時,任務(wù)正在執(zhí)行中,那么這個任務(wù)將繼續(xù)執(zhí)行下去,但不會再加入到隊列中。清空工作隊列中的所有任務(wù)使用:

  void flush_workqueue(struct workqueue_struct *queue);

   

    銷毀工作隊列使用:

  void destroy_workqueue(struct workqueue_struct *queue);

    不是所有的驅(qū)動程序都必須有自己的工作隊列。驅(qū)動程序可以使用內(nèi)核提供的缺省工作隊列。由于這個工作隊列由很多驅(qū)動程序共享,

任務(wù)可能會需要比較長一段時間才能開始執(zhí)行。為了解決這一問題,工作函數(shù)中的延遲應(yīng)該保持最小或者干脆不要。

 

  需要特別注意的是缺省隊列對所有驅(qū)動程序來說都是可用的,但是只有經(jīng)過GP許可的驅(qū)動程序可以用自定義的工作隊列:

  int schedule_work(struct work_struct *work); -- 向工作隊列中添加一個任務(wù)

    int schedule_delayed_work(struct work_struct *work, unsigned long delay); -- 向工作隊列中添加一個任務(wù)并延遲執(zhí)行

 

  當(dāng)模塊被缷載時應(yīng)該去調(diào)用一個 flash_scheduled_work() 函數(shù),這個函數(shù)會使等待隊列中所有的任務(wù)都被執(zhí)行。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美成人免费一级特黄| 亚洲国产一区精品一区二区三区色| 色哟哟在线免费一区二区三区| 国产成人精品一区二区三区| 色播五月激情五月婷婷| 亚洲视频在线观看免费中文字幕 | 九九热视频免费在线视频| 亚洲一区二区精品久久av| 亚洲一区二区三区精选| 欧美日韩综合在线第一页| 久久综合日韩精品免费观看| 99国产成人免费一区二区| 九九九热视频免费观看| 日本高清一区免费不卡| 偷自拍亚洲欧美一区二页| 欧美一级特黄大片做受大屁股| 午夜精品麻豆视频91| 亚洲一区二区三区三区| 中文字幕中文字幕在线十八区 | 免费人妻精品一区二区三区久久久 | 国产超薄黑色肉色丝袜| 亚洲精品中文字幕一二三| 日韩免费av一区二区三区| 欧美激情一区=区三区| 99在线视频精品免费播放| 免费观看一级欧美大片| 国产精品午夜性色视频| 高清不卡视频在线观看| 精品人妻一区二区三区免费| 日本午夜精品视频在线观看| 国产麻豆成人精品区在线观看| 国产专区亚洲专区久久| 色婷婷在线视频免费播放| 麻豆精品在线一区二区三区| 91精品蜜臀一区二区三区| 欧美国产精品区一区二区三区| 国产日韩欧美专区一区| 亚洲国产香蕉视频在线观看| 黄片在线免费观看全集| 国产精欧美一区二区三区久久| 国产在线不卡中文字幕|