管理器

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);
					
            } 
            
    }
}

以上就是创建任务管理器所进行的工作,主要就是开了几个工作线程,以及处理好工作线程和配置文件的加载工作,以及工作线程主要通过回调的方式处理任务

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值