BIND9原始碼分析之UDP資料處理

2021-08-07 06:14:35 字數 2479 閱讀 5386

本文簡要介紹一下bind9中的udp資料處理,包括如何建立socket、設定什麼socket引數、多執行緒環境中如何讓多個執行緒讀取53埠的資料等等。

bind9的架構採用event-driven和task-based。對於像tcp這樣的事件採用event-driven來等待讀寫時間,而在多執行緒方面,採用task-based的方式,伺服器啟動時由task_manager建立一系列幹活的執行緒task(cpu個數個),然後外部的使用者通過呼叫isc_task_send函式將事件掛到task_manager的事件佇列中,各執行緒(task)從這個事件佇列中獲得要幹的活,開始幹活。

對於udp而言,bind9的做法是在主程序中建立好udp socket,然後各個執行緒都在這個fd上通過recvmsg來收取訊息並處理(由於recvmsg是執行緒安全的,所以多執行緒環境中可以這樣用而不用加鎖)。

關鍵流程如下所示:

1, 主程序:ns_inte***ce_listenudp

其中ns_inte***ce_listenudp的作用是在給定的ip位址和埠上監聽udp事件:

首先,呼叫

dns_dispatch_getudp()從inte***ce manager中獲取到udpdispatch(如果沒有,就建立乙個),其中在建立udpdispatch的

dispatch_createudp函式中:

a, 會呼叫get_udpsocket()建立新的isc_socket_t物件。get_udpsocket()中關鍵的是open_socket(sockmgr, localaddr,isc_socket_reuseaddress, &sock)函式,它建立乙個socket,並呼叫bind繫結ip位址和埠。關鍵步驟是:

1

2

3

4

1),呼叫socket(af_inet, sock_dgram, ipproto_udp);建立socket.

2),呼叫 flags = fcntl(fd, f_getfl, 0); flags |= port_nonblock;  ret = fcntl(fd, f_setfl, flags);將socket設定為非阻塞模式

3),呼叫setsockopt(sock->fd, sol_socket, so_reuseaddr, (void*)&on,sizeof(on))設定套接字選項

4),呼叫bind(sock->fd, &sockaddr->type.sa, sockaddr->length) 為該socket繫結ip位址和埠

b, socket建立好,bind好之後。分配64個task(這些task存在dispatch->task[64]陣列中),然後為dispatch->ctlevent分配事件,它呼叫distroy_disp來銷毀乙個dispatch(當然其中會呼叫closesocket來關閉乙個socket).

2,通過getudp獲取到udpdispatch之後,就呼叫

ns_clientmgr_createclients(ifp->clientmgr, ns_g_cpus, ifp, isc_false);來建立執行緒監聽這個inte***ce ifp了:

建立ns_g_cpu個client,然後呼叫ev = &client->ctlevent;  isc_task_send(client->task, &ev);開始幹client_start活

其中建立client的時候,client->sendevent幹client_senddone活;  client->recvevent幹client_request活; client->ctlevent幹client_start活

3, client_start

對於udp,執行client_udprecv()函式,client_udprecv的核心是執行isc_socket_recv2函式,isc_socket_recv2的核心是執行socket_recv();socket_recv的核心是doio_recv()。

doio_recv()中就是recvmsg()系統呼叫。

工作執行緒:

上面的isc_task_send(client->task, &ev)被呼叫後,主程序就把任務丟給了工作執行緒去做。

示意圖如下所示:

從上圖中可以看到,當執行緒通過recvmsg接收到資料後會交給佇列中的執行緒執行client_request()函式;client_request就是解包處理dns請求。

如果執行緒通過recvmsg沒有接收到資料,則將這個fd加入到epoll的監聽佇列中(bind9有乙個單獨的執行緒執行epoll迴圈),當這個fd上有讀事件的時候,epoll執行緒會呼叫internal_recv()處理udp資料,internal_recv的核心依然是doio_recv函式。

BIND9原始碼分析奠基

因為從事的是dns方面的工作,而且也很想學習高效能伺服器的知識,所以學習bind9的實現既能兼顧工作又可以學到高效能伺服器的知識。一是市面上bind9原始碼分析的資料太少了,而且 量又很大,所以對於像我這樣的新手來說入門門檻實在太高,所以想把自己的總結分享出來,幫助那些新人。by the way,我...

bind9原始碼分析 檢視A記錄的資料組織

方法 gdb除錯時,如下可以看到某一node節點資料,gdb p rdatasetheader t dns rbtdb t dns zone t named g server viewlist head zonetable table root data db tree root down data...

BIND9原始碼學習筆記1 gdb除錯篇

學習bind9原始碼之前,首先要知道如何用gdb來除錯bind。bind9的原始碼我是先看 弄懂它的架構,像什麼event drive,epoll等,再去看它的業務流程。看業務流程的時候要追蹤它的資料流和處理過程,所以用gdb比較好,當然自己加日誌再結合bind9自有的日誌也不是不 行,只是覺得這樣...