参考资料:

https://blog.csdn.net/zqixiao_09/article/details/77131283

https://www.cnblogs.com/lopnor/p/6158800.html

Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink 进行应用与内核通信的应用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系统(NETLINK_W1),用户态 socket 协议(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket 监视(NETLINK_INET_DIAG),netfilter 日志(NETLINK_NFLOG),ipsec 安全策略(NETLINK_XFRM),SELinux 事件通知(NETLINK_SELINUX),iSCSI 子系统(NETLINK_ISCSI),进程审计(NETLINK_AUDIT),转发信息表查询(NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter 子系统(NETLINK_NETFILTER),IPv6 防火墙(NETLINK_IP6_FW),DECnet 路由信息(NETLINK_DNRTMSG),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)。

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink。

Netlink 相对于系统调用,ioctl 以及 /proc 文件系统而言具有以下优点:

1,为了使用 netlink,用户仅需要在 include/linux/netlink.h 中增加一个新类型的 netlink 协议定义即可, 如 #define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件, 那需要不少代码,proc 文件系统则需要在 /proc 下添加新的文件或目录,那将使本来就混乱的 /proc 更加混乱。

问题:

增加了netlink协议后,需要重新编译内核吗?还是仅仅修改了头文件netlink.h就可以了呢?在后期的实验中,需要对这一问题进行验证。

2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,但系统调用与 ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。

异步通信与同步通信的区别:看接受者的响应方式。异步通讯是不需要接受者立即响应的

3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。

可以像编写驱动模块一样的实现方式来实现netlink部分。

4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件,在后面的文章中将介绍这一机制的使用。

5.内核可以使用 netlink 首先发起会话,但系统调用和 ioctl 只能由用户应用发起调用。

内核主动向用户应用发起数据

6.netlink 使用标准的 socket API,因此很容易使用,但系统调用和 ioctl则需要专门的培训才能使用。

NETLINK_GENERIC是一个通用的协议类型,它是专门为用户使用的,因此,用户可以直接使用它,而不必再添加新的协议类型。

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/netlink.h> #define NETLINK_USER 22
#define USER_MSG (NETLINK_USER + 1)
#define USER_PORT 50  /*此处是写死了,可以用进程ID,表示来自用户态的唯一进程*/ MODULE_LICENSE("GPL");
MODULE_AUTHOR("arvik");
MODULE_DESCRIPTION("netlink_demo"); static void test_netlink_rcv(struct sk_buff *skb); struct netlink_kernel_cfg cfg = {
.input = test_netlink_rcv,
/*...*/
};
static struct sock *test_netlink_sock = NULL; int send_msg(int8_t *pbuf, uint16_t len)
{
struct sk_buff *nl_skb;
struct nlmsghdr *nlh; int ret; nl_skb = nlmsg_new(len, GFP_ATOMIC);
if (!nl_skb) {
printk("netlink_alloc_skb error\n");
return -;
}
/*将header填充到skb中*/
nlh = nlmsg_put(nl_skb, , , USER_MSG, len, );
if (nlh == NULL) {
printk("put error\n");
nlmsg_free(nl_skb);
return -;
}
/*拷贝data*/
memcpy(nlmsg_data(nlh), pbuf, len); /*发送*/
ret = netlink_unicast(test_netlink_sock, nl_skb, USER_PORT, MSG_DONTWAIT); return ret;
} static void test_netlink_rcv(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL; void *data = NULL; printk("skb->len %u\n", skb->len);
if (skb->len >= nlmsg_total_size())
{
nlh = nlmsg_hdr(skb);
data = NLMSG_DATA(nlh);
if (data) {
printk("kernel receive date : %s\n", (int8_t *)data);
send_msg(data, nlmsg_len(nlh));
}
}
} static int __init test_netlink_init(void)
{
printk("test netlink init\n"); test_netlink_sock = netlink_kernel_create(&init_net, USER_MSG, &cfg);
if (test_netlink_sock == NULL) {
printk("test netlink init error\n");
return -;
} printk("test netlink init ok\n"); return ;
}
static void __exit test_netlink_exit(void)
{
netlink_kernel_release(test_netlink_sock);
test_netlink_sock = NULL; printk("test netlink exit\n");
} module_init(test_netlink_init);
module_exit(test_netlink_exit);
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h> #define NETLINK_USER 22
#define USER_MSG (NETLINK_USER + 1) #define MSG_LEN 100 #define MAX_PLOAD 100 struct _my_msg {
struct nlmsghdr hdr;
int8_t data[MSG_LEN];
}; int main()
{
char *data = "hello kernel";
socklen_t addr_len;
struct sockaddr_nl local, dest_addr; int skfd;
struct nlmsghdr *nlh = NULL;
struct _my_msg info;
int ret; //创建socket
skfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG);
if (skfd == (-)) {
fprintf(stderr, "create socket error...%s\n", strerror(errno));
return (-);
} /*绑定*/
memset(&local, , sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_pid = ;  /*此处的pid通常用进程ID,表示来自用户态的唯一进程*/
local.nl_groups = ;
if (bind(skfd, (struct sockaddr *)&local, sizeof(local)) != ) {
fprintf(stderr, "bind error\n");
close(skfd);
return (-);
} //初始化目的地址
memset(&dest_addr, , sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = ;
dest_addr.nl_groups = ;
/*填写data*/
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));
memset(nlh, , sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
nlh->nlmsg_flags = ;
nlh->nlmsg_type = ;
nlh->nlmsg_seq = ;
nlh->nlmsg_pid = local.nl_pid; memcpy(NLMSG_DATA(nlh), data, strlen(data));
ret = sendto(skfd, nlh, nlh->nlmsg_len, , (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));
if (ret < ) {
// fprintf(stderr, "sendto error\n");
perror("sendto error: ");
close(skfd);
return (-);
} printf("wait kernel msg!\n");
memset(&info, , sizeof(info)); /*接受信息*/
ret = recvfrom(skfd, &info, sizeof(struct _my_msg), , (struct sockaddr *)&dest_addr, &addr_len);
if (ret <= ) {
// fprintf(stderr, "recv from kernel error\n");
perror("recv from kernel error: ");
close(skfd);
return (-);
} printf("msg receive from kernel : %s\n", info.data);
close(skfd); free((void *)nlh); close(skfd); return ;
}

