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通信机制学习的更多相关文章

  1. linux netlink通信机制

    一.什么是Netlink通信机制  Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...

  2. Netlink通信机制【转】

    本文转载自:http://www.cnblogs.com/wenqiang/p/6306727.html 一.什么是Netlink通信机制  Netlink套接字是用以实现用户进程与内核进程通信的一种 ...

  3. linux netlink通信机制简介

    一.什么是Netlink通信机制  Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口. Netlink 是一种特殊的 s ...

  4. 1、netlink 连接器 通信机制

    使用netlink之前,先参考一下资料:http://www.ibm.com/developerworks/cn/linux/l-connector/ netlink通信机制介绍:资料来源 linux ...

  5. 【单页应用之通信机制】view之间应该如何通信

    前言 在单页应用中,view与view之间的通信机制一直是一个重点,因为单页应用的所有操作以及状态管理全部发生在一个页面上 没有很好的组织的话很容易就乱了,就算表面上看起来没有问题,事实上会有各种隐忧 ...

  6. 【转】跟我学Kafka之NIO通信机制

    from:云栖社区 玛德,今天又被人打脸了,小看人,艹,确实,相对比起来,在某些方面差一点,,,,该好好捋捋了,强化下短板,规划下日程,,,引以为耻,铭记于心. 跟我学Kafka之NIO通信机制   ...

  7. netlink---Linux下基于socket的内核和上层通信机制 (转)

    需要在linux网卡 驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文COPY下必要的数据交给用户 态来完成(因为过于复杂的报文消耗 ...

  8. 其实没那么复杂!探究react-native通信机制

    近段时间来Android上最火的框架非react native莫属了,这里我不去评价这个框架的好坏,毕竟只有用过的人才会有深刻的体会.但是我个人有一个习惯,在使用一个开源库之前,一定要看过它的源码,不 ...

  9. React Native通信机制详解

    React Native是facebook刚开源的框架,可以用javascript直接开发原生APP,先不说这个框架后续是否能得到大众认可,单从源码来说,这个框架源码里有非常多的设计思想和实现方式值得 ...

随机推荐

  1. springboot 整合 elasticsearch

    1引入jar包 <!--elasticsearch--> <dependency> <groupId>org.springframework.boot</gr ...

  2. 面试总结之C/C++

    source code https://github.com/haotang923/interview/blob/master/interview%20summary%20of%20C%20and%2 ...

  3. win7右下角的网络连接图标不见了~终极必杀技

    1.打开程序管理器(ctrl+alt+delete) 2.在进程那里找到"explorer.exe",然后按结束进程(此时工具栏会消失) 3.然后在文件(程序管理器左上角),点击& ...

  4. c语言个人财务管理系统

    这个是我的一个网上朋友写的,仅供大家参考: 在这里留个记录 #include<stdio.h>#include<string.h>#define null 0#define m ...

  5. strongswan

    StrongSwan is an open source IPsec-based VPN Solution. It supports both the IKEv1 and IKEv2 key exch ...

  6. Django框架之模板语法【转载】

    Django框架之模板语法 一.什么是模板? 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法分类 一.模板语法之变量:语法为 {{ }}: 在 Django ...

  7. 通过PicturreId获取图片路径(Url)

    1.直接使用接口服务 _pictureService.GetPictureUrl((int)entity.SponsorPictureId); //entity是具体查询出来的实体对象 Sponsor ...

  8. 4 MySQL--表(增删改查)

    1.表的介绍 表相当于文件,表中的一条记录就相当于文件的一行内容,不同的是,表中的一条记录有对应的标题,称为表的字段 id,name,qq,age称为字段,其余的,一行内容称为一条记录 2.创建表: ...

  9. 代替Reflection(反射)的一些方法(转)

    作者:JustRun 林肯: http://www.cnblogs.com/JustRun1983/p/3830764.html 代替Reflection(反射)的一些方法(转) 2014-07-08 ...

  10. LeetCode之二叉树作题java

    100. Same Tree Total Accepted: 127501 Total Submissions: 294584 Difficulty: Easy Given two binary tr ...