携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情
一、Netlink
netlink
在Linux
中被广泛用于用户空间和内核之间的消息传递,netlink
在本质上是一种基于socket
的通信方式,但相对于传统的socket
,netlink
通信地址不是IP
和端口号,而是以进程PID
作为通信地址,这为进程间的消息传递提供了很大的便利。当用户空间进程和内核通信时。内核被看作一个大的进程,PID
为0
。当用户空间进程互相通信时,就为各自的进程,通过getpid()
函数可以获得进程自身的PID
。
1、用户空间Netlink套接字
在用户空间,Netlink
支持标准的套接字接口,与普通套接字一样使用socket
方法来对其进行初始化:
/* 创建netlink socket连接 */
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ATNLPROXY);
初始化函数中NETLINK_ATNLPROXY
表示了通信协议,对于内核空间驱动和应用程序之间的消息传递,通信双方必须有相同的协议,其实质上是一个整数型:
#define NETLINK_ATNLPROXY 27
在套接字初始化后,必须使用Netlink
特有的地址结构体对当前进程进行标识。Netlink
地址结构如下所示:
struct sockaddr_nl {
__kernel_sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* zero */
__u32 nl_pid; /* port ID */
__u32 nl_groups; /* multicast groups mask */
};
nl_family
定义了消息的地址域,一般情况下赋值为AF_NETLINK
;nl_pad
作为Netlink
的填充字段,一般默认值0
;- 在单播情况下,
nl_pid
代表进程的唯一标识符PID
,而在多播情况下,需将nl_pid
设置为0
,同时使用nl_groups
记录组播地址掩码。 当程序执行时,sockaddr_nl
结构会被转化为标准的socke
t地址,通过bind()
函数将其与Netlink
套接字进行绑定后作为参数传递给用于发送和接受消息的接口sendmsg()
和recvmsg()
。
二、Netlink IPC 数据结构
1、netlink消息的开头
每个 netlink
消息的开头是固定长度的netlink
报头,报头后才是实际的载荷。netlink
报头一共占16个字节。
struct nlmsghdr
{
__u32 nlmsg_len; /* Length of message */
__u16 nlmsg_type; /* Message type*/
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process PID */
};
- nlmsg_len 指定消息的总长度
- nlmsg_type 用于应用内部定义消息的类型
- nlmsg_flags 用于设置消息标志
- nlmsg_seq 用于应用追踪消息,表示顺序号
- nlmsg_pid 用于应用追踪消息,后者为消息来源进程ID
实例:
/* 填充消息头 */
(void)osa_memset_s((char*)nlmsg, size, 0, size);
nlmsg->nlmsg_len = NLMSG_LENGTH(final_len + sizeof(*device_event));
nlmsg->nlmsg_type = NLMSG_DONE;
nlmsg->nlmsg_pid = getpid();
nlmsg->nlmsg_seq = seq++; /* seq暂时没有用到,后期可能会进行扩展使用 */
2、设置结构 iovec
struct iovec
定义了一个向量元素。通常,这个结构用作一个多元素的数组。对于每一个传输的元素,指针成员iov_base
指向一个缓冲区,这个缓冲区是存放的是readv
所接收的数据或是writev
将要发送的数据。成员iov_len
在各种情况下分别确定了接收的最大长度以及实际写入的长度。
struct iovec {
ptr_t iov_base; /* Starting address */
size_t iov_len; /* Length in bytes */
};
实现:
/* 设置结构 iovec */
iov.iov_base = nlmsg;
iov.iov_len = nlmsg->nlmsg_len;
3、结构 msghdr
struct msghdr
{
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
};