内核Generic Netlink通信

本文介绍了内核Generic Netlink通信,它提供了一种在内核与用户应用间进行双向数据传输的方法,相比系统调用等有多种优势。文章通过一个简单的demo展示了如何在用户态和内核态实现netlink通信,包括命令参数定义、连接建立、消息发送与接收等,并提供了内核态代码的Makefile。
摘要由CSDN通过智能技术生成

内核Generic Netlink通信编程

因为之前对内核的代码接触的不是很多,这篇只是记录自己学习内核通信的内容,肯定存在诸多缺陷,请大佬们批评指正。
github地址:https://github.com/yisenFangW/Netlink

netlink通信介绍

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,
内核态需要使用专门的内核 API 来使用 netlink。
Netlink 相对于系统调用,ioctl 以及 /proc文件系统而言具有以下优点:
1,netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,(如 #define NETLINK_TEST 20 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换);
2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖;
4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
5.内核可以使用 netlink 首先发起会话;
这一段废话抄自:https://www.cnblogs.com/wenqiang/p/6306727.html

netlink通信demo

demo功能

实现功能的demo已上传到github:https://github.com/yisenFangW/Netlink
demo实现的功能即从用户态向内核态发送消息,然后内核获取消息之后直接打印(可以实现其他功能),用法为在用户态传入参数:

usage:./user  add|del name age weight

通过dmesg看内核打印到的接受内容;
具体实现的demo很简单,内核态未做任何处理,直接打印,结果如图所示:
用户态演示:
在这里插入图片描述
内核态演示:
在这里插入图片描述

用户态实现

用户端实现是通过libnl3的库实现的,我理解的libnl库是对netlink进行上一层封装,让接口调用更加简洁。libnl3库查看链接.
在用户态传入的参数中,首先第一个参数作为命令参数,定义一个命令参数的枚举类型,后面传入的name,age,weight作为一个命令参数的枚举类型;

enum {
   
     CTRL_ATTR_UNDO,
     CTRL_ATTR_NAME,
     CTRL_ATTR_AGE,
     CTRL_ATTR_WEIGHT,
    __CTRL_ATTR_MAX_TEST,
};
#define CTRL_ATTR_MAX_TEST (__CTRL_ATTR_MAX_TEST - 1)

enum {
   
     CTRL_CMD_UNDO,
     CTRL_CMD_ADD,
     CTRL_CMD_DEL,
     __CTRL_CMD_MAX_TEST,
};
#define CTRL_CMD_MAX_TEST (__CTRL_CMD_MAX_TEST -1)

进入到main函数中,因为设计到通信,首先看是如何建立连接的,nl_sock应该是libnl在socket编程的基础上封装的套接字结构体。
对于nl_sock的操作,首先是初始化nl_sock,用到的函数是struct nl_sock* nl_socket_alloc ( void )

// nl_socket_alloc内部主要是调用__alloc_socket实现的,cb应该是回调函数,先不管
 struct nl_sock *nl_socket_alloc(void)
{
   
	struct nl_cb *cb;
	struct nl_sock *sk;
	cb = nl_cb_alloc(default_cb);
	if (!cb)
		return NULL;
  	/* will increment cb reference count on success */
	sk = __alloc_socket(cb);
	nl_cb_put(cb);

	return sk;
}

// 再看下__alloc_socket的实现,可以看到初始化nl_sock的哪些内容:
 static struct nl_sock *__alloc_socket(struct nl_cb *cb)
 {
   
	struct nl_sock *sk;
	// 申请的内存空间,要记得释放
	sk = calloc(1, sizeof(*sk));
 	if (!sk)
		return NULL;
	sk->s_fd = -1;
	sk->s_cb = nl_cb_get(cb);
	// 就是对netlink通信的一个封装
	sk->s_local.nl_family = AF_NETLINK;
	sk->s_peer.nl_family = AF_NETLINK;
	sk->s_seq_expect = sk->s_seq_next = time(0);
	sk->s_local.nl_pid = generate_local_port();
	if (sk->s_local.nl_pid == UINT_MAX) {
   
		nl_socket_free(sk);
	return NULL;
	}
	return sk;
}

调用完nl_sock_alloc()执行完成之后,相应的应该调用void nl_socket_free(struct nl_sock * sk),释放掉nl_sock申请的内存空间;
通过genl_connect()连接的内核,genl netlink与libnl混用了…

// 通过genl_connect连接一个netlink的socket
/* func:connect a generic netlink socket
 *returns : 0 on success or a negative error code.
 */
int genl_connect(struct nl_sock* sk);

内核中有那么多socket,如何准确的与内核中想要连接的socket建立连接呢,通过自定义一个与内核中同名的TEST_GENL_NAME作为标识(名字阔以自己起)

#define TEST_GENL_NAME "TEST"
// func:resolve generic netlink family name to numeric identifier
// name参数即传入之前定义的参数名
int genl_ctrl_resolve(struct nl_sock* sk, const char* name);

这里还要说明一个结构体nl_msg用于承载消息内容,中间会涉及到nl_msg的初始化与nl_msg的数据填充,nl_msg的结构很复杂,在网上找的一张结构图,demo中传输全部都放到attr中;
在这里插入图片描述
user实现的具体代码:

//
// Created by 方伟 on 2019-10-11.
//
// user端编译方式 gcc test4_user.c -o user $(pkg-config --cflags --libs libnl-genl-3.0)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值