免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
1234下一页
最近访问板块 发新帖
查看: 10219 | 回复: 32
打印 上一主题 下一主题

再谈 NETLINK [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-03 17:58 |只看该作者 |倒序浏览
上一年接触netlink,几乎现在这种方式非常方便用户空间和内核进程空间通信,并且可以监视异步时间,例如网卡的状态改变,ARP缓存的修改,这种改变用原有的IOCTL方式几乎是不可想象的。后来看了网上很多关于netlink的狗屁文章,看的我头大,正好这段时间重新看网络,所以爷们就写点东西,请看::


一 :相关的数据结构:
     1:struct netlink_table *nl_table;
        struct netlink_table {
           struct nl_pid_hash hash;
           struct hlist_head mc_list;
           unsigned long *listeners;
           unsigned int nl_nonroot;
           unsigned int groups;
           struct mutex *cb_mutex;
           struct module *module;
           int registered;
        };

        描述:
             socket(PF_NETLINK, SOCK_DGRAM, protocol(例如INET,NETLINK_ROUTE)),其中的每一个protocol都有一个对应的netlink_table。一般当这些协议模块(例如NETLINK_ROUTE)在初始化的时候,使用函数`netlink_kernel_create`去利用自己协议模块的信息去填充nl_table,函数原型如下:
             struct sock * netlink_kernel_create(struct net *net, int unit, unsigned int groups,
                                                 void (*input)(struct sk_buff *skb),
                                                 struct mutex *cb_mutex, struct module *module)
             例如当NETLINK_ROUTE在初始化的时候调用的方式就如下:
                       sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
                                                  rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
             其中NETLINK_ROUTE(宏)用来确定NETLINK_ROUTE子协议去填充哪一个netlink_table,可以把unit看成是nl_tabl数组的索引。


        使用:
             为了简单说明netlink的工作模式,让我们假想一种情况,因为不论是内核中的协议模块,还是用户自己创建的协议模块,实际上都是使用socket来进行通信,比方说a为了给b发送一条消息,那么a就一定要知道怎么找到b。再比方说我们现在想出了一种方法用于a和b来通信:创建一个固定长度的数组array,每个数组元素里面的数组是一个函数,该函数的都类似于这样:sayhi-to_X,比方分配给a的数组的索引是1,分配给b的索引是2,a初始化元素array[1]= sayhello-to-a,b初始化元素array[2]=sayhello-to-b。当a要和b通信的时候,只要a知道b的索引,那么就可以直接调用array[index(b)]()来给b发送信息了。

            让我们来看看上面的这个模型对于真实的内核中的netlink是如何工作的。

            当netlink子协议模块在把自己的模块信息用netink_kernel_create填充nl_table的时候,netlink_kernel_create同时还为该模块在nl_table的元素nl_pid_hash中分配一个元素,该元素的索引就为0,你可以把nl_pid_hash简单的想象成一个数组,netlink的子协议模块占用的索引为0,并且一定为0,这个数组中剩下的元素则可以被用户空间的程序在调用socket(...) -> netlink_create的时候使用,也就是说实际上如果用户空间想利用socket发送消息的时候,那么它一定在nl_pid_hash这个数组中占用一个元素。再来看NETLINK_ROUTE子协议模块在把自己注册到nl_table中的那个函数:      
                                  sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
                                                             rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);

             该函数中的rtnetlink_rcv实际上就类似于上面说的array[index(a)]的函数,因为NETLINK子协议模块的索引都为0,所以当发送给给内核协议模块发送消息的索引也就为0,索引为0也就意味着给内核自协议模块发消息,那比方说要给子协议模块INET发送信息,那么只需要两个参数就可以了:INET该参数用来对nl_table进行索引,找到用来描述INET协议模块的那个netlink_table,然后0用来在nl_pid_hash中找到inet注册的用来和它通信的函数,比方说sayhello-to-inet。这样就可以给inet来发送信息了。



            因为不论是子协议模块还是用户空间的soket,实际上都拥有一个socket这样的结构题,这个结构体里都有两个链表,一个recv列队,一个是send列队。比方说当用户空间发送一条信息给子协议模块inet一个信息的时候,实际上inet子协议并不是立刻的处理该请求,而是把这个用户空间发来的信息加入到一个inet自模块所占有的socket中的recv列队中,然后就返回给用户空间说该信息已经发送过去了。在某一个时刻inet发送他的recv类对中有信息需要处理,那么他就会处理该信息,然后发送给用户空间一个响应,这种响应的方式和用户的socket找到inet的方式是一样的,这个响应会把inet发送的响应加入到用户空间socket的recv列队中去,然后通知用户空间在该socket上有数据存在了。这种异步的方式是和ioctl的方式不一样的。ioctl则把请求和响应放在了同一个函数里面,这样就要求内核在处理请求的时候,用户空间需要一直等待。

        编程:
             socket(...)

             ?bind(...)本来意味bind是必须的,但是发现根本不用bind,当用户空间的socket发送数据给内核的时候,如果没有为该socket在nl_pid_hash上分配索引,那么内核会自动的分配一个索引,并且这个索引会一直被该socket使用。那么什么时候需要这个东西呢?那就是用户进程通过socket给用户进程的socket发送信息,这样一方必须知道另外一方的固定的索引,这就和真正的socket编程中分配端口的概念是一样的,如果一方是服务器,那么他的端口一定是固定的。我经常看到很多人都在bind地址的时候设定sockadd_nl的元素pid=getpid(),这样作简直就是吃包了撑的,首先这个pid在进程启动的时候都极有可能是不一样的,这就意味着该socket不能做为固定的接受方使用,分配这个pid还有一个不好的地方就是如果你设置pid为一个值的话,那么如果有另外一个进程就故意设置自己的bind pid也为那个数值,虽然这样很变态,但是却足以让你的程序在bind的时候就出错。所以我建议永远不要设置pid为你的getpid,或者是永远都不要bind,除非你的程序要接受来自其他的进程的消息,如果真的是那样的话,请给你的程序设定一个固定的数值,你可以通过定义一个宏来亲送实现。
             send() || recv()



        相关资料:
                 struct socket
                 netlink_create netlink_kernel_create netlink_sendmsg netlink_recvmsg
                 netlink_unicast

