读书笔记—— Under Standing Linux Network Internals(3)

来源:互联网 发布:福彩3dapp软件 编辑:程序博客网 时间:2024/05/08 12:34

读书笔记—— Under Standing Linux Network Internals(3)

简介:
介绍了本书的第3~4章


Table of Contents
3 用户空间和内核空间的接口

简要介绍用户空间程序与内核交互的机理。

3.1 概述 3.1.1 内核还提供了三种特殊的接口,用于将内核的内部信息暴露给用户空间。这三种之中,有两种是虚拟文件系统。
  • procfs(/proc 文件系统)
    这是一个虚拟文件系统,通常挂在在/proc上,内核通过它,将内部信息以文件的形 式输出到用户空间。/proc上面的文件并不真的存在在磁盘上,但是用户可以通过 cat,more来读取文件,通过重定向(>)来写入文件,甚至可以对他们进行权限的设置。 用户不能在该文件夹(挂载点)中新建文件或文件夹。
  • sysctl (/proc/sys directory)
    该接口可以允许用户读取和修改内核中变量的值。这种变量的修改方式有两种:一是 通过系统调用sysctl,二是使用/proc文件系统。
  • sysfs (/sys filesystem)
    2.6内核新增,输出更加clean。
3.1.2 系统调用
  • ioctl
    很复杂的系统调用,后文涉及到时候在详述。
  • Netlink socket
    较新的一种网络程序与内核交互的接口。
3.2 Procfs 与 sysctl

procfs与sysctl都可以输出内核内部的信息,但是procfs输出的大多为只读数据,而 sysctl的输出则大多为可写的(对root来讲)。

3.2.1 procfs

网络模块在系统初始化或者模块加载的时候,会在/proc文件系统中注册一个或者多 个文件。当用户读取文件时,会间接地诱发内核运行一些内核工具,从而返回一些输 出给用户。网络模块注册的信息位于/proc/net。

/proc中的目录可以通过函数proc_mkdir来创建。而/proc/net中的文件则可以通过 proc_net_fops_create和proc_net_remove来注册和卸载。上述两个函数定义在 include/linux/proc_fs中。在上述两个函数的实现中,他们分别调用了更底层的函 数crete_proc_entry和remove_proc_entry。对proc_ner_fops_create来讲, 该函数除了通过proc_net_create来创建文件以外,还负责文件操作的初始化。如 下面这个例子所示:

/*net/ipv4/arp.c*/

static const struct file_operations arp_seq_fops = {
.owner = THIS_MODULE,
.open = arp_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_net,
};


static int __net_init arp_net_init(struct net *net)
{
if (!proc_net_fops_create(net, "arp", S_IRUGO, &arp_seq_fops))
return -ENOMEM;
return 0;
}

static void __net_exit arp_net_exit(struct net *net)
{
proc_net_remove(net, "arp");
}

static struct pernet_operations arp_net_ops = {
.init = arp_net_init,
.exit = arp_net_exit,
};

static int __init arp_proc_init(void)
{
return register_pernet_subsys(&arp_net_ops);
} 4 Notification Chains

Linux内核中各个子系统的独立性很强,这样,一个子系统不能直接获取另外一个子系 统的事件。为此,Linux内核采用了Notification Chains.

本章重点如下:

Notification Chains如何声明的,网络代码中使用了哪些notification Chain

内核的子系统如何向Notification Chain 注册

一个Chain上的内核子系统如何产生一个通知

注意,这里的notification指的是内核的各个子系统中,而非用户空间和内核之间。用 户空间和内核之间的通讯方法参见前面一章。

4.1 概述

所谓的notification chain就是一系列的用于在特定事件发生时候执行的函数,这些 函数可以让其他的子系统获知此时发生的事件。每一个Chain都分为主动方 (notifier,发布者)和被动方(notified,订阅者):

notified

当某一事件发生时候需要被告知的子系统。

notifier

发生事件的子系统,该子系统讲会调用回调函数。

4.2 定义一个Chain

Notifier有四种类型,如下:

Atomic notifier chains
Chain callbacks run in interrupt/atomic context. Callouts are not allowed to block.
Blocking notifier chains
Chain callbacks run in process context. Callouts are allowed to block.
Raw notifier chains
There are no restrictions on callbacks, registration, or unregistration. All locking and protection must be provided by the caller.
SRCU notifier chains
A variant of blocking notifier chains, with the same restrictions.

Notification Chain的元素为notifierbloc类型,其定义为:

struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block *next;
int priority;
};
struct atomic_notifier_head {
spinlock_t lock;
struct notifier_block *head;
};

struct blocking_notifier_head {
struct rw_semaphore rwsem;
struct notifier_block *head;
};

struct raw_notifier_head {
struct notifier_block *head;
};

struct srcu_notifier_head {
struct mutex mutex;
struct srcu_struct srcu;
struct notifier_block *head;
};

其中,notifiercall待执行的函数, next为链表中指向下一个元素的指针。priority 是函数运行时候的优先级,优先级高的函数时会优先执行。但实际上,几乎所有的 notifier的priority都被设置成了0,因此他们将会按照插入列表的先后顺序来执行。

4.3 notification 的注册

注册notification的通用函数为notifierchainregister。除此之外,内核含提供了 基于它的若干wrapper。例如前面提到的几种notifier都有自己的注册函数:

atomic_notifier_chain_register ()
blocking_notifier_chain_cond_register ()
blocking_notifier_chain_register ()
notifier_chain_cond_register ()
notifier_chain_register ()
raw_notifier_chain_register ()
srcu_notifier_chain_register () 4.4 Chain上的通知事件

notifiercallchain( )用于对每一个接收者发送事件消息。该函数遍历Chain上的注 册的所有回调函数,并按优先级依次调用。代码如下:

/**
* notifier_call_chain - Informs the registered notifiers about an event.
* @nl: Pointer to head of the blocking notifier chain
* @val: Value passed unmodified to notifier function
* @v: Pointer passed unmodified to notifier function
* @nr_to_call: Number of notifier functions to be called. Don't care
* value of this parameter is -1.
* @nr_calls: Records the number of notifications sent. Don't care
* value of this field is NULL.
* @returns: notifier_call_chain returns the value returned by the
* last notifier function called.
*/
static int __kprobes notifier_call_chain(struct notifier_block **nl,
unsigned long val, void *v,
int nr_to_call, int *nr_calls)
{
int ret = NOTIFY_DONE;
struct notifier_block *nb, *next_nb;

nb = rcu_dereference(*nl);

while (nb && nr_to_call) {
next_nb = rcu_dereference(nb->next);

#ifdef CONFIG_DEBUG_NOTIFIERS
if (unlikely(!func_ptr_is_kernel_text(nb->notifier_call))) {
WARN(1, "Invalid notifier called!");
nb = next_nb;
continue;
}
#endif
ret = nb->notifier_call(nb, val, v);

if (nr_calls)
(*nr_calls)++;

if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
break;
nb = next_nb;
nr_to_call--;
}
return ret;
}

内核针对前面提到的notifier的分类而提供了若干他的wrapper,详情见kernel/notifier.c

4.5 Notification Chains for the Networking Subsystems

内核2.6.32版本中未找到这一节中提到的数据结构,如netdevchain等等。

可能是因为我刚用gtags取代了cscope,用的不太熟练,当然也可能是新的内核中对网 络部分做了较大的调整。

Any way,略过本章剩下的几个小节。

Author:

Date: 2010-05-31 16:26:17

HTML generated by org-mode 6.33x in emacs 23


Author:yangyingchao, 2010-05-31
原创粉丝点击