Nuttx工作隊列機制

Nuttx相關的歷史文章:

介紹

Nuttx提供工作隊列機制。工作隊列是一個存放線程的隊列,它對于將任務負載減荷到不同的線程上下文中润脸,以便于延遲執(zhí)行勾笆,或者串行執(zhí)行很有幫助轿偎。

工作隊列分類

有三種不同類型的工作隊列窘哈,每一類都有不同的屬性和用途稀火。

  1. 高優(yōu)先級內(nèi)核工作隊列
  • 高優(yōu)先級內(nèi)核工作隊列
    專用的高優(yōu)先級工作隊列用于中斷處理函數(shù)中的延遲處理鲜棠,在有些驅動中可能需要這樣一個工作隊列肌厨,如果沒有必要的話,也可以安全的禁掉豁陆。高優(yōu)先級的線程也可以充當資源回收器--從中斷處理函數(shù)中完成內(nèi)存的延遲釋放柑爸。如果高優(yōu)先級工作線程被disable了的話,清理工作有兩種方式來完成:1)如果使能了低優(yōu)先級的工作線程盒音,在該線程中完成表鳍;2)如果低優(yōu)先級線程沒有使能,則IDLE線程來完成(如果內(nèi)存回收優(yōu)先級比較高祥诽,可能不太合適)譬圣。

  • 設備驅動底半部
    高優(yōu)先級工作線程可以用于設備驅動程序的底半部,因此它必須運行在一個非常高雄坪,并且固定的優(yōu)先級厘熟,與中斷處理程序本身的優(yōu)先級競爭。通常维哈,高優(yōu)先級工作隊列應該是系統(tǒng)中最高優(yōu)先級的線程绳姨。默認的優(yōu)先級為224。

  • 線程池
    工作隊列可以被配置成支持多個低優(yōu)先級線程阔挠,這本質(zhì)上是一個線程池飘庄,為隊列工作提供多線程服務,這打破了“隊列”的嚴格序列化(因此谒亦,工作隊列也不再是一種隊列)竭宰。
    當在I/O操作,暫停等待輸入時份招,多個工作線程是需要的切揭,如果只有一個工作線程的話,那么整個工作隊列處理就會停止锁摔。這對于異步I/O廓旬、AIO是必要的。

  • 與低優(yōu)先級內(nèi)核工作隊列比較
    對于不太關鍵、較低優(yōu)先級孕豹、面向應用程序的工作線程支持涩盾,考慮使用較低優(yōu)先級的工作隊列。較低優(yōu)先級的工作隊列以較低的優(yōu)先級運行励背,但是它有一個額外的優(yōu)點春霍,那就是支持優(yōu)先級繼承(如果CONFIG_PRIORITY_INHERITANCE=y選中的話):低優(yōu)先級的工作線程可以被調(diào)整優(yōu)先級。

  • 配置選項
    CONFIG_SCHED_HPWORK:使能高優(yōu)先級工作隊列
    CONFIG_SCHED_HPNTHREADS:高優(yōu)先級工作隊列線程池中的線程數(shù)量叶眉,默認是1.
    CONFIG_SCHED_HPWORKPRIORITY:高優(yōu)先級工作線程的執(zhí)行優(yōu)先級址儒,默認是224.
    CONFIG_SCHED_HPWORKSTACKSIZE:工作線程的棧空間大小衅疙,默認是2048字節(jié)

  • 通用配置選項
    這個選項通用于所有的工作隊列:
    CONFIG_SIG_SIGWORK:用于喚醒工作線程的信號值莲趣,默認使用17.

  1. 低優(yōu)先級內(nèi)核工作隊列
  • 低優(yōu)先級內(nèi)核工作隊列
    低優(yōu)先級工作隊列更適合于具備擴展性的,面向應用程序處理的場景饱溢,比如文件系統(tǒng)清理喧伞、內(nèi)存垃圾回收、異步I/O操作等绩郎。

  • 與高優(yōu)先內(nèi)核工作隊列比較
    低優(yōu)先級內(nèi)核工作隊列潘鲫,由于優(yōu)先級會低一些,因此不適合用作驅動程序的底半部嗽上。除此之外次舌,它與高優(yōu)先級內(nèi)核工作隊列非常相似,上文中關于高優(yōu)先級工作隊列的大部分討論同樣適用兽愤。但是低優(yōu)先級內(nèi)核工作隊列彼念,有一個重要的特點就是優(yōu)先級繼承,這個讓它更適合于某些任務浅萧。

  • 優(yōu)先級繼承
    低優(yōu)先級內(nèi)核工作線程支持優(yōu)先級繼承(需要選擇CONFIG_PRIORITY_INHERITANCE=y)逐沙,可以根據(jù)實際情況調(diào)整優(yōu)先級。優(yōu)先級繼承不是自動完成的洼畅,低優(yōu)先級工作線程總是運行在一個固定的優(yōu)先級上吩案。可以通過調(diào)用lpwork_bootstpriority()接口來提升優(yōu)先級(通常在調(diào)度這個任務之前調(diào)用)帝簇,在任務完成之后可以通過lpwork_restorepriority()接口來恢復優(yōu)先級(一般在任務完成時的work handler中調(diào)用)徘郭。目前,只有Nuttx異步I/O邏輯使用了這個動態(tài)優(yōu)先級特性丧肴。

  • 配置選項
    CONFIG_SCHED_LPWORK:使能低優(yōu)先級工作隊列
    CONFIG_SCHED_LPNTHREADS:低優(yōu)先級工作隊列中線程數(shù)量残揉,默認值為1
    CONFIG_SCHED_LPWORKPRIORITY:低優(yōu)先級工作線程中最小的執(zhí)行優(yōu)先級,隊列中每個線程都以這個優(yōu)先級的值開始運行芋浮。如果優(yōu)先級繼承使能了的話抱环,優(yōu)先級會在這個基礎上往上提升,默認50.
    CONFIG_SCHED_LPWORKPRIOMAX:低優(yōu)先級線程中最大的執(zhí)行優(yōu)先級。運行的優(yōu)先級不能超過這個值镇草,默認176.
    CONFIG_SCHED_LPWORKSTACKSIZE:低優(yōu)先級工作線程的棧大小眶痰,默認2048Byte。

  1. 用戶模式工作隊列
  • 工作隊列訪問權限
    低優(yōu)先級和高優(yōu)先級工作線程梯啤,都是內(nèi)核線程竖伯。在Nuttx flat build模式下編譯時,應用程序是可以訪問和使用的条辟。但是黔夭,在Nuttx protected/kernel build模式下編譯時,內(nèi)核模式下的代碼是獨立的羽嫡,用戶模式是沒法訪問的。

  • 工作模式工作隊列
    用戶模式工作隊列接口與內(nèi)核模式工作隊列接口相同肩袍,用戶模式工作隊列的功能等效于高優(yōu)先級工作隊列杭棵,不同之處在于,它的實現(xiàn)不依賴于內(nèi)核內(nèi)部提供的資源氛赐。

  • 配置選項
    CONFIG_LIB_USRWORK:使能用戶模式工作隊列
    CONFIG_LIB_USRWORKPRIORITY:用戶模式下工作線程的執(zhí)行優(yōu)先級魂爪,默認為100.
    CONFIG_LIB_USRWORKSTACKSIZE:用戶模式下工作線程的棧大小,默認2048.

