声明:此处代码分析,来源与 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_PAGING
TSTATE_WAIT_PAGEFILL, /* BLOCKED - Waiting for page fill */
#endif
#ifdef CONFIG_SIG_SIGSTOP_ACTION
TSTATE_TASK_STOPPED, /* BLOCKED - Waiting for SIGCONT */
#endif
......
};