bind中有俩类重要的线程,一类是工作线程,它的任务就是跑业务代码,常见的查询,发送等就是它来实现的,一般线程的个数是cpu的个数;还有一类是管理socket的线程,一般就是一个,bind通过io多路复用来实现高并发,我这里就拿select来举例,这类线程负责监听fd的情况,包括增加,删除,针对读写调用相关action。它和工作线程之间的通信往往通过一对pipe进行。对各个线程的同步则通过条件变量进行。
1.首先来分析一下taskmanger
setup()
{
result = create_managers();
{
//确定cpu的个数
cpus_detected = isc_os_ncpus()
//生成任务管理器ns_g_taskmgr,第一个参数理解为内存池,每次申请空间都从那里申请
result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr);
{
//先申请空间
isc_taskmgr_t *manager = isc_mem_get(mctx, sizeof(*manager));
然后初始化manger的一些变量,包括锁,魔树等
//初始化线程
manager->threads = isc_mem_allocate(mctx, workers * sizeof(isc_thread_t));
//初始化俩个重要的条件变量
isc_condition_init(&manager->work_available) != ISC_R_SUCCESS
isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS
//初始化manger的俩个task
INIT_LIST(manager->tasks)
INIT_LIST(manager->ready_tasks);
//初启动参数
manager->tasks_running = 0;//正在running的task
manager->exclusive_requested = ISC_FALSE;
manager->exiting = ISC_FALSE;
//它的目的是通过manger可以找到分配内存的接口
isc_mem_attach(mctx, &manager->mctx);
//这里是cpu个数个workers
for (i = 0; i < workers; i++)
{
//创建workers个run线程,就是上面所说的工作线程
isc_thread_create(run, manager,&manager->threads[manager->workers])
}
}
}
}
2.下面重点分析run函数,run是处理任务的,server的线程还会有配置文件的加载,那么在同步这俩个线程的时候需要用到它里面的俩个条件变量,这里先简单的做一个小结,server刚启动的时候会把 manager->exclusive_requeste设置为false,假设在执行任务的时候需要加载配置文件了,那么在加载配置文件之前,server会把manager->exclusive_requeste设置为true,这样的话工作线程就会阻塞到manager->work_available了,而server这个时候如果发现工作线程的task_running>1的话,他也会阻塞到manager->exclusive_grante条件变量,直到该manger把task_running减到少于1,它会signal(manager->exclusive_grante),这样就可以开始加载配置文件了。当配置文件加载完成以后,他又会设置 manager->exclusive_requested 为false,同时BROADCAST(&manager->work_available)唤醒所有的工作线程继续工作,也就是说工作线程处理任务和server线程加载配置文件不会同时发生,而且在加载配置文件的时候要保证只有一个task在跑
run函数就是工作线程,假设工作线程是5个
run()
{
//进行任务的分发
dispatch(manager);
{
//进来是一个while循环,它的结束条件是(m)->exiting==true && EMPTY((m)->tasks==true),这个很好理解,一般shutdown的时候exiting会被设置为true,也就是说他为true但是该manger中还有task没有跑完,这个时候还不可以退出循环,得把manger中所有的task跑完才可以
while(!FINISHED(manager))
{
//当manger中ready_task为空的时候,5个工作线程处于等待条件变量work_available很正常,因为这个时候没有task可做,还有一个就是在server加载配置文件前,他会把manager->exclusive_requested设置为false,加载完配置文件以后设置manager->exclusive_requested为true,也就是说在加载配置文件期间工作线程也是处于wait的状态
while ((EMPTY(manager->ready_tasks) || manager->exclusive_requested) &&!FINISHED(manager))
{
WAIT(&manager->work_available, &manager->lock);
}
//当某个线程被唤醒的时候,它会获取manger的ready_task中的task
task = HEAD(manager->ready_tasks)
//初始化task的一些信息
unsigned int dispatch_count = 0;
isc_boolean_t done = ISC_FALSE;
isc_boolean_t requeue = ISC_FALSE;
isc_boolean_t finished = ISC_FALSE;
//出队该task
DEQUEUE(manager->ready_tasks, task, ready_link);
manager->tasks_running++
//修改task状态
task->state = task_state_running;
//开始循环处理事件,注意处理task都是加锁的,为了防止每个task以及每个线程不被饿死,每个task最多只能处理指定数量个事件
lock(task)
do
{
//获取到task的事件
event = HEAD(task->events);
DEQUEUE(task->events, event, ev_link);
//调用事件的action,这里就包括对各个请求的处理
(event->ev_action)(task,event)
dispatch_count++
//如果task的引用为空同时event为空,但是shutdonw不为空,则迁移shutdown事件到event事件,同时修改task的状态为ready
if (task->references == 0 && EMPTY(task->events) && !TASK_SHUTTINGDOWN(task))
{
was_idle = task_shutdown(task);
}
//事件为空,分情况讨论,但是无论如何对该task的处理都得结束了
if (EMPTY(task->events))
{
//该task的引用为空同时没有shutdown事件代表该task可以被销毁
if (task->references == 0 && TASK_SHUTTINGDOWN(task))
{
finished = ISC_TRUE;
task->state = task_state_done;
}
else//事件为空但是还有别的引用,因此只是空闲而已
{
task->state = task_state_idle;
}
done = ISC_TRUE;
}
else if (dispatch_count >= task->quantum) //保证每个task最多处理5个事件
{
task->state = task_state_ready;//代表该task还需要被再次加入处理
requeue = ISC_TRUE
done = ISC_TRUE
}
}while(!done)
unlock(task)
if (finished)
{
task_finished
{
//将该task从manager中移除
UNLINK(manager->tasks, task, link);
//这里假设所有的task都完成了而且manager要退出了,那么通知所有的线程也退出
if (FINISHED(manager))
{
BROADCAST(&manager->work_available);
}
}
}
manager->tasks_running--
//这里也是对应server加载配置文件的时候,当它开始加载的时候,如果该manger的running_task>1的话,他就会阻塞到manager->exclusive_granted条件变量,也就是说manger的running_task大于1的时候,他就先不加载配置文件,当manger的task_running为1的时候,他才会唤醒server去加载配置文件,当配置文件加载完成以后再唤醒工作线程去处理任务
if (manager->exclusive_requested && manager->tasks_running == 1)
{
SIGNAL(&manager->exclusive_granted);
}
//也就是说这个task还有事件没处理完,需要再次被调度
if (requeue)
{
ENQUEUE(manager->ready_tasks, task,ready_link);
}
}
}
以上就是创建任务管理器所进行的工作,主要就是开了几个工作线程,以及处理好工作线程和配置文件的加载工作,以及工作线程主要通过回调的方式处理任务