數(shù)據(jù)結構及接口

數(shù)據(jù)結構

數(shù)據(jù)結構分為兩部分艰管,一部分是用戶使用的結構滓侍,另一部分是內(nèi)核實現(xiàn)用到的結構:

  1. 用戶數(shù)據(jù)結構
/* Defines the work callback */

typedef void (*worker_t)(FAR void *arg);

/* Defines one entry in the work queue.  The user only needs this structure
 * in order to declare instances of the work structure.  Handling of all
 * fields is performed by the work APIs
 */

struct work_s
{
  struct dq_entry_s dq;  /* Implements a doubly linked list */
  worker_t  worker;      /* Work callback */
  FAR void *arg;         /* Callback argument */
  systime_t qtime;       /* Time work queued */
  systime_t delay;       /* Delay until work performed */
};

struct work_s結構只需要用來聲明實例即可,該數(shù)據(jù)結構中的內(nèi)部成員牲芋,全部由相應的API接口來操作撩笆,其中qtime表示的是該任務入隊的時間赊时,而delay表示的是需要延遲多長時間去執(zhí)行唉锌,如果delay值為0,表明立刻執(zhí)行嘶炭。

  1. 內(nèi)核實現(xiàn)數(shù)據(jù)結構
/* This represents one worker */

struct kworker_s
{
  pid_t             pid;    /* The task ID of the worker thread */
  volatile bool     busy;   /* True: Worker is not available */
};

