免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 23430 | 回复: 0

[通信方式] 使用netlink通讯时需要注意的一些问题 [复制链接]

论坛徽章:
169
申猴
日期:2013-10-09 10:10:16天秤座
日期:2013-10-10 15:28:08天蝎座
日期:2014-07-17 14:02:54丑牛
日期:2014-07-17 14:03:04处女座
日期:2014-07-17 14:03:12双子座
日期:2014-07-17 14:03:21天秤座
日期:2014-07-17 14:03:29酉鸡
日期:2014-07-17 14:03:39金牛座
日期:2014-07-21 10:37:54水瓶座
日期:2014-07-22 16:56:09巳蛇
日期:2014-07-23 11:48:03天蝎座
日期:2014-07-31 10:16:36
发表于 2019-07-26 14:02 |显示全部楼层
使用netlink通讯时需要注意的一些问题   原作者:duanjigang


之前发过一个用户态通过netlink从内核中获取网络卡列表以及每个网卡状态信息的例子

大概的原理就是内核创建netlink socket,然后用户态调用应用程序发送查询命令,或者获取所有网卡列表,或者获取某一个网卡的状态信息。
当时做的比较简单,也就过去了,最近要用到这个通讯,传输比较大量数据,遇到了一些问题,今天刚刚解决,稍微小结下,发上来。
希望能对大家有点用(估计很多高手早都注意这个问题了^_^)

首先列举下问题:
其一,内核多次发送数据的问题。
在上篇文章中,我们看到,kernel是收到一个命令,就获取数据,然后简单的完成一次发送,代码片段如下:
  •         nlhdr->nlmsg_pid = 0;
  •                    nlhdr->nlmsg_flags = 0;
  •                    NETLINK_CB(skb).pid = 0;
  •                     NETLINK_CB(skb).dst_pid = pid;
  •                     NETLINK_CB(skb).dst_group = 0;
  •                     memset(nlhdr, 0, NLMSG_SPACE(nlhdr->nlmsg_len));
  •                     strcpy(NLMSG_DATA(nlhdr), szBuff);
  •                     netlink_unicast(netlink_exam_sock, skb, pid, MSG_DONTWAIT);
复制代码

当时没太注意,后来遇到情况是,数据有多条,内核需要多次发送,怎么办??结果我尝试用netlink_unicast多次发送,比如
  • for (int i = 0; i < n ;i++)
  • {
  •    //make data for record i
  •    netlink_unicast(netlink_exam_sock, skb, pid, MSG_DONTWAIT);
  • }

复制代码

结果一运行,就崩溃,后来知道netlink_unicast发送后会把skb释放掉,所以第二次调用是无效的了,这才会崩溃。
大体上感觉在每次发送的时候,可能需要clone一个或者自己构造一个包发送,上文的例子中的是直接利用从队列中拿出来的
skb做为负载发送的,所以没问题,但是还不能偷懒。就在网上找资料。
终于还是找到说法了,居然也是在CU的帖子,就是另外写一个函数,自己构造包,填数据,然后发送,就能多次发送了。
参考“执一”的博文:
通过Netlink与TC进行通信
(让我们再次对九贱兄和执一表示感谢!)修改了下,写(基本上是copy,只不过修改了参数)了个发送的函数,如下:
  • static int send_to_user(struct sock * ps, int pid, const char* szdata, unsigned int len)
  • {
  •         int ret;
  •         int size;
  •         unsigned char *old_tail;
  •         struct sk_buff *skb;
  •         struct nlmsghdr *nlhdr;
  •         struct cha *packet;
  •         /*计算消息总长:消息首部加上数据加度*/
  •         size = NLMSG_SPACE(len);
  •         /*分配一个新的套接字缓存*/
  •         skb = alloc_skb(size, GFP_ATOMIC);
  •         old_tail = skb->tail;
  •         /*初始化一个netlink消息首部*/
  •         nlhdr = NLMSG_PUT(skb, 0, 0, NETLINK_CME, size - sizeof(*nlhdr));
  •         /*跳过消息首部,指向数据区*/
  •         packet = NLMSG_DATA(nlhdr);
  •         /*初始化数据区*/
  •         memset(packet, 0, len);
  •         memcpy(packet, szdata, len);
  •         nlhdr->nlmsg_len = skb->tail - old_tail;
  •         /*设置控制字段*/
  •         nlhdr->nlmsg_pid = 0;
  •         nlhdr->nlmsg_flags = 0;
  •         NETLINK_CB(skb).pid = 0;
  •         NETLINK_CB(skb).dst_pid = pid;
  •         NETLINK_CB(skb).dst_group = 0;
  •         /*发送数据*/
  •         ret = netlink_unicast(ps, skb, pid, MSG_DONTWAIT);
  •         nlmsg_failure:
  •         return ret;
  •         ;
  • }

复制代码

这样,把原来的代码稍作修改
改成这样就能多次发送了。
  • if(strncmp(data, "all", 3) == 0)
  •                     {
  •                         get_dev_info(0, NULL);
  •                     }
  •                     else
  •                     {
  •                         get_dev_info(1, data);
  •                     }
  •                     pid = nlhdr->nlmsg_pid;
  •                     for(i = 0; i < time; i++)
  •                 {
  •                         send_to_user(netlink_exam_sock, pid, szBuff, strlen(szBuff));
  •                 }
  •         }

复制代码


其二:skb释放问题。(问题解决按照轻重缓急来说^_^)

刚解决了多次发送的问题,我就有些得意忘形,结果dmesg时看到一个很2的信息,是在rmmod时报告的

KERNEL: assertion (!atomic_read(&sk->sk_rmem_alloc)) failed at net/netlink/af_netlink.c (145)

于是再想是不是一楼了什么东西,哦,从队列中拿出来的skb没有释放,这下好解决了。两种途径。
A:既然netlink_unicast发送完后会把skb释放掉,那我们为啥不第一次发送时用从队列中拿出来的skb做载体,这样既发送数据包,又
释放了skb,果然,报错没了。。真是得了便宜还卖乖啊
B:最简单的,直接释放掉从队列拿出来的skb,从一而终的构造包发送,不再脚踩两只船。
  • kfree_skb(skb);

复制代码

好了,第二个问题解决了。

其三:也是最末的。应用层的阻塞读问题。
以前我都是一次sendto,然后内核一个回复,应用层再一个recvfrm就了事了。
结果后来改成
  • while(1)
  • {
  •     recvfrom();
  •     //把数据入库
  • }

复制代码

的方式,发现后面的语句没执行,发现是recvfrom阻塞住了。。这个好办,要让while循环跳出,内核通知应用层:“我没数据了,别再再苦苦追寻了,不要浪费你时间”。。这似乎听起来有些悲哀啊,呵呵
这种人来的语言用程序来写就是IP报文的分片标志吧,那就自己做个标志吧。
可以这样做,自己定一个消息头,放在netlink消息的开始位置,大小固定,或者直接放一个整数都行,反正就是用来标识是否还有数据的。
当内核中还有数据要发送时,每次发送消息中,这个标志位为1,告诉他:“你还有希望,继续追”
如果没有数据了,发送一个空包或者带数据的报文,其中标志位为0,告诉他:“我已经领证了,终止吧”,用户态读到这个
标志位,跳出循环,后续工作继续。。

就以上这三个问题,是我实际中遇到的,希望对大家有用。





您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP