Bind是一款开放源码的DNS服务器软件,Bind由美国加州大学Berkeley分校研发和维护的,全名为Berkeley Internet Name Domain他是目前世界上使用最为广泛的DNS服务器软件,支持各种unix平台和windows平台。官方网站:http://www.bind.com/
下面介绍一下Bind软件的主要的socket处理模块:
Bind业务处理主要要关注一个run函数和isc_app_run函数,前者是线程函数,相当于一个消息泵,这个for循环作用通过管道发送事件,这个事件在到task里的run中被处理,如此反复和windows编程中的消息循环类似;
Bind中的socket采用的是epoll模型,在main.c中的setup函数里的result = create_managers()->result = isc_socketmgr_create2(ns_g_mctx, &ns_g_socketmgr, maxsocks)-> setup_watcher;创建epool。if(isc_thread_create(watcher, manager, &manager->watcher) != ISC_R_SUCCESS) 启动watcher这个线程函数process_fds来处理epoll,具体的处理函数是process_fd,根据不同的事件来执行dispatch_accept(sock); dispatch_recv(sock); dispatch_connect(sock); dispatch_send(sock);这几个函数最后执行的都是isc_task_send(它的作用是将event加到task队列中);
在main函数中,setup创建进程必要的服务,setup里比较重要的函数是create_managers和ns_server_create;ns_server_create主要是对一些结构体中的函数指针赋值还有用到的数据结构分配内存,create_managers启动各种线程;
isc_app_run则是见将on-run事件(通过管道)加入到任务队列中,最终执行的是isc__task_sendanddetach。在ns_server_create中的CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),"isc_app_onrun")里的ISC_LIST_APPEND(isc_g_appctx.on_run, event, ev_link)比较关键,将on-run事件到链表中,这个链表在isc_app_run里的for循环中被遍历;
Socket的初始化:
在setup(void)-> create_managers->isc__socketmgr_create2函数中:
manager->common.methods = &socketmgrmethods;将函数指针赋值。在ns_server_create(ns_g_mctx, &ns_g_server)
CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),"isc_app_onrun");实际执行的是run_server函数
load_configuration->scan_interfaces(server, ISC_TRUE)->ns_interfacemgr_scan(server->interfacemgr, verbose)->ns_interfacemgr_scan0(mgr, NULL, verbose)-> do_scan->ns_interface_setup->if (accept_tcp == ISC_TRUE) {result = ns_interface_accepttcp(ifp);->isc_socket_create->manager->methods->socketcreate(manager, pf, type, socketp)这句代码会调用上面赋给的函数地址(socketmgrmethods),将调用socketmgrmethods的isc__socket_create函数。前面阐述的是有关tcp的初始化,下面是udp的初始化:
do_scan->ns_interface_setup->ns_interface_listenudp->ns_clientmgr_createclients->client_create->
clclient->sendevent = (isc_socketevent_t *)
isc_event_allocate(client->mctx, client,
ISC_SOCKEVENT_SENDDONE,
client_senddone, client,
sizeof(isc_socketevent_t));
client->recvevent = (isc_socketevent_t *)
isc_event_allocate(client->mctx, client,
ISC_SOCKEVENT_RECVDONE,
client_request, client,
sizeof(isc_socketevent_t));
主要功能就是赋值ISC_EVENT_INIT(event, size, 0, NULL, type, action, deconst_arg,sender, destroy, mctx);active函数指针赋值为client_request,即在这里给UDP的处理函数赋值(接收);发送同样如此:这样每进来一次udp就查询一次,,会在task的run函数里执行active指针函数,进而调用这个client_request
switch (client->message->opcode) {
case dns_opcode_query:
CTRACE("query");
ns_query_start(client);
break;
case dns_opcode_update:
CTRACE("update");
ns_client_settimeout(client, 60);
ns_update_start(client, sigresult);
break;
case dns_opcode_notify:
CTRACE("notify");
ns_client_settimeout(client, 60);
ns_notify_start(client);
break;
case dns_opcode_iquery:
CTRACE("iquery");
ns_client_error(client, DNS_R_NOTIMP);
break;
default:
CTRACE("unknown opcode");
ns_client_error(client, DNS_R_NOTIMP);
}
Socket和task的关联(和一些关键性代码的说明):
1、isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,isc_task_t **taskp)—创建ns_g_taskmgr,实参:ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr
/*分配内存*/manager = isc_mem_get(mctx, sizeof(*manager));返回的是ret = ctx->freelists[new_size];或(ctx->memalloc)(ctx->arg, size);前者是链表的最后,后在则是分配的内存的起始位置。
manag的数据类型是isc__taskmgr_t,ctx是isc__mem类型,ctx->freelist是element ** freelists,element则是typedef struct element element;
struct element {
element * next;
};
//初始化条件变量(linux系统的函数) 一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁 并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_condition_init() %s",
isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed"));
result = ISC_R_UNEXPECTED;
goto cleanup_threads;
}
/*初始化条件变量*/
if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_condition_init() %s",
isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed"));
result = ISC_R_UNEXPECTED;
goto cleanup_workavailable;
}
/*启线程*/
for (i = 0; i < workers; i++) {
if (isc_thread_create(run, manager,
&manager->threads[manager->workers]) ==
ISC_R_SUCCESS) {
manager->workers++;
started++;
}
}
/*设置并发线程*/
isc_thread_setconcurrency(workers);
/*附加到特定的内存块上*/
isc_mem_attach(mctx, &manager->mctx);将muct的值赋给&manager->mctx。
最后启动线程。重点说明一下这个run线程函数,这个线程函数使用来处理task的。run(void *uap) {
…………………………………………………………………….
isc__taskmgr_t *manager = uap;
dispatch(manager);和windows的DispatchMessage类似分发消息
……………………………………………………………………..。
return ((isc_threadresult_t)0);
}
在static void dispatch(isc__taskmgr_t *manager)里面有三个while循环
//判断(manager)->exiting && (manager)->tasks. .head == NULL
while (!FINISHED(manager))
{while ((EMPTY(manager->ready_tasks) ||manager->exclusive_requested) &&!FINISHED(manager)) // 判断(manager->ready_tasks).head ==NULL
{、
XTHREADTRACE(isc_msgcat_get(isc_msgcat,ISC_MSGSET_GENERAL,ISC_MSG_WAIT, "wait"));
WAIT(&manager->work_available, &manager->lock);
//实际执行的是isc_condition_wait((&manager->work_available), (&manager->lock)) == ISC_R_SUCCESS);-----------------> (pthread_cond_wait((&manager->work_available), &((&manager->lock)->mutex)) == 0,这里用来发信号,对互斥量加以保护,线程释放互斥量,等待其他线程发给该条件变量的信号(唤醒一个等待者)或广播该条件变量(唤醒所有等待者)。当等待条件变量时,互斥量必须始终为释放的,这样其他线程才有机会锁住互斥量,修改条件变量。当线程从条件变量等待中醒来时,它重新继续锁住互斥量,对临界资源进行处理。
XTHREADTRACE(isc_msgcat_get(isc_msgcat,ISC_MSGSET_TASK,ISC_MSG_AWAKE, "awake"));
}
task = HEAD(manager->ready_tasks);
if (task != NULL) {
task->state = task_state_running;
XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_RUNNING, "running"));
isc_stdtime_get(&task->now);
//遍历链表
do {
if (!EMPTY(task->events)) {
event = HEAD(task->events);
DEQUEUE(task->events, event, ev_link);
/**执行事件—消息处理 */
.........................................................
if (event->ev_action != NULL) {
UNLOCK(&task->lock);
(event->ev_action)( (isc_task_t *)task, event);
LOCK(&task->lock);
}
dispatch_count++;
} if (task->references == 0 && EMPTY(task->events) &&!TASK_SHUTTINGDOWN(task)) { isc_boolean_t was_idle;
/*在这个任务里没有引用没有待定的事件,这意味者不会有实际的事件(动作)发生,初始化关机以防止成为一个废止(状态)。替代"if EMPTY(task->events)"的原因:如果发送没有关机事件,将派发一个结束任务;如果没有派发关机事件,(总是)希望任务的总量是实际中(任务的总量)*/
was_idle = task_shutdown(task);
INSIST(!was_idle);
}
if (EMPTY(task->events)) {
XTRACE(isc_msgcat_get(isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_EMPTY,
"empty"));
if (task->references == 0 &&
TASK_SHUTTINGDOWN(task)) {
/*The task is done */
XTRACE(isc_msgcat_get(
isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_DONE,
"done"));
finished = ISC_TRUE;
task->state = task_state_done;
} else
task->state = task_state_idle;
done = ISC_TRUE;
} else if (dispatch_count >= task->quantum) {
/*(任务)总量用完,但是还有更多的work在做,将重新排列放到在准备队列后。不必检查总量直到至少派发一个事件,因此最小数是1*/ XTRACE(isc_msgcat_get(isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_QUANTUM,
"quantum"));
task->state = task_state_ready;
requeue = ISC_TRUE;
done = ISC_TRUE;
}
} while (!done); 初始化是ISC_FALSE
………
}
}
注意:上面处理的是ns_g_taskmgr里的task。
2、isc__socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp,unsigned int maxsocks)//创建ns_g_socketmgr
实参:ns_g_mctx, &ns_g_socketmgr, maxsocks
isc__socketmgr_t *manager;
*managerp = (isc_socketmgr_t *)socketmgr;//初始化
manager = isc_mem_get(mctx, sizeof(*manager));//分配内存给manger的各个成员变量和方法赋值;
................................................
//初始化manager->shutdown_ok—条件变量,和互斥对象配合使用在多线程编程中。
if (isc_condition_init(&manager->shutdown_ok) != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_condition_init() %s",
isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_FAILED, "failed"));
result = ISC_R_UNEXPECTED;
goto cleanup_lock;
}
//创建管道-----epoll模型中使用
if (pipe(manager->pipe_fds) != 0) {
isc__strerror(errno, strbuf, sizeof(strbuf));
UNEXPECTED_ERROR(__FILE__, __LINE__,
"pipe() %s: %s",
isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,\
ISC_MSG_FAILED, "failed"),
strbuf);
result = ISC_R_UNEXPECTED;
goto cleanup_condition;
}
//设置EPOLL中的事件和文件描述的关联
result = setup_watcher(mctx, manager);这个函数实际上执行的是epoll_ctl(manager->epoll_fd, EPOLL_CTL_ADD, fd, &event);
if (isc_thread_create(watcher, manager, &manager->watcher) !=ISC_R_SUCCESS)//启动线程,实际执行的是cc = epoll_wait(manager->epoll_fd, manager->events,
manager->nevents, -1);开始监听管道里的事件,如果需要处理就处理。