/* This structure defines the state of one kernel-mode work queue */

struct kwork_wqueue_s
{
  systime_t         delay;     /* Delay between polling cycles (ticks) */
  struct dq_queue_s q;         /* The queue of pending work */
  struct kworker_s  worker[1]; /* Describes a worker thread */
};

/* This structure defines the state of one high-priority work queue.  This
 * structure must be cast-compatible with kwork_wqueue_s.
 */

#ifdef CONFIG_SCHED_HPWORK
struct hp_wqueue_s
{
  systime_t         delay;     /* Delay between polling cycles (ticks) */
  struct dq_queue_s q;         /* The queue of pending work */
  struct kworker_s  worker[1]; /* Describes the single high priority worker */
};
#endif

/* This structure defines the state of one high-priority work queue.  This
 * structure must be cast compatible with kwork_wqueue_s
 */

#ifdef CONFIG_SCHED_LPWORK
struct lp_wqueue_s
{
  systime_t         delay;  /* Delay between polling cycles (ticks) */
  struct dq_queue_s q;      /* The queue of pending work */

  /* Describes each thread in the low priority queue's thread pool */

  struct kworker_s  worker[CONFIG_SCHED_LPNTHREADS];
};
#endif

/****************************************************************************
 * Public Data
 ****************************************************************************/

#ifdef CONFIG_SCHED_HPWORK
/* The state of the kernel mode, high priority work queue. */

extern struct hp_wqueue_s g_hpwork;
#endif

#ifdef CONFIG_SCHED_LPWORK
/* The state of the kernel mode, low priority work queue(s). */

extern struct lp_wqueue_s g_lpwork;
#endif

上述結構體中:
struct kworker_s:對應一個工作線程裂逐,其中包含了線程ID號及運行狀態(tài)歹鱼。
struct kwork_wqueue_s:描述內(nèi)核模式下的工作隊列,在接口中都使用這個數(shù)據(jù)結構卜高,實際上是將struct hp_wqueue_s/struct lp_wqueue_s數(shù)據(jù)結構進行強制類型轉換弥姻。
struct hp_wqueue_s:描述高優(yōu)先級內(nèi)核工作隊列,從數(shù)據(jù)結構中可以看出掺涛,該隊列中默認只支持1個工作線程庭敦。
struct lp_wqueue_s:描述低優(yōu)先級內(nèi)核工作隊列,從數(shù)據(jù)結構中可以看出鸽照,該隊列中的工作線程是可以配置的螺捐,CONFIG_SCHED_LPNTHREADS的值就代表線程數(shù)量。
g_hpwork/g_lpwork:分別為兩個全局描述符,對應到兩種類型的內(nèi)核工作隊列定血。

