内核通信之 Netlink 源码分析和实例分析
原创内核通信之 Netlink 源码分析和实例分析
Netlink 是 Linux 内核与用户空间进程之间进行通信的一种机制。它提供了一种高效、可靠和灵活的通信方法,允许用户空间进程可以与内核模块进行交互,从而实现对网络配置、系统状态查询等功能的控制。本文将对 Netlink 的源码进行分析,并通过实例展示怎样使用 Netlink 进行内核通信。
一、Netlink 简介
Netlink 是一种特殊的 Linux 内核协议,它允许用户空间进程与内核模块之间进行双向通信。Netlink 通信通常使用 socket 进行,故而它也被称为 Netlink Socket。Netlink 通信的特点如下:
- 异步通信:Netlink 通信是异步的,即发送方不需要等待接收方的响应。
- 消息传递:Netlink 通信通过消息传递进行,每个消息包含特定的类型和数据。
- 多播赞成:Netlink 赞成多播通信,允许多个用户空间进程接收同一个消息。
二、Netlink 源码分析
Netlink 的源码重点位于 Linux 内核的 net/Netlink 目录下。下面将简要分析 Netlink 源码的重点部分。
2.1 Netlink 协议栈
Netlink 协议栈包括 Netlink 协议头和用户数据两部分。Netlink 协议头定义了消息的基本信息,如消息类型、消息长度等。用户数据则是消息携带的具体内容。
struct nlmsghdr {
__u32 nlmsg_len; /* Total length of this message */
__u16 nlmsg_type; /* Message type */
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sender port ID */
};
2.2 Netlink 消息处理
Netlink 消息处理是 Netlink 通信的核心部分。内核中负责处理 Netlink 消息的模块通常会实现 nlmsghdr 中的 nlmsg_type 字段对应的处理函数。用户空间进程发送的 Netlink 消息会经过协议栈的处理,最终到达相应的处理函数。
int netlink_rcv_skb(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int len;
...
nlh = nlmsg_data(skb);
len = nlmsg_len(skb);
...
return call_netlink_handle_skmsg(skb);
}
2.3 Netlink 接口
Netlink 接口提供了用户空间进程与内核模块通信的接口。用户空间进程可以通过 socket 调用来创建 Netlink Socket,并通过 sendmsg 或 sendto 函数发送 Netlink 消息。内核模块则通过 netlink_socket_register 注册 Netlink Socket,以便接收和处理消息。
int netlink_socket_register(struct socket *sock, struct netlink_sock *nlsk)
{
...
nlsk->sk_family = AF_NETLINK;
nlsk->sk_protocol = protocol;
...
return register_netlink_socket(sock);
}
三、Netlink 实例分析
下面通过一个简洁的例子来展示怎样使用 Netlink 进行内核通信。
3.1 用户空间进程
用户空间进程可以通过创建 Netlink Socket 并发送消息来与内核模块通信。
#include <sys/socket.h>
#include <linux/netlink.h>
#include <string.h>
int main()
{
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh;
int sock_fd;
struct iovec iov;
struct msghdr msg;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
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; /* 0 for Linux kernel */
dest_addr.nl_groups = RTMGRP_LINK; /* subscribe to link layer messages */
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(1024));