Bind是一款開放源碼的DNS服務(wù)器軟件,Bind由美國加州大學(xué)Berkeley分校研發(fā)和維護的,全名為Berkeley Internet Name Domain他是目前世界上使用最為廣泛的DNS服務(wù)器軟件,支持各種unix平臺和windows平臺。官方網(wǎng)站:http://www.bind.com/
下面介紹一下Bind軟件的主要的socket處理模塊:
Bind業(yè)務(wù)處理主要要關(guān)注一個run函數(shù)和isc_app_run函數(shù),前者是線程函數(shù),相當(dāng)于一個消息泵,這個for循環(huán)作用通過管道發(fā)送事件,這個事件在到task里的run中被處理,如此反復(fù)和windows編程中的消息循環(huán)類似;
Bind中的socket采用的是epoll模型,在main.c中的setup函數(shù)里的result = create_managers()->result = isc_socketmgr_create2(ns_g_mctx, &ns_g_socketmgr, maxsocks)-> setup_watcher;創(chuàng)建epool。if(isc_thread_create(watcher, manager, &manager->watcher) != ISC_R_SUCCESS) 啟動watcher這個線程函數(shù)process_fds來處理epoll,具體的處理函數(shù)是process_fd,根據(jù)不同的事件來執(zhí)行dispatch_accept(sock); dispatch_recv(sock); dispatch_connect(sock); dispatch_send(sock);這幾個函數(shù)最后執(zhí)行的都是isc_task_send(它的作用是將event加到task隊列中);
在main函數(shù)中,setup創(chuàng)建進程必要的服務(wù),setup里比較重要的函數(shù)是create_managers和ns_server_create;ns_server_create主要是對一些結(jié)構(gòu)體中的函數(shù)指針賦值還有用到的數(shù)據(jù)結(jié)構(gòu)分配內(nèi)存,create_managers啟動各種線程;
isc_app_run則是見將on-run事件(通過管道)加入到任務(wù)隊列中,最終執(zhí)行的是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)比較關(guān)鍵,將on-run事件到鏈表中,這個鏈表在isc_app_run里的for循環(huán)中被遍歷;
Socket的初始化:
在setup(void)-> create_managers->isc__socketmgr_create2函數(shù)中:
manager->common.methods = &socketmgrmethods;將函數(shù)指針賦值。在ns_server_create(ns_g_mctx, &ns_g_server)
CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),"isc_app_onrun");實際執(zhí)行的是run_server函數(shù)
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)這句代碼會調(diào)用上面賦給的函數(shù)地址(socketmgrmethods),將調(diào)用socketmgrmethods的isc__socket_create函數(shù)。前面闡述的是有關(guān)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函數(shù)指針賦值為client_request,即在這里給UDP的處理函數(shù)賦值(接收);發(fā)送同樣如此:這樣每進來一次udp就查詢一次,,會在task的run函數(shù)里執(zhí)行active指針函數(shù),進而調(diào)用這個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的關(guān)聯(lián)(和一些關(guān)鍵性代碼的說明):
1、isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum,isc_task_t **taskp)—創(chuàng)建ns_g_taskmgr,實參:ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr
/*分配內(nèi)存*/manager = isc_mem_get(mctx, sizeof(*manager));返回的是ret = ctx->freelists[new_size];或(ctx->memalloc)(ctx->arg, size);前者是鏈表的最后,后在則是分配的內(nèi)存的起始位置。
manag的數(shù)據(jù)類型是isc__taskmgr_t,ctx是isc__mem類型,ctx->freelist是element ** freelists,element則是typedef struct element element;
struct element {
element * next;
};
//初始化條件變量(linux系統(tǒng)的函數(shù)) 一旦其它的某個線程改變了條件變量,它將通知相應(yīng)的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖 并重新測試條件是否滿足。一般說來,條件變量被用來進行線程間的同步。
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++;
}
}
/*設(shè)置并發(fā)線程*/
isc_thread_setconcurrency(workers);
/*附加到特定的內(nèi)存塊上*/
isc_mem_attach(mctx, &manager->mctx);將muct的值賦給&manager->mctx。
最后啟動線程。重點說明一下這個run線程函數(shù),這個線程函數(shù)使用來處理task的。run(void *uap) {
…………………………………………………………………….
isc__taskmgr_t *manager = uap;
dispatch(manager);和windows的DispatchMessage類似分發(fā)消息
……………………………………………………………………..。
return ((isc_threadresult_t)0);
}
在static void dispatch(isc__taskmgr_t *manager)里面有三個while循環(huán)
//判斷(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);
//實際執(zhí)行的是isc_condition_wait((&manager->work_available), (&manager->lock)) == ISC_R_SUCCESS);-----------------> (pthread_cond_wait((&manager->work_available), &((&manager->lock)->mutex)) == 0,這里用來發(fā)信號,對互斥量加以保護,線程釋放互斥量,等待其他線程發(fā)給該條件變量的信號(喚醒一個等待者)或廣播該條件變量(喚醒所有等待者)。當(dāng)?shù)却龡l件變量時,互斥量必須始終為釋放的,這樣其他線程才有機會鎖住互斥量,修改條件變量。當(dāng)線程從條件變量等待中醒來時,它重新繼續(xù)鎖住互斥量,對臨界資源進行處理。
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);
/**執(zhí)行事件—消息處理 */
.........................................................
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;
/*在這個任務(wù)里沒有引用沒有待定的事件,這意味者不會有實際的事件(動作)發(fā)生,初始化關(guān)機以防止成為一個廢止(狀態(tài))。替代"if EMPTY(task->events)"的原因:如果發(fā)送沒有關(guān)機事件,將派發(fā)一個結(jié)束任務(wù);如果沒有派發(fā)關(guān)機事件,(總是)希望任務(wù)的總量是實際中(任務(wù)的總量)*/
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) {
/*(任務(wù))總量用完,但是還有更多的work在做,將重新排列放到在準(zhǔn)備隊列后。不必檢查總量直到至少派發(fā)一個事件,因此最小數(shù)是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)//創(chuàng)建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));//分配內(nèi)存給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;
}
//創(chuàng)建管道-----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;
}
//設(shè)置EPOLL中的事件和文件描述的關(guān)聯(lián)
result = setup_watcher(mctx, manager);這個函數(shù)實際上執(zhí)行的是epoll_ctl(manager->epoll_fd, EPOLL_CTL_ADD, fd, &event);
if (isc_thread_create(watcher, manager, &manager->watcher) !=ISC_R_SUCCESS)//啟動線程,實際執(zhí)行的是cc = epoll_wait(manager->epoll_fd, manager->events,
manager->nevents, -1);開始監(jiān)聽管道里的事件,如果需要處理就處理。