register_pernet_subsys相关学习

    在阅读路由相关的代码时,读到了这个函数,对于这个函数及相关机制不熟悉,所以就仔细分析了下。

 

   首先从数据结构入手:

 

1、涉及的变量或者数据结构:

extern struct list_head net_namespace_list;

网络空间相关的链表,每一个新注册的网络空间,都会添加到该链表中

static LIST_HEAD(pernet_list);

static struct list_head *first_device = &pernet_list;

每一个新注册的网络子系统,都会将该子系统对应的pernet_operations类型遍历添加到该链表中
(结构体pernet_operations中主要包括该网络子系统相关的初始化与退出接口,以及该网络子系统
的id等,针对网络子系统,包括arp模块、bridge、ipv4、ipv6、ppp、icmp、icmp6、iptables、iptable6等等)。

#define for_each_net(VAR) \

list_for_each_entry(VAR, &net_namespace_list, list)

遍历链表net_namespace_list中的每一个网络空间对应的数据结构

 

 然后是代码分析:

 

 

2、函数分析

该函数的主要作用是将一个网络协议子系统添加到网络命令空间对应的全局链表pernet_list中,并针对每一个注册在net_namespace_list链表中的网络命令空间,均执行其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的定义如下:

当支持多网络命令空间时,会调用该函数,该函数会遍历目前net_namespace_list链表中所有已注册的的网络命令空间,将网络协议子系统添加到每一个网络命令空间中;并执行该网络协议子系统的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不支持网络命令空间时,则不需要将该pernet_operations类型的变量添加到pernet_list中,直接对网络命名空间调用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两张路由表;若支持策略路由,则会进行策略路由的一些初始化操作)

 

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值