天天看点

内核通知链 学习笔记 【转】

最近在看《深入理解Linux网络内幕》一书,学习了一下书中讲到的内核通知链方面的知识,写了一个读书笔记和一点代码来加深理解,希望能够对大家有一点帮助。内核通知链在网络方面得到了广泛的使用。

1.通知链表简介

  大多数内核子系统都是相互独立的,因此某个子系统可能对其它子系统产生的事件感兴趣。为了满足这个需求,也即是让某个子系统在发生某个事件时通知其它的子系统,Linux内核提供了通知链的机制。通知链表只能够在内核的子系统之间使用,而不能够在内核与用户空间之间进行事件的通知。

通知链表是一个函数链表,链表上的每一个节点都注册了一个函数。当某个事情发生时,链表上所有节点对应的函数就会被执行。所以对于通知链表来说有一个通知方与一个接收方。在通知这个事件时所运行的函数由被通知方决定,实际上也即是被通知方注册了某个函数,在发生某个事件时这些函数就得到执行。其实和系统调用signal的思想差不多。

2.通知链表数据结构

  通知链表的节点类型为notifier_block,其定义如下:

struct notifier_block

{

    int (*notifier_call)(struct notifier_block

*self, unsigned long, void *);

    struct notifier_block

*next;

    int priority;

};

复制代码

其中最重要的就是notifier_call这个函数指针,表示了这个节点所对应的要运行的那个函数。next指向下一个节点,即当前事件发生时还要继续执行的那些节点。

3.注册通知链

  在通知链注册时,需要有一个链表头,它指向这个通知链表的第一个元素。这样,之后的事件对该链表通知时就会根据这个链表头而找到这个链表中所有的元素。

注册的函数是:

int notifier_chain_register(struct notifier_block **nl, struct

notifier_block *n)

    也即是将新的节点n加入到nl所指向的链表中去。

    卸载的函数是:

int

notifier_chain_unregister(strut notifier_block **nl, struct notifier_block

*n)

    也即是将节点n从nl所指向的链表中删除。

4.通知链表

当有事件发生时,就使用notifier_call_chain向某个通知链表发送消息。

int notifier_call_chain(struct

notifier_block **nl, unsigned long val, void *v)

这个函数是按顺序运行nl指向的链表上的所有节点上注册的函数。简单地说,如下所示:

    struct notifier_block *nb =

*n;

    while (nb)

    {

        ret = nb->notifier_call(nb, val,

v);

        if (ret &

NOTIFY_STOP_MASK)

        {

            return ret;

        }

        nb = nb->next;

    }

5.示例

    在这里,写了一个简单的通知链表的代码。

实际上,整个通知链的编写也就两个过程:

    首先是定义自己的通知链的头节点,并将要执行的函数注册到自己的通知链中。

其次则是由另外的子系统来通知这个链,让其上面注册的函数运行。

我这里将第一个过程分成了两步来写,第一步是定义了头节点和一些自定义的注册函数(针对该头节点的),第二步则是使用自定义的注册函数注册了一些通知链节点。分别在代码buildchain.c与regchain.c中。

  发送通知信息的代码为notify.c。

代码1 buildchain.c

它的作用是自定义一个通知链表test_chain,然后再自定义两个函数分别向这个通知链中加入或删除节点,最后再定义一个函数通知这个test_chain链。

#include <asm/uaccess.h>

#include <linux/types.h>

#include

<linux/kernel.h>

#include <linux/sched.h>

<linux/notifier.h>

#include <linux/init.h>

<linux/module.h>

MODULE_LICENSE("GPL");

/*

* 定义自己的通知链头结点以及注册和卸载通知链的外包函数

*/

*

RAW_NOTIFIER_HEAD是定义一个通知链的头部结点,

通过这个头部结点可以找到这个链中的其它所有的notifier_block

static

RAW_NOTIFIER_HEAD(test_chain);

自定义的注册函数,将notifier_block节点加到刚刚定义的test_chain这个链表中来

raw_notifier_chain_register会调用notifier_chain_register

