hostapd的分析

来源:互联网 发布:淘宝店铺被彻底释放 编辑:程序博客网 时间:2024/05/30 12:30

Hostapd的功能就是作为AP的认证服务器,负责控制管理stations(通常可以认为带无线网卡的PC)的接入和认证。
通过Hostapd可以将无线网卡切换为AP/Master模式,通过修改配置文件,可以建立一个开放式的(不加密)的,WEP,WPA或WPA2的无线网络。并且通过修改配置文件可以设置无线网卡的各种参数,包括频率,信号,beacon包时间间隔,是否发送beacon包,如果响应探针请求等等。还可以设置mac地址过滤条件等。
这里写图片描述
hostapd的main函数

int main(int argc, char *argv[]){    struct hapd_interfaces interfaces;    int ret = 1;    size_t i, j;    int c, debug = 0, daemonize = 0;    char *pid_file = NULL;    const char *log_file = NULL;    const char *entropy_file = NULL;    char **bss_config = NULL, **tmp_bss;    size_t num_bss_configs = 0;#ifdef CONFIG_DEBUG_LINUX_TRACING    int enable_trace_dbg = 0;#endif /* CONFIG_DEBUG_LINUX_TRACING */    int start_ifaces_in_sync = 0;    char **if_names = NULL;    size_t if_names_size = 0;    if (os_program_init())        return -1;    os_memset(&interfaces, 0, sizeof(interfaces));    interfaces.reload_config = hostapd_reload_config;    interfaces.config_read_cb = hostapd_config_read;    interfaces.for_each_interface = hostapd_for_each_interface;    interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;    interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;    interfaces.driver_init = hostapd_driver_init;    interfaces.global_iface_path = NULL;    interfaces.global_iface_name = NULL;    interfaces.global_ctrl_sock = -1;    dl_list_init(&interfaces.global_ctrl_dst);    for (;;) {        c = getopt(argc, argv, "b:Bde:f:hi:KP:STtu:vg:G:");        if (c < 0)            break;        switch (c) {        case 'h':            usage();            break;        case 'd':            debug++;            if (wpa_debug_level > 0)                wpa_debug_level--;            break;        case 'B':            daemonize++;            break;        case 'e':            entropy_file = optarg;            break;        case 'f':            log_file = optarg;            break;        case 'K':            wpa_debug_show_keys++;            break;        case 'P':            os_free(pid_file);            pid_file = os_rel2abs_path(optarg);            break;        case 't':            wpa_debug_timestamp++;            break;#ifdef CONFIG_DEBUG_LINUX_TRACING        case 'T':            enable_trace_dbg = 1;            break;#endif /* CONFIG_DEBUG_LINUX_TRACING */        case 'v':            show_version();            exit(1);            break;        case 'g':            if (hostapd_get_global_ctrl_iface(&interfaces, optarg))                return -1;            break;        case 'G':            if (hostapd_get_ctrl_iface_group(&interfaces, optarg))                return -1;            break;        case 'b':            tmp_bss = os_realloc_array(bss_config,                           num_bss_configs + 1,                           sizeof(char *));            if (tmp_bss == NULL)                goto out;            bss_config = tmp_bss;            bss_config[num_bss_configs++] = optarg;            break;        case 'S':            start_ifaces_in_sync = 1;            break;#ifdef CONFIG_WPS        case 'u':            return gen_uuid(optarg);#endif /* CONFIG_WPS */        case 'i':            if (hostapd_get_interface_names(&if_names,                            &if_names_size, optarg))                goto out;            break;        default:            usage();            break;        }    }    if (optind == argc && interfaces.global_iface_path == NULL &&        num_bss_configs == 0)        usage();    wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);    if (log_file)        wpa_debug_open_file(log_file);    else        wpa_debug_setup_stdout();#ifdef CONFIG_DEBUG_LINUX_TRACING    if (enable_trace_dbg) {        int tret = wpa_debug_open_linux_tracing();        if (tret) {            wpa_printf(MSG_ERROR, "Failed to enable trace logging");            return -1;        }    }#endif /* CONFIG_DEBUG_LINUX_TRACING */    interfaces.count = argc - optind;    if (interfaces.count || num_bss_configs) {        interfaces.iface = os_calloc(interfaces.count + num_bss_configs,                         sizeof(struct hostapd_iface *));        if (interfaces.iface == NULL) {            wpa_printf(MSG_ERROR, "malloc failed");            return -1;        }    }    if (hostapd_global_init(&interfaces, entropy_file)) {        wpa_printf(MSG_ERROR, "Failed to initialize global context");        return -1;    }    eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,                   hostapd_periodic, &interfaces, NULL);    if (fst_global_init()) {        wpa_printf(MSG_ERROR,               "Failed to initialize global FST context");        goto out;    }#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)    if (!fst_global_add_ctrl(fst_ctrl_cli))        wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */    /* Allocate and parse configuration for full interface files */    for (i = 0; i < interfaces.count; i++) {        char *if_name = NULL;        if (i < if_names_size)            if_name = if_names[i];        interfaces.iface[i] = hostapd_interface_init(&interfaces,                                 if_name,                                 argv[optind + i],                                 debug);        if (!interfaces.iface[i]) {            wpa_printf(MSG_ERROR, "Failed to initialize interface");            goto out;        }        if (start_ifaces_in_sync)            interfaces.iface[i]->need_to_start_in_sync = 1;    }    /* Allocate and parse configuration for per-BSS files */    for (i = 0; i < num_bss_configs; i++) {        struct hostapd_iface *iface;        char *fname;        wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);        fname = os_strchr(bss_config[i], ':');        if (fname == NULL) {            wpa_printf(MSG_ERROR,                   "Invalid BSS config identifier '%s'",                   bss_config[i]);            goto out;        }        *fname++ = '\0';        iface = hostapd_interface_init_bss(&interfaces, bss_config[i],                           fname, debug);        if (iface == NULL)            goto out;        for (j = 0; j < interfaces.count; j++) {            if (interfaces.iface[j] == iface)                break;        }        if (j == interfaces.count) {            struct hostapd_iface **tmp;            tmp = os_realloc_array(interfaces.iface,                           interfaces.count + 1,                           sizeof(struct hostapd_iface *));            if (tmp == NULL) {                hostapd_interface_deinit_free(iface);                goto out;            }            interfaces.iface = tmp;            interfaces.iface[interfaces.count++] = iface;        }    }    /*     * Enable configured interfaces. Depending on channel configuration,     * this may complete full initialization before returning or use a     * callback mechanism to complete setup in case of operations like HT     * co-ex scans, ACS, or DFS are needed to determine channel parameters.     * In such case, the interface will be enabled from eloop context within     * hostapd_global_run().     */    interfaces.terminate_on_error = interfaces.count;    for (i = 0; i < interfaces.count; i++) {         //根据配置文件设置iface信息        if (hostapd_driver_init(interfaces.iface[i]) ||            hostapd_setup_interface(interfaces.iface[i]))  //将配置文件通过写入驱动            goto out;    }    hostapd_global_ctrl_iface_init(&interfaces);    if (hostapd_global_run(&interfaces, daemonize, pid_file)) {        wpa_printf(MSG_ERROR, "Failed to start eloop");        goto out;    }    ret = 0; out:    hostapd_global_ctrl_iface_deinit(&interfaces);    /* Deinitialize all interfaces */    for (i = 0; i < interfaces.count; i++) {        if (!interfaces.iface[i])            continue;        interfaces.iface[i]->driver_ap_teardown =            !!(interfaces.iface[i]->drv_flags &               WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);        hostapd_interface_deinit_free(interfaces.iface[i]);    }    os_free(interfaces.iface);    if (interfaces.eloop_initialized)        eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);    hostapd_global_deinit(pid_file, interfaces.eloop_initialized);    os_free(pid_file);    if (log_file)        wpa_debug_close_file();    wpa_debug_close_linux_tracing();    os_free(bss_config);    for (i = 0; i < if_names_size; i++)        os_free(if_names[i]);    os_free(if_names);    fst_global_deinit();    os_program_deinit();    return ret;}

该函数主要分为三部分:
第一部是读取命令行参数作相应的处理。
第二部分主要是根据配置文件设置hapd_interface的参数然后通过一系列的函数调用进入内核态设置相应的内核参数
第三部分是在函数hostapd_global_run中死循环来检测socket或者timeout或者event的相关量是否发生变化进而调用相应的提前注册到该事件上的函数。

在函数hostapd_global_init 中初始化eloop这个全局变量并进入eloop死循环中:

static int hostapd_global_init(struct hapd_interfaces *interfaces,                   const char *entropy_file){    int i;    os_memset(&global, 0, sizeof(global)); //重置global变量    hostapd_logger_register_cb(hostapd_logger_cb);    if (eap_server_register_methods()) {    //注册eap server的加密方法        wpa_printf(MSG_ERROR, "Failed to register EAP methods");        return -1;    }    if (eloop_init()) {        wpa_printf(MSG_ERROR, "Failed to initialize event loop");        return -1;    }    interfaces->eloop_initialized = 1;    random_init(entropy_file);#ifndef CONFIG_NATIVE_WINDOWS    eloop_register_signal(SIGHUP, handle_reload, interfaces);    eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);#endif /* CONFIG_NATIVE_WINDOWS */    eloop_register_signal_terminate(handle_term, interfaces);#ifndef CONFIG_NATIVE_WINDOWS    openlog("hostapd", 0, LOG_DAEMON);#endif /* CONFIG_NATIVE_WINDOWS */    for (i = 0; wpa_drivers[i]; i++)        global.drv_count++;    if (global.drv_count == 0) {        wpa_printf(MSG_ERROR, "No drivers enabled");        return -1;    }    global.drv_priv = os_calloc(global.drv_count, sizeof(void *));    if (global.drv_priv == NULL)        return -1;    return 0;}

使用eap_server_register_methods函数注册eap server支持的安全模式,并存放在一个链表里面。
调用 eloop_init 函数初始化全局变量eloop结构体。
调用 random_init对各个事件注册,

void random_init(const char *entropy_file){    os_free(random_entropy_file);    if (entropy_file)        random_entropy_file = os_strdup(entropy_file);    else        random_entropy_file = NULL;    random_read_entropy();#ifdef __linux__    if (random_fd >= 0)        return;    random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);    if (random_fd < 0) {        wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",               strerror(errno));        return;    }    wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "           "/dev/random");    eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);#endif /* __linux__ */    random_write_entropy();}

eloop_register_read_sock 函数继续调用,eloop_register_read_sock,再使用eloop_register_sock 函数来实现注册socket。

int eloop_register_sock(int sock, eloop_event_type type,            eloop_sock_handler handler,            void *eloop_data, void *user_data){    struct eloop_sock_table *table;    assert(sock >= 0);    table = eloop_get_sock_table(type);    return eloop_sock_table_add_sock(table, sock, handler,                     eloop_data, user_data);}

从代码中可以看到该函数将相应的handler和data放进sock_table表中,实现注册。

回到main函数中,hostapd_interface_init 读取hostapd配置文件并进行分配和解析:

/** * hostapd_interface_init - Read configuration file and init BSS data * * This function is used to parse configuration file for a full interface (one * or more BSSes sharing the same radio) and allocate memory for the BSS * interfaces. No actiual driver operations are started. */static struct hostapd_iface *hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,               const char *config_fname, int debug){    struct hostapd_iface *iface;    int k;    wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);    iface = hostapd_init(interfaces, config_fname);    if (!iface)        return NULL;    if (if_name) {        os_strlcpy(iface->conf->bss[0]->iface, if_name,               sizeof(iface->conf->bss[0]->iface));    }    iface->interfaces = interfaces;    for (k = 0; k < debug; k++) {        if (iface->bss[0]->conf->logger_stdout_level > 0)            iface->bss[0]->conf->logger_stdout_level--;    }    if (iface->conf->bss[0]->iface[0] == '\0' &&        !hostapd_drv_none(iface->bss[0])) {        wpa_printf(MSG_ERROR,               "Interface name not specified in %s, nor by '-i' parameter",               config_fname);        hostapd_interface_deinit_free(iface);        return NULL;    }    return iface;}

其中调用hostapd_init() 初始化配置:

/** * hostapd_init - Allocate and initialize per-interface data * @config_file: Path to the configuration file * Returns: Pointer to the allocated interface data or %NULL on failure * * This function is used to allocate main data structures for per-interface * data. The allocated data buffer will be freed by calling * hostapd_cleanup_iface(). */struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,                    const char *config_file){    struct hostapd_iface *hapd_iface = NULL;    struct hostapd_config *conf = NULL;    struct hostapd_data *hapd;    size_t i;    hapd_iface = hostapd_alloc_iface();    if (hapd_iface == NULL)        goto fail;    hapd_iface->config_fname = os_strdup(config_file);    if (hapd_iface->config_fname == NULL)        goto fail;    conf = interfaces->config_read_cb(hapd_iface->config_fname);//读取配置文件    if (conf == NULL)        goto fail;    hapd_iface->conf = conf;    hapd_iface->num_bss = conf->num_bss;    hapd_iface->bss = os_calloc(conf->num_bss,                    sizeof(struct hostapd_data *));    if (hapd_iface->bss == NULL)        goto fail;//根据配置文件中bss的配置个数conf->num_bss的值通过调用hostapd_alloc_bss_data分配空间及相关设置    for (i = 0; i < conf->num_bss; i++) {        hapd = hapd_iface->bss[i] =            hostapd_alloc_bss_data(hapd_iface, conf,                           conf->bss[i]);        if (hapd == NULL)            goto fail;        hapd->msg_ctx = hapd;    }    return hapd_iface;fail:    wpa_printf(MSG_ERROR, "Failed to set up interface with %s",           config_file);    if (conf)        hostapd_config_free(conf);    if (hapd_iface) {        os_free(hapd_iface->config_fname);        os_free(hapd_iface->bss);        wpa_printf(MSG_DEBUG, "%s: free iface %p",               __func__, hapd_iface);        os_free(hapd_iface);    }    return NULL;}

通过调用函数hostapd_driver_init获取配置信息保存在iface[i]中,然后通过调用函数hostapd_setup_interface函数将其配置信息写入内核。写入通过依次调用一下函数来实现:

hostapd_setup_interface------>setup_interface    ------>hostapd_set_country    ------>setup_interface2        ------>hostapd_setup_interface_complete                ------>hostapd_set_freq                ------>hostapd_set_rts    ------>hostapd_set_state    ------>hostapd_tx_queue_para,   

通过这几个调用进入netlink层。

通过 hostapd_global_run处理socket事件的过程如下

static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,                  const char *pid_file){#ifdef EAP_SERVER_TNC    int tnc = 0;    size_t i, k;    for (i = 0; !tnc && i < ifaces->count; i++) {        for (k = 0; k < ifaces->iface[i]->num_bss; k++) {            if (ifaces->iface[i]->bss[0]->conf->tnc) {                tnc++;                break;            }        }    }    if (tnc && tncs_global_init() < 0) {//调用tncs_global_init完成tnc相关的初始化        wpa_printf(MSG_ERROR, "Failed to initialize TNCS");        return -1;    }#endif /* EAP_SERVER_TNC */    if (daemonize) {        if (os_daemonize(pid_file)) {//调用os_daemonize函数实现将该程序以后台进程运行            wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));            return -1;        }        if (eloop_sock_requeue()) {            wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",                   strerror(errno));            return -1;        }    }    eloop_run();    return 0;}

其中核心函数是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 */#ifdef CONFIG_ELOOP_KQUEUE    struct timespec ts;#endif /* CONFIG_ELOOP_KQUEUE */    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;        if (eloop.pending_terminate) {            /*             * This may happen in some corner cases where a signal             * is received during a blocking operation. We need to             * process the pending signals and exit if requested to             * avoid hitting the SIGALRM limit if the blocking             * operation took more than two seconds.             */             // eloop_process_pending_signals函数对发生的信号进行处理            eloop_process_pending_signals();            if (eloop.terminate)                break;        }        timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,                    list);        if (timeout) {         //对超时时间进行设置timeout,主要是为下面调用的select函数会用到超时时间做准备            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_KQUEUE            ts.tv_sec = tv.sec;            ts.tv_nsec = tv.usec * 1000L;#endif /* CONFIG_ELOOP_KQUEUE */        }#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对象相结合,并调用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 */#ifdef CONFIG_ELOOP_KQUEUE        if (eloop.count == 0) {            res = 0;        } else {            res = kevent(eloop.kqueuefd, NULL, 0,                     eloop.kqueue_events, eloop.kqueue_nevents,                     timeout ? &ts : NULL);        }#endif /* CONFIG_ELOOP_KQUEUE */        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 */#ifdef CONFIG_ELOOP_KQUEUE                   "kqueue"#endif /* CONFIG_ELOOP_EKQUEUE */                   , strerror(errno));            goto out;        }        eloop.readers.changed = 0;        eloop.writers.changed = 0;        eloop.exceptions.changed = 0;        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;        if (eloop.readers.changed ||            eloop.writers.changed ||            eloop.exceptions.changed) {             /*              * Sockets may have been closed and reopened with the              * same FD in the signal or timeout handlers, so we              * must skip the previous results and check again              * whether any of the currently registered sockets have              * events.              */            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 */#ifdef CONFIG_ELOOP_KQUEUE        eloop_sock_table_dispatch(eloop.kqueue_events, res);#endif /* CONFIG_ELOOP_KQUEUE */    }    eloop.terminate = 0;out:#ifdef CONFIG_ELOOP_SELECT    os_free(rfds);    os_free(wfds);    os_free(efds);#endif /* CONFIG_ELOOP_SELECT */    return;}

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;             //调用处理函数对相应的信号进行处理            eloop.signals[i].handler(eloop.signals[i].sig,                         eloop.signals[i].user_data);        }    }}

eloop_run 函数的核心将要处理的socket事件添加到相应的表中,如下:

    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_sock_table_dispatch(&eloop.readers, rfds);    eloop_sock_table_dispatch(&eloop.writers, wfds);    eloop_sock_table_dispatch(&eloop.exceptions, efds); 
0 0
原创粉丝点击