src/tm-threads.h,src/tm-threads.c
一、结构体
c
typedef struct Thread_ {
ThreadVars *tv; /**< threadvars structure */
const char *name;
int type;
int in_use; /**< bool to indicate this is in use */
SC_ATOMIC_DECLARE(SCTime_t, pktts); /**< current packet time of this thread
* (offline mode) */
SCTime_t sys_sec_stamp; /**< timestamp in real system
* time when the pktts was last updated. */
SCSpinlock spin;
} __attribute__((aligned(CLS))) Thread;
typedef struct Threads_ {
Thread *threads;
size_t threads_size;
int threads_cnt;
} Threads;

二、API
c
int TmThreadsRegisterThread(ThreadVars *tv, const int type);
三、实现
3.1 TmThreadsRegisterThread
线程注册,将存储到全局动态数组thread_store.threads 中,数组空间不足时,以32为步长扩容,返回idx+1。
c
#define STEP 32
/**
* \retval id thread id, or 0 if not found
*/
int TmThreadsRegisterThread(ThreadVars *tv, const int type)
{
SCMutexLock(&thread_store_lock);
DEBUG_VALIDATE_BUG_ON(thread_store_sealed);
if (thread_store.threads == NULL) {
thread_store.threads = SCCalloc(STEP, sizeof(Thread));
BUG_ON(thread_store.threads == NULL);
thread_store.threads_size = STEP;
}
size_t s;
for (s = 0; s < thread_store.threads_size; s++) {
if (thread_store.threads[s].in_use == 0) {
Thread *t = &thread_store.threads[s];
SCSpinInit(&t->spin, 0);
SCSpinLock(&t->spin);
t->name = tv->name;
t->type = type;
t->tv = tv;
t->in_use = 1;
SCSpinUnlock(&t->spin);
SCMutexUnlock(&thread_store_lock);
return (int)(s+1);
}
}
/* if we get here the array is completely filled */
void *newmem = SCRealloc(thread_store.threads, ((thread_store.threads_size + STEP) * sizeof(Thread)));
BUG_ON(newmem == NULL);
thread_store.threads = newmem;
memset((uint8_t *)thread_store.threads + (thread_store.threads_size * sizeof(Thread)), 0x00, STEP * sizeof(Thread));
Thread *t = &thread_store.threads[thread_store.threads_size];
SCSpinInit(&t->spin, 0);
SCSpinLock(&t->spin);
t->name = tv->name;
t->type = type;
t->tv = tv;
t->in_use = 1;
SCSpinUnlock(&t->spin);
s = thread_store.threads_size;
thread_store.threads_size += STEP;
SCMutexUnlock(&thread_store_lock);
return (int)(s+1);
}
#undef STEP
从实现上看,每次都是从0开始逐一遍历,查找in_use=0的项,并且也没有更新threads_cnt, 此字段并没有使用,猜测最开始想使用此字段表示当前已经注册了多少个线程。
3.2 TmThreadsUnregisterThread
根据线程id进行线程注销,当所有线程都被注销后,释放thread_store.threads。
c
void TmThreadsUnregisterThread(const int id)
{
SCMutexLock(&thread_store_lock);
DEBUG_VALIDATE_BUG_ON(thread_store_sealed);
if (id <= 0 || id > (int)thread_store.threads_size) {
SCMutexUnlock(&thread_store_lock);
return;
}
/* id is one higher than index */
int idx = id - 1;
/* reset thread_id, which serves as clearing the record */
thread_store.threads[idx].in_use = 0;
/* check if we have at least one registered thread left */
size_t s;
for (s = 0; s < thread_store.threads_size; s++) {
Thread *t = &thread_store.threads[s];
if (t->in_use == 1) {
goto end;
}
}
/* if we get here no threads are registered */
SCFree(thread_store.threads);
thread_store.threads = NULL;
thread_store.threads_size = 0;
thread_store.threads_cnt = 0;
end:
SCMutexUnlock(&thread_store_lock);
}
3.3 TmThreadsSetThreadTimestamp
更新线程时间戳。
c
void TmThreadsSetThreadTimestamp(const int id, const SCTime_t ts)
{
SCTime_t now = SCTimeGetTime();
int idx = id - 1;
Thread *t = &thread_store.threads[idx];
SCSpinLock(&t->spin);
SC_ATOMIC_SET(t->pktts, ts);
t->sys_sec_stamp = now;
SCSpinUnlock(&t->spin);
}
3.4 TmThreadsTimeSubsysIsReady
检查所有已注册线程中TVT_PPT类型的线程:若其sys_sec_stamp等于零值nullts,则表明尚未进行初始化,此时整个子系统处于未就绪状态。
c
bool TmThreadsTimeSubsysIsReady(void)
{
static SCTime_t nullts = SCTIME_INITIALIZER;
bool ready = true;
for (size_t s = 0; s < thread_store.threads_size; s++) {
Thread *t = &thread_store.threads[s];
if (!t->in_use) {
break;
}
SCSpinLock(&t->spin);
if (t->type != TVT_PPT) {
SCSpinUnlock(&t->spin);
continue;
}
if (SCTIME_CMP_EQ(t->sys_sec_stamp, nullts)) {
ready = false;
SCSpinUnlock(&t->spin);
break;
}
SCSpinUnlock(&t->spin);
}
return ready;
}
3.5 TmThreadsInitThreadsTimestamp
初始化所有已注册线程中TVT_PPT类型的线程,设置pktts和sys_sec_stamp 。此函数仅在lib和pcapfile模式下被调用。
c
void TmThreadsInitThreadsTimestamp(const SCTime_t ts)
{
SCTime_t now = SCTimeGetTime();
for (size_t s = 0; s < thread_store.threads_size; s++) {
Thread *t = &thread_store.threads[s];
if (!t->in_use) {
break;
}
SCSpinLock(&t->spin);
if (t->type != TVT_PPT) {
SCSpinUnlock(&t->spin);
continue;
}
SC_ATOMIC_SET(t->pktts, ts);
t->sys_sec_stamp = now;
SCSpinUnlock(&t->spin);
}
}
3.6 TmThreadsGetThreadTime
获取指定线程当前数据包的时间戳(仅用于离线模式下,如pcapfile模式)。
数组下标从0开始,而idx是下标加1,因此需要将idx减1进行转换。
c
SCTime_t TmThreadsGetThreadTime(const int idx)
{
DEBUG_VALIDATE_BUG_ON(idx == 0);
const int i = idx - 1;
Thread *t = &thread_store.threads[i];
return SC_ATOMIC_GET(t->pktts);
}
3.7 TmThreadsGetMinimalTimestamp
在所有注册线程中筛选出类型为TVT_PPT且pktts不为空的线程,然后从中找出非休眠线程的最小pktts值。
c
void TmThreadsGetMinimalTimestamp(struct timeval *ts)
{
struct timeval local = { 0 };
static SCTime_t nullts = SCTIME_INITIALIZER;
bool set = false;
SCTime_t now = SCTimeGetTime();
for (size_t s = 0; s < thread_store.threads_size; s++) {
Thread *t = &thread_store.threads[s];
if (t->in_use == 0) {
break;
}
SCSpinLock(&t->spin);
/* only packet threads set timestamps based on packets */
if (t->type != TVT_PPT) {
SCSpinUnlock(&t->spin);
continue;
}
SCTime_t pktts = SC_ATOMIC_GET(t->pktts);
if (SCTIME_CMP_NEQ(pktts, nullts)) {
SCTime_t sys_sec_stamp = SCTIME_ADD_SECS(t->sys_sec_stamp, 5);
/* ignore sleeping threads */
if (SCTIME_CMP_LT(sys_sec_stamp, now)) {
SCSpinUnlock(&t->spin);
continue;
}
if (!set) {
SCTIME_TO_TIMEVAL(&local, pktts);
set = true;
} else {
if (SCTIME_CMP_LT(pktts, SCTIME_FROM_TIMEVAL(&local))) {
SCTIME_TO_TIMEVAL(&local, pktts);
}
}
}
SCSpinUnlock(&t->spin);
}
*ts = local;
SCLogDebug("ts->tv_sec %"PRIuMAX, (uintmax_t)ts->tv_sec);
}
3.8 TmThreadsGetWorkerThreadMax
获取worker线程的最大值,取值范围[1,1024]。
c
uint16_t TmThreadsGetWorkerThreadMax(void)
{
uint16_t ncpus = UtilCpuGetNumProcessorsOnline();
int thread_max = TmThreadGetNbThreads(WORKER_CPU_SET);
/* always create at least one thread */
if (thread_max == 0)
thread_max = ncpus * threading_detect_ratio;
if (thread_max < 1)
thread_max = 1;
if (thread_max > 1024) {
SCLogWarning("limited number of 'worker' threads to 1024. Wanted %d", thread_max);
thread_max = 1024;
}
return (uint16_t)thread_max;
}
四、总结
- 所有注册的线程均保存在全局数组
thread_store.threads中 - 该数组每次扩容时容量增加32个元素
- 通过检查
in_use标志位判断节点是否已被占用 - 每次都需要从数组首元素开始遍历,且未使用threads_cnt字段