本文主要是介绍netlink sample,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在 Linux 2.4 版以后版本的内核中,几乎全部的中断过程与用户态进程的通信都是使用 netlink套接字实现的,同时还使用 netlink 实现了 ip queue 工具,但 ip queue的使用有其局限性,不能自由地用于各种中断过程。内核的帮助文档和其他一些 Linux 相关文章都没有对 netlink套接字在中断过程和用户空间通信的应用上作详细的说明,使得很多用户对此只有一个模糊的概念。
Unicast Communication between Kernel and Application
在下面的例子中,一个用户空间进程发送一个netlink消息给内核模块,内核模块应答一个消息给发送进程,这里是用户空间的代码:
#include <sys/socket.h>
#include <linux/netlink.h>
#define MAX_PAYLOAD 1024
struct sockaddr_nl src_addr, dest_addr;
struct msghdr msg;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
void main() ...{
sock_fd = socket(PF_NETLINK,SOCK_RAW,NETLINK_TEST);
memset(&src_addr, 0,sizeof(src_addr));
src__addr.nl_family =AF_NETLINK;
src_addr.nl_pid =getpid();
src_addr.nl_groups = 0;
bind(sock_fd, (structsockaddr*)&src_addr,
sizeof(src_addr));
memset(&dest_addr, 0,sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid =0;
dest_addr.nl_groups = 0;
nlh=(struct nlmsghdr *)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len =NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid =getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello you!");
iov.iov_base = (void *)nlh;
iov.iov_len =nlh->nlmsg_len;
msg.msg_name = (void*)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(fd, &msg, 0);
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
recvmsg(fd, &msg, 0);
printf(" Received message payload: %s ",
NLMSG_DATA(nlh));
close(sock_fd);
}
这里是内核代码:
struct sock *nl_sk = NULL;
void nl_data_ready (struct sock *sk, int len)
...{
wake_up_interruptible(sk->sleep);
}
void netlink_test() ...{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh = NULL;
int err;
u32pid;
nl_sk = netlink_kernel_create(NETLINK_TEST,
nl_data_ready);
skb = skb_recv_datagram(nl_sk, 0, 0,&err);
nlh = (struct nlmsghdr*)skb->data;
printk("%s: received netlink message payload:%s",
__FUNCTION__, NLMSG_DATA(nlh));
pid = nlh->nlmsg_pid;
NETLINK_CB(skb).groups = 0;
NETLINK_CB(skb).pid =0;
NETLINK_CB(skb).dst_pid = pid;
NETLINK_CB(skb).dst_groups =0;
netlink_unicast(nl_sk, skb, pid,MSG_DONTWAIT);
sock_release(nl_sk->socket);
}
在内核模块被加载到内核,当我们运行用户程序,我们将看到下面的信息:
Received message payload: Hello you!
然后用dmesg我们可以看到内核输出:
netlink_test: received netlink message payload:
Hello you!
Multicast Communication between Kernel and Applications
这个例子中,两个应用程序在监听同一个netlink广播组.内核模块发送一个netlink消息给这个广播组,所用的应用程序都收到它,如下是用户程序代码:
#include <sys/socket.h>
#include <linux/netlink.h>
#define MAX_PAYLOAD 1024
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
void main() ...{
sock_fd=socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
memset(&src_addr, 0,sizeof(local_addr));
src_addr.nl_family =AF_NETLINK;
src_addr.nl_pid =getpid();
src_addr.nl_groups = 1;
bind(sock_fd, (structsockaddr*)&src_addr,
sizeof(src_addr));
memset(&dest_addr, 0,sizeof(dest_addr));
nlh = (struct nlmsghdr *)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0,NLMSG_SPACE(MAX_PAYLOAD));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
msg.msg_name = (void*)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Waiting for message from kernel ");
recvmsg(fd, &msg, 0);
printf(" Received message payload: %s ",
NLMSG_DATA(nlh));
close(sock_fd);
}
内核代码:
#define MAX_PAYLOAD 1024
struct sock *nl_sk = NULL;
void netlink_test() ...{
sturct sk_buff *skb = NULL;
struct nlmsghdr *nlh;
int err;
nl_sk = netlink_kernel_create(NETLINK_TEST,
nl_data_ready);
skb=alloc_skb(NLMSG_SPACE(MAX_PAYLOAD),GFP_KERNEL);
nlh = (struct nlmsghdr*)skb->data;
nlh->nlmsg_len =NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid =0;
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Greeting fromkernel!");
NETLINK_CB(skb).groups = 1;
NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid =0;
NETLINK_CB(skb).dst_groups = 1;
netlink_broadcast(nl_sk, skb, 0, 1,GFP_KERNEL);
sock_release(nl_sk->socket);
}
我们运行用户程序:
./nl_recv &
Waiting for message from kernel
./nl_recv &
Waiting for message from kernel
然后我们加载内核模块到内核空间,会看到如下信息::
Received message payload: Greeting from kernel!
Received message payload: Greeting from kernel!
以下是一个简单的测试内核事件的应用程序:
#defineMAX_PAYLOAD 1024
structsockaddr_nl src_addr, dest_addr;
char *KernelMsg = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int msglen;
#defineDEVICE_ADD "add"
#defineDEVICE_REMOVE "remove"
#defineDEVICE_NAME "event0"
#defineDEVICE_NAMELEN 6
void * DeviceManagement(void *arg)
...{
sock_fd =socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
intmsgle;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = pthread_self() <<16 | getpid();
src_addr.nl_groups = 1;
bind(sock_fd, (struct sockaddr *)&src_addr,sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
KernelMsg =(struct nlmsghdr *)malloc(MAX_PAYLOAD);
memset(KernelMsg,0, MAX_PAYLOAD);
iov.iov_base= (void *)KernelMsg;
iov.iov_len= MAX_PAYLOAD;
msg.msg_name= (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov= &iov;
msg.msg_iovlen = 1;
while(1)...{
//printf("Waiting for message from kernel ");
recvmsg(sock_fd, &msg, 0);
//printf("Receved message payload: %s ", KernelMsg);
msglen = strlen(KernelMsg);
//printf("Device: %s ", KernelMsg+msglen-DEVICE_NAMELEN);
if(!strncmp(DEVICE_NAME, KernelMsg+msglen-DEVICE_NAMELEN,DEVICE_NAMELEN)) ...{
if(!strncmp(DEVICE_ADD, KernelMsg, strlen(DEVICE_ADD)))
...{
printf("Add event0 device ");
USBKeyboardReady = 1;
}
else if(!strncmp(DEVICE_REMOVE, KernelMsg, strlen(DEVICE_REMOVE)))...{
printf("Remove event0 device ");
USBKeyboardReady = 0;