Netlink 是一种IPC(Inter Process Commumicate)机制,它是一种用于内核与用户空间通信的机制,在一般情况下,用户态和内核态通信会使用传统的Ioctl、sysfs属性文件或者procfs属性文件,这3种通信方式都是同步通信方式,由用户态主动发起向内核态的通信,内核无法主动发起通信。Netlink是一种异步全双工的通信方式,它支持由内核态主动发起通信,内核为Netlink通信提供了一组特殊的API接口,用户态则基于socket API,内核发送的数据会保存在接收进程socket 的接收缓存中,由接收进程处理。
netlink 优点:
netlink 内核子接口初始化实在 net/netlink/af_netlink.c中初始化完成
static const struct rhashtable_params netlink_rhashtable_params = {
.head_offset = offsetof(struct netlink_sock, node),
.key_len = netlink_compare_arg_len,
.obj_hashfn = netlink_hash,
.obj_cmpfn = netlink_compare,
.automatic_shrinking = true,
};
/*
这里的hash(哈希表)用来索引同种协议类型的不同netlink套接字实例,mc_list为多播使用的sock散列表,listeners为监听者掩码,
groups为协议支持的最大多播组数量,
同时还定义了一些函数指针,它们会在内核首次创建netlink时被赋值,后续应用层创建和绑定socket时调用到。
回到初始化函数中,接下来初始化应用层使用的NETLINK_USERSOCK协议类型的netlink(用于应用层进程间通信);
然后调用sock_register向内核注册协议处理函数,即将netlink的socket创建处理函数注册到内核中,
如此以后应用层创建netlink类型的socket时将会调用该协议处理函数,其中
*/
static int __init netlink_proto_init(void)
{
int i;
int err = proto_register(&netlink_proto, 0);
if (err != 0)
goto out;
BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > FIELD_SIZEOF(struct sk_buff, cb));
//之开辟了MAX_LINKS个na_talbe指针空间
nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);
if (!nl_table)
goto panic;
//分配MAX_LINKS个netlink表结构, nl_table, 每个成员代表一种协议类型
for (i = 0; i < MAX_LINKS; i++) {
if (rhashtable_init(&nl_table[i].hash,
&netlink_rhashtable_params) < 0) {
while (--i > 0)
rhashtable_destroy(&nl_table[i].hash);
kfree(nl_table);
goto panic;
}
}
INIT_LIST_HEAD(&netlink_tap_all);
netlink_add_usersock_entry();
sock_register(&netlink_family_ops);//将netlink socket 创建 函数注册到内核中,即创建netlink socket 时 回调函数为 netlink_family_ops->create 回调
register_pernet_subsys(&netlink_net_ops);
/* The netlink device handler may be needed early. */
rtnetlink_init();
out:
return err;
panic:
panic("netlink_init: Cannot allocate nl_table\n");
}
core_initcall(netlink_proto_init
本初始化函数首先向内核注册netlink协议;然后创建并初始化了nl_table表数组,这个表是整个netlink实现的最关键的一步,每种协议类型占数组中的一项,后续内核中创建的不同种协议类型的netlink都将保存在这个表中.
struct netlink_table {
struct rhashtable hash;// hash表控制块,内部的hash表记录了已经创建的同种协议类型的所有netlink套接字
struct hlist_head mc_list; // 这个hash表头节点用于记录同种协议类型下所有阅订了组播功能的套接字
struct listeners __rcu *listeners; // 记录了同种协议类型下所有被阅订了的组播消息集合 为监听者掩码
unsigned int flags;
unsigned int groups;// 记录了该协议类型支持的最大组播数量(通常就是32个)
struct mutex *cb_mutex;
struct module *module;
//函数指针会在内核首次创建netlink时被赋值,后续应用层创建和绑定socket时调用到
int (*bind)(struct net *net, int group);
void (*unbind)(struct net *net, int group);
bool (*compare)(struct net *net, struct sock *sock);
int registered;
};
/*
应用层创建PF_NETLINK(AF_NETLINK)类型的socket()系统调用时将由netlink_create()函数负责处理
*/
static const struct net_proto_family netlink_family_ops = {
.family = PF_NETLINK,
.create = netlink_create,
.owner = THIS_MODULE, /* for consistency 8) */
};
对于每个类型都会创建一个内核的netlink 套接字用于和应用层通信;以NETLINK_ROUTE为例
static int __net_init rtnetlink_net_init(struct net *net)
{
struct sock *sk;
struct netlink_kernel_cfg cfg = {
.groups = RTNLGRP_MAX,
.input = rtnetlink_rcv,
.cb_mutex = &rtnl_mutex,
.flags = NL_CFG_F_NONROOT_RECV,
};
sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
if (!sk)
return -ENOMEM;
net->rtnl = sk;
return 0;
}
/*
定义了一个netlink_kernel_cfg结构体实例,设置groups为RTNLGRP_MAX后指定消息接收处理函数为rtnetlink_rcv,
并设置flag为NL_CFG_F_NONROOT_RECV,这表明非超级用户可以绑定到多播组,但是没有设置NL_CFG_F_NONROOT_SEND,
这表明非超级用户将不能发送组播消息。
随后init函数调用netlink_kernel_create()向当前的网络命名空间创建NETLINK_ROUTE类型的套接字,
并指定定义的那个配置结构cfg。进入netlink_kernel_create()函数内部:
*/
/*
* We export these functions to other modules. They provide a
* complete set of kernel non-blocking support for message
* queueing.
*/
struct sock *
__netlink_kernel_create(struct net *net, int unit, struct module *module,
struct netlink_kernel_cfg *cfg)
{
struct socket *sock;
struct sock *sk;
struct netlink_sock *nlk;
struct listeners *listeners = NULL;
struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
unsigned int groups;
BUG_ON(!nl_table);
if (unit < 0 || unit >= MAX_LINKS)
return NULL;
if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
return NULL;
/*
首先进行简单的参数判断之后就调用sock_create_lite()函数创建了一个以PF_NETLINK为地址族的SOCK_DGRAM类型的socket套接字,
其协议类型就是作为参数传入的NETLINK_ROUTE。
然后该函数调用最核心的__netlink_create()函数向内核初始化netlink套接字
*/
if (__netlink_create(net, sock, cb_mutex, unit, 1) < 0)
goto out_sock_release_nosk;
sk = sock->sk;
if (!cfg || cfg->groups < 32)
groups = 32;
else
groups = cfg->groups;
listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
if (!listeners)
goto out_sock_release;
sk->sk_data_ready = netlink_data_ready;
if (cfg && cfg->input)
nlk_sk(sk)->netlink_rcv = cfg->input; //设置netlink socket 的input 函数 即 rcv //rtnetlink_rcv函数
if (netlink_insert(sk, 0))
goto out_sock_release;
/*
来校验groups,默认最小支持32个组播地址(用户层在绑定地址时最多绑定32个组播地址),但内核也有可能支持大于32个组播地址的情况(Genetlink就属于这种情况),
然后分配listeners内存空间,这里边保存了监听者(监听套接字)的信息;接下来继续初始化函数指针,这里将前文中定义的rtnetlink_rcv注册到了nlk_sk(sk)->netlink_rcv中,
这样就设置完了内核态的消息处理函数;然后调用netlink_insert()函数将本次创建的这个套接字添加到nl_table中去(其核心是调用__netlink_insert()),
注册的套接字是通过nl_table中的哈希表来管理的。
然后设置标识NETLINK_KERNEL_SOCKET表明这个netlink套接字是一个内核套接字
*/
nlk = nlk_sk(sk);
nlk->flags |= NETLINK_F_KERNEL_SOCKET;
netlink_table_grab();
if (!nl_table[unit].registered) {
nl_table[unit].groups = groups;
rcu_assign_pointer(nl_table[unit].listeners, listeners);
nl_table[unit].cb_mutex = cb_mutex;
nl_table[unit].module = module;
if (cfg) {
nl_table[unit].bind = cfg->bind;
nl_table[unit].unbind = cfg->unbind;
nl_table[unit].flags = cfg->flags;
if (cfg->compare)
nl_table[unit].compare = cfg->compare;
}
nl_table[unit].registered = 1;
} else {
kfree(listeners);
nl_table[unit].registered++;
}
netlink_table_ungrab();
return sk;
/*
nl_table[NETLINK_ROUTE].groups = RTNLGRP_MAX;
nl_table[NETLINK_ROUTE].cb_mutex = &rtnl_mutex;
nl_table[NETLINK_ROUTE].module = THIS_MODULE;
nl_table[NETLINK_ROUTE].bind = NULL;
nl_table[NETLINK_ROUTE].unbind = NULL;
nl_table[NETLINK_ROUTE].compare = NULL;
nl_table[NETLINK_ROUTE].flags= NL_CFG_F_NONROOT_RECV;
以 NETLINK_ROUTE 为参考的结果
*/
out_sock_release:
kfree(listeners);
netlink_kernel_release(sk);
return NULL;
out_sock_release_nosk:
sock_release(sock);
return NULL;
}
static int __netlink_create(struct net *net, struct socket *sock,
struct mutex *cb_mutex, int protocol,
int kern)
{
struct sock *sk;
struct netlink_sock *nlk;
//将sock的操作函数集指针设置为netlink_ops
sock->ops = &netlink_ops;// 设置socket 的bind 等回调函数
sk = sk_alloc(net, PF_NETLINK, GFP_KERNEL, &netlink_proto, kern);
if (!sk)
return -ENOMEM;
/*
分配sock结构并进行初始化,主要包括初始化发送接收消息队列、数据缓存、等待队列和互斥锁等等,
最后设置sk_destruct回调函数和协议类型。
*/
sock_init_data(sock, sk);
nlk = nlk_sk(sk);
if (cb_mutex) {
nlk->cb_mutex = cb_mutex;
} else {
nlk->cb_mutex = &nlk->cb_def_mutex;
mutex_init(nlk->cb_mutex);
}
init_waitqueue_head(&nlk->wait);
sk->sk_destruct = netlink_sock_destruct;
sk->sk_protocol = protocol;
return 0;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章