Linux 内核态与用户态通信 netlink的更多相关文章

  1. 在linux系统中实现各项监控的关键技术(2)--内核态与用户态进程之间的通信netlink

    Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 ...

  2. linux内核态和用户态的信号量

    在Linux的内核态和用户态都有信号量,使用也不同,简单记录一下. 1> 内核信号量,由内核控制路径使用.内核信号量是struct semaphore类型的对象,它在中定义struct sema ...

  3. Linux内核态、用户态简介与IntelCPU特权级别--Ring0-3

    一.现代操作系统的权限分离: 现代操作系统一般都至少分为内核态和用户态.一般应用程序通常运行于用户态,而当应用程序调用系统调用时候会执行内核代码,此时会处于内核态.一般的,应用程序是不能随便进入内核态 ...

  4. Linux操作系统学习_用户态与内核态之切换过程

    因为操作系统的很多操作会消耗系统的物理资源,例如创建一个新进程时,要做很多底层的细致工作,如分配物理内存,从父进程拷贝相关信息,拷贝设置页目录.页表等,这些操作显然不能随便让任何程序都可以做,于是就产 ...

  5. Linux内核态和用户态

    两张图说明Linux内核态和用户态之间的关系

  6. 【转】linux内核态和用户态的区别

    原文网址:http://www.mike.org.cn/articles/linux-kernel-mode-and-user-mode-distinction/ 内核态与用户态是操作系统的两种运行级 ...

  7. linux内核态和用户态小结

    一 内核态和用户态的区别 当进程执行系统调用而陷入内核代码中执行时,我们就称进程处于内核状态.此时处理器处于特权级最高的(0级)内核代码.当进程处于内核态时,执行的内核代码会使用当前的内核栈.每个进程 ...

  8. go语言学习--内核态和用户态(协程)

    go中的一个特点就是引入了相比于线程更加轻量级的协程(用户态的线程),那么什么是用户态和内核态呢? 一.什么是用户态和内核态 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核 ...

  9. [OS] 内核态和用户态的区别

    http://blog.csdn.net/fatsandwich/article/details/2131707# http://jakielong.iteye.com/blog/771663 当一个 ...

随机推荐

  1. K-means算法的实现

    K-MEANS算法是一种经典的聚类算法,在模式识别得到了广泛的应用.算法中有两个关键问题需要考虑:一是如何评价对象的相似性,通常用距离来度量,距离越近越相似:另外一个是如何评价聚类的效果,通常采用误差 ...

  2. css-background-image 背景图片太大或太小

    .zoomImage {                background-image:url(images/yuantiao.jpg);                background-rep ...

  3. JavaScript Drag处理

    [JavaScript Drag处理] 在拖动目标上触发事件 (源元素): ondragstart - 用户开始拖动元素时触发 ondrag - 元素正在拖动时触发 ondragend - 用户完成元 ...

  4. mysql 连表查询

    现有tablea:                                                                              tableb:       ...

  5. 跨域导致无法获取cookie

    首先我用的框架是vue,请求协议用的是ajax,跨域的处理办法是使用了反向代理,在我之前的博文有详细说明,有兴趣的可以去查看下,在做身份认证权限限制的时候,后台有在http-header的respon ...

  6. openstack(Pike 版)集群部署(六)--- Horizon 部署

    一.介绍 参照官网部署:https://docs.openstack.org/horizon/pike/install/    继续上一博客进行部署:http://www.cnblogs.com/we ...

  7. Numpy 数组操作

    Numpy 数组操作 Numpy 中包含了一些函数用于处理数组,大概可分为以下几类: 修改数组形状 翻转数组 修改数组维度 连接数组 分割数组 数组元素的添加与删除 修改数组形状 函数 描述 resh ...

  8. python3与python2的区别(目前遇到的)

    1.进击的print,变成一个函数,print() 2.urllib大一统,呵呵 3.python3默认绝对路径导入

  9. windows phpstudy如何扩展MongoDB

    phpstudy如何扩展MongoDB 作者: default|标签:phpstudy MongoDB PHP|2017-9-9 10:17 phpstudy扩展MongoDB 前置工作安装PHPst ...

  10. IOS是否存在32位和64位版本的区分

    苹果于2013年9月推出了iPhone 5S新手机,采用的全新A7处理器其最大特色就是支持64位运算.其64位A7处理器的使用意味着iPhone性能会大有提高,性能和速度更加出色:而要到达到这样的性能 ...