BIND9的架构与机制笔记1

来源:互联网 发布:默纳克调试软件下载 编辑:程序博客网 时间:2024/06/07 19:29

 BIND9采用的是事件驱动的机制来工作,而事件的源头则是IO,IO在linux使用的EPOLL的边缘触发模式。

  本篇说的是epoll,BIND9如果创建了watcher线程(宏USE_WATCHER_THREAD控制),这里就讨论有线程的情况,实际上即使不创建

线程干的也都是一样的活。在lib/isc/socket.c中setup_watcher函数:(所有的代码都是截取的epoll下的片段,因为还有kqueue,devpoll,select等的实现代码,太多了)

复制代码
#elif defined(USE_EPOLL)    manager->nevents = ISC_SOCKET_MAXEVENTS;    manager->events = isc_mem_get(mctx, sizeof(struct epoll_event) *                      manager->nevents);    if (manager->events == NULL)        return (ISC_R_NOMEMORY);    manager->epoll_fd = epoll_create(manager->nevents);    if (manager->epoll_fd == -1) {        result = isc__errno2result(errno);        isc__strerror(errno, strbuf, sizeof(strbuf));        UNEXPECTED_ERROR(__FILE__, __LINE__,                 "epoll_create %s: %s",                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,                        ISC_MSG_FAILED, "failed"),                 strbuf);        isc_mem_put(mctx, manager->events,                sizeof(struct epoll_event) * manager->nevents);        return (result);    }#ifdef USE_WATCHER_THREAD    result = watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ);    if (result != ISC_R_SUCCESS) {        close(manager->epoll_fd);        isc_mem_put(mctx, manager->events,                sizeof(struct epoll_event) * manager->nevents);        return (result);    }#endif    /* USE_WATCHER_THREAD */
复制代码

先是创建了要监视的最大socket fd数目(manager->nevents)对应的epoll_event结构体数组,然后调用epoll_create函数创建一个epoll fd,参数则是指定监视的socket fd

最大数目。我的内核版本是3.13,man一下epoll_create发现它是这样说的:epoll_create()  creates  an  epoll(7) instance.  Since Linux 2.6.8, thesize argument is ignored, but must be  greater  than  zero。这个函数在2.6.8内核以后就忽略参数size了,但是传递的参数值一定要大于0。后来找了一下资料,网上的高手的博客说的就很清楚了http://www.cnblogs.com/apprentice89/p/3234677.html。继续往下说,后面的watch_fd实在创建线程的情况下才有,就是将pipe_fds[0]这个管道描述符,也就是一个可读的流,而上述的socket fd都是可以归为流。watch_fd的实现代码:

复制代码
#elif defined(USE_EPOLL)        struct epoll_event event;        if (msg == SELECT_POKE_READ)                event.events = EPOLLIN;        else                event.events = EPOLLOUT;        memset(&event.data, 0, sizeof(event.data));        event.data.fd = fd;        if (epoll_ctl(manager->epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1 &&            errno != EEXIST) {                result = isc__errno2result(errno);        }        return (result);
复制代码

这是将pipe_fds[0]加入epoll_fd的监听队列,EPOLL_CTL_ADD是操作类型,注册该fd到epoll_fd上。这个管道的目的是接收管理该线程的消息,比如线程退出。

那么进入线程看:

复制代码
static isc_threadresult_twatcher(void *uap) {    isc__socketmgr_t *manager = uap;    isc_boolean_t done;    int ctlfd;    int cc;#ifdef USE_KQUEUE    const char *fnname = "kevent()";#elif defined (USE_EPOLL)    const char *fnname = "epoll_wait()";#elif defined(USE_DEVPOLL)    const char *fnname = "ioctl(DP_POLL)";    struct dvpoll dvp;#elif defined (USE_SELECT)    const char *fnname = "select()";    int maxfd;#endif    char strbuf[ISC_STRERRORSIZE];#ifdef ISC_SOCKET_USE_POLLWATCH    pollstate_t pollstate = poll_idle;#endif    /*     * Get the control fd here.  This will never change.     */    ctlfd = manager->pipe_fds[0];    done = ISC_FALSE;    while (!done) {        do {#ifdef USE_KQUEUE            cc = kevent(manager->kqueue_fd, NULL, 0,                    manager->events, manager->nevents, NULL);#elif defined(USE_EPOLL)            cc = epoll_wait(manager->epoll_fd, manager->events,                    manager->nevents, -1);#elif defined(USE_DEVPOLL)            dvp.dp_fds = manager->events;            dvp.dp_nfds = manager->nevents;#ifndef ISC_SOCKET_USE_POLLWATCH            dvp.dp_timeout = -1;#else            if (pollstate == poll_idle)                dvp.dp_timeout = -1;            else                dvp.dp_timeout = ISC_SOCKET_POLLWATCH_TIMEOUT;#endif    /* ISC_SOCKET_USE_POLLWATCH */            cc = ioctl(manager->devpoll_fd, DP_POLL, &dvp);#elif defined(USE_SELECT)            LOCK(&manager->lock);            memcpy(manager->read_fds_copy, manager->read_fds,                   manager->fd_bufsize);            memcpy(manager->write_fds_copy, manager->write_fds,                   manager->fd_bufsize);            maxfd = manager->maxfd + 1;            UNLOCK(&manager->lock);            cc = select(maxfd, manager->read_fds_copy,                    manager->write_fds_copy, NULL, NULL);#endif    /* USE_KQUEUE */            if (cc < 0 && !SOFT_ERROR(errno)) {                isc__strerror(errno, strbuf, sizeof(strbuf));                FATAL_ERROR(__FILE__, __LINE__,                        "%s %s: %s", fnname,                        isc_msgcat_get(isc_msgcat,                               ISC_MSGSET_GENERAL,                               ISC_MSG_FAILED,                               "failed"), strbuf);            }#if defined(USE_DEVPOLL) && defined(ISC_SOCKET_USE_POLLWATCH)            if (cc == 0) {                if (pollstate == poll_active)                    pollstate = poll_checking;                else if (pollstate == poll_checking)                    pollstate = poll_idle;            } else if (cc > 0) {                if (pollstate == poll_checking) {                    /*                     * XXX: We'd like to use a more                     * verbose log level as it's actually an                     * unexpected event, but the kernel bug                     * reportedly happens pretty frequently                     * (and it can also be a false positive)                     * so it would be just too noisy.                     */                    manager_log(manager,                            ISC_LOGCATEGORY_GENERAL,                            ISC_LOGMODULE_SOCKET,                            ISC_LOG_DEBUG(1),                            "unexpected POLL timeout");                }                pollstate = poll_active;            }#endif        } while (cc < 0);#if defined(USE_KQUEUE) || defined (USE_EPOLL) || defined (USE_DEVPOLL)        done = process_fds(manager, manager->events, cc);#elif defined(USE_SELECT)        process_fds(manager, maxfd, manager->read_fds_copy,                manager->write_fds_copy);        /*         * Process reads on internal, control fd.         */        if (FD_ISSET(ctlfd, manager->read_fds_copy))            done = process_ctlfd(manager);#endif    }    manager_log(manager, TRACE, "%s",            isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,                   ISC_MSG_EXITING, "watcher exiting"));    return ((isc_threadresult_t)0);}
复制代码

无限循环,epoll_wait当监听的epoll_fd队列上有IO事件发生时,将对应的socket fd和事件放入events数组中,并且将这些注册在epoll_fd上的socket fd对应事件清空。

process_fds遍历数组,找到对应的socket fd,并判断该fd是不是线程控制管道,如果是则会在执行完其他socket fd上的对应事件后再处理管道中的控制消息。

复制代码
static isc_boolean_tprocess_fds(isc__socketmgr_t *manager, struct epoll_event *events, int nevents){    int i;    isc_boolean_t done = ISC_FALSE;#ifdef USE_WATCHER_THREAD    isc_boolean_t have_ctlevent = ISC_FALSE;#endif    if (nevents == manager->nevents) {        manager_log(manager, ISC_LOGCATEGORY_GENERAL,                ISC_LOGMODULE_SOCKET, ISC_LOG_INFO,                "maximum number of FD events (%d) received",                nevents);    }    for (i = 0; i < nevents; i++) {        REQUIRE(events[i].data.fd < (int)manager->maxsocks);#ifdef USE_WATCHER_THREAD        if (events[i].data.fd == manager->pipe_fds[0]) {            have_ctlevent = ISC_TRUE;            continue;        }#endif        if ((events[i].events & EPOLLERR) != 0 ||            (events[i].events & EPOLLHUP) != 0) {            /*             * epoll does not set IN/OUT bits on an erroneous             * condition, so we need to try both anyway.  This is a             * bit inefficient, but should be okay for such rare             * events.  Note also that the read or write attempt             * won't block because we use non-blocking sockets.             */            events[i].events |= (EPOLLIN | EPOLLOUT);        }        process_fd(manager, events[i].data.fd,               (events[i].events & EPOLLIN) != 0,               (events[i].events & EPOLLOUT) != 0);    }#ifdef USE_WATCHER_THREAD    if (have_ctlevent)        done = process_ctlfd(manager);#endif    return (done);}
复制代码

 待续

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 主播工资被欠怎么办 直播平台不发工资坑主播怎么办 主播公司不发工资怎么办 梦幻月卡用完了怎么办 网易星球实名认证通过不了怎么办 认证过荔枝主播怎么办 苹果手机相机不对焦怎么办 苹果手机摄像头不能对焦了怎么办 闪电邮里面邮件太多怎么办 苹果手机和助理打不开怎么办 苹果我的世界打不开怎么办 ps试用7天到期了怎么办 皮肤锁不住水份怎么办 硫酸弄到皮肤上怎么办 直播时图像反看怎么办 快手直播权限被收回怎么办 快手直播权限被收回了怎么办 腾讯手游助手玩游戏卡怎么办 电脑直播视频打不开了怎么办 平板进水开不了机怎么办 苹果平板进水开不了机怎么办 苹果平板电脑进水了怎么办 电脑换主机以前的文件怎么办 货车卖了没过户怎么办 微交易出金不了怎么办 直播时出现央视影音客户端怎么办 qq账号暂时无法登录怎么办 饿了吃东西胃疼怎么办 早上不吃饭胃疼怎么办 孕晚期胃疼呕吐怎么办 胃疼了一晚上怎么办 微信视频图像倒立怎么办 ps链接图层锁定怎么办 慕课过时间了怎么办 异地恋又要考研怎么办 阴阳师手机绑定上限了怎么办 高考口令卡丢了怎么办 网易将军令换手机了怎么办 须弥bb有弱点土怎么办 战网密码忘记了怎么办 梦三账号忘了怎么办