int register_test_notifier(struct

notifier_block *nb)

        return

raw_notifier_chain_register(&test_chain, nb);

}

EXPORT_SYMBOL(register_test_notifier);

int unregister_test_notifier(struct

raw_notifier_chain_unregister(&test_chain, nb);

EXPORT_SYMBOL(unregister_test_notifier);

自定义的通知链表的函数,即通知test_chain指向的链表中的所有节点执行相应的函数

int test_notifier_call_chain(unsigned long

val, void *v)

raw_notifier_call_chain(&test_chain, val, v);

EXPORT_SYMBOL(test_notifier_call_chain);

* init and exit

static int __init

init_notifier(void)

printk("init_notifier\n");

        return 0;

static void __exit

exit_notifier(void)

printk("exit_notifier\n");

module_init(init_notifier);

module_exit(exit_notifier);

代码2 regchain.c

    该代码的作用是将test_notifier1

test_notifier2

test_notifier3这三个节点加到之前定义的test_chain这个通知链表上,同时每个节点都注册了一个函数。

* 注册通知链

extern int register_test_notifier(struct

notifier_block*);

extern int unregister_test_notifier(struct

static int test_event1(struct notifier_block

*this, unsigned long event, void *ptr)

        printk("In Event 1: Event Number is

%d\n", event);

static int test_event2(struct notifier_block

        printk("In Event 2: Event Number is

static int test_event3(struct notifier_block

        printk("In Event 3: Event Number is

* 事件1,该节点执行的函数为test_event1

static struct notifier_block test_notifier1

=

        .notifier_call =

test_event1,

* 事件2,该节点执行的函数为test_event1

static struct notifier_block test_notifier2

test_event2,

* 事件3,该节点执行的函数为test_event1

static struct notifier_block test_notifier3

test_event3,

* 对这些事件进行注册

reg_notifier(void)

        int err;

        printk("Begin to

register:\n");

        err =

register_test_notifier(&test_notifier1);

        if (err)

        {

                printk("register

test_notifier1 error\n");

                return -1;

        }

        printk("register test_notifier1

completed\n");

register_test_notifier(&test_notifier2);

test_notifier2 error\n");

        printk("register test_notifier2

register_test_notifier(&test_notifier3);

test_notifier3 error\n");

        printk("register test_notifier3

        return err;

* 卸载刚刚注册了的通知链

unreg_notifier(void)

unregister\n");

unregister_test_notifier(&test_notifier1);

unregister_test_notifier(&test_notifier2);

unregister_test_notifier(&test_notifier3);

        printk("Unregister

finished\n");

module_init(reg_notifier);

module_exit(unreg_notifier);

代码3 notify.c

该代码的作用就是向test_chain通知链中发送消息,让链中的函数运行。

extern int test_notifier_call_chain(unsigned

long val, void *v);

* 向通知链发送消息以触发注册了的函数

call_notifier(void)

notify:\n");

* 调用自定义的函数,向test_chain链发送消息

printk("==============================\n");

        err = test_notifier_call_chain(1,

NULL);

                printk("notifier_call_chain

error\n");

uncall_notifier(void)

        printk("End

notify\n");

module_init(call_notifier);

module_exit(uncall_notifier);

Makefile文件

obj-m:=buildchain.o regchain.o

notify.o

KERNELDIR:=/lib/modules/$(shell uname -r)/build

default:

        make -C $(KERNELDIR) M=$(shell pwd)

modules

运行:

make

insmod buildchain.ko

insmod regchain.ko

insmod

notify.ko

这样就可以看到通知链运行的效果了

下面是我在自己的机器上面运行得到的结果:

QUOTE:

init_notifier

Begin to

register:

register test_notifier1 completed

register test_notifier2

completed

register test_notifier3 completed

notify:

==============================

In Event 1: Event Number is 1

In

Event 2: Event Number is 1

In Event 3: Event Number is

1

【新浪微博】 张昺华--sky

【twitter】 @sky2030_

【facebook】 张昺华 zhangbinghua

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

继续阅读