模式介绍

责任链将需要触发的对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。

图表 1责任链模式流程图

责任链在C语言里也是实现形式非常明显的模式。最典型的责任链有linux内核的中断处理机制的纯软件部分和内核网络netfiler的HOOK机制。这两者均强化了责任链机制,重点在引入了责任优先级方法和增加了通过/终结两种处理结果。

责任链模式的最重要的数据结构是handler链表。事件发生时,handler链表上的回调函数会被以此调用。优先级决定了那个handler会被先调,哪些会被后调用。在扩展特性里,每个handler可以有不处理和处理完之后继续交给下一个handler两种选择。如果该事件最后没有被消费,会有一个异常处理函数。如果责任链上任意一个handler消费了事件,那么就不传给下一个handler,直接结束。

逻辑上和责任链模式最相近的一个设计模式为观察者模式。流程图如下。观察者模式和责任链模式的最大的差别在于,事件会被通知到每一个平等的handler,而不是逐级处理。也不存在优先级的说法,也不会出现事件没有处理需要异常函数收尾。

图表 2观察者模式流程图

责任链模式实现

责任链模式事件怎么触发不要紧,关键就是handler的数据结构组织和处理逻辑。

责任链节点定义

//两类处理结果,子类可以扩展

#define CHAIN_PASS 0#define CHAIN_STOP 1typedef int (*chain_func)(char *buf);struct chain_ops_node {struct list_head list;  //内核链表标准结构chain_func *handler;  //handler的回调函数int priority; //优先级};

责任链和处理函数

//全局的责任链struct list_head chain_global_list;//具体的处理函数int chain_handler1(char *buf){//do somethingif(/*some conditions*/){return CHAIN_PASS;}return CHAIN_STOP;}int chain_handler2(char *buf){//do somethingif(/*some conditions*/){return CHAIN_PASS;}return CHAIN_STOP;}//封装成节点struct chain_ops_node node1 ={.handler = chain_handler1,.priority = 0}struct chain_ops_node node2 ={.handler = chain_handler2,.priority = 1}

注册和反注册函数

特别注意,一般是需要信号量锁定的,因为很可能链条上的数据正在执行。内核里喜欢用rcu锁,可以避免资源互斥引起cpu浪费。

