您的位置:首页 > 运维架构 > Linux

genl-netlink 协议 Linux kernel 实现 欣赏

2017-04-09 19:31 190 查看
先看一幅图



当注册一个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: