聲明:此處代碼分析,來源與 nuttx 12.8.0版本。
在分析之前,需要一圖鎮樓。
/***************************************************************************** Name: nxsched_add_blocked** Description:* This function adds a TCB to one of the blocked state task lists as* inferred from task_state.** Input Parameters:* btcb - Points to the TCB that is blocked* task_state - identifies the state of the blocked task** Returned Value:* None** Assumptions:* - The caller has established a critical section before* calling this function.*****************************************************************************/void nxsched_add_blocked(FAR struct tcb_s *btcb, tstate_t task_state)
{FAR dq_queue_t *tasklist;/* Make sure that we received a valid blocked state */DEBUGASSERT(task_state >= FIRST_BLOCKED_STATE &&task_state <= LAST_BLOCKED_STATE);/* Make sure the TCB's state corresponds to the list */btcb->task_state = task_state;/* Add the TCB to the blocked task list associated with this state. */tasklist = TLIST_BLOCKED(btcb);/* Determine if the task is to be added to a prioritized task list. */if (TLIST_ISPRIORITIZED(task_state)){/* Add the task to a prioritized list */nxsched_add_prioritized(btcb, tasklist);}else{/* Add the task to a non-prioritized list */dq_addlast((FAR dq_entry_t *)btcb, tasklist);}
}
顯然,此函數的作用是依據task_state,將btcb放入阻塞隊列。
既然是放入隊列,首先是要找到具體的隊列。
/* Make sure the TCB's state corresponds to the list */btcb->task_state = task_state;/* Add the TCB to the blocked task list associated with this state. */tasklist = TLIST_BLOCKED(btcb);
TLIST_BLOCKED在sched.h中的定義如下。
/* List attribute flags */#define TLIST_ATTR_PRIORITIZED (1 << 0) /* Bit 0: List is prioritized */
#define TLIST_ATTR_INDEXED (1 << 1) /* Bit 1: List is indexed by CPU */
#define TLIST_ATTR_RUNNABLE (1 << 2) /* Bit 2: List includes running tasks */
#define TLIST_ATTR_OFFSET (1 << 3) /* Bit 3: Pointer of task list is offset */#define __TLIST_ATTR(s) g_tasklisttable[s].attr
#define TLIST_ISPRIORITIZED(s) ((__TLIST_ATTR(s) & TLIST_ATTR_PRIORITIZED) != 0)
#define TLIST_ISINDEXED(s) ((__TLIST_ATTR(s) & TLIST_ATTR_INDEXED) != 0)
#define TLIST_ISRUNNABLE(s) ((__TLIST_ATTR(s) & TLIST_ATTR_RUNNABLE) != 0)
#define TLIST_ISOFFSET(s) ((__TLIST_ATTR(s) & TLIST_ATTR_OFFSET) != 0)#define __TLIST_HEAD(t) \(TLIST_ISOFFSET((t)->task_state) ? (FAR dq_queue_t *)((FAR uint8_t *)((t)->waitobj) + \(uintptr_t)g_tasklisttable[(t)->task_state].list) : g_tasklisttable[(t)->task_state].list)#ifdef CONFIG_SMP
# define TLIST_HEAD(t,c) \((TLIST_ISINDEXED((t)->task_state)) ? (&(__TLIST_HEAD(t))[c]) : __TLIST_HEAD(t))
# define TLIST_BLOCKED(t) __TLIST_HEAD(t)
#else
# define TLIST_HEAD(t) __TLIST_HEAD(t)
# define TLIST_BLOCKED(t) __TLIST_HEAD(t)
#endif
在計算__TLIST_HEAD的時候,會有一個TLIST_ISOFFSET的判斷。那什么隊列會符合要求呢?根據定義,我們知道當g_tasklisttable[s].attr &TLIST_ATTR_OFFSET? != 0 就滿足條件。
那,具體是什么隊列呢?
static void tasklist_initialize(void)
{....../* TSTATE_WAIT_SEM */tlist[TSTATE_WAIT_SEM].list = (FAR void *)offsetof(sem_t, waitlist);tlist[TSTATE_WAIT_SEM].attr = TLIST_ATTR_PRIORITIZED |TLIST_ATTR_OFFSET;....../* TSTATE_WAIT_MQNOTEMPTY */tlist[TSTATE_WAIT_MQNOTEMPTY].list =(FAR void *)offsetof(struct mqueue_inode_s, cmn.waitfornotempty);tlist[TSTATE_WAIT_MQNOTEMPTY].attr = TLIST_ATTR_PRIORITIZED |TLIST_ATTR_OFFSET;/* TSTATE_WAIT_MQNOTFULL */tlist[TSTATE_WAIT_MQNOTFULL].list =(FAR void *)offsetof(struct mqueue_inode_s, cmn.waitfornotfull);tlist[TSTATE_WAIT_MQNOTFULL].attr = TLIST_ATTR_PRIORITIZED |TLIST_ATTR_OFFSET;......}
顯然,信號量和消息隊列會符合要求。
那么,
(FAR dq_queue_t *)((FAR uint8_t *)((t)->waitobj) + (uintptr_t)g_tasklisttable[(t)->task_state].list)具體指向那里呢?
為回答此問題,我們需要弄清楚對于信號量和消息隊列來說,waitobj 和g_tasklisttable[(t)->task_state].list各指代什么意思。
對于g_tasklisttable[(t)->task_state].list,由上述代碼可知,g_tasklisttable[TSTATE_WAIT_SEM].list 是 struct mqueue_inode_s結構體中,waitlist的偏移量。
struct sem_s
{......dq_queue_t waitlist;......}typedef struct sem_s sem_t;
對于g_tasklisttable[(t)->task_state].list,由上述代碼可知,g_tasklisttable[TSTATE_WAIT_MQNOTEMPTY].list 是 sem_t結構體中,mn.waitfornotempty的偏移量。
struct mqueue_inode_s
{struct mqueue_cmn_s cmn; /* Common prologue */......
}struct mqueue_cmn_s
{dq_queue_t waitfornotempty; /* Task list waiting for not empty */dq_queue_t waitfornotfull; /* Task list waiting for not full */......
};
g_tasklisttable[TSTATE_WAIT_MQNOTFULL].list 同理。
waitobj的定義如下。
struct tcb_s
{..../* POSIX Semaphore and Message Queue Control Fields ***********************/FAR void *waitobj; /* Object thread waiting on */......
}
那么,waitobj的賦值是什么呢?
int nxmq_wait_receive(FAR struct mqueue_inode_s *msgq,FAR struct mqueue_msg_s **rcvmsg,FAR const struct timespec *abstime,sclock_t ticks)
{......FAR struct tcb_s *rtcb = this_task();......rtcb->waitobj = msgq;......
}
對于信號量,waitobj同理。可見,waitobj指代的是具體等待的信號量或者消息隊列。
那,為什么要這樣呢?答案就藏在數據結構的定義里。
以sem_t來說,
struct sem_s
{......dq_queue_t waitlist;......}typedef struct sem_s sem_t;
有人可能會問,這能說明什么?這能說明,對于每一個sem_t實例來說,他們都有各自的隊列。與之對應的g_tasklisttable.list中存放的是偏移值。
為什么這樣設計呢??
這樣設計實現了所有阻塞類型的統一管理。
找到具體的隊列之后,調用nxsched_add_prioritized或dq_addlast 將tcb_s加入隊列。
除了,信號量和消息隊列,還有哪些阻塞隊列呢?
enum tstate_e
{......TSTATE_TASK_INACTIVE, /* BLOCKED - Initialized but not yet activated */TSTATE_WAIT_SEM, /* BLOCKED - Waiting for a semaphore */TSTATE_WAIT_SIG, /* BLOCKED - Waiting for a signal */
#if !defined(CONFIG_DISABLE_MQUEUE) || !defined(CONFIG_DISABLE_MQUEUE_SYSV)TSTATE_WAIT_MQNOTEMPTY, /* BLOCKED - Waiting for a MQ to become not empty. */TSTATE_WAIT_MQNOTFULL, /* BLOCKED - Waiting for a MQ to become not full. */
#endif
#ifdef CONFIG_LEGACY_PAGINGTSTATE_WAIT_PAGEFILL, /* BLOCKED - Waiting for page fill */
#endif
#ifdef CONFIG_SIG_SIGSTOP_ACTIONTSTATE_TASK_STOPPED, /* BLOCKED - Waiting for SIGCONT */
#endif......
};