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. 用例 ...
随机推荐
- CentOS 6.3下rsync服务器的安装与配置[转]
CentOS 6.3下rsync服务器的安装与配置 一.rsync 简介 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也 ...
- gzip优化网络传输量提高传输效率[转]
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;us ...
- sass笔记-1|Sass是如何帮你又快又好地搞定CSS的
Sass学习笔记持续整理中,开篇不讲怎么安装,sass是什么,这些搜索引擎会告诉你,我们从sass的作用开始讲起,知道sass用来干什么,有什么作用,我们才能相信用sass的好处,并且时时刻刻想着sa ...
- 在PHP中使用CURL,“撩”服务器只需几行——php curl详细解析和常见大坑
在PHP中使用CURL,"撩"服务器只需几行--php curl详细解析和常见大坑 七夕啦,作为开发,妹子没得撩就"撩"下服务器吧,妹子有得撩的同学那就左拥妹子 ...
- 简单dp ---HDU3485 Count 101
题目大意:让求长度为n的0 和 1 构成的串中不包含101子串的个数有多少. 这个题当时想了好久,以为是一个规律题,一直在推规律,最后还是wa了,上网一看原来是dp问题, 不过确实递推式挺巧妙的. 递 ...
- nginx 2.基本配置
死磕nginx 2.基本配置 鉴于深入浅出的原理,我们先从一个简单的配置了解nginx的配置 1.一个典型配置 nginx的配置文件默认在nginx安装目录的conf二级目录下面,主配置文件为 ngi ...
- MediaPlayer 音频播放 示例
状态机.流程图.生命周期 对播放音频/视频文件和流的控制是通过一个状态机来管理的.下图显示一个MediaPlayer对象被支持的播放控制操作驱动的生命周期和状态. 椭圆代表MediaPlayer对象可 ...
- SpringMVC05使用注解的方式
<body> <a href="add">新增</a> <a href="update">修改</a> ...
- MVC4数据访问EF查询linq语句的时候报错找不到表名问题
一天做项目的时候遇到这样的问题,MVC4用EF访问数据查询用linq语句的时候报错找不到表名:报错如下图: 研究了几种情况,最后还是没有找到正真的问题所在,不过可能是和路由解析问题有关,暂时还没有进行 ...
- 在公网上布署Web Api的时候,不能调用,返回404
在internet上布署web API做的站点时,发现不能调用web api的任何action, 返回404. 经过很多的努力,也找不到原因,环境是win server 2008, IIS 75. n ...