int chain_register(struct chain_ops_node *node){   //lock chain_global_list//add node into chain_global_list according to priority//unlock chain_global_listreturn 0;}int chain_unregister(struct chain_ops_node *node){//lock chain_global_list//delete node into chain_global_list//unlock chain_global_listreturn 0;}

调用流程

int main(){struct list_head *node;struct chain_ops_node *node_func;char buf[16];chain_register(&node1);chain_register(&node2);//something happend, should trigger responsibility chain//fill buf with eventlist_for_each(node, &chain_global_list){node_func = (struct chain_ops_node *)node;if(node_func.handler(buf) == CHAIN_STOP){break;}}return 0;}

内核的责任链模式实例

内核里最典型的就是内核中断处理和和内核网络netfiler的HOOK机制。而内核网络netfiler的HOOK机制的责任链模式体现更为完整充分。所以本文以netfiler的HOOK机制为例讲解。

handler的格式

内核的hook链就是责任链模式的handler链。nf_hook_ops就是handler链的一个handler节点。

struct nf_hook_ops {struct list_head list;  //内核链表标准结构/* User fills in from here down. */nf_hookfn *hook;  //handler的回调函数struct module *owner; //模式无关,可忽略u_int8_t pf; //协议族,用来区分事件处理的,可以看作辅助标记。作为单链可忽略。unsigned int hooknum; //挂在在哪个hook链上,netfiler的hook设计支持多hook链条,不过同一类事件只是触发一个hook链条的函数。所以从设计模式上讲这里只是同时实现了4条互不相干的责任链模式的handler链。作为单链可忽略。/* Hooks are ordered in ascending priority. */int priority; //优先级};

比如如下定义:

static struct nf_hook_ops nf_nat_ops[] __read_mostly = {/* Before packet filtering, change destination */{.hook                   = nf_nat_in,.owner                 = THIS_MODULE,.pf                        = NFPROTO_IPV4,.hooknum           = NF_INET_PRE_ROUTING,.priority = NF_IP_PRI_NAT_DST,},……}

handler的注册

int nf_register_hook(struct nf_hook_ops *reg)

非常简单的操作,在锁的保护下,将handler节点加入到链表nf_hooks[reg->pf][reg->hooknum]上。插入链表的顺序由priority决定,升序排列。

从这里可以看出,链表是二维的,区分了协议族和hooknum(网络通路的位置)。本质上,对于一个固定handler,可以认为只是和一个handler链条发生关系。

事件触发的处理函数

以IPV4的NF_INET_PRE_ROUTING hook为例,ip_rcv函数最后会调用nf_hook_slow,遍历链表调用handler函数。Handler返回处理结果有NF_ACCEPT,NF_STOLEN,NF_DROP等好几种。nf_hook_slow会根据这些结果决定接着调用链表上下一个handler还是终止等一系列动作。

linux内核遍历的方法基本上都是list_for_each_xxx函数。

模式实现总结

1. 责任链模式在内核的实现很普遍,实现代码典型而简单,都是先定义各异handler的链表节点,包含list结构体,优先级,回调处理函数3个要素即可。更复杂的责任链模式实现只不过多条链,但是单个链的属性是没有改变的。而netfilter的链已经算比较复杂的,所以绝大部分编码学习到这个水平就足够了。

2.每个handler的处理结果根据需要定义,总体上讲都是继续和不继续两种。

来源:华为云社区  作者:lurayvis

设计模式的C语言应用-责任链模式-第三章相关推荐

  1. Java设计模式之行为型:责任链模式

    背景: 学校规定参加校招的同学必须要请假,且要有相关人员的签字,三天以下需辅导员签字.三到七天需要系主任签字,一个星期以上需要院长签字,更多的则需要校长签字! 上图将学生.辅导员.系主任.院长.校长组 ...

  2. 设计模式笔记十四:责任链模式

    原文:http://www.runoob.com/design-pattern/ 少许个人理解,如有错误请指出.欢迎一起讨论. 责任链模式 顾名思义,责任链模式(Chain of Responsibi ...

  3. 23种设计模式(11):责任链模式

    定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止. 类型:行为类模式. 类图: 首先来看一段代码: p ...

  4. java责任链模式做优惠_Java设计模式菜鸟系列(十八)责任链模式建模与实现

    责任链模式(ChainOfResponsibility): 有多个对象,每个对象持有下一个对象的引用,形成一条链,请求在这条链上传递,直到某一对象决定处理该请求,但是发出者并不清楚最终哪个对象会处理该 ...

  5. 《大话设计模式》java实现之责任链模式

    责任链模式各个对象的下一个处理对象在客户端指定,可以自由组合,灵活性强,状态模式各状态则是在编译时设置,即在各状态对象中指定,可对比struts的filter. 另外,书中变量的命名如jingli.z ...

  6. 设计模式学习总结(二十一)--责任链模式

    定义 职责链模式就是避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止. 在职责链模式中我们可以随时随地的增加或者更改一 ...

  7. java责任链模式的三种方式,分享一点面试小经验

    字节跳动 ⾯试前 ⼀⾯ ⼆⾯ ⼩结 ⾯试前 头条的⾯试是三家⾥最专业的,每次⾯试前有专⻔的HR和你约时间,确定OK后再进⾏⾯试.每次都是通过视频⾯试,因为都是之前都是电话⾯或现场⾯,所以视频⾯试还是有 ...

  8. 软件设计模式之责任链模式实验

    软件设计模式之责任链模式实验 一.实验目的 掌握软件责任链模式中的命令模式,能够用责任链模式编写程序,解决实际问题. 二.实验内容与要求 内容:某小学老师指定班里的三位学生收寒假作业,三位学生分别负责 ...

  9. 设计模式——行为型模式之责任链模式(简简单单入门责任链,理解I/O流消息怎么逐步传递处理以及服务器框架转发)

    文章目录 前言 一.责任链模式定义 二.责任链模式结构 三.责任链高级应用 四.责任链模式优缺点 设计模式系列文章 结尾 前言 作者更有一系列设计模式文章(还在持续更新),图例丰富,少量代码,适合入门 ...

最新文章

  1. Python 应用领域以及版本之间的区别
  2. 产品经理岗位职责说明_公司销售经理岗位职责说明书
  3. java venus_来认识一下venus-init——一个让你仅需一个命令开始Java开发的命令行工具...
  4. 堆的应用--并查集解决“擒贼先擒王”问题(JAVA)
  5. python如何将图片打包进exe里_用python将图片切分为九宫格 并打包成exe可执行文件(附源码)...
  6. 面试时被问如何进行接口测试怎么回答
  7. 滑轮滚动到页面底部ajax加载数据的实例
  8. VB6-改造ComUnit(免除用例名称注册)
  9. regionserver.HRegionServer: Failed construction RegionServer
  10. scala构造器的介绍
  11. 剑指offer——面试题3:二维数组中的查找
  12. android在副屏中运行一个应用_android一个app打开另一个app的指定页面
  13. PPT文件太大怎么办?如何压缩PPT?这几招帮你搞定
  14. python全栈工程师待遇如何_python全栈工程师工作待遇
  15. 如何对客户行为进行数据分析?
  16. [备忘] 下载youtube表单的工具
  17. Linux运维——高级指令
  18. 校园招聘的秋招和春招有什么区别?
  19. 如何获取R语言中向量的元素
  20. 程序员的沟通技巧-耗子叔

热门文章

  1. linux测试api,testing - 测试基本使用接口
  2. c语言继承与派生作用,C++中继承与派生是如何实现的?
  3. 安装百分之80卡住_新车买回来要不要安装发动机护板呢
  4. java键盘输入一个数_Java如何实现从键盘输入一个数?
  5. html 文件域变滑块,小巧的jQuery区域范围滑块插件noUiSlider
  6. [转]如何让DIV固定在页面的某个位置而不随着滚动条随意滚动
  7. 《JAVA程序设计》第四周学习总结
  8. MySQL 添加、查看字段注释
  9. 返回一个循环数组中最大子数组的和
  10. FireBug调试工具笔记