从main函数开始,解析配置参数,解析配置文件
void run()
{
dispatch(manager);
{
//finish就是它的exiting为true同时它的task队列为空
while (!FINISHED(manager))
{
#ifdef USE_WORKER_THREADS
//这里进去wait的条件是ready_task队列为空或者pause_requested为1或者exclusive_requested为1
//注意是所有的线程刚开始都会睡眠在work_available条件变量上,只唤醒一个线程,唤醒以后又会去陷入第二个循环,如果发现没有任务了就继续睡眠
while ((empty_readyq(manager) || manager->pause_requested ||
manager->exclusive_requested) && !FINISHED(manager))
{
//线程创建好的时候,就阻塞在这个work_available条件变量上
WAIT(&manager->work_available, &manager->lock);
}
//从ready_task队列中获取到任务
task = pop_readyq(manager)
if (task != NULL)
{
manager->tasks_ready--;
manager->tasks_running++;
task->state = task_state_running;
//这个是调用回调函数并且修改task的状态的
//一个task处理的事件个数是有上限的。当task的事件为空或者处理的事件超过阈值的时候,就跳出循环了
do
{
//这是针对task做的
event = HEAD(task->events);
DEQUEUE(task->events, event, ev_link);
task->nevents--;
//执行封装好的回调
(event->ev_action)((isc_task_t *)task,event);
dispatch_count++;
if (task->references == 0 &&
EMPTY(task->events) &&
!TASK_SHUTTINGDOWN(task))
//处理shutdown事件
if (EMPTY(task->events))
done = true
finished = true
//一个task处理的事件的个数是有上限的超过5个就拒绝处理了
else if dispatch_count >= task->quantum
requeue = true;
done = true;
}while (!done);
//如果一个task是finished的,那么他就结束,释放掉该task的内存
if (finished)
task_finished(task);
manager->tasks_running--;
#ifdef USE_WORKER_THREADS
//exclusive_requested为true同时只剩下一个task,那就唤醒阻塞的线程
if (manager->exclusive_requested &&manager->tasks_running == 1)
SIGNAL(&manager->exclusive_granted);
else if (manager->pause_requested &&manager->tasks_running == 0)
{
SIGNAL(&manager->paused);
}
//该task是由于超过上限才退出的,因此还要再次入队到ready_task
if (requeue)
push_readyq(manager, task);
//如果这种,
if (manager->tasks_running == 0 && empty_readyq(manager))
BROADCAST(&manager->work_available);
}
}
}
}
isc_result_t
isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
unsigned int default_quantum, isc_taskmgr_t **managerp)
{
{
manager = isc_mem_get(mctx, sizeof(*manager));
//taskmgrmethods方法是外面写好的
manager->common.methods = &taskmgrmethods;
#ifdef USE_WORKER_THREADS
//这里是关键,创建named_g_cpus个线程
manager->threads = isc_mem_allocate(mctx,
workers * sizeof(isc_thread_t));
#endif /* USE_WORKER_THREADS */
//如果不用多线程的话,就直接单线程
//初始化三个条件变量
if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS)
if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS)
if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS)
//初始化三个队列
manager->default_quantum = default_quantum;
INIT_LIST(manager->tasks);
INIT_LIST(manager->ready_tasks);
INIT_LIST(manager->ready_priority_tasks);
manager->tasks_running = 0;
manager->tasks_ready = 0;
manager->exclusive_requested = false;
manager->pause_requested = false;
manager->exiting = false;
manager->excl = NULL;
#ifdef USE_WORKER_THREADS
//这里开始创建线程,每个线程跑的函数是run
for (i = 0; i < workers; i++)
{
isc_thread_create(run, manager,&manager->threads[manager->workers]) ==
ISC_R_SUCCESS)
}
#endif /* USE_WORKER_THREADS */
}
}
// 注意调用该函数的时候会获取sock的锁,也就是说最终只有一个线程可能进入到while循环中去
void internal_recv()
{
//加锁
dev = ISC_LIST_HEAD(sock->recv_list);
while (dev != NULL)
{
//doio_recv内部调用的就是recvmsg函数来接收报文
switch (doio_recv(sock, dev))
{
case DOIO_HARD:
send_recvdone_event(sock, &dev);
{
//
task = (*dev)->ev_sender;
(*dev)->ev_sender = sock;
//这里由于本次事件已经处理完了,所以把该事件从该task中移除
ISC_LIST_DEQUEUE(sock->recv_list, *dev, ev_link);
//每次无论新增还是删除一个事件,都要对task进行一次处理
if (((*dev)->attributes & ISC_SOCKEVENTATTR_ATTACHED)
== ISC_SOCKEVENTATTR_ATTACHED)
{
isc_task_sendanddetach(&task, (isc_event_t **)dev);
{
//把事件添加到task中
idle1 = task_send(task, eventp);
//如果task没有事件了,并且状态为idle的,则修改状态返回true
idle2 = task_detach(task);
、
//满足任意一个都添加进去
if (idle1 || idle2)
task_ready(task);
}
}
else
{
//只负责添加event进到task中
isc_task_send(task, (isc_event_t **)dev);
}
}
break;
}
dev = ISC_LIST_HEAD(sock->recv_list);
}
//如果说recv_list中不为空的话,则说明还有新的数据到来,则添加53端口对应的fd到epoll中,具体方法是通过pipe[1]写入,这样的话watcher线程就会从pipi[0]中读取数据,然后调用process_ctlfd函数内部是epoll_ctrl,把该fd再次添加进去进行监听
if (!ISC_LIST_EMPTY(sock->recv_list))
select_poke(sock->manager, sock->fd, SELECT_POKE_READ);
{
cc = write(mgr->pipe_fds[1], buf, sizeof(buf));
}
//释放锁
}
void watcher()
{
while (!done)
{
do
{
//调用poll_wait进行监听了
cc = epoll_wait(manager->epoll_fd, manager->events,manager->nevents, -1);
}} while (cc < 0);
done = process_fds(manager, manager->events, cc);
{
bool done = false;
bool have_ctlevent = false;
//开始处理有事件发生的套接字
for (i = 0; i < nevents; i++)
{
//就是主进程监听的那个socket传来的信息
if (events[i].data.fd == manager->pipe_fds[0])
{
have_ctlevent = true;
continue;
}
//传进去的参数的意义是,该fd是可读的还是可写的
process_fd(manager, events[i].data.fd,
(events[i].events & EPOLLIN) != 0,
(events[i].events & EPOLLOUT) != 0);
{
//当套接字来了消息以后
//取出对应fd的socket信息
sock = manager->fds[fd];
//如果可读
if (readable)
{
if sock==NULL:
goto unlock_fd;
if (sock->listener)
//这个是tcp协议的
dispatch_accept(sock);
else
dispatch_recv(sock);
{
//设置事件的action, 并且封装event到task中,如果是新的task的话,就会把task添加到ready_task中,这样的话阻塞的run线程就会执行了
iev->ev_action = internal_recv;
iev->ev_arg = sock;
//该函数的功效是将事件添加到处于running或者ready的task中,如果说task是idle的状态的话,则最后还要把 task添加到ready_task队列中去
isc_task_send(sender, (isc_event_t **)&iev);
}
unwatch_read = true;
}
if (writeable)
{
if sock==NULL:
goto unlock_fd;
if (sock->connecting)
//tcp的暂且不关注
dispatch_connect(sock);
else
dispatch_send(sock);
{
iev->ev_action = internal_send;
iev->ev_arg = sock;
isc_task_send(sender, (isc_event_t **)&iev);
}
unwatch_write = true;
}
unlock_fd:
//这里是如果fd对应的sock为空的情况,则再次调用epoll_ctrl添加socket进去
}
}
//以上处理完了来信息的所有fd以后,这里就处理pipe[0]来信息的情况
if (have_ctlevent)
done = process_ctlfd(manager);
{
for(;;)
{
//这个函数就是从pipe[0]中读数据,然后把fd和msg返回
select_readmsg(manager, &fd, &msg);
//如果是关机事件的话,那就尽量让该waicher线程退出
if (msg == SELECT_POKE_SHUTDOWN)
return (done = true);
//这个函数就是调用epoll_ctrl
wakeup_socket(manager, fd, msg);
}
return done=false
}
}
}
}
void setup_watcher()
{
//设置epoll的结构
manager->nevents = ISC_SOCKET_MAXEVENTS;
manager->events = isc_mem_get(mctx, sizeof(struct epoll_event) *
manager->nevents);
//创建fd
manager->epoll_fd = epoll_create(manager->nevents);
//这里把管道的一端添加到epoll中,
result = watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ);
{
//这里将管道的一段添加到epoll中进行监听,且监听读事件
ret = epoll_ctl(manager->epoll_fd, op, fd, &event);
}
}
isc_socketmgr_create2()
{
if (maxsocks == 0)
maxsocks = ISC_SOCKET_MAXSOCKETS;
//manager
manager = isc_mem_get(mctx, sizeof(*manager));
manager->maxsocks = maxsocks;
//预留socket的位置
manager->fds = isc_mem_get(mctx,manager->maxsocks * sizeof(isc__socket_t *));
manager->fdstate = isc_mem_get(mctx, manager->maxsocks * sizeof(int));
//预留epoll的位置
manager->epoll_events = isc_mem_get(mctx, (manager->maxsocks *sizeof(uint32_t)));
//设置预处理方法
manager->common.methods = &socketmgrmethods;
#ifdef USE_WATCHER_THREAD
//创建条件变量
if (isc_condition_init(&manager->shutdown_ok) != ISC_R_SUCCESS)
//创建管道
if (pipe(manager->pipe_fds) != 0)
//这里只看使用epoll的情况
result = setup_watcher(mctx, manager);
//这里开始创建watcher线程
if (isc_thread_create(watcher, manager, &manager->watcher))
}
void create_managers()
{
//如果使用多线程的话就设置为CPU的个数
#ifdef ISC_PLATFORM_USETHREADS
if (named_g_cpus == 0)
named_g_cpus = named_g_cpus_detected;
else
named_g_cpus = 1;
//线程分发个数的设置
if (named_g_udpdisp == 0)
if (named_g_cpus_detected == 1)
named_g_udpdisp = 1;
else
named_g_udpdisp = named_g_cpus_detected - 1;
//创建一个task的管理器,内部创建named_g_cpus个run线程,run线程在满足某些条件后要么都睡眠在某个条件变量上,要么就循环去执行task中的事件的回调,线程通过锁来竞争,通过条件变量来同步
result = isc_taskmgr_create(named_g_mctx, named_g_cpus, 0,
&named_g_taskmgr);
//这个是建立监听端口的,重点只看udp协议的
result = isc_socketmgr_create2(named_g_mctx, &named_g_socketmgr,
maxsocks);
}
void run_server()
{
//这个就是创建一个分发管理器
dns_dispatchmgr_create();
ns_interfacemgr_create();
//这个是最主要的,是打开一些socket的
load_configuration()
{
//内部有一系列的函数调用,直到调用到ns_interface_setup
//这个函数主要就是初始化ifp的一些内容
result = ns_interface_create(mgr, addr, name, &ifp);
{
//
ifp = isc_mem_get(mgr->mctx, sizeof(*ifp));
result = ns_clientmgr_create()
for (disp = 0; disp < MAX_UDP_DISPATCH; disp++)
ifp->udpdispatch[disp] = NULL;
}
result = ns_interface_listenudp(ifp)
{
for (disp = 0; disp < ifp->nudpdispatch; disp++)
{
result = dns_dispatch_getudp_dup()
{
result = dns_dispatchmgr_setudp()
//没有的话就创建一个,disp=0的时候传递进去的dup_socket是NULL,对于后面所有的disp传递进去的都是第一个创建好的dispatch的socket,注意采用dup的方式进行地址复用
result = dispatch_createudp()
{
disp->socktype = isc_sockettype_udp;
//就是socket,bind那一系列
result = get_udpsocket()
result = open_socket(sockmgr, localaddr, 0, &sock, NULL);
disp->socket = sock;
disp->local = *localaddr;
//分配64个task
for (i = 0; i < disp->ntasks; i++)
{
result = isc_task_create(taskmgr, 0, &disp->task[i]);
}
ISC_LIST_APPEND(mgr->list, disp, link);
disp->ctlevent = isc_event_allocate()
}
}
}
//创建client,为每个client创建不同的事件
result = ns_clientmgr_createclients
{
for (disp = 0; disp < n; disp++)
{
result = get_client()
{
//每一个dispatch就有一个client,client里面的是dns信息,task等
result = client_create(manager, &client);
{
result = isc_task_create(manager->taskmgr, 0, &client->task);
result = dns_message_create()
//设置client_senddone事件
client->sendevent = isc_socket_socketevent()
//设置ns__client_request事件
client->recvevent = isc_socket_socketevent()
//初始化查询的一些信息
result = ns_query_init(client);
}
isc_socket_t *sock;
//赋值client的dispatch,
dns_dispatch_attach(disp, &client->dispatch);
//获取到前面创建的socket
sock = dns_dispatch_getsocket(client->dispatch);
//赋值client的udpsocket
isc_socket_attach(sock, &client->udpsocket);
//就是client_start事件,然后工作线程就开始执行client_start事件了
ev = &client->ctlevent;
isc_task_send(client->task, &ev);
//然后就是多个线程开始执行client_start函数了,
client_start
{
client_udprecv(client);
{
它的内部就是recvmsg函数
下面有一个比较关键的一步操作是,这个就是把fd加入到了watcher线程中进行监听了,如果监听到信息了就调用internal_recv处理了,它内部仍然是recvmsg函数
if (ISC_LIST_EMPTY(sock->recv_list) && !sock->pending_recv)
select_poke(sock->manager, sock->fd, SELECT_POKE_READ);
}
}
}
}
}
}
}
}
void
named_server_create(isc_mem_t *mctx, named_server_t **serverp)
{
named_server_t *server = isc_mem_get(mctx, sizeof(*server));
//设置server的一些信息
isc_task_create(named_g_taskmgr, 0, &server->task),"creating server task")
//把run_server添加到onrun中,这样的话主程序一起起来就会调用run_server
isc_app_onrun(named_g_mctx, server->task, run_server, server)
}
void setup()
{
//这里是查询系统cpu的个数
#ifdef ISC_PLATFORM_USETHREADS
named_g_cpus_detected = isc_os_ncpus();
//如果是后台的话这里就启动一个后台进程
if (!named_g_foreground)
named_os_daemonize();
//这部分内容开始日志输出,有限资源获取,直接略过
//
result = create_managers();
named_server_create(named_g_mctx, &named_g_server);
}
//解析参数,初始化各个变量中
parse_command_line(argc, argv);
//这中间会处理很多用户和权限的问题
setup();