网上有很多netlink的教程,但多针对2.6.*的内核,3.8.13内核的netlink API做了很多改动,但总体上差不多
学习netlink除了看别人的教程,感觉要写出个能跑的程序还得自己去读内核代码,就比如netlink_kernel_create这个函数,各版本间有很大不同,如2.6.18和2.6.34都不同,教程上的代码只能作参考
下面主要写一下3.8.13内核相比2.6.*内核在使用netlink上的不同,其他的请参考教程链接。
PS:代码是在虚拟机里写的,不知道怎么拷出来,所以下面的代码是重新手写的,并不完整
PPS:经过这次学习也发现,学内核编程真得在虚拟机里面学,不然时间都花在开关机上面了,另外最好是字符模式,减少开机时间
1. 创建内核sock代码
struct netlink_kernel_cfg cfg = { .input = nl_data_ready,//该函数原型可参考内核代码,其他参数默认即可,可参考内核中的调用 }; nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);// init_net是内核定义的变量,貌似是不建议使用的,测试足够了
2. 内核端发送消息
参考链接里的代码有使用nl_data_ready内接收的skb来发送消息的,测试中发现这个skb不能用来发送(内核直接崩掉),猜测是因为有些变量设置的值不适合发送。
我曾怀疑过是因为不能使用接收的skb来发送,就重新申请了一个skb,然后将接收的skb拷过来,修改参数(portid)发送,仍然崩溃。
教程里的代码在nlmsg_put还设置了skb的一些参数,比如pid什么的,3.8.13内核中不需要设置,默认即可
struct sk_buf *skb; struct nlmsghdr *nlh; // 创建skb skb = nlmsg_new(MAX_PAYLOAD, GFP_ATOMIC); if(!skb) { printk(KERN_ERR"FAILED TO ALLOC SKB\n"); return; } // 看了下内核的代码,下面的函数不是直观上的put到什么列表里,而是针对nlh的做些初始化工作 nlh = nlmsg_put(skb, 0, 0, 0, MAX_PAYLOAD, 0); memcpy(NLMSG_DATA(nlh), "AA", sizeof("AA")); <br>// 最后一个参数其实没什么用,因为内核会检测nl_sk是否为内核sock,是的话不会使用该参数 if(netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT) < 0) { printk(KERN_ERR"FAILED TO send SKB\n"); return; }
3. 释放skb
发送的skb不需要内核模块去释放,也不能释放,否则会崩溃,因为不能保证netlink_unicast返回不能保证用户层已经接受到消息。内核会处理skb的释放,所以不会出现内存泄露问题
I'm developing a kernel module which send messages to user space via netlink.
To create a message (message to send): skb_out = nlmsg_new(msg_size,0);
.
After sending the first message and before sending the second one, I tried to free the skb_out withnlmsg_free(skb_out)
but this function cause a kernel crash.
or
here after the source code:
skb_out = nlmsg_new(msg_size,0); if(!skb_out) { printk(KERN_ERR "Failed to allocate new skb\n"); return; } nlh=nlmsg_put(skb_out,0,0,NLMSG_DONE,msg_size,0); NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ strncpy(nlmsg_data(nlh),msg,msg_size); res=nlmsg_unicast(nl_sk,skb_out,pid); if(res<0) { printk(KERN_INFO "Error while sending bak to user\n"); } nlmsg_free(skb_out);
You're not allowed to free the skb after you've sent it. nlmsg_unicast()
will take care of that.
The reason is fairly simple: once you send the message it can be queued in the netlink socket for a while before anyone reads it. Just because nlmsg_unicast()
returned it doesn't mean that the other side of the socket already got the message. If you free it before it's received you end up with a freed message in the queue, which causes the crash when the kernel tries to deliver it.
Simply allocate a new skb for every message.
所以上述代码中 nlmsg_free(skb_out);这一行代码注释掉·