netlink 非阻塞实现
时间: 2023-09-05 14:12:26 浏览: 53
netlink 是 Linux 内核中用于内核与用户空间进程通信的一种机制。默认情况下,netlink 是阻塞的,即用户空间进程向内核发送消息后会一直等待内核的回复,直到收到回复或者超时。但是可以通过设置 netlink socket 的标志位,将 netlink 实现为非阻塞模式。
在 netlink socket 创建后,可以通过调用 fcntl 函数设置 socket 的 O_NONBLOCK 标志位来将其设置为非阻塞模式。设置后,在调用 recvmsg 函数接收消息时,如果没有消息可以接收,该函数会立即返回,并且设置 errno 为 EAGAIN 或 EWOULDBLOCK。因此,用户进程可以通过轮询或者使用 select、poll 等多路复用函数等待 netlink 消息的到来。
另外,也可以使用 epoll 来实现非阻塞的 netlink 通信。首先需要创建一个 epoll 实例,并将 netlink socket 注册到该实例中,然后调用 epoll_wait 函数等待事件发生。当有 netlink 消息到达时,epoll_wait 函数会返回,并且可以通过 epoll_event 结构体中的 data.ptr 字段获取 netlink 消息的信息。
需要注意的是,由于 netlink 是一种异步通信机制,因此在多线程环境下使用时需要注意线程安全问题。
相关问题
请写出netlink 非阻塞的实现程序,用C语言实现
以下是使用 epoll 实现 netlink 非阻塞通信的示例代码:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <sys/epoll.h>
#define MAX_EVENTS 10
#define NETLINK_USER 31
int main() {
int sockfd, epfd, ret;
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
struct epoll_event ev, events[MAX_EVENTS];
// 创建 netlink socket
sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 绑定本地地址
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid();
src_addr.nl_groups = 0;
if (bind(sockfd, (struct sockaddr*)&src_addr, sizeof(src_addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
// 设置目标地址
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; // 内核
dest_addr.nl_groups = 0;
// 初始化 netlink 消息头
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(1024));
memset(nlh, 0, NLMSG_SPACE(1024));
nlh->nlmsg_len = NLMSG_SPACE(1024);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
// 设置 netlink 消息体
strcpy(NLMSG_DATA(nlh), "Hello from user space!");
// 初始化 iov 和 msg 结构体
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
// 发送 netlink 消息
ret = sendmsg(sockfd, &msg, 0);
if (ret < 0) {
perror("sendmsg");
exit(EXIT_FAILURE);
}
// 创建 epoll 实例
epfd = epoll_create1(0);
if (epfd < 0) {
perror("epoll_create1");
exit(EXIT_FAILURE);
}
// 将 netlink socket 注册到 epoll 实例中
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {
perror("epoll_ctl");
exit(EXIT_FAILURE);
}
// 等待事件发生
while (1) {
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
if (nfds < 0) {
perror("epoll_wait");
exit(EXIT_FAILURE);
}
// 处理每个事件
for (int i = 0; i < nfds; i++) {
if (events[i].data.fd == sockfd) {
// 读取 netlink 消息
while (1) {
char buffer[1024];
struct nlmsghdr *nlh_recv;
struct iovec iov_recv;
struct msghdr msg_recv;
int len;
memset(&iov_recv, 0, sizeof(iov_recv));
iov_recv.iov_base = buffer;
iov_recv.iov_len = sizeof(buffer);
memset(&msg_recv, 0, sizeof(msg_recv));
msg_recv.msg_name = &src_addr;
msg_recv.msg_namelen = sizeof(src_addr);
msg_recv.msg_iov = &iov_recv;
msg_recv.msg_iovlen = 1;
len = recvmsg(sockfd, &msg_recv, MSG_DONTWAIT);
if (len < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break; // 没有更多数据了,跳出循环
}
perror("recvmsg");
exit(EXIT_FAILURE);
}
nlh_recv = (struct nlmsghdr *)buffer;
while (NLMSG_OK(nlh_recv, len)) {
if (nlh_recv->nlmsg_type == NLMSG_DONE) {
break;
}
printf("Received message: %s\n", (char*)NLMSG_DATA(nlh_recv));
nlh_recv = NLMSG_NEXT(nlh_recv, len);
}
}
}
}
}
// 关闭 socket
close(sockfd);
return 0;
}
```
该程序发送一个 netlink 消息到内核,并使用 epoll 实现非阻塞地读取内核发送的回复消息。当没有消息可读时,程序会阻塞在 epoll_wait 函数上。需要注意的是,在读取 netlink 消息时,需要通过循环来读取所有可用的消息。
netlink 实现非阻塞数据的完整示例
以下是一个使用 netlink 实现非阻塞数据传输的示例程序。该程序使用 netlink 套接字与内核通信,并使用 poll 函数实现非阻塞模式。
首先,我们需要包含必要的头文件:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <poll.h>
```
接下来,我们定义一些常量和变量:
```c
#define MAX_PAYLOAD 1024 /* 最大消息长度 */
#define NETLINK_USER 31 /* 自定义 netlink 协议 */
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd, ret;
char *msg = "Hello from userspace!";
```
接着,我们创建一个 netlink 套接字:
```c
/* 创建 netlink 套接字 */
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
```
然后,我们初始化源地址和目标地址:
```c
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* PID */
src_addr.nl_groups = 0; /* 不加入多播组 */
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* 内核进程 */
dest_addr.nl_groups = 0;
```
接下来,我们将 netlink 套接字绑定到源地址:
```c
/* 绑定 netlink 套接字到源地址 */
ret = bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
if (ret < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
```
然后,我们构造 netlink 消息:
```c
/* 构造 netlink 消息 */
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(); /* PID */
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), msg);
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
/* 发送 netlink 消息 */
sendmsg(sock_fd, &iov, 1, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
```
接下来,我们使用 poll 函数实现非阻塞模式:
```c
/* 设置 poll 监听套接字 */
struct pollfd fds;
fds.fd = sock_fd;
fds.events = POLLIN;
/* 轮询监听 netlink 套接字 */
while (1) {
ret = poll(&fds, 1, 5000); /* 5 秒超时 */
if (ret < 0) {
perror("poll");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("timeout\n");
} else if (fds.revents & POLLIN) {
/* 接收 netlink 消息 */
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
ret = recvmsg(sock_fd, &msg, 0);
if (ret < 0) {
perror("recvmsg");
exit(EXIT_FAILURE);
}
printf("Received message: %s\n", NLMSG_DATA(nlh));
break;
}
}
```
最后,我们关闭 netlink 套接字并释放资源:
```c
/* 关闭 netlink 套接字并释放资源 */
close(sock_fd);
free(nlh);
```
完整的程序代码如下:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <poll.h>
#define MAX_PAYLOAD 1024 /* 最大消息长度 */
#define NETLINK_USER 31 /* 自定义 netlink 协议 */
int main()
{
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd, ret;
char *msg = "Hello from userspace!";
/* 创建 netlink 套接字 */
sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/* 初始化源地址和目标地址 */
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* PID */
src_addr.nl_groups = 0; /* 不加入多播组 */
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* 内核进程 */
dest_addr.nl_groups = 0;
/* 绑定 netlink 套接字到源地址 */
ret = bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr));
if (ret < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
/* 构造 netlink 消息 */
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(); /* PID */
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), msg);
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
/* 发送 netlink 消息 */
sendmsg(sock_fd, &iov, 1, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
/* 设置 poll 监听套接字 */
struct pollfd fds;
fds.fd = sock_fd;
fds.events = POLLIN;
/* 轮询监听 netlink 套接字 */
while (1) {
ret = poll(&fds, 1, 5000); /* 5 秒超时 */
if (ret < 0) {
perror("poll");
exit(EXIT_FAILURE);
} else if (ret == 0) {
printf("timeout\n");
} else if (fds.revents & POLLIN) {
/* 接收 netlink 消息 */
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
ret = recvmsg(sock_fd, &msg, 0);
if (ret < 0) {
perror("recvmsg");
exit(EXIT_FAILURE);
}
printf("Received message: %s\n", NLMSG_DATA(nlh));
break;
}
}
/* 关闭 netlink 套接字并释放资源 */
close(sock_fd);
free(nlh);
return 0;
}
```