genl-netlink 协议 Linux kernel 实现 欣赏

先看一幅图
这里写图片描述

当注册一个genl_ops时,会根据genl_ops->cmd 到链表上查找有没有同名的genl_ops.
比较:
在设备驱动模型中,当你添加一个device的时候,需要到device链表来查找有无同名的device,
添加deviec_driver的时候,也是一样利用name来查找有没有同名的device_driver(具体的是driver_find() 函数),
添加genl_ops原理一样,只不过这里是利用cmd来判断的.Linu内核中重要的结构体大部分都是这样组织成链表(另一部分是数组),
然后对该链表进行增加、删除、遍历.. list_entry()取出一个 然后调用该节点中的回调函数,  

设备驱动模型中查找请参考我下面的一片博文

http://blog.csdn.net/leesagacious/article/details/46486995

源码欣赏

/*
    @family
         通用netlink家族
         在调用该函数前,需要构造结构体struct genl_family,然后调用函数genl_register_family()来注册
         这个函数很丰富,最终还是把构造的genl_family 添加到散列表上,下面会说

         注意,
              它的pre_doit()函数比 genl_ops 的 doit()函数调用还要早,
              post_doit()函数在doit()函数调用后调用
              这样的设计 比 ioctl 要好多了
    @ops
        这个参数很重要,而且对该参数的校验也是很严格的
        必需提供回调函数 doit()或 dumpit(),不然,你就别想注册成功了   
*/
int genl_register_ops(struct genl_family *family, struct genl_ops *ops)
{
    int err = -EINVAL;

    /**
        这里对传入的参数进行校验,不合法的别想进来,如果不提供过doit、dupit 就会错误,
        其实,这里校验不是最严重的地方,最严重的是对 注册的组播组的名字进行校验,连续用了两个BUG_ON(),
        这是Linux kernel中为数不多的一次
        源码 : 
        int genl_register_mc_group()
       {
           ......
           BUG_ON(grp->name[0] == '\0');
           BUG_ON(memchr(grp->name, '\0', GENL_NAMSIZ) == NULL);
    */
    if (ops->dumpit == NULL && ops->doit == NULL)
        goto errout;

    /**
        该函数就是上图说的了,遍历整条链表,发现有相同命令的,就在这儿返回了
        源码:
            static struct genl_ops *genl_get_cmd()
            {
                struct genl_ops *ops;
                遍历链表吧 
                list_for_each_entry(ops, &family->ops_list, ops_list)
                    if (ops->cmd == cmd) 比较CMD
                        return ops;
                return NULL;        
            }
    */
    if (genl_get_cmd(ops->cmd, family)) {
        err = -EEXIST;
        goto errout;
    }
    /*
        #define GENL_ADMIN_PERM     0x01
        #define GENL_CMD_CAP_DO     0x02
        #define GENL_CMD_CAP_DUMP   0x04
        #define GENL_CMD_CAP_HASPOL 0x08

        如果你注册的genl_ops提供了dumpit、doit、policy时,会改变ops中的flags的位,
        以后的函数 会检查这个flags的位吗 ?
        内核中仅有一处检查这个flags的位,而且检查的是 #define GENL_CMD_CAP_DUMP 0x04
        那么 netlink内核 用这个flags字段 又有哪些含义呢!

        使用通用netlink套接字从user space 发送的数据,会交给这个函数处理
        genl_rcv()
        {
            ↓
            genl_rcv_msg()
            {
                ↓
                genl_family_rcv_msg()
                {
                        if ((ops->flags & GENL_ADMIN_PERM) &&!netlink_capable(skb,CAP_NET_ADMIN))
                }
            }
        }
    */
    if (ops->dumpit)   // 转储回调函数
        ops->flags |= GENL_CMD_CAP_DUMP;
    if (ops->doit)    // 命令回调函数
        ops->flags |= GENL_CMD_CAP_DO;
    if (ops->policy)  // 属性有效性策略
        ops->flags |= GENL_CMD_CAP_HASPOL;

    /*
        这里用了两把锁,保护临界区
        down_write(&cb_lock);    
        mutex_lock(&genl_mutex);
        读写锁、互斥锁 同时用上了,
    */
    genl_lock_all();
    /*
        添加到链表上去把
    */
    list_add_tail(&ops->ops_list, &family->ops_list);
    genl_unlock_all();

    /**
        在这里函数中有一个 大的switch case,
        if (!init_net.genl_sock)
             return 0;
        switch (event) 
        {
        case CTRL_CMD_NEWFAMILY:
        case CTRL_CMD_DELFAMILY:
        case CTRL_CMD_NEWMCAST_GRP:
        default:
                 return -EINVAL;
        }         
       看该函数的第一个参数 CTRL_CMD_NEWOPS
       没有匹配任何一个,直接 return 了         
    */
    genl_ctrl_event(CTRL_CMD_NEWOPS, ops);
    err = 0;
errout:
    return err;
}

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/69895135