Linux下C语言使用 netlink sockets与内核模块通信

netlink简介

Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。在Linux标准内核中,系统默认集成了很多netlink实例,比如日志上报、路由系统等,netlink消息是双向的,应用层可以发送消息到内核,同时内核也可以发送消息到应用层进程,非常适合涉及到内核信息采集的模块。

与ioctl的区别

netlink采用sock方式实现,而ioctl采用驱动注册方式实现。netlink通常用于传输大量消息,而ioctl通常用于下发系统命令,不支持内核主动发送消息到应用层。
什么情况下用netlink?
在开发过程中,我们会面临消息通信机制的选型,这和应用层消息通信选型一样,每个通信方式都有自己的优缺点。采用netlink机制一般需要应用层单独起进程用于监听内核发送上来的消息,开发工作量相对于ioctl较大,而ioctl主要用于驱动消息下发,比如无线驱动的iwpriv命令实现,就采用ioctl机制。

内核模块hello_kernel.c

#include <linux/module.h>
#include <net/sock.h> 
#include <linux/netlink.h>
#include <linux/skbuff.h> 
#define NETLINK_USER 31struct sock *nl_sk = NULL;static void hello_nl_recv_msg(struct sk_buff *skb)
{struct nlmsghdr *nlh;int pid;struct sk_buff *skb_out;int msg_size;char *msg = "Hello from kernel";int res;printk(KERN_INFO "Entering: %s\n", __FUNCTION__);msg_size = strlen(msg);nlh = (struct nlmsghdr *)skb->data;printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));pid = nlh->nlmsg_pid; /*pid of sending process */skb_out = nlmsg_new(msg_size, 0);if (!skb_out) {printk(KERN_ERR "Failed to allocate new skb\n");return;}nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */strncpy(nlmsg_data(nlh), msg, msg_size);res = nlmsg_unicast(nl_sk, skb_out, pid);if (res < 0)printk(KERN_INFO "Error while sending bak to user\n");
}static int __init hello_init(void)
{printk("Entering: %s\n", __FUNCTION__);//nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);struct netlink_kernel_cfg cfg = {.input = hello_nl_recv_msg,};nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);if (!nl_sk) {printk(KERN_ALERT "Error creating socket.\n");return -10;}return 0;
}static void __exit hello_exit(void)
{printk(KERN_INFO "exiting hello module\n");netlink_kernel_release(nl_sk);
}module_init(hello_init); 
module_exit(hello_exit);
MODULE_LICENSE("GPL");

应用层代码hello.c

#include <linux/netlink.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>#define NETLINK_USER 31#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;int main()
{sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);if (sock_fd < 0)return -1;memset(&src_addr, 0, sizeof(src_addr));src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = getpid(); /* self pid */bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));memset(&dest_addr, 0, sizeof(dest_addr));dest_addr.nl_family = AF_NETLINK;dest_addr.nl_pid = 0; /* For Linux Kernel */dest_addr.nl_groups = 0; /* unicast */nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_pid = getpid();nlh->nlmsg_flags = 0;strcpy(NLMSG_DATA(nlh), "Hello");iov.iov_base = (void *)nlh;iov.iov_len = nlh->nlmsg_len;msg.msg_name = (void *)&dest_addr;msg.msg_namelen = sizeof(dest_addr);msg.msg_iov = &iov;msg.msg_iovlen = 1;printf("Sending message to kernel\n");sendmsg(sock_fd, &msg, 0);printf("Waiting for message from kernel\n");/* Read message from kernel */recvmsg(sock_fd, &msg, 0);printf("Received message payload: %s\n", NLMSG_DATA(nlh));close(sock_fd);return 0;
}

 Makefile

bin=hello
obj-m = hello_kernel.o
KVERSION = $(shell uname -r)
all:$(bin)make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
bin:gcc hello.c -o hello
clean:make -C /lib/modules/$(KVERSION)/build M=$(PWD) cleanrm -rf hello.o hello

Makefile问题记录一下

chat:KVERSION = $(shell uname -r)去掉shell有什么区别?

在Makefile中,`$(shell ...)`是一个用于执行命令并获取其输出的内置函数。在你提到的语句中,`$(shell uname -r)`用于执行`uname -r`命令来获取当前Linux内核的版本,并将结果赋值给`KVERSION`变量。如果你去掉`shell`,那么整个语句就会变成:
KVERSION = uname -r
这将不再是一个命令执行语句,而是一个简单的变量赋值语句。这意味着`KVERSION`的值将直接设置为字符串`"uname -r"`,而不是执行`uname -r`命令并获取其输出。
因此,区别在于:
- `KVERSION = $(shell uname -r)`:这会执行`uname -r`命令,获取内核版本,并将结果作为字符串赋值给`KVERSION`变量。
- `KVERSION = uname -r`:这只是将字符串`"uname -r"`赋值给`KVERSION`变量,不会执行命令或获取内核版本。
如果你想要将内核版本赋值给`KVERSION`变量,你应该使用`$(shell ...)`来执行命令。否则,你将只是将命令字符串本身赋值给变量,而不会获取命令的输出。

make  -C什么意思?

`make -C` 是 `make` 命令的一个选项,用于指定 `make` 命令应该在哪个目录下执行。
语法为:
make -C <目录>
其中 `<目录>` 是你希望 `make` 命令执行的目录的路径。
这个选项通常用于在项目的子目录中执行 `make` 命令,以便构建子项目或依赖于其他目录中的代码的项目。这对于在大型项目中管理多个模块或组件的构建过程非常有用,因为你可以在每个子目录中维护一个独立的 `Makefile` 文件,并使用 `-C` 选项来指定要构建的子目录。
例如,如果你有一个名为 `subproject` 的子目录,并在该目录中有一个名为 `Makefile` 的构建文件,你可以使用以下命令在 `subproject` 目录中执行 `make`:
make -C subproject
这将导致 `make` 命令在 `subproject` 目录中查找并执行 `Makefile` 文件,执行与该子项目相关的构建任务。

