首先分享下网上关于netlink的几篇好的文章
http://blog.csdn.net/leonan/article/details/8712157?utm_source=tuicool&utm_medium=referral
http://blog.csdn.net/haomcu/article/details/7371835
自己总结的也只是对代码的一些理解,也并未深入原理。
1、关键函数简介
内核态
netlink_kernel_create
函数简介
在内核中使用 uevent事件通知用户空间。uevent首先在内核中调用 netlink_kernel_create()函数创建一个socket套接字,该函数原型在netlink.h有定义,其类型是表示往用户空间发送消息的
struct sock *netlink_kernel_create(structnet *net, int unit, unsigned intgroups,
void (*input)(struct sk_buff *skb),
struct mutex *cb_mutex, struct module *module)
struct net是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量
参数unit表示netlink协议类型,系统定义了16个,可以在net/netlink.h中找到。
参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被引用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct sock结构来表示。
函数input()会在发送进程执行sendmsg()时被调用,这样处理消息比较及时,但是,如果消息特别长时,这样处理将增加系统调用sendmsg()的执行时间,也就是说当用户的程序调用sendmsg ()函数时,如果input()函数处理时间过长,也就是说input()函数不执行不完,用户程序调用的sendmsg()函数就不会返回。只有当内核空间中的input()函数返回时,用户调用的sendmsg()函数才会返回。对于这种情况,可以定义一个内核线程专门负责消息接收,而函数input的工作只是唤醒该内核线程,这样sendmsg将很快返回。(这里网上的的说明)不过在查看Linux2.6.37版本的内核时并没有发现这种处理过程,一般都是按下面的方法进行处理。
nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX,
xfrm_netlink_rcv, NULL, THIS_MODULE);
static void xfrm_netlink_rcv(struct sk_buff *skb)
{
mutex_lock(&xfrm_cfg_mutex);
netlink_rcv_skb(skb, &xfrm_user_rcv_msg);
mutex_unlock(&xfrm_cfg_mutex);
}
在netlink_rcv_skb()函数中进行接收处理。
netlink_unicast
函数简介
int netlink_unicast(struct sock *ssk, struct sk_buff *skb,
u32 pid, int nonblock)
模块调用函数 netlink_unicast来发送单播消息。
参数ssk为函数 netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而 skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠。
发送之前数据准备:
1、nlmsg_new
函数简介
static inline struct sk_buff *nlmsg_new(size_tpayload,gfp_t flags)
payload是要发送数据的大小,flags设定为GFP_KERNEL
函数就不具体分析了,nlmsg_new会新申请一个socket buffer,其大小为socket消息头大小 + netlink消息头大小 +用户消息大小,传入参数payload即为用户消息大小。
2、NLMSG_NEW
该宏基本等同于nlmsg_put函数,填充netlink消息头的部分内容。
3、NLMSG_DATA
该宏获取存放数据的指针
代码示例:
pSkb = nlmsg_new(iByteSize,ETNO_WAIT);//GFP_KERNEL = 0
if (NULL ==pSkb){
STAT_INC(SEND_GET_SKBUFF_FAILED);
PrintfLog(LOG_ERROR, FID_ETRAINF_KERN,"NULL == pSkb\r\n");
returnETERROR;
}
pNlMsgHdr =NLMSG_NEW(pSkb, 0, 0, eType, iByteSize, 0);
pData =NLMSG_DATA(pNlMsgHdr);
memcpy(pData,pBuf, iByteSize);
netlink_broadcast
函数简介
int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32pid,
u32 group,gfp_t allocation)
前面的三个参数与 netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = 0;
NETLINK_CB(skb).dst_group = 1;
字段pid表示消息发送者进程 ID,也即源地址,对于内核,它为 0, dst_pid表示消息接收者进程 ID,也即目标地址,如果目标为组或内核,它设置为 0,否则 dst_group 表示目标组地址,如果它目标为某一进程或内核,dst_group应当设置为 0
用户态使用的函数就是标准的socked一些接口就不多说了