接口定義

  • int work_usrstart(void):啟動用戶模式下的工作隊列赔癌。
  • int work_queue(int qid, FAR struct work_s *work, worker_t worker, FAR void *arg, systime_t delay):將任務添加到工作隊列中,任務將會在工作隊列中的線程上延遲運行澜沟。
  • int work_cancel(int qid, FAR struct work_s *work):將之前入列的任務刪除掉灾票。
  • int work_signal(int qid):通過工作隊列中的線程去執(zhí)行任務處理。
  • work_available(work):檢查任務的結構體是否可用茫虽。
  • void lpwork_boostpriority(uint8_t reqprio):提升線程執(zhí)行的優(yōu)先級。
  • void lpwork_restorepriority(uint8_t reqprio):恢復線程執(zhí)行的優(yōu)先級正什。
    代碼說明一切:
/****************************************************************************
 * Name: work_usrstart
 *
 * Description:
 *   Start the user mode work queue.
 *
 * Input parameters:
 *   None
 *
 * Returned Value:
 *   The task ID of the worker thread is returned on success.  A negated
 *   errno value is returned on failure.
 *
 ****************************************************************************/

#if defined(CONFIG_LIB_USRWORK) && !defined(__KERNEL__)
int work_usrstart(void);
#endif
/****************************************************************************
 * Name: work_queue
 *
 * Description:
 *   Queue work to be performed at a later time.  All queued work will be
 *   performed on the worker thread of execution (not the caller's).
 *
 *   The work structure is allocated by caller, but completely managed by
 *   the work queue logic.  The caller should never modify the contents of
 *   the work queue structure; the caller should not call work_queue()
 *   again until either (1) the previous work has been performed and removed
 *   from the queue, or (2) work_cancel() has been called to cancel the work
 *   and remove it from the work queue.
 *
 * Input parameters:
 *   qid    - The work queue ID
 *   work   - The work structure to queue
 *   worker - The worker callback to be invoked.  The callback will invoked
 *            on the worker thread of execution.
 *   arg    - The argument that will be passed to the worker callback when
 *            it is invoked.
 *   delay  - Delay (in clock ticks) from the time queue until the worker
 *            is invoked. Zero means to perform the work immediately.
 *
 * Returned Value:
 *   Zero on success, a negated errno on failure
 *
 ****************************************************************************/

int work_queue(int qid, FAR struct work_s *work, worker_t worker,
               FAR void *arg, systime_t delay);
/****************************************************************************
 * Name: work_cancel
 *
 * Description:
 *   Cancel previously queued work.  This removes work from the work queue.
 *   After work has been cancelled, it may be re-queue by calling work_queue()
 *   again.
 *
 * Input parameters:
 *   qid    - The work queue ID
 *   work   - The previously queue work structure to cancel
 *
 * Returned Value:
 *   Zero on success, a negated errno on failure
 *
 *   -ENOENT - There is no such work queued.
 *   -EINVAL - An invalid work queue was specified
 *
 ****************************************************************************/

int work_cancel(int qid, FAR struct work_s *work);

/****************************************************************************
 * Name: work_signal
 *
 * Description:
 *   Signal the worker thread to process the work queue now.  This function
 *   is used internally by the work logic but could also be used by the
 *   user to force an immediate re-assessment of pending work.
 *
 * Input parameters:
 *   qid    - The work queue ID
 *
 * Returned Value:
 *   Zero on success, a negated errno on failure
 *
 ****************************************************************************/

int work_signal(int qid);

/****************************************************************************
 * Name: work_available
 *
 * Description:
 *   Check if the work structure is available.
 *
 * Input parameters:
 *   work - The work queue structure to check.
 *   None
 *
 * Returned Value:
 *   true if available; false if busy (i.e., there is still pending work).
 *
 ****************************************************************************/

#define work_available(work) ((work)->worker == NULL)
/****************************************************************************
 * Name: lpwork_boostpriority
 *
 * Description:
 *   Called by the work queue client to assure that the priority of the low-
 *   priority worker thread is at least at the requested level, reqprio. This
 *   function would normally be called just before calling work_queue().
 *
 * Parameters:
 *   reqprio - Requested minimum worker thread priority
 *
 * Return Value:
 *   None
 *
 ****************************************************************************/

#if defined(CONFIG_SCHED_LPWORK) && defined(CONFIG_PRIORITY_INHERITANCE)
void lpwork_boostpriority(uint8_t reqprio);
#endif
/****************************************************************************
 * Name: lpwork_restorepriority
 *
 * Description:
 *   This function is called to restore the priority after it was previously
 *   boosted.  This is often done by client logic on the worker thread when
 *   the scheduled work completes.  It will check if we need to drop the
 *   priority of the worker thread.
 *
 * Parameters:
 *   reqprio - Previously requested minimum worker thread priority to be
 *     "unboosted"
 *
 * Return Value:
 *   None
 *
 ****************************************************************************/

#if defined(CONFIG_SCHED_LPWORK) && defined(CONFIG_PRIORITY_INHERITANCE)
void lpwork_restorepriority(uint8_t reqprio);
#endif

原理

按慣例号杏,先來一張圖吧:


工作隊列

簡單來說盾致,工作隊列就如上圖所示主经,由三個部分組成:

  • 任務隊列:用于存放需要延遲執(zhí)行的任務,這個也就是通過work_queue()接口添加任務的任務隊列庭惜。
  • 工作線程:在高優(yōu)先級內(nèi)核工作隊列中,默認只有一個線程惠遏;在低優(yōu)先級內(nèi)核工作隊列中支持多個工作線程百揭。任務隊列中的任務就分發(fā)到這些線程上來執(zhí)行。
  • 延時參數(shù)delay:這個參數(shù)定義了輪詢時的間隔時間器一,進而判斷任務隊列中的任務是否已經(jīng)到需要執(zhí)行的時間點了祈秕。

Nuttx操作系統(tǒng)執(zhí)行的入口在os_start()请毛,從這開始,最終會調(diào)用到工作隊列線程的創(chuàng)建固棚,調(diào)用關系如下:
os_start() ---> os_bringup() ---> os_workqueue() ---> work_hpstart()/work_lpstart()/USERSPACE->work_usrstart()
其中work_hpstart()/work_lpstart()/USERSPACE->work_usrstart()分別對應內(nèi)核高優(yōu)先級工作隊列此洲、內(nèi)核低優(yōu)先級工作隊列、用戶模式工作隊列三種情況娶桦,由于原理類似汁汗,我將選擇內(nèi)核高優(yōu)先級工作隊列來進行分析知牌。入口為:work_hpstart()角寸。

work_hpstart()主要完成以下幾點:

  1. 初始化高優(yōu)先級工作隊列數(shù)據(jù)結構;
  2. 在該工作隊列中,創(chuàng)建一個高優(yōu)先級的工作線程work_hpthread纹磺,默認只支持一個橄杨;
int work_hpstart(void)
{
  pid_t pid;

  /* Initialize work queue data structures */

  g_hpwork.delay          = CONFIG_SCHED_HPWORKPERIOD / USEC_PER_TICK;
  dq_init(&g_hpwork.q);

  /* Start the high-priority, kernel mode worker thread */

  sinfo("Starting high-priority kernel worker thread\n");

  pid = kernel_thread(HPWORKNAME, CONFIG_SCHED_HPWORKPRIORITY,
                      CONFIG_SCHED_HPWORKSTACKSIZE,
                      (main_t)work_hpthread,
                      (FAR char * const *)NULL);

  DEBUGASSERT(pid > 0);
  if (pid < 0)
    {
      int errcode = errno;
      DEBUGASSERT(errcode > 0);

      serr("ERROR: kernel_thread failed: %d\n", errcode);
      return -errcode;
    }

  g_hpwork.worker[0].pid  = pid;
  g_hpwork.worker[0].busy = true;
  return pid;

實際的工作由work_hpthread線程來處理式矫,在該函數(shù)中運行一個死循環(huán)采转,在循環(huán)中調(diào)用work_process()來處理實際的任務故慈。

/****************************************************************************
 * Name: work_hpthread
 *
 * Description:
 *   This is the worker thread that performs the actions placed on the high
 *   priority work queue.
 *
 *   This, along with the lower priority worker thread(s) are the kernel
 *   mode work queues (also build in the flat build).  One of these threads
 *   also performs periodic garbage collection (that would otherwise be
 *   performed by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined).
 *   That will be the higher priority worker thread only if a lower priority
 *   worker thread is available.
 *
 *   All kernel mode worker threads are started by the OS during normal
 *   bring up.  This entry point is referenced by OS internally and should
 *   not be accessed by application logic.
 *
 * Input parameters:
 *   argc, argv (not used)
 *
 * Returned Value:
 *   Does not return
 *
 ****************************************************************************/

static int work_hpthread(int argc, char *argv[])
{
  /* Loop forever */

  for (; ; )
    {
#ifndef CONFIG_SCHED_LPWORK
      /* First, perform garbage collection.  This cleans-up memory
       * de-allocations that were queued because they could not be freed in
       * that execution context (for example, if the memory was freed from
       * an interrupt handler).
       *
       * NOTE: If the work thread is disabled, this clean-up is performed by
       * the IDLE thread (at a very, very low priority).  If the low-priority
       * work thread is enabled, then the garbage collection is done on that
       * thread instead.
       */

      sched_garbage_collection();
#endif

      /* Then process queued work.  work_process will not return until: (1)
       * there is no further work in the work queue, and (2) the polling
       * period provided by g_hpwork.delay expires.
       */

      work_process((FAR struct kwork_wqueue_s *)&g_hpwork, g_hpwork.delay, 0);
    }

  return OK; /* To keep some compilers happy */
}

所以工作隊列的任務處理核心是work_process()接口,該接口對于內(nèi)核的高優(yōu)先級工作隊列和內(nèi)核低優(yōu)先級工作隊列是一致的津辩。

work_process()完成的主要任務有:

  1. 獲取執(zhí)行時候的系統(tǒng)時間,這個時間主要用于統(tǒng)計任務進入工作隊列后竭贩,消耗了多久娶视,是否到了需要去執(zhí)行的時間點睁宰。
  2. 從工作隊列的頭部獲取一個任務柒傻,通過比較兩個時間值:1)消耗的時間,也就是當前的系統(tǒng)時間減去任務入列的時間青柄;2)任務延遲執(zhí)行的時間致开,也就是數(shù)據(jù)結構中描述的delay時間双戳。
  3. 如果消耗的時間大于延遲執(zhí)行的時間糜芳,那就立刻執(zhí)行任務的回調(diào)函數(shù)峭竣。
  4. 如果消耗的時間小于延遲執(zhí)行的時間,計算剩余時間扣墩,并最終讓任務睡眠等待一下沮榜。
    5.高優(yōu)先級內(nèi)核工作隊列和低優(yōu)先內(nèi)核工作隊列的實現(xiàn)方式有一些細微的差異蟆融,主要體現(xiàn)在守呜,高優(yōu)先級的情況下,如果還不到執(zhí)行時間郁竟,工作線程選擇睡眠讓出CPU由境;低優(yōu)先級的情況下虏杰,會選擇讓第一個線程輪詢(與高優(yōu)先級工作線程行為一致)纺阔,而讓其他的工作線程調(diào)用sigwaitinfo()接口等待信號。
    代碼如下:
void work_process(FAR struct kwork_wqueue_s *wqueue, systime_t period, int wndx)
{
  volatile FAR struct work_s *work;
  worker_t  worker;
  irqstate_t flags;
  FAR void *arg;
  systime_t elapsed;
  systime_t remaining;
  systime_t stick;
  systime_t ctick;
  systime_t next;

  /* Then process queued work.  We need to keep interrupts disabled while
   * we process items in the work list.
   */

  next  = period;
  flags = enter_critical_section();

  /* Get the time that we started this polling cycle in clock ticks. */

  stick = clock_systimer();

  /* And check each entry in the work queue.  Since we have disabled
   * interrupts we know:  (1) we will not be suspended unless we do
   * so ourselves, and (2) there will be no changes to the work queue
   */

  work = (FAR struct work_s *)wqueue->q.head;
  while (work)
    {
      /* Is this work ready?  It is ready if there is no delay or if
       * the delay has elapsed. qtime is the time that the work was added
       * to the work queue.  It will always be greater than or equal to
       * zero.  Therefore a delay of zero will always execute immediately.
       */

      ctick   = clock_systimer();
      elapsed = ctick - work->qtime;
      if (elapsed >= work->delay)
        {
          /* Remove the ready-to-execute work from the list */

          (void)dq_rem((struct dq_entry_s *)work, &wqueue->q);

          /* Extract the work description from the entry (in case the work
           * instance by the re-used after it has been de-queued).
           */

          worker = work->worker;

          /* Check for a race condition where the work may be nullified
           * before it is removed from the queue.
           */

          if (worker != NULL)
            {
              /* Extract the work argument (before re-enabling interrupts) */

              arg = work->arg;

              /* Mark the work as no longer being queued */

              work->worker = NULL;

              /* Do the work.  Re-enable interrupts while the work is being
               * performed... we don't have any idea how long this will take!
               */

              leave_critical_section(flags);
              worker(arg);

              /* Now, unfortunately, since we re-enabled interrupts we don't
               * know the state of the work list and we will have to start
               * back at the head of the list.
               */

              flags = enter_critical_section();
              work  = (FAR struct work_s *)wqueue->q.head;
            }
          else
            {
              /* Cancelled.. Just move to the next work in the list with
               * interrupts still disabled.
               */

              work = (FAR struct work_s *)work->dq.flink;
            }
        }
      else /* elapsed < work->delay */
        {
          /* This one is not ready.
           *
           * NOTE that elapsed is relative to the the current time,
           * not the time of beginning of this queue processing pass.
           * So it may need an adjustment.
           */

          elapsed += (ctick - stick);
          if (elapsed > work->delay)
            {
              /* The delay has expired while we are processing */

              elapsed = work->delay;
            }

          /* Will it be ready before the next scheduled wakeup interval? */

          remaining = work->delay - elapsed;
          if (remaining < next)
            {
              /* Yes.. Then schedule to wake up when the work is ready */

              next = remaining;
            }

          /* Then try the next in the list. */

          work = (FAR struct work_s *)work->dq.flink;
        }
    }

#if defined(CONFIG_SCHED_LPWORK) && CONFIG_SCHED_LPNTHREADS > 0
  /* Value of zero for period means that we should wait indefinitely until
   * signalled.  This option is used only for the case where there are
   * multiple, low-priority worker threads.  In that case, only one of
   * the threads does the poll... the others simple.  In all other cases
   * period will be non-zero and equal to wqueue->delay.
   */

  if (period == 0)
    {
      sigset_t set;

      /* Wait indefinitely until signalled with SIGWORK */

      sigemptyset(&set);
      sigaddset(&set, SIGWORK);

      wqueue->worker[wndx].busy = false;
      DEBUGVERIFY(sigwaitinfo(&set, NULL));
       wqueue->worker[wndx].busy = true;
    }
  else
#endif
    {
      /* Get the delay (in clock ticks) since we started the sampling */

      elapsed = clock_systimer() - stick;
      if (elapsed < period && next > 0)
        {
          /* How much time would we need to delay to get to the end of the
           * sampling period?  The amount of time we delay should be the smaller
           * of the time to the end of the sampling period and the time to the
           * next work expiry.
           */

          remaining = period - elapsed;
          next      = MIN(next, remaining);

          /* Wait awhile to check the work list.  We will wait here until
           * either the time elapses or until we are awakened by a signal.
           * Interrupts will be re-enabled while we wait.
           */

          wqueue->worker[wndx].busy = false;
          usleep(next * USEC_PER_TICK);
          wqueue->worker[wndx].busy = true;
        }
    }

  leave_critical_section(flags);
}

總結

Nuttx中的工作隊列機制還是比較簡單的:一個工作隊列,對應到一個任務的隊列潭陪,以及一個工作線程的數(shù)組最蕾。內(nèi)核負責來調(diào)度這些工作線程瘟则,而任務隊列中的任務會分發(fā)到各個線程上執(zhí)行枝秤。三種類型的工作隊列淀弹,實現(xiàn)都是大同小異薇溃。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沐序,一起剝皮案震驚了整個濱河市堕绩,隨后出現(xiàn)的幾起案子奴紧,更是在濱河造成了極大的恐慌晶丘,老刑警劉巖浅浮,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件件缸,死亡現(xiàn)場離奇詭異叔遂,居然都是意外死亡已艰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚼吞,“玉大人舱禽,你說我怎么就攤上這事誊稚。” “怎么了城瞎?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵脖镀,是天一觀的道長认然。 經(jīng)常有香客問我,道長盈匾,這世上最難降的妖魔是什么削饵? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任窿撬,我火速辦了婚禮叙凡,結果婚禮上握爷,老公的妹妹穿的比我還像新娘新啼。我一直安慰自己,他們只是感情好座柱,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布色洞。 她就那樣靜靜地躺著锋玲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伞插。 梳的紋絲不亂的頭發(fā)上媚污,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天耗美,我揣著相機與錄音,去河邊找鬼芥玉。 笑死备图,一個胖子當著我的面吹牛揽涮,可吹牛的內(nèi)容都是我干的蒋困。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼汰聋!你這毒婦竟也來了烹困?” 一聲冷哼從身側響起髓梅,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤枯饿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搔扁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稿蹲,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡苛聘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年唱捣,在試婚紗的時候發(fā)現(xiàn)自己被綠了网梢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澎粟。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡活烙,死狀恐怖啸盏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情气笙,我是刑警寧澤潜圃,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布谭期,位于F島的核電站隧出,受9級特大地震影響阀捅,放射性物質(zhì)發(fā)生泄漏饲鄙。R本人自食惡果不足惜忍级,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一颤练、第九天 我趴在偏房一處隱蔽的房頂上張望嗦玖。 院中可真熱鬧,春花似錦苛吱、人聲如沸器瘪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽历帚。三九已至挽牢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刘离,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工奏赘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留寥闪,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓磨淌,卻偏偏與公主長得像疲憋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梁只,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理缚柳,服務發(fā)現(xiàn),斷路器秋忙,智...
    卡卡羅2017閱讀 134,715評論 18 139
  • 完全公平調(diào)度CFS CFS(Completely Fair Scheduler)試圖按照對 CPU 時間的 “最大...
    batbattle閱讀 3,392評論 0 5
  • 本文是我自己在秋招復習時的讀書筆記,整理的知識點构舟,也是為了防止忘記灰追,尊重勞動成果,轉載注明出處哦!如果你也喜歡弹澎,那...
    波波波先森閱讀 11,273評論 4 56
  • 4.私有庫創(chuàng)建 創(chuàng)建私有 Spec Repo Spec Repo介紹 Spec Repo是所有的Pods的一個索引...
    coderPoo閱讀 664評論 0 0
  • 摘錄:如果你喜歡一個人朴下,迫于寂默與顏面,你退而求其次苦蒿,接受了某一位你的追求者殴胧,但當看到他(她)的消息時,你依舊會小...
    皇氏三墳閱讀 436評論 4 6