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_THREADresult = 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 */
View Code
先是创建了要监视的最大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;elseevent.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);
View Code
这是将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_KQUEUEconst 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; #endifchar strbuf[ISC_STRERRORSIZE]; #ifdef ISC_SOCKET_USE_POLLWATCHpollstate_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_KQUEUEcc = 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_POLLWATCHdvp.dp_timeout = -1; #elseif (pollstate == poll_idle)dvp.dp_timeout = -1;elsedvp.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); }
View Code
无限循环,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_THREADisc_boolean_t have_ctlevent = ISC_FALSE; #endifif (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_THREADif (events[i].data.fd == manager->pipe_fds[0]) {have_ctlevent = ISC_TRUE;continue;} #endifif ((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_THREADif (have_ctlevent)done = process_ctlfd(manager); #endifreturn (done); }
View Code
待续
转载于:https://www.cnblogs.com/ding-linux-coder/p/4432666.html
BIND9的架构与机制笔记1相关推荐
- 《大型网站技术架构》读书笔记四:瞬时响应之网站的高性能架构
来源:http://www.cnblogs.com/edisonchou/p/3809839.html 此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. ...
- 《大型网站技术架构》读书笔记三:大型网站核心架构要素
来源:http://www.cnblogs.com/edisonchou/p/3806348.html 此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. ...
- 《大型网站技术架构》读书笔记一:大型网站架构演化
来源:http://www.cnblogs.com/edisonchou/p/3773891.html 此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. ...
- 《大型网站技术架构》读书笔记之七:随需应变之网站的可扩展架构
来源:http://www.cnblogs.com/edisonchou/p/3862389.html 此篇已收录至<大型网站技术架构>读书笔记系列目录贴,点击访问该目录可获取更多内容. ...
- Flux架构小白入门笔记
Flux架构小白入门笔记 Flux是facebook提出的一种处理前端数据的架构,学习Flux就是学习它的思想. 这个笔记是我在学习了阮一峰老师的Flux 架构入门教程之后得出, 里面的例子和部分原文 ...
- 系统架构师学习笔记_第六章(下)_连载
系统架构师学习笔记_第六章(下)_连载 6.3 基于 UML 的软件开发过程 6.3.1 开发过程概述 UML 是独立于软件开发过程的,能够在几乎任何一种软件开发过程中使用.迭代的渐进式软件开发过程 ...
- 阿里P8架构师进阶心得:分布式数据库架构MyCat学习笔记送给你
前言: MyCat 是一个数据库分库分表中间件,使用 MyCat 可以非常方便地实现数据库的分库分表查询,并且减少项目中的业务代码.今天我们将通过数据库架构发展的演变来介绍 MyCat 的诞生背景,以 ...
- 【Azure 架构师学习笔记】-Azure Data Factory (4)-触发器详解-事件触发器
本文属于[Azure 架构师学习笔记]系列. 本文属于[Azure Data Factory]系列. 接上文[Azure 架构师学习笔记]-Azure Data Factory (3)-触发器详解-翻 ...
- 好家伙,阿里P8撰写的Java微服务架构全栈笔记GitHub一夜飞到榜首
Java微服务作为当下最常用的架构技术,快速实现编程开发而且维护起来十分的方便,可以简单是实现高可用,分布式开发而且也很安全! 今天给大家分享的这份<Java微服务架构全栈笔记>,用140 ...
最新文章
- Linux上安装jdk并配置环境变量
- 爬虫的系统框架组成-解析器
- 应用程序启动器选项卡以及页面内容的设置
- mysql PREPARE用法_PHP5 mysqli的
- Interference Signal 第八届
- 小试牛刀JavaScript鼠标事件
- 用Prime31实现Google Play In-App-Blling
- 飞鸽传书下载2013
- 淘宝网架构分享总结[转]
- makefile函数集锦【转】
- java计算机毕业设计劳务外包管理系统源码+系统+mysql数据库+lw文档
- 百度地图生成器不显示图片的原因
- Ubuntu Install Zhengma
- 康师傅红烧牛肉面:守护平凡英雄,成就烟火人生
- 不老嘞 - 2004和老婆游香山
- 重新振作起来,继续战斗
- RJ45接头 和 RJ48接头的区别
- 上下文无关文法的分析树(Context-Free Grammar, CFG)的分析树--编译原理
- XShell4 SSH服务器拒绝了密码解决办法
- alpine是什么 ?