与 Linux 中的 pthread 线程不同,thread 只是对线程的应用层模拟。

keepalived借助 thread 结构,将所有的事件(如文件描述符的读写事件、定时事件等)和对应的处理函数封装在一起。然后这些 threads 又被装入不同的链表,挂载到名为 thread_master 的结构中,这样所有的操作只需要面向 thread_master

thread_master 线程管理者维护了 7 个线程队列:read、write、timer、child、event、ready 和 unuse。

  • read 队列对应于描述符的读事件
  • write 队列对应于描述符的写事件
  • timer 通常为定时事件
  • event 为自定义事件

这些事件需要我们自己在适合的时候触发,并且这类事件不需要对描述符操作,也不需要延时。

  • ready 队列通常只是在内部使用,比如 read, write 或 event 队列中因事件触发,就会把该线程移入 ready 队列进行统一处理。

  • unuse 是在一个线程执行完毕后被移入此队列,并且在需要创建一个新的线程时,将从该队列中取出资源,这样就避免了再次申请内存。只有在取不到的情况下才进行新线程的内存申请。

数据结构

thread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* Thread itself. */
typedef struct _thread {
unsigned long id;
unsigned char type; /* thread type */
struct _thread *next; /* next pointer of the thread */
struct _thread *prev; /* previous pointer of the thread */
struct _thread_master *master; /* pointer to the struct thread_master. */
int (*func) (struct _thread *); /* event function */
void *arg; /* event argument */
TIMEVAL sands; /* rest of time sands value. */
union {
int val; /* second argument of the event. */
int fd; /* file descriptor in case of read/write. */
struct {
pid_t pid; /* process id a child thread is wanting. */
int status; /* return status of the process */
} c;
} u;
} thread;

thread_master

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Linked list of thread. */
typedef struct _thread_list {
thread *head;
thread *tail;
int count;
} thread_list;

/* Master of the theads. */
typedef struct _thread_master {
thread_list read;
thread_list write;
thread_list timer;
thread_list child;
thread_list event;
thread_list ready;
thread_list unuse;
fd_set readfd;
fd_set writefd;
fd_set exceptfd;
unsigned long alloc;
} thread_master;

API

thread_fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
┌─────────────────────────────────────────────────────────────┐
│ thread_fetch() │
├─────────────────────────────────────────────────────────────┤
1. 处理事件链表 (event) │
while (thread = thread_trim_head(&m->event)) │
return fetch; // 优先处理终止事件 │
├─────────────────────────────────────────────────────────────┤
2. 处理就绪链表 (ready) │
while (thread = thread_trim_head(&m->ready)) │
return fetch; // 返回已就绪的线程 │
├─────────────────────────────────────────────────────────────┤
3. 计算 select 超时时间 │
│ thread_compute_timer(m, &timer_wait) │
├─────────────────────────────────────────────────────────────┤
4. 调用 select() 阻塞等待 │
│ select(FD_SETSIZE, &readfd, &writefd, &exceptfd, │
│ &timer_wait) │
├─────────────────────────────────────────────────────────────┤
5. 处理超时和就绪的线程 │
│ - 超时子进程 → ready 链表 │
│ - 可读文件描述符 → ready 链表 │
│ - 超时读事件 → READ_TIMEOUT │
│ - 超时写事件 → WRITE_TIMEOUT │
│ - 定时器到期 → ready 链表 │
├─────────────────────────────────────────────────────────────┤
6. 返回一个就绪线程 │
│ thread = thread_trim_head(&m->ready) │
return fetch; │
└─────────────────────────────────────────────────────────────┘