NetLink通信机制学习
Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink 套接字实现的。当 netlink 套接字用于内核空间与用户空间的通信时,在用户空间的创建方法和一般套接字使用类似,但内核空间的创建方法则不同,下图是 netlink 套接字实现此类通信时创建的过程:

用户空间:
1. 创建套接字
skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2);
skfd为static int类型,AF_NETLINK是netlink对应的协议簇,第二个参数必须是SOCK_RAW或SOCK_DGRAM, 第三个参数指定netlink协议类型,它可以是一个自定义的类型,也可以使用内核预定义的类型
2. 绑定套接字
bind(skfd, (struct sockaddr*)&local, sizeof(local));
♦ local为netlink的socket地址,其结构描述为:
struct sockaddr_nl
{
sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
/*成员 nl_family为协议簇 AF_NETLINK,成员 nl_pad 当前没有使用,因此要总是设置为 0,成员 nl_pid 为接收或发送消息的进程的 ID*/
♦ 定义socket地址
struct sockaddr_nl local;
memset(&local, , sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = getpid(); /*设置pid为自己的pid值*/
local.nl_groups = ;
3. 用户空间调用send函数(如sendto、sendmsg等)向内核发送数据,使用同样的socket地址来描述内核,不过需要注意,由于对端是内核,nl_pid必须设置为0。
♦ 内核的socket地址
struct sockaddr_nl kpeer;
memset(&kpeer, , sizeof(kpeer));
kpeer.nl_family = AF_NETLINK;
kpeer.nl_pid = ;
kpeer.nl_groups = ;
♦ 用户进程想内核发送的数据包格式为:“netlink消息头 + 数据”,消息头描述为:
struct nlmsghdr
{
__u32 nlmsg_len; /* Length of message */
__u16 nlmsg_type; /* Message type*/
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process PID */
};
♦ 自定义消息首部,它仅包含了netlink的消息首部
struct msg_to_kernel
{
struct nlmsghdr hdr;
};
♦ 填充首部信息
struct msg_to_kernel message;
memset(&message, , sizeof(message));
message.hdr.nlmsg_len = NLMSG_LENGTH(); /*没有数据,所以长度为0.*/
message.hdr.nlmsg_flags = ;
message.hdr.nlmsg_type = IMP2_U_PID;
message.hdr.nlmsg_pid = local.nl_pid;
♦ 向内核发送消息
sendto(skfd, &message, message.hdr.nlmsg_len, ,(struct sockaddr*)&kpeer, sizeof(kpeer));
4. 当发送完请求后,就可以调用recv函数簇从内核接收数据了,接收的数据包含了netlink消息首部和自定义数据结构。
♦ 自定义的数据结构
struct u_packet_info
{
struct nlmsghdr hdr; /*netlink消息头*/
struct packet_info icmp_info;
};
struct u_packet_info info;
♦ 接受和处理从内核接受到的信息
while()
{
kpeerlen = sizeof(struct sockaddr_nl);
/*接收内核空间返回的数据*/
rcvlen = recvfrom(skfd, &info, sizeof(struct u_packet_info),, (struct sockaddr*)&kpeer, &kpeerlen);
/*处理接收到的数据*/
……
}
5. 函数close用于关闭打开的netlink socket。程序中,因为程序一直循环接收处理内核的消息,需要收到用户的关闭信号才会退出
♦ 关闭套接字的工作放在了自定义的信号函数sig_int中处理
static void sig_int(int signo)
{
struct sockaddr_nl kpeer; /*内核的socket地址*/
struct msg_to_kernel message; /*自定义netlink消息首部*/
memset(&kpeer, , sizeof(kpeer));
kpeer.nl_family = AF_NETLINK;
kpeer.nl_pid = ;
kpeer.nl_groups = ;
memset(&message, , sizeof(message));
message.hdr.nlmsg_len = NLMSG_LENGTH();
message.hdr.nlmsg_flags = ;
message.hdr.nlmsg_type = IMP2_CLOSE;
message.hdr.nlmsg_pid = getpid();
/*向内核发送一个消息,由nlmsg_type表明,应用程序将关闭*/
sendto(skfd, &message, message.hdr.nlmsg_len, ,(struct sockaddr *)(&kpeer),sizeof(kpeer));
close(skfd);
exit();
}
内核空间:
1. 创建netlink套接字(netlink_kernel_create函数)
♦ 通过netlink_kernel_create创建一个netlink套接字,同时,注册一个回调函数,用于接收处理用户空间的消息。
struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));
2. 接收处理用户空间发送的数据(kernel_receive 函数)
♦ 用户空间向内核发送了两种自定义消息类型:IMP2_U_PID和IMP2_CLOSE,分别是请求和关闭。kernel_receive 函数分别处理这两种消息:
DECLARE_MUTEX(receive_sem); /*初始化信号量*/
static void kernel_receive(struct sock *sk, int len)
{
do
{
struct sk_buff *skb;
if(down_trylock(&receive_sem)) /*获取信号量*/
return;
/*从sk接收队列中取得skb,然后进行一些基本的长度的合法性校验*/
while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
{
{
struct nlmsghdr *nlh = NULL;
if(skb->len >= sizeof(struct nlmsghdr))
{
/*获取数据中的nlmsghdr 结构的报头*/
nlh = (struct nlmsghdr *)skb->data;
if((nlh->nlmsg_len >= sizeof(struct nlmsghdr))&& (skb->len >= nlh->nlmsg_len))
{
/*长度的全法性校验完成后,处理应用程序自定义消息类型,主要是对用户PID的保存,即为内核保存“把消息发送给谁”*/
if(nlh->nlmsg_type == IMP2_U_PID)/*请求*/
{
write_lock_bh(&user_proc.pid);
user_proc.pid = nlh->nlmsg_pid;
write_unlock_bh(&user_proc.pid);
}
else if(nlh->nlmsg_type == IMP2_CLOSE)/*应用程序关闭*/
{
write_lock_bh(&user_proc.pid);
if(nlh->nlmsg_pid == user_proc.pid)
user_proc.pid = ;
write_unlock_bh(&user_proc.pid);
}
}
}
}
kfree_skb(skb);
}
up(&receive_sem); /*返回信号量*/
}while(nlfd && nlfd->receive_queue.qlen);
}
3. 发送数据至用户空间(send_to_user函数)
♦ send_to_user 用于将数据发送给用户空间进程,发送调用的是API函数netlink_unicast完成的:
int netlink_unicast(struct sock *sk , struct sk_buff *skb , u32 pid , int nonblock);
♦ 向用户空间进程发送的消息包含三个部份:netlink 消息头部、数据部份和控制字段。函数初始化netlink 消息首部,填充数据区,然后设置控制字段,这三部份都包含在skb_buff中,最后调用netlink_unicast函数把数据发送出去。
static int send_to_user(struct packet_info *info)
{
int ret;
int size;
unsigned char *old_tail;
struct sk_buff *skb;
struct nlmsghdr *nlh;
struct packet_info *packet;
/*计算消息总长:消息首部加上数据加度*/
size = NLMSG_SPACE(sizeof(*info));
/*分配一个新的套接字缓存*/
skb = alloc_skb(size, GFP_ATOMIC);
old_tail = skb->tail;
/*初始化一个netlink消息首部,NLMSG_PUT(skb, pid, seq, type, len)*/
nlh = NLMSG_PUT(skb, , , IMP2_K_MSG, size-sizeof(*nlh));
/*跳过消息首部,指向数据区*/
packet = NLMSG_DATA(nlh);
/*初始化数据区*/
memset(packet, , sizeof(struct packet_info));
/*填充待发送的数据*/
packet->src = info->src;
packet->dest = info->dest;
/*计算skb两次长度之差,即netlink的长度总和*/
nlh->nlmsg_len = skb->tail - old_tail;
/*设置控制字段*/
NETLINK_CB(skb).dst_groups = ; /*如果它目标为某一进程或内核,dst_group 应当设置为 0。*/
/*发送数据*/
read_lock_bh(&user_proc.lock);
ret = netlink_unicast(nlfd, skb, user_proc.pid, MSG_DONTWAIT);
read_unlock_bh(&user_proc.lock);
}
NetLink通信机制学习的更多相关文章
- linux netlink通信机制
一.什么是Netlink通信机制 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...
- Netlink通信机制【转】
本文转载自:http://www.cnblogs.com/wenqiang/p/6306727.html 一.什么是Netlink通信机制 Netlink套接字是用以实现用户进程与内核进程通信的一种 ...
- linux netlink通信机制简介
一.什么是Netlink通信机制 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...
- 1、netlink 连接器 通信机制
使用netlink之前,先参考一下资料:http://www.ibm.com/developerworks/cn/linux/l-connector/ netlink通信机制介绍:资料来源 linux ...
- 【单页应用之通信机制】view之间应该如何通信
前言 在单页应用中,view与view之间的通信机制一直是一个重点,因为单页应用的所有操作以及状态管理全部发生在一个页面上 没有很好的组织的话很容易就乱了,就算表面上看起来没有问题,事实上会有各种隐忧 ...
- 【转】跟我学Kafka之NIO通信机制
from:云栖社区 玛德,今天又被人打脸了,小看人,艹,确实,相对比起来,在某些方面差一点,,,,该好好捋捋了,强化下短板,规划下日程,,,引以为耻,铭记于心. 跟我学Kafka之NIO通信机制 ...
- netlink---Linux下基于socket的内核和上层通信机制 (转)
需要在linux网卡 驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文COPY下必要的数据交给用户 态来完成(因为过于复杂的报文消耗 ...
- 其实没那么复杂!探究react-native通信机制
近段时间来Android上最火的框架非react native莫属了,这里我不去评价这个框架的好坏,毕竟只有用过的人才会有深刻的体会.但是我个人有一个习惯,在使用一个开源库之前,一定要看过它的源码,不 ...
- React Native通信机制详解
React Native是facebook刚开源的框架,可以用javascript直接开发原生APP,先不说这个框架后续是否能得到大众认可,单从源码来说,这个框架源码里有非常多的设计思想和实现方式值得 ...
随机推荐
- 建立dblink(database link)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/bisal/article/details/26730993 database linke是建立一个数 ...
- 黄聪:360、chrome开发插件扩展如何跨域调用其他网站的信息并且显示在扩展、tab中的api
chrome插件提供了查找tab的api chrome.tabs.get(integer tabId, function callback) 但是出于安全的考虑,tab的属性中没有document 因 ...
- ThreadCachedInt
folly/ThreadCachedInt.h High-performance atomic increment using thread caching. folly/ThreadCachedIn ...
- django rest_framework 框架的使用02
rest_framework 访问频率的限制(节流) 对于用户访问频率的显示,rest_framework 也有自己的类进行约束 先来一个自己的基于它的类的节流类的限制 class VisitCont ...
- Unix socket的准备(一)
套接字地址结构 套接字编程中,五元组是广为人知的. (host_ip, host_port, target_ip, target_port, protocol). 其中 ip 和 port 就是由套接 ...
- 机器视觉和Tesseract
机器视觉 从 Google 的无人驾驶汽车到可以识别假钞的自动售卖机,机器视觉一直都是一个应用广 泛且具有深远的影响和雄伟的愿景的领域. 我们将重点介绍机器视觉的一个分支:文字识别,介绍如何用一些 P ...
- OpenMP 简单的规约
▶ 简单的计算和规约 ● 第一种方法,将全局和的指针传入工作函数中进行加和,使用 critical 来控制临界区的访问 #include <stdio.h> #include <st ...
- signal模块简介
signal模块简介 最近在看Linux signal 相关内容,signal可以被用来进程间通信和异步处理.Python标准库提供了signal包可以用来处理信号相关.这里讨论的是Unix系统中Py ...
- padding margin border 和元素大小
元素占用宽度 = 元素宽度+padding+border+margin 注意margin只是隔开元素,不会使得元素变大,而padding会使得元素变大,也就是说 元素真实宽度=元素宽度+padding ...
- CentOS 多版本python安装pip
首先配置环境 安装 zlib,zlib-devel,openssl,openssl-devel yum install zlib yum install zlib-devel yum install ...