运行结果:

应用层

内核,先加载.ko,运行hello之后,用dmesg就可查看收到的消息

代码来源:

Linux下C语言如何使用 netlink sockets与内核模块通信?_c语言nlmsghdr的使用_wellnw的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/103248.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.8 PE结构:资源表详细解析

在Windows PE中&#xff0c;资源是指可执行文件中存放的一些固定不变的数据集合&#xff0c;例如图标、对话框、字符串、位图、版本信息等。PE文件中每个资源都会被分配对应的唯一资源ID&#xff0c;以便在运行时能够方便地查找和调用它们。PE文件中的资源都被组织成一个树形结…

Splunk Enterprise for Mac:卓越的数据分析与管理工具

在当今的数字化时代&#xff0c;数据已经成为企业成功的核心驱动力。然而&#xff0c;如何有效地管理和分析这些数据&#xff0c;却常常让企业感到困惑。Splunk Enterprise for Mac 是一款领先的数据分析和管理工具&#xff0c;可以帮助你解决这一难题。 Splunk Enterprise fo…

Win11打不开组策略编辑器怎么办

近期有小伙伴反映在使用Win11打开组策略编辑器的时候发现无法打开&#xff0c;这是怎么回事&#xff0c;遇到这种问题应该怎么解决呢&#xff0c;下面小编就给大家详细介绍一下Win11打不开组策略编辑器的解决方法&#xff0c;有需要的小伙伴快来和小编一起看看吧。 Win11打不开…

乐鑫 ESP-Mesh-Lite:轻松覆盖更大范围,连接更多设备

乐鑫科技 (688018.SH) 基于 Wi-Fi 协议推出了 Mesh 组网方案 ESP-Mesh-Lite&#xff0c;支持更多设备在更大范围内轻松联网。这一创新性的 Wi-Fi Mesh 技术通过构建灵活、可靠的物联网组网方案&#xff0c;使用户可以享受到快速、稳定且安全的 Wi-Fi 覆盖&#xff0c;不再受到设…

提升你的Android开发技能:从AR/VR沉浸到UI设计和故障排除

文章目录 探索最新AR/VR应用在教育、游戏、医疗等领域的应用教育领域游戏领域医疗领域 深入了解Android内存管理与性能优化的方法与技巧垃圾回收机制内存泄漏使用弱引用避免过度渲染内存优化图像优化延迟加载Android中的调试技术应用程序分析 分享如何提高Android应用的易用性和…

npm publish包报404,is not in the npm registry错误

1. 指定发布目标2. 登录npm&#xff0c;使用登录名发布包&#xff0c;包名命名原则“登录名/包名”&#xff0c;或 “包名” 3. 删除某一个版本npm unpublish pvfhv/eslint-config-prettier1.0.1 --force 删除后的版本不能重复使用&#xff0c;正式解释&#xff1a; Unfortun…

BMS电池管理系统——BMS的功能模块及基本要素(二)

BMS电池管理系统 文章目录 BMS电池管理系统前言一、BMS电池管理系统各个功能模块的关系二、BMS的边界及基本要素 前言 前面了解了BMS以及他的功能模块&#xff0c;这些功能模块之间的关系是什么呢&#xff1f; 一、BMS电池管理系统各个功能模块的关系 下面我们分析一下这张图…

java文件命令行报错: 找不到或无法加载主类XXX报错及解决

前言 之前遇到过几次&#xff0c;后面稀里糊涂的解决了。今天详细记录一下&#xff0c;可能不全或有些错误&#xff0c;还请各位指正。 你要启动一个类的话首先要有类。 在这里&#xff0c;类有两种&#xff0c; 一个是带包名&#xff08;package&#xff09;的还有一个是没包…

redis高可用——主从复制、哨兵模式、cluster集群

1、redis群集有三种模式 分别是主从同步/复制、哨兵模式、Cluster&#xff0c;下面会讲解一下三种模式的工作方式&#xff0c;以及如何搭建cIustr群集 主从复制:主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的…

VSCode学习笔记一:添加代码模板

一目了然 1 简述2 设置模板3 Global Snippets file示例 1 简述 问&#xff1a;为什么要设置代码模板&#xff1f; 答&#xff1a;编程语言是有个性的&#xff0c;不同语言的演讲风格是不一样的。 旁白&#xff1a;我不懂&#xff1f;&#xff01; 问&#xff1a;为什么要设置…

idea2021.1.3版本双击启动,没反应

今天打开电脑&#xff0c;点开idea&#xff0c;界面悬在这里&#xff0c;几秒然后就是没了。然后就一直打不开idea了。 然后又是卸载重装&#xff0c;又是删除缓存文件。我把电脑关于idea的文件全都删除了 。重新安装后&#xff08;首次运行倒是可以打开&#xff0c;但是关掉id…

搭建自己的OCR服务,第三步:PPOCRLabel标注工具安装

一、安装说明 安装好了PaddleOCR后&#xff0c;还需要安装PPOCRLabel这个标注工具&#xff0c;想要自己训练模型的话&#xff0c;有个标注工具会起很大作用。 尤其是PPOCRLabel就是跟PaddleOCR配套的标注工具&#xff0c;同样是开源的。 在下载 PaddleOCR 整个源码中&#x…