main函數的前面部分做好了傳入參數的處理、config文件的讀取、初始化等準備工作,這里將進入最主要的部分,hostapd_global_run函數。
這里就不貼繁瑣的代碼了,這個函數主要分三步:
1.調用tncs_global_init完成tnc相關的初始化,這里就不詳細說了
2.? 調用os_daemonize函數實現將該程序以后臺進程運行,它主要實現過程是調用os_daemon將標準輸入、標準輸出和標準出錯都重定向到/dev/null文件下,這樣就不能通過
終端進行交互了,但是交互過程是使用hostapd_cli這個進程實現的,前面章節有介紹;然后檢查pid_file文件的合法性。
3.? eloop_run核心函數,這個函數很重要,所以下面將詳細介紹。
~~~
void eloop_run(void)
{
#ifdef CONFIG_ELOOP_POLL
int num_poll_fds;
int timeout_ms = 0;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
fd_set *rfds, *wfds, *efds;
struct timeval _tv;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
int timeout_ms = -1;
#endif /* CONFIG_ELOOP_EPOLL */
int res;
struct os_reltime tv, now;
#ifdef CONFIG_ELOOP_SELECT
rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds));
if (rfds == NULL || wfds == NULL || efds == NULL)
goto out;
#endif /* CONFIG_ELOOP_SELECT */
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
struct eloop_timeout *timeout;
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
timeout_ms = tv.sec * 1000 + tv.usec / 1000;
#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
#ifdef CONFIG_ELOOP_SELECT
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_SELECT */
}
#ifdef CONFIG_ELOOP_POLL
num_poll_fds = eloop_sock_table_set_fds(
&eloop.readers, &eloop.writers, &eloop.exceptions,
eloop.pollfds, eloop.pollfds_map,
eloop.max_pollfd_map);
res = poll(eloop.pollfds, num_poll_fds,
timeout ? timeout_ms : -1);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds);
res = select(eloop.max_sock + 1, rfds, wfds, efds,
timeout ? &_tv : NULL);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
if (eloop.count == 0) {
res = 0;
} else {
res = epoll_wait(eloop.epollfd, eloop.epoll_events,
eloop.count, timeout_ms);
}
#endif /* CONFIG_ELOOP_EPOLL */
if (res < 0 && errno != EINTR && errno != 0) {
wpa_printf(MSG_ERROR, "eloop: %s: %s",
#ifdef CONFIG_ELOOP_POLL
"poll"
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
"select"
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
"epoll"
#endif /* CONFIG_ELOOP_EPOLL */
, strerror(errno));
goto out;
}
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (!os_reltime_before(&now, &timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler =
timeout->handler;
eloop_remove_timeout(timeout);
handler(eloop_data, user_data);
}
}
if (res <= 0)
continue;
#ifdef CONFIG_ELOOP_POLL
eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
&eloop.exceptions, eloop.pollfds_map,
eloop.max_pollfd_map);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
eloop_sock_table_dispatch(&eloop.readers, rfds);
eloop_sock_table_dispatch(&eloop.writers, wfds);
eloop_sock_table_dispatch(&eloop.exceptions, efds);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
eloop_sock_table_dispatch(eloop.epoll_events, res);
#endif /* CONFIG_ELOOP_EPOLL */
}
eloop.terminate = 0;
out:
#ifdef CONFIG_ELOOP_SELECT
os_free(rfds);
os_free(wfds);
os_free(efds);
#endif /* CONFIG_ELOOP_SELECT */
return;
~~~
首先為三個文件描述符集申請空間:?
rfds = os_malloc(sizeof( * rfds));
wfds = os_malloc(sizeof( * wfds));
efds = os_malloc(sizeof( * efds));
然后進入while循環:
while (!eloop.terminate && (eloop.timeout || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0))
它的循環條件如括號里面描述,正常情況都在這里面循環,除非terminate為1,而這個有信號處理設置,參見
~~~
eloop_register_signal_terminate(handle_term, NULL);
static void handle_term(int sig, void *eloop_ctx, void *signal_ctx)
{
?? wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
?? eloop_terminate();
}
void eloop_terminate(void)
{
?? eloop.terminate = 1;
}
~~~
接下來對超時時間進行設置timeout,主要是為下面調用的select函數會用到超時時間做準備。
~~~
???? if (timeout) {
??? ??? ?os_get_reltime(&now);
??? ??? ?if (os_reltime_before(&now, &timeout->time))
??? ??? ??? ?os_reltime_sub(&timeout->time, &now, &tv);
??? ??? ?else
??? ??? ??? ?tv.sec = tv.usec = 0;
??? ???? _tv.tv_sec = tv.sec;
??? ??? ?_tv.tv_usec = tv.usec;
??? ?}
~~~
將申請的文件描述符集與eloop對象相結合,并調用select函數對這些文件發生異常進行監聽:
~~~
???? eloop_sock_table_set_fds(&eloop.readers, rfds);
??? ?eloop_sock_table_set_fds(&eloop.writers, wfds);
??? ?eloop_sock_table_set_fds(&eloop.exceptions, efds);
??? ?res = select(eloop.max_sock + 1, rfds, wfds, efds, timeout ? &_tv : NULL);
~~~
最后eloop_process_pending_signals對發生的信號進行處理:
~~~
static void eloop_process_pending_signals(void)
{
int i;
if (eloop.signaled == 0) //有沒有信號產生,如果有,那么這個標志位將為1,說明有信號需要處理,如果為0,那么沒有信號要處理,函數返回
return;
eloop.signaled = 0; //將信號標示為置0,以便下次有信號產生時,置1
if (eloop.pending_terminate) { //如果不用處理后面將會產生的信號,則立即向進程發送一個SIGALARM信號,然后將這個標志置0
#ifndef CONFIG_NATIVE_WINDOWS
alarm(0);
#endif /* CONFIG_NATIVE_WINDOWS */
eloop.pending_terminate = 0;
}
for (i = 0; i < eloop.signal_count; i++) { //對信號標示進行處理
if (eloop.signals[i].signaled) {
eloop.signals[i].signaled = 0;
#ifndef CONFIG_NATIVE_WINDOWS
eloop_register_signal(SIGHUP, handle_reload, NULL); //對中斷信號和中斷處理函數進行注冊
eloop_register_signal(SIGUSR1, handle_dump_state, NULL);
#endif /* CONFIG_NATIVE_WINDOWS */
eloop_register_signal_terminate(handle_term, NULL);
eloop.signals[i].handler(eloop.signals[i].sig, //調用處理函數對相應的信號進行處理,那么到底調用的是什么處理函數呢?這些處理函數的注冊是在
eloop.user_data, //什么地方呢?這個進程是怎么樣對數據包進行處理的呢?在哪里處理呢?
eloop.signals[i].user_data);
}
}
}
~~~
到這里,應該對hostapd這個程序的整體有了一定的把握,應該能看懂第一篇中的那張結構圖了,但也有局限的地方,比如好多細節的地方沒有講清楚,
比如:數據包是在哪里接收的? 數據包是在哪里發送的? 數據包是這樣存放的?這些處理函數是在哪里注冊的? 客戶選擇的加密方式是怎么起作用的?
hostapd怎么將一塊網卡切換到了ap模式? 等等。
接下來將盡力弄清楚這些問題。