BIND9的架构与机制笔记1

  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_t
watcher(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_t
process_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);
}

待续

时间: 2024-10-31 15:31:37

BIND9的架构与机制笔记1的相关文章

《大型网站技术架构》读书笔记二:大型网站架构模式

一.分层 最常见的架构模式,将系统在横向维度上切分成几个部分,每个部分单一职责.网站一般分为三个层次:应用层.服务层和数据层,其具体结构如下图所示: 通过分层,一个庞大系统切分成不同部分,便于分工合作和维护. 但是,分层架构也有一些挑战:①必须合理规划层次边界和接口:②禁止跨层次的调用及逆向调用. 二.分割 分割是在纵向方面对软件进行切分->将不同的功能和服务分割开来,包装成高内聚低耦合的模块单元,有助于软件开发和维护,还便于不同模块的分布式部署,提高网站的并发处理能力和功能扩展能力. 三.分布

《大型网站技术架构》读书笔记四:瞬时响应之网站的高性能架构

一.网站性能测试 (1)性能测试指标:①响应时间:②并发数:③吞吐量:④性能计数器: (2)性能测试方法:①性能测试:②负载测试:③压力测试:④稳定性测试: (3)性能优化策略: ①性能分析:检查请求处理各个环节的日志,分析哪个环节响应时间不合理,检查监控数据分析影响性能的因素: ②性能优化:Web前端优化,应用服务器优化,存储服务器优化: 二.Web前端性能优化 (1)浏览器访问优化: ①减少http请求:因为http是无状态的,每次请求的开销都比较昂贵(需要建立通信链路.进行数据传输,而服务

《大型网站技术架构》读书笔记之七:随需应变之网站的可扩展架构

此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. 一.可伸缩与可扩展-傻傻分不清楚 上篇笔记我们学习了可伸缩架构,但在实际场合中,包括许多架构师也常常混淆可伸缩和可扩展,用可扩展表示伸缩性.那么在此,跟随作者我们来理清这两个概念,避免我们以后对其傻傻分不清楚. (1)扩展性(Extensibiltiy) 指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力.我们不禁想到了面向对象中一大原则:开闭原则,对扩展开放,对修改封闭.也就说,当系统新增一个功能时

《大型网站技术架构》读书笔记之六:永无止境之网站的伸缩性架构

此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. 首先,所谓网站的伸缩性,指不需要改变网站的软硬件设计,仅仅通过改变部署的服务器数量就可以扩大或者缩小网站的服务处理能力.在整个互联网行业的发展渐进演化中,最重要的技术就是服务器集群,通过不断地向集群中添加服务器来增强整个集群的处理能力. 一.网站架构的伸缩性设计 1.1 不同功能进行物理分离实现伸缩 (1)纵向分离:将业务处理流程上得不同部分分离部署,实现系统的伸缩性: (2)横向分离:将不同的业务模块分离部署

《大型网站技术架构》读书笔记之八:固若金汤之网站的安全性架构

此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. 一.网站应用攻击与防御 二.信息加密技术与密钥安全 三.信息过滤与反垃圾 四.电子商务风险控制 五.学习总结 转眼之间,<大型网站技术架构>的读书笔记到此就结束了.最近时间非常紧,因此本篇没有详细对笔记进行介绍(本篇涉及太多内容,而且都是安全相关的).通过本书的学习,我们从高性能.高可用.伸缩性.可扩展性.安全性五个方面的架构学习了每个方面经典的技术方案,虽然以理论偏多,但还是可以从中管中窥豹,一览

Java的垃圾回收机制笔记

Java的垃圾回收机制笔记 java垃圾回收的意义 确保不再被引用的对象的内存空间被回收. 确保被引用的对象的内存不被错误回收. 再分配内存. java垃圾回收的常用方法 引用计数收集器 堆中的每个对象(不是对象的引用)都有一个引用计数.当一个对象被创建时,给该对象分配一个变量,该变量计数设置设置为1.当任何其他变量被赋值为这个对象的引用,计数加1(a=b,则b引用的对象计数+1),但当一个对象的某个引用超过生命周期或者被设置为一个新值的时候,引用的计数减1(a=c,则a不再指向b指向的对象,而

bind9域名服务器的转发机制

最近在配置Bind9.发现bind9的转发器fowarder机制总是没有效果.配置细节如下: 1.在virtualbox中建立两台虚拟机host1和host2,host1和host2的操作系统时ubuntu14.04 server版本. 2.在host1和host2上都安装bind9. 3.在host1上配置域名zql.com.在host1上使用nslookup工具解析该域名成功. 4.在host2上配置域名zyt.com.在host2上使用nslookup工具解析该域名成功. 5.配置虚拟机h

《大型网站技术架构》读书笔记一:大型网站架构演化

一.大型网站系统特点 (1)高并发.大流量:PV量巨大 (2)高可用:7*24小时不间断服务 (3)海量数据:文件数目分分钟xxTB (4)用户分布广泛,网络情况复杂:网络运营商 (5)安全环境恶劣:黑客的攻击 (6)需求快速变更,发布频繁:快速适应市场,满足用户需求 (7)渐进式发展:慢慢地运营出大型网站 二.大型网站架构演化过程 (1)初始阶段网站架构:一台Server就刚需-应用程序.数据库.文件等所有资源都集中在一台Server上,典型案例:基于LAMP架构的PHP网站 (2)应用和数据

Web高级征程:《大型网站技术架构》读书笔记系列

一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计案例,为读者呈现一幅包括技术选型.架构设计.性能优化.Web安全.系统发布.运维监控等在内的大型网站开发全景视图. 本书不仅适用于指导网站工程师.架构师进行网站技术架构设计,也可用于指导产品经理.项目经理.测试运维人员等了解网站技术架构的基础概念:还可供包括企业系统开发人员在内的各类软件开发从业人员借鉴,了解