首先从数据结构入手:
1、涉及的变量或者数据结构:
extern struct list_head net_namespace_list;
网络空间相关的链表,每一个新注册的网络空间,都会添加到该链表中
static LIST_HEAD(pernet_list);
static struct list_head *first_device = &pernet_list;
每一个新注册的网络协议模块都会添加到该链表中。
#define for_each_net(VAR) \
list_for_each_entry(VAR, &net_namespace_list, list)
遍历链表net_namespace_list中的每一个网络空间对应的数据结构
然后是代码分析:
2、函数分析
该函数的主要作用是将一个网络协议模块添加到每一个网络命令空间中,然后再执行其ops->init程序进行初始化,一般其ops->init会在其对应的proc目录下,生成一个网络协议模块对应的proc文件或proc目录,并执行一些协议初始化相关的函数。
int register_pernet_subsys(struct pernet_operations *ops)
{
int error;
mutex_lock(&net_mutex);
error = register_pernet_operations(first_device, ops);
mutex_unlock(&net_mutex);
return error;
}
该函数是register_pernet_operations的包裹函数,其中first_device的定义如下:
static struct list_head *first_device = &pernet_list;
所有要添加到网络命名空间的网络协议模块都会添加到链表first_device中。
当kernel支持网络命令空间时,register_pernet_operations的定义如下:
当支持多网络命令空间时,会调用该函数,该函数会遍历目前已存在的所有网络命名空间,将网络协议模块添加到每一个网络命令空间中;并执行init操作,在每一个网络命名空间中,执行协议初始化相关的东东(生成proc相关的文件或者为协议申请缓存等)。
static int __register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
struct net *net;
int error;
LIST_HEAD(net_exit_list);
/*1、将一个网络协议模块对应的数据结构pernet_operations添加到
网络空间链路pernet_list的尾部
2、然后调用for_each_net,为每一个网络命名空间,都执行该网络协议模块的init函数,执行一些初始化操作,并在/proc/NameSpace/下生成一个该网络协议模块对应的proc文件或者proc目录
*/
list_add_tail(&ops->list, list);
if (ops->init || (ops->id && ops->size)) {
for_each_net(net) {error = ops_init(ops, net);
if (error)
goto out_undo;
list_add_tail(&net->exit_list, &net_exit_list);
}
}
return 0;
out_undo:
/* If I have an error cleanup all namespaces I initialized */
list_del(&ops->list);
ops_exit_list(ops, &net_exit_list);
ops_free_list(ops, &net_exit_list);
return error;
}
当kernel不支持网络命令空间时,register_pernet_operations的定义如下:
该函数是将一个网络协议模块注册到网络空间init_net中,然后再调用ops->init执行协议初始化相关的东东(生成proc文件或者为协议申请缓存、创建socket等)
static int ops_init(const struct pernet_operations *ops, struct net *net)
{
int err;
if (ops->id && ops->size) {
void *data = kzalloc(ops->size, GFP_KERNEL);
if (!data)
return -ENOMEM;
err = net_assign_generic(net, *ops->id, data);
if (err) {
kfree(data);
return err;
}
}
if (ops->init)
return ops->init(net);
return 0;
}
static int __register_pernet_operations(struct list_head *list,
struct pernet_operations *ops)
{
int err = 0;
err = ops_init(ops, &init_net);
if (err)
ops_free(ops, &init_net);
return err;
}
3、网络空间初始化相关
在linux中,不管支不支持多网络空间,均会默认生成网络空间init_net。
在初始化函数net_ns_init中,若支持多网络空间,则会调用 kmem_cache_create申请一块缓存用于申请新的网络空间;若不支持多网络空间,则不会申请缓存。
当支持多网络空间时,通过调用函数copy_net_ns,注册一个新的网络空间。
而在网络空间init_net中,是如何生成/proc/net目录的呢?
是在proc_net_ns_init中生成的net目录,在该函数中并生成了/proc/net/stat目录
一个例子:
在路由表初始化函数ip_fib_init中,会调用register_pernet_subsys,注册路由表模块proc中,即文件/proc/net/route,而fib_net_init中并不仅仅是生成proc文件那么简单,其会调用ip_fib_net_init,为每一个网络空间申请256个hash表,用于存放256个路由表;然后调用fib4_rules_init,进行路由规则的初始化(当不支持策略路由时,则fib4_rules_init会默认生成local、main两张路由表;若支持策略路由,则会进行策略路由的一些初始化操作)
[来自: http://blog.csdn.net/lickylin/article/details/18013879]