上一节说到proc文件系统,这是用户态和内核态通信的一种方法,本节将要说到另外一种通信的方法,该方法相比于其他的内核和用户通信有时在于:
Netlink相对于其他的通信机制具有以下优点:
1.使用Netlink通过自定义一种新的协议并加入协议族即可通过socket API使用Netlink协议完成数据交换,而ioctl和proc文件系统均需要通过程序加入相应的设备或文件。
2.Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能。
3.Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息。
4.Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起。
建立netlink会话如下:
1.在内核使用netlink_kernel_create()创建套接字,指明接受函数。
2.在用户态创建套接字,并将进程ID发送至内核空间。
3.内核接受函数收到用户空间进程ID。
4.用户空间与内核空间可以通信。
需要详细描述的内容太多,下面直接给一个例子算了,以后有时间在细说,
下面是内核部分代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/version.h> //LINUX_VERSION_CODE
#include <net/sock.h> //struct sock *
#include <linux/netlink.h> //netlink_kernel_create()
#include <linux/sched.h>
#include "netlink_k.h"
static struct sock *nlfd;
static int pid;
int netlink_deliver(uint16_t type, void *data, unsigned short dlen)
{
int ret, len;
char *old_tail;
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh = NULL;
if (0 == pid) {
return 0;
}
len = NLMSG_SPACE(dlen + sizeof(unsigned short));
skb = alloc_skb(len, GFP_ATOMIC);
if (NULL == skb) {
printk(KERN_ALERT"alloc skb failed!\n");
goto nlmsg_failure;
}
old_tail = (char *)skb->tail;
nlh = NLMSG_PUT(skb, 0, 0, type, len - sizeof(*nlh));
memcpy((char *)NLMSG_DATA(nlh), &dlen, sizeof(unsigned short));
memcpy((char *)NLMSG_DATA(nlh) + sizeof(unsigned short), data, dlen);
nlh->nlmsg_len = (char *)skb->tail - (char *)old_tail;
NETLINK_CB(skb).dst_group = 0;
printk(KERN_ALERT"Send: data: %s, dlen = %d, nlh->nlmsg_len: %d\n", (char*)data, (int)dlen, nlh->nlmsg_len);
ret = netlink_unicast(nlfd, skb, pid, MSG_DONTWAIT);
return ret;
nlmsg_failure:
if(skb)
kfree_skb(skb);
return -1;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
void nlsock_receive_process(struct sock *sk, int len)
{
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh = NULL;
char info[] = "Haha, welcome to Kernel!";
unsigned char data[MAX_PACKET];
unsigned short dlen;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
nlh = (struct nlmsghdr *)skb->data;
#else
void nlsock_receive_process(struct sk_buff *skb)
{
struct nlmsghdr *nlh = NULL;
char info[] = "Haha, welcome to Kernel!";
unsigned char *data = NULL;
unsigned short dlen;
nlh = nlmsg_hdr(skb);
#endif
printk(KERN_ALERT"===%s===\n", __func__);
pid = nlh->nlmsg_pid;
memcpy(&dlen, (char *)NLMSG_DATA(nlh), sizeof(unsigned short));
printk(KERN_ALERT"dlen: %d\n", dlen);
data = (char *)NLMSG_DATA(nlh) + sizeof(unsigned short);
//sln_dump_pkt(NLMSG_DATA(nlh), 100);
printk(KERN_ALERT"Receive netlink data: %s, nlh->nlmsg_len = %d\n",
data, nlh->nlmsg_len);
netlink_deliver(NL_FRAMEIO, info, strlen(info));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
}
#endif
}
static int __init nlk_init(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
nlfd = netlink_kernel_create(NL_FRAMEIO,
0,
nlsock_receive_process,
THIS_MODULE);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,5)
struct netlink_kernel_cfg cfg = {
.input = nlsock_receive_process,
};
nlfd = netlink_kernel_create(&init_net,
NL_FRAMEIO,
THIS_MODULE,
&cfg);
#else
nlfd = netlink_kernel_create(&init_net,
NL_FRAMEIO,
0,
nlsock_receive_process,
NULL,
THIS_MODULE);
#endif
printk(KERN_ALERT"===%s===\n", __func__);
if (NULL == nlfd) {
printk(KERN_ALERT"Can't create netlink sock!\n");
return -1;
}
return 0;
}
static void __exit nlk_exit(void)
{
printk(KERN_ALERT"===%s===\n", __func__);
if (NULL != nlfd) {
netlink_kernel_release(nlfd);
nlfd = NULL;
}
}
module_init(nlk_init);
module_exit(nlk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("sln");
下面是用户态部分代码:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include "netlink_u.h"
struct sockaddr_nl local_addr, dest_addr;
int sockfd;
int bcm_netlink_init(void)
{
sockfd = socket(PF_NETLINK, SOCK_RAW, NL_FRAMEIO);
if (sockfd < 0) {
fprintf(stderr, "sockfd: %s\n", strerror(errno));
return -1;
}
memset(&local_addr, 0, sizeof(local_addr));
local_addr.nl_family = AF_NETLINK;
local_addr.nl_pid = getpid(); /* self pid */
local_addr.nl_groups = 0; /* not in mcast groups */
if (bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
fprintf(stderr, "bind: %s\n", strerror(errno));
close(sockfd);
return -1;
}
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* To Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
return 0;
}
int bcm_netlink_send(char *data, unsigned short dlen)
{
int len;
struct iovec iov;
struct msghdr msg;
struct nlmsghdr *nlh = NULL;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(dlen + sizeof(unsigned short)));
if (NULL == nlh) {
printf("malloc failed!\n");
return -1;
}
/* Fill the netlink message header */
memset(nlh, 0, sizeof(struct nlmsghdr));
// nlh->nlmsg_len = NLMSG_SPACE(MAX_PACKET);
nlh->nlmsg_len = NLMSG_SPACE(dlen + sizeof(unsigned short));
nlh->nlmsg_pid = getpid(); /* self pid */
nlh->nlmsg_flags = 0;
/* Fill in the netlink message payload */
memcpy(NLMSG_DATA(nlh), &dlen, sizeof(unsigned short));
memcpy((char *)NLMSG_DATA(nlh) + sizeof(unsigned short), data, dlen);
/*
* if not two line below, send will failure. Very IMPORTANT !
*/
memset(&iov, 0, sizeof(struct iovec));
memset(&msg, 0, sizeof(struct msghdr));
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;
while ((len = sendmsg(sockfd, &msg, 0)) < 0) {
if (errno == EINTR) {
continue;
}
fprintf(stderr, "sendmsg: %s, len = %d\n", strerror(errno), len);
return -1;
}
fprintf(stderr, "senddata: %s, sizeof(struct nlmsghdr) = %d, mydatalen: %d, sendlen = %d\n",
data, (int)sizeof(struct nlmsghdr), (int)dlen, len);
return 0;
}
int bcm_netlink_recv()
{
struct sockaddr_nl peer_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int recvlen;
struct msghdr msg;
char *data = NULL;
unsigned short dlen;
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(dlen + sizeof(unsigned short)));
if (NULL == nlh) {
printf("malloc failed!\n");
return -1;
}
/* Fill the netlink message header */
memset(nlh, 0, sizeof(struct nlmsghdr));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PACKET);
nlh->nlmsg_pid = getpid(); /* self pid */
nlh->nlmsg_flags = 0;
/*
* if not two line below ,send will failure. Very IMPORTANT!
*/
memset(&iov, 0, sizeof(struct iovec));
memset(&msg, 0, sizeof(struct msghdr));
iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&peer_addr;
msg.msg_namelen = sizeof(peer_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* A loop to read message from kernel */
for (;;) {
memset(nlh, 0, nlh->nlmsg_len);
if ((recvlen = recvmsg(sockfd, &msg, 0)) < 0) {
fprintf(stderr, "recvmsg: %s\n", strerror(errno));
continue;
}
memcpy(&dlen, (char *)NLMSG_DATA(nlh), sizeof(unsigned short));
data = (char *)NLMSG_DATA(nlh) + sizeof(unsigned short);
fprintf(stdout, "Received message payload: %s, recvlen: %d, dlen: %d\n",
data, recvlen, dlen);
}
}
int main(int argc, char* argv[])
{
int ret;
pthread_t pid;
char sendbuf[MAX_PACKET] = "info from user!";
ret = bcm_netlink_init();
if (ret < 0) {
goto err;
}
if (pthread_create(&pid, NULL, (void *)bcm_netlink_recv, NULL) < 0) {
fprintf(stderr, "Create receive thread failed!\n");
goto err;
}
for (;;) {
sleep(1);
bcm_netlink_send(sendbuf, strlen(sendbuf));
}
// pthread_join(pid, NULL);
err:
if (sockfd > 0) {
close(sockfd);
}
return 0;
}
|