
Linux中与内核通信的Netlink机制.doc
20页Linux 中与内核通信的 Netlink 机制Netlink 在 2.6 版本的内核中变化也是很大的, 在最新的 2.6.37 内核中,其定义已经改成下面这种形式, 传递的参数已经达到 6 个其中第一个参数和 mutex 参数都是最新添加的 Mutex也可以为空这里主要是关于内核空间中的 netlink 函数的使用extern 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);struct net 是一个网络名字空间 namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套 net_device 等等默认情况下都是使用 init_net 这个全局变量,下面是内核中调用 netlink_kernel_create() 函数的一个示例在内核中,audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0,audit_receive, NULL, THIS_MODULE);模块调用函数 netlink_unicast 来发送单播消息:int netlink_unicast(struct sock *ssk,structsk_buff *skb, u32 pid, int nonblock)参数 ssk 为函数 netlink_kernel_create() 返回的 socket,参数 skb 存放消息,它的 data 字段指向要发送的netlink消息结构,而skb 的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块, 参数 pid 为接收消息进程的 pid,参数 nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为 0,该函数在没有接收缓存可利用定时睡眠。
netlink 的内核实现在 .c 文件 net/core/af_netlink.c 中,内核模块要想使用 netlink ,也必须包含头文件 linux/netlink.h 内核使用netlink 需要专门的 API ,这完全不同于用户态应用对netlink 的使用 如果用户需要增加新的netlink 协议类型, 必须通过修改 linux/netlink.h来实现,当然,目前的 netlink 实现已经包含了一个通用的协议类型NETLINK_GENERIC 以方便用户使用, 用户可以直接使用它而不必增加新的协议类型前面讲到, 为了增加新的 netlink协议类型,用户仅需增加如下定义到linux/netlink.h就可以:只要增加这个定义之后,用户就可以在内核的任何地方引用该协议在内核中,为了创建一个netlink socket 用户需要调用如下函数:extern 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);struct net 是一个网络名字空间 namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套 net_device 等等。
默认情况下都是使用init_net这个全局变量参数 unit 表示 netlink 协议类型,如 NETLINK_MYTEST,参数 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 版本的内核时并没有发现这种处理过程,一般都是按下面的方法进行处理这里注册的 netlink 协议为 NETLINK_XFRM 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() 函数中进行接收处理int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, 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 ,也即源地址,对于内核,它为接收者进程 ID ,也即目标地址,如果目标为组或内核,它设置为目标组地址,如果它目标为某一进程或内核, dst_group 应当设置为0, dst_pid 表示消息0,否则 dst_group 表示0下面是参考网上使用 netlink 写的和内核通信的两个程序,一个是用户空间,一个是内核空间内核空间:#include