2:监听内核的事件(网卡up或者down,ip地址改变,arp缓冲区改变)
        一般使用ioctl的方式要监听这些事件的话要一直从内核中获取数据,而利用netlink则可以非常方便的来获知该事件,因为它的操作是分为2步的,你甚至可以使用epoll来监听那个socket。
        当内核中一个网络时间发生的时候,一般都会使用通知链通知这一事情。简单来说:当网卡down的时候,那么路由表就要修改,以真实的反应这一情况。在这种情况下通知链的作用就体现出来了。例如在上面的例子中路由系统注册一个函数到网卡的通知链中去,当网卡发生路由系统感兴趣的事情的时候就会调用路由系统提供的函数,这样网卡的情况就能真实的反应到路由系统中去了。

         netlink也有一些子协议模块例如rtnetlink去做这样的事情,例如rtnetlink也会注册到网卡的通知链中,当网卡出现一些情况的时候,例如down,那么rtnetlink就会广播该事件,该广播的时间照样是通过nl_table来处理的,在netlink_table中有一个mc_list,用户程序可以通过setsockopt netlink_add_membership来把自己加入到mc_list中去,这样当网卡有事情的时候,那么就可以通过这种方式来通知应用层了。



        相关资料:
                 netlink_broadcast
                 rtnl_notify
                 rtnl_register





下期我会给出足够多的程序来表述详细的使用方式。

评分

参与人数 1可用积分 +6 收起 理由
Roemer + 6

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2009-09-03 21:33 |只看该作者
写了这么多东西,竟然没有人看,难道这些东西都没有什么用吗?看见别的人发的帖子,我忍不住鄙视,可是想想到底论坛该是什么样子的呢?我没有答案

论坛徽章:
0
3 [报告]
发表于 2009-09-03 21:59 |只看该作者
lz消消气,可能是最近大家正好都没在弄netlink吧,而且内核源码版人一直都不是很多。
几个小时内没人回帖也是正常现象了。

看了lz的帖子才明白了bind的那个pid不是必须的。
以前看网上帖子都说要设置成pid,也想过几次为什么要设置为pid
后来也没深究。

论坛徽章:
0
4 [报告]
发表于 2009-09-03 22:23 |只看该作者
很不错,不过netlink,期待下期的实例

论坛徽章:
0
5 [报告]
发表于 2009-09-03 23:48 |只看该作者
当内核通过 netlink 的单播方式向用户进程发送数据时,用户进程是否需要 bind 并提前告诉内核 pid 呢?
这样就需要了吧?

论坛徽章:
0
6 [报告]
发表于 2009-09-04 08:40 |只看该作者

回复 #5 platinum 的帖子

同样不需要,道理很简单:因为unicast都是基于内核空间对用户空间的请求作出的响应,当用户空间发送请求到内核的时候,实际上整个过程会一直到调用自模块的注册的那个函数,整个过程是连贯的,所以同样完全没有必要设置pid。唯一需要设置pid的地方就是:当用户空间通过netlink和另外的用户空间通信的时候,这个时候因为接受方没有注册相关的可以直接被调用的函数,所以发送方只能把该请求加到接受方的recv链上,然后内核通知接受方socket上有数据了。而发送方设置的pid实际上就是对nl_pid_hash的一个索引,因为这个索引不是内核子协议模块固定使用的0,所以这个时候就需要发送方指定接受方了,而这个指定就是通过pid来定位的。

论坛徽章:
0
7 [报告]
发表于 2009-09-04 09:37 |只看该作者
多谢 wojiaohesen  的详细解释,我去实验一下!

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
8 [报告]
发表于 2009-09-04 10:13 |只看该作者
原帖由 wojiaohesen 于 2009-9-3 21:33 发表
写了这么多东西,竟然没有人看,难道这些东西都没有什么用吗?看见别的人发的帖子,我忍不住鄙视,可是想想到底论坛该是什么样子的呢?我没有答案


呵呵,LZ别激动。大家对帖子的反应速度没法做到你一发贴,别人都有反馈啊。况且,好文章更需要仔细体会之后,才发表一些意见和看法。

论坛徽章:
0
9 [报告]
发表于 2009-09-04 10:41 |只看该作者
实验了一下

测试方法:
A、userspace ---> kernelspace
B、kernelspace ---> userspace

1、userspace 不 bind
A:成功
B:成功

2、userspace 用 0 做 pid 发送给 kernelspace,kernelspace 发回时也用 0 做 pid
A:成功
B:不成功

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
10 [报告]
发表于 2009-09-04 10:42 |只看该作者
白金兄,你的头像现在显示不出来,而且显示的还是游客
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP