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 == -) {
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[], 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, , sizeof(event.data));
event.data.fd = fd;
if (epoll_ctl(manager->epoll_fd, EPOLL_CTL_ADD, fd, &event) == - &&
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[];
done = ISC_FALSE;
while (!done) {
do {
#ifdef USE_KQUEUE
cc = kevent(manager->kqueue_fd, NULL, ,
manager->events, manager->nevents, NULL);
#elif defined(USE_EPOLL)
cc = epoll_wait(manager->epoll_fd, manager->events,
manager->nevents, -);
#elif defined(USE_DEVPOLL)
dvp.dp_fds = manager->events;
dvp.dp_nfds = manager->nevents;
#ifndef ISC_SOCKET_USE_POLLWATCH
dvp.dp_timeout = -;
#else
if (pollstate == poll_idle)
dvp.dp_timeout = -;
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 + ;
UNLOCK(&manager->lock); cc = select(maxfd, manager->read_fds_copy,
manager->write_fds_copy, NULL, NULL);
#endif /* USE_KQUEUE */ if (cc < && !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 == ) {
if (pollstate == poll_active)
pollstate = poll_checking;
else if (pollstate == poll_checking)
pollstate = poll_idle;
} else if (cc > ) {
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(),
"unexpected POLL timeout");
}
pollstate = poll_active;
}
#endif
} while (cc < ); #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));
}
无限循环,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 = ; i < nevents; i++) {
REQUIRE(events[i].data.fd < (int)manager->maxsocks);
#ifdef USE_WATCHER_THREAD
if (events[i].data.fd == manager->pipe_fds[]) {
have_ctlevent = ISC_TRUE;
continue;
}
#endif
if ((events[i].events & EPOLLERR) != ||
(events[i].events & EPOLLHUP) != ) {
/*
* 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) != ,
(events[i].events & EPOLLOUT) != );
} #ifdef USE_WATCHER_THREAD
if (have_ctlevent)
done = process_ctlfd(manager);
#endif return (done);
}
待续
BIND9的架构与机制笔记1的更多相关文章
- Magento架构师的笔记-----Magento显示当前目录的父分类和子分类的分类名
在Magento目录的分类页面里,希望在左侧导航获取到父分类和子分类,可以用以下方法:打开app/your_package/your_themes/template/catalog/navigatio ...
- 剖析虚幻渲染体系(12)- 移动端专题Part 2(GPU架构和机制)
目录 12.4 移动渲染技术要点 12.4.1 Tile-based (Deferred) Rendering 12.4.2 Hierarchical Tiling 12.4.3 Early-Z 12 ...
- Web高级征程:《大型网站技术架构》读书笔记系列
一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...
- .net架构设计读书笔记--第三章 第8节 域模型简介(Introducing Domain Model)
一.数据--行为转变 很长的时间,典型的分析方法或多或少是以下两种,第一,收集需求并做一些分析,找出有关实体 (例如,客户. 订单. 产品) 和进程来实现. 第二,手持这种理解你尝试推断一个物 ...
- OpenCV基本架构[OpenCV 笔记0]
最近正在系统学习OpenCV,将不定期发布笔记,主要按照毛星云的<OpenCV3编程入门>的顺序学习,会参考官方教程和文档.学习工具是Xcode+CMake,会对书中一部分内容更正,并加入 ...
- Spark cache、checkpoint机制笔记
Spark学习笔记总结 03. Spark cache和checkpoint机制 1. RDD cache缓存 当持久化某个RDD后,每一个节点都将把计算的分片结果保存在内存中,并在对此RDD或衍生出 ...
- 微服务架构(Microservice Architect Pattern)综述——什么是微服务架构(读书笔记)
简单定义: 微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调,相互配合,为用户提供最终价值.每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制相互沟通(通 ...
- .net架构设计读书笔记--第三章 第10节 命令职责分离(CQRS)简介(Introducing CQRS)
一.分离查询命令 Separating commands from queries 早期的面向DDD设计方法的难点是如何设计一个类,这个类要包含域的方方面面.通常来说,任务软件系统方法调用可以 ...
- .net架构设计读书笔记--第三章 第9节 域模型实现(ImplementingDomain Model)
我们长时间争论什么方案是实现域业务领域层架构的最佳方法.最后,我们用一个在线商店案例来说明,其中忽略了许多之前遇到的一些场景.在线商店对很多人来说更容易理解. 一.在线商店项目简介 1. 用例 ...
随机推荐
- 定时关机命令——shutdown
通常会用到的定时关机命令有两种: Shutdown -s -t 36001小时后自己主动关机(3600秒) at 12:00 Shutdown -s 12:00自己主动关闭计算机 系统定时关机: Wi ...
- "蓝筹"如何使程序猿?
"蓝筹"这个词可能不是很多人知道这意味着什么.我会来普及知识.这是最重要的概念是指"越来越从长远来看更有价值"的含义.作为一个程序猿,我想你想使自己通过实际行动 ...
- [转] linux 信号量之SIGNAL
我们可以使用kill -l查看所有的信号量解释,但是没有看到SIGNAL 0的解释. [root@testdb~]# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) ...
- JVM内存回收对象及引用分析
自动垃圾回收是Java相较于C++的一个重要的特点,想了解JVM的垃圾回收机制,首先我们要知道垃圾回收是回收什么地方的垃圾,我在我的上一篇博客<JVM内存区域划分>里面有写到JVM里面的内 ...
- linux lvm的操作手册_pvcreate_vgcreate_lvcreate_相关
一. 前言 每个Linux使用者在安装Linux时都会遇到这样的困境:在为系统分区时,如何精确评估和分配各个硬盘分区的容量,因为系统管理员不但要考虑到当前某 个分区需要的容量,还要预见该分区以后可能需 ...
- NYOJ 980 格子刷油漆 动态规划
这道题目状态转移方程比较复杂,刚开始以为没这么多情况,看了好多大牛的博客再加上与同学讨论才看懂,写下心得. 因为起点不固定,所以我们一个一个来考虑,先从角上考虑,设三个数组来表示分别为D,A,Sum, ...
- centos redis安装
对redis不是很了解,先写一个简单的安装过程 系统版本:centos 6.5 redis版本:2.8.23 一.安装依赖 sudo yum install tcl gcc gcc-c++ -y 二. ...
- 应用框架 ViewPager Tab Fragment SlidingMenu
介绍 常见的应用框架 框架一:多个tab+Fragment,点击不同的tab加载不同的Fragment,不能滑动切换只能点击切换: 框架二:多个tab+ViewPager+FragmentPagerA ...
- HTML5文件加载进度管理
/** * 文件加载进度管理 */ DownloadUtils = function(options){ options = options || {}; this.init(options); }; ...
- apache的域名添加虚拟端口号
1. vi /etc/httpd/conf/httpd.conf 2. 搜索Listen 80,在后面添加Listen 8080 3. 重启apache服务器./usr/sbin/apachectl ...