Linux用户与内核空间交互—netlink

目录

简介

一、netlink soket

优点

二、用户空间

1、API

2、编程流程

3、源码

三、内核编程

1、API

2、内核空间编程流程

3、内核源码


简介

用户空间与内核的交互方式,使用copy_from_user(), copy_to_user().

除了这两种交互方式,内核还提供了其他高级的方式,对于写驱动来说很重要。有proc、sysfs、debugfs、netlink、ioctl。

本文学习netlink

一、netlink soket

优点

  • 双向同步传输数据;
  • 高速数据传输,特别是在本地主机上;
  • 异步通信;消息可以排队,因此发送方不必等待接收方;
  • 在其他用户到内核通信方法(如procfs、sysfs、debugfs和ioctl)中,用户空间程序必须开始传输到内核空间的;而使用netlink套接字,内核可以起始传输到用户空间;
  • procfs、sysfs、debugfs中可能引起域名污染;对于netlink套接字,情况并非如此,因为没有文件。

二、用户空间

1、API

socket(PF_NETLINK, SOCK_RAW, NETLINK_MY_UNIT_PROTO)

2、编程流程

  1. 创建netlink;socket(PF_NETLINK, SOCK_RAW, NETLINK_MY_UNIT_PROTO);域名 PF_NETLINK,类型SOCK_RAW,协议 NETLINK_MY_UNIT_PROTO
  2. 初始化netlink源的sockerddr; bind socket
  3. 初始化netlink目的地址结构。目的地址设置成0表示是内核;
  4. 初始化netlink的头数据结构,指定源PID,数据负载;
  5. 初始化iovec结构,指向引用netlink头,初始化msghdr 数据结构,指向目的地址和iov
  6. 数据的发送sendmsg,数据的接收recvmsg

3、源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_MY_UNIT_PROTO  31

#define NLSPACE              1024

//数据
static const char *thedata = "hello world";

int main(int argc, char **argv)
{
	int sd;
	struct sockaddr_nl src_nl, dest_nl;
	struct nlmsghdr *nlhdr; // 'carries' the payload
	struct iovec iov;
	struct msghdr msg;
	ssize_t nsent, nrecv;

    //、创建节点,域名PF_NETLINK,类型SOCK_RAW,协议NETLINK_MY_UNIT_PROTO
	sd = socket(PF_NETLINK, SOCK_RAW, NETLINK_MY_UNIT_PROTO);
	if (sd < 0) {
		exit(EXIT_FAILURE);
	}

	//2、设置netlink源地址结构体已经绑定
	memset(&src_nl, 0, sizeof(src_nl));
	src_nl.nl_family = AF_NETLINK;
	src_nl.nl_pid = getpid();
	src_nl.nl_groups = 0x0;   // no multicast
	if (bind(sd, (struct sockaddr *)&src_nl, sizeof(src_nl)) < 0) {
		exit(EXIT_FAILURE);
	}
		
	/* 3. 设置目的地址结构体*/
	memset(&dest_nl, 0, sizeof(dest_nl));
	dest_nl.nl_family = AF_NETLINK;
	dest_nl.nl_groups = 0x0; // no multicast
	dest_nl.nl_pid = 0;      // destined for the kernel

	/* 4. 分配netlink头内存*/
	nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(NLSPACE));
	if (!nlhdr) {
		exit(EXIT_FAILURE);
	}
	memset(nlhdr, 0, NLMSG_SPACE(NLSPACE));
	nlhdr->nlmsg_len = NLMSG_SPACE(NLSPACE);
	nlhdr->nlmsg_pid = getpid();

	/* 设置payload  thedata是待发送的数据*/
	strncpy(NLMSG_DATA(nlhdr), thedata, strnlen(thedata, NLSPACE)+1);

	/* 5.设置iovec*/
	memset(&iov, 0, sizeof(struct iovec));
	iov.iov_base = (void *)nlhdr;//上面申请的空间
	iov.iov_len = nlhdr->nlmsg_len;

	/* 设置消息头结构体 */
	memset(&msg, 0, sizeof(struct msghdr));
	msg.msg_name = (void *)&dest_nl;   // 目的地址
	msg.msg_namelen = sizeof(dest_nl); // 目的地址大学
	msg.msg_iov = &iov;//
	msg.msg_iovlen = 1; // # elements in msg_iov

	
	/* 6. sendmsg */
	nsent = sendmsg(sd, &msg, 0);
	if (nsent < 0) {
		free(nlhdr);
		exit(EXIT_FAILURE);
	} else if (nsent == 0) {
		free(nlhdr);
		exit(EXIT_FAILURE);
	}

	printf("%s:sendmsg(): *** success, sent %ld bytes all-inclusive\n"
		   " (see kernel log for dtl)\n", argv[0], nsent);

	fflush(stdout);

	/* 7. 阻塞接收数据*/
	printf("%s: now blocking on kernel netlink msg via recvmsg() ...\n", argv[0]);
	nrecv = recvmsg(sd, &msg, 0);
	if (nrecv < 0) {
		free(nlhdr);
		exit(EXIT_FAILURE);
	}
	printf("%s:recvmsg(): *** success, received %ld bytes:"
		"\nmsg from kernel netlink: \"%s\"\n",
		argv[0], nrecv, (char *)NLMSG_DATA(nlhdr));

	free(nlhdr);
	close(sd);
	exit(EXIT_SUCCESS);
}

三、内核编程

1、API

struct sock * netlink_kernel_create(struct net *, int , struct netlink_kernel_cfg *);

2、内核空间编程流程

  1. netlink_kernel_create 创建netlink
  2. nlmsg_new()分配netlink内存,nlmsg_put()添加netlink信息到分配的内存;借助nlmsg_data()
    拷贝数据;
  3. nlmsg_unicast()发送内核数据到用户空间;
  4. netlink_kernel_release() 释放netlink

3、内核源码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>

MODULE_AUTHOR("wy");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION("0.1");

//文件名
#define OURMODNAME   "netlink_simple_intf"
#define NETLINK_MY_UNIT_PROTO   31
#define NLSPACE              1024

static struct sock *nlsock;

//收到数据,触发回调函数。
static void netlink_recv_and_reply(struct sk_buff *skb)
{
	struct nlmsghdr *nlh;
	struct sk_buff *skb_tx;

    //返回的数据
	char *reply = "Reply from kernel netlink";
	int pid, msgsz, stat;

    //发生在进程上下文,而不是中断上下文
	PRINT_CTX();

	nlh = (struct nlmsghdr *)skb->data;
	pid = nlh->nlmsg_pid;	//发送进程的PID
	pr_info("received from PID %d:\n" "\"%s\"\n", pid, (char *)NLMSG_DATA(nlh));

    //返回数据长度
	msgsz = strnlen(reply, NLSPACE);
    //新netlink申请
	skb_tx = nlmsg_new(msgsz, 0);
	if (!skb_tx) {
		pr_warn("skb alloc failed!\n");
		return;
	}

	// 设置 payload
	nlh = nlmsg_put(skb_tx, 0, 0, NLMSG_DONE, msgsz, 0);
	NETLINK_CB(skb_tx).dst_group = 0; 

	//将发送的数据reply,使用nlmsg_data拷贝到netlink 
    strncpy(nlmsg_data(nlh), reply, msgsz);

	// 发送
	stat = nlmsg_unicast(nlsock, skb_tx, pid);
	if (stat < 0)
		pr_warn("nlmsg_unicast() failed (err=%d)\n", stat);
	pr_info("reply sent\n");
}

//当用户空间向kernel发数据,将触发此回调函数
static struct netlink_kernel_cfg nl_kernel_cfg = {
	.input = netlink_recv_and_reply,
};

static int __init netlink_simple_intf_init(void)
{
    //创建netlink
	nlsock = netlink_kernel_create(&init_net, NETLINK_MY_UNIT_PROTO,&nl_kernel_cfg);
	if (!nlsock) {
		pr_warn("netlink_kernel_create failed\n");
		return PTR_ERR(nlsock);
	}

	pr_info("inserted\n");
	return 0;		/* success */
}

//模块退出清理
static void __exit netlink_simple_intf_exit(void)
{
	netlink_kernel_release(nlsock);
	pr_info("removed\n");
}

module_init(netlink_simple_intf_init);
module_exit(netlink_simple_intf_exit);

四、串口输出

kernel

[  310.858620] netlink_simple_intf: loading out-of-tree module taints kernel.
[  310.858650] netlink_simple_intf: module verification failed: signature and/or required key missing - tainting kernel
[  310.860505] netlink_simple_intf:netlink_simple_intf_init(): creating kernel netlink socket
[  310.860510] netlink_simple_intf:netlink_simple_intf_init(): inserted
[  320.874385] netlink_simple_intf:netlink_recv_and_reply(): 005)  netlink_userapp :3730   |  ...0   /* netlink_recv_and_reply() */
[  320.874391] netlink_simple_intf:netlink_recv_and_reply(): received from PID 3730:
               "sample user data to send to kernel via netlink"
[  320.874394] netlink_simple_intf:netlink_recv_and_reply(): reply sent

app

./netlink_userapp:PID 3730: netlink socket created
./netlink_userapp: bind done
./netlink_userapp: destination struct, netlink hdr, payload setup
./netlink_userapp: initialized iov structure (nl header folded in)
./netlink_userapp: initialized msghdr structure (iov folded in)
./netlink_userapp:sendmsg(): *** success, sent 1040 bytes all-inclusive
 (see kernel log for dtl)
./netlink_userapp: now blocking on kernel netlink msg via recvmsg() ...
./netlink_userapp:recvmsg(): *** success, received 44 bytes:
msg from kernel netlink: "Reply from kernel netlink"

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值