天天看點

核心通知鍊 學習筆記 【轉】

最近在看《深入了解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

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.

繼續閱讀