http://blog.csdn.net/max415/archive/2008/02/01/2076814.aspx原文出处
在 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 /* maximum payload size*/
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(); /**/ /* self pid */
src_addr.nl_groups = 0 ; /**/ /* not in mcast groups */
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 ; /**/ /* For Linux Kernel */
dest_addr.nl_groups = 0 ; /**/ /* unicast */
nlh = ( struct nlmsghdr * )malloc(
NLMSG_SPACE(MAX_PAYLOAD));
/**/ /* Fill the netlink message header */
nlh -> nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh -> nlmsg_pid = getpid(); /**/ /* self pid */
nlh -> nlmsg_flags = 0 ;
/**/ /* Fill in the netlink message payload */
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 );
/**/ /* Read message from kernel */
memset(nlh, 0 , NLMSG_SPACE(MAX_PAYLOAD));
recvmsg(fd, & msg, 0 );
printf( " Received message payload: %s " ,
NLMSG_DATA(nlh));
/**/ /* Close Netlink Socket */
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;
u32 pid;
nl_sk = netlink_kernel_create(NETLINK_TEST,
nl_data_ready);
/**/ /* wait for message coming down from user-space */
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; /**/ /* pid of sending process */
NETLINK_CB(skb).groups = 0 ; /**/ /* not in mcast group */
NETLINK_CB(skb).pid = 0 ; /**/ /* from kernel */
NETLINK_CB(skb).dst_pid = pid;
NETLINK_CB(skb).dst_groups = 0 ; /**/ /* unicast */
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 /* maximum payload size*/
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(); /**/ /* self pid */
/**/ /* interested in group 1<<0 */
src_addr.nl_groups = 1 ;
bind(sock_fd, ( struct sockaddr * ) & 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 " );
/**/ /* Read 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 ; /**/ /* from kernel */
nlh -> nlmsg_flags = 0 ;
strcpy(NLMSG_DATA(nlh), " Greeting from kernel! " );
/**/ /* sender is in group 1<<0 */
NETLINK_CB(skb).groups = 1 ;
NETLINK_CB(skb).pid = 0 ; /**/ /* from kernel */
NETLINK_CB(skb).dst_pid = 0 ; /**/ /* multicast */
/**/ /* to mcast group 1<<0 */
NETLINK_CB(skb).dst_groups = 1 ;
/**/ /* multicast the message to all listening processes */
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!
以下是一个简单的测试内核事件的应用程序:
#define
MAX_PAYLOAD 1024
struct
sockaddr_nl src_addr, dest_addr;
char
*
KernelMsg
=
NULL;
struct
iovec iov;
int
sock_fd;
struct
msghdr msg;
int
msglen;
#define
DEVICE_ADD "add"
#define
DEVICE_REMOVE "remove"
#define
DEVICE_NAME "event0"
#define
DEVICE_NAMELEN 6
void
*
DeviceManagement(
void
*
arg)
...
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
int msgle;
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 ;
}
}
}
close(sock_fd);
}
示例:一个用户进程发送数据到内核,然后通过内核发送给另一个用户进程。
内核进程:netlink-exam-kern.c
//
kernel module: netlink-exam-kern.c
#ifndef __KERNEL__
#define
__KERNEL__
#endif
#ifndef MODULE
#define
MODULE
#endif
#include
<
linux学习
/
config.h
>
#include
<
linux学习
/
module.h
>
#include
<
linux学习
/
netlink.h
>
#include
<
linux学习
/
sched.h
>
#include
<
net
/
sock.h
>
#include
<
linux学习
/
proc_fs.h
>
#define
BUF_SIZE 16384
#define
NL 30
static
struct
sock
*
netlink_exam_sock;
static
unsigned
char
buffer[BUF_SIZE];
static
unsigned
int
buffer_tail
=
0
;
static
int
exit_flag
=
0
;
static
DECLARE_COMPLETION(exit_completion);
static
void
recv_handler(
struct
sock
*
sk,
int
length)
...
{
wake_up(sk -> sk_sleep);
}
static
int
process_message_thread(
void
*
data)
...
{
struct sk_buff * skb = NULL;
struct nlmsghdr * nlhdr = NULL;
int len;
DEFINE_WAIT(wait);
daemonize( " mynetlink " );
while (exit_flag == 0 ) ... {
prepare_to_wait(netlink_exam_sock -> sk_sleep, & wait, TASK_INTERRUPTIBLE);
schedule();
finish_wait(netlink_exam_sock -> sk_sleep, & wait);
while ((skb = skb_dequeue( & netlink_exam_sock -> sk_receive_queue))
!= NULL) ... {
nlhdr = ( struct nlmsghdr * )skb -> data;
if (nlhdr -> nlmsg_len < sizeof ( struct nlmsghdr)) ... {
printk( " Corrupt netlink message. " );
continue ;
}
len = nlhdr -> nlmsg_len - NLMSG_LENGTH( 0 );
if (len + buffer_tail > BUF_SIZE) ... {
printk( " netlink buffer is full. " );
}
else ... {
memcpy(buffer + buffer_tail, NLMSG_DATA(nlhdr), len);
buffer_tail += len;
}
nlhdr -> nlmsg_pid = 0 ;
nlhdr -> nlmsg_flags = 0 ;
NETLINK_CB(skb).pid = 0 ;
NETLINK_CB(skb).dst_pid = 0 ;
NETLINK_CB(skb).dst_group = 1 ;
netlink_broadcast(netlink_exam_sock, skb, 0 , 1 , GFP_KERNEL);
}
}
complete( & exit_completion);
return 0 ;
}
static
int
netlink_exam_readproc(
char
*
page,
char
**
start, off_t off,
int
count,
int
*
eof,
void
*
data)
...
{
int len;
if (off >= buffer_tail) ... {
* eof = 1 ;
return 0 ;
}
else ... {
len = count;
if (count > PAGE_SIZE) ... {
len = PAGE_SIZE;
}
if (len > buffer_tail - off) ... {
len = buffer_tail - off;
}
memcpy(page, buffer + off, len);
* start = page;
return len;
}
}
static
int
__init netlink_exam_init(
void
)
...
{
netlink_exam_sock = netlink_kernel_create(NL, 0 , recv_handler, THIS_MODULE);
if ( ! netlink_exam_sock) ... {
printk( " Fail to create netlink socket. " );
return 1 ;
}
kernel_thread(process_message_thread, NULL, CLONE_KERNEL);
create_proc_read_entry( " netlink_exam_buffer " , 0444 , NULL, netlink_exam_readproc, 0 );
return 0 ;
}
static
void
__exit netlink_exam_exit(
void
)
...
{
exit_flag = 1 ;
wake_up(netlink_exam_sock -> sk_sleep);
wait_for_completion( & exit_completion);
sock_release(netlink_exam_sock -> sk_socket);
}
module_init(netlink_exam_init);
module_exit(netlink_exam_exit);
MODULE_LICENSE(
"
GPL
"
);
编译成模块:
ifneq ($(KERNELRELEASE),)
debug
-
objs :
=
netlink
-
exam
-
kern.o
obj
-
m :
=
netlink
-
exam
-
kern1.o
CFLAGS
+=
-
w
-
Wimplicit
-
function
-
declaration
else
PWD :
=
$(shell pwd)
KVER
?=
$(shell uname
-
r)
KDIR :
=
/
lib
/
modules
/
$(KVER)
/
build
all:
$(MAKE)
-
C $(KDIR) M
=
$(PWD)
clean:
rm
-
rf .
*
.cmd
*
.o
*
.mod.c
*
.ko .tmp_versions
endif
用户发送进程:netlink-exam-user-send.c
//
application sender: netlink-exam-user-send.c
#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
unistd.h
>
#include
<
string
.h
>
#include
<
sys
/
types.h
>
#include
<
sys
/
socket.h
>
#include
<
linux学习
/
netlink.h
>
#define
MAX_MSGSIZE 1024
int
main(
int
argc,
char
*
argv[])
...
{
FILE * fp;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr * nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
char text_line[MAX_MSGSIZE];
int ret = - 1 ;
if (argc < 2 ) ... {
printf( " Usage: %s atextfilename " , argv[ 0 ]);
exit( 1 );
}
if ((fp = fopen(argv[ 1 ], " r " )) == NULL) ... {
printf( " File %s dosen't exist. " );
exit( 1 );
}
sd = socket(AF_NETLINK, SOCK_RAW, 30 );
memset( & saddr, 0 , sizeof (saddr));
memset( & daddr, 0 , sizeof (daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 0 ;
bind(sd, ( struct sockaddr * ) & saddr, sizeof (saddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0 ;
daddr.nl_groups = 0 ;
nlhdr = ( struct nlmsghdr * )malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (fgets(text_line, MAX_MSGSIZE, fp)) ... {
memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
memset( & msg, 0 , sizeof ( struct msghdr));
nlhdr -> nlmsg_len = NLMSG_LENGTH(strlen(text_line));
nlhdr -> nlmsg_pid = getpid(); /**/ /* self pid */
nlhdr -> nlmsg_flags = 0 ;
iov.iov_base = ( void * )nlhdr;
iov.iov_len = nlhdr -> nlmsg_len;
msg.msg_name = ( void * ) & daddr;
msg.msg_namelen = sizeof (daddr);
msg.msg_iov = & iov;
msg.msg_iovlen = 1 ;
ret = sendmsg(sd, & msg, 0 );
if (ret == - 1 ) ... {
perror( " sendmsg error: " );
}
}
close(sd);
return 0 ;
}
用户接收进程:netlink-exam-user-recv.c
//
application sender: netlink-exam-user-send.c
#include
<
stdio.h
>
#include
<
stdlib.h
>
#include
<
unistd.h
>
#include
<
string
.h
>
#include
<
sys
/
types.h
>
#include
<
sys
/
socket.h
>
#include
<
linux学习
/
netlink.h
>
#define
MAX_MSGSIZE 1024
int
main(
int
argc,
char
*
argv[])
...
{
FILE * fp;
struct sockaddr_nl saddr, daddr;
struct nlmsghdr * nlhdr = NULL;
struct msghdr msg;
struct iovec iov;
int sd;
char text_line[MAX_MSGSIZE];
int ret = - 1 ;
if (argc < 2 ) ... {
printf( " Usage: %s atextfilename " , argv[ 0 ]);
exit( 1 );
}
if ((fp = fopen(argv[ 1 ], " r " )) == NULL) ... {
printf( " File %s dosen't exist. " );
exit( 1 );
}
sd = socket(AF_NETLINK, SOCK_RAW, 30 );
memset( & saddr, 0 , sizeof (saddr));
memset( & daddr, 0 , sizeof (daddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();
saddr.nl_groups = 0 ;
bind(sd, ( struct sockaddr * ) & saddr, sizeof (saddr));
daddr.nl_family = AF_NETLINK;
daddr.nl_pid = 0 ;
daddr.nl_groups = 0 ;
nlhdr = ( struct nlmsghdr * )malloc(NLMSG_SPACE(MAX_MSGSIZE));
while (fgets(text_line, MAX_MSGSIZE, fp)) ... {
memcpy(NLMSG_DATA(nlhdr), text_line, strlen(text_line));
memset( & msg, 0 , sizeof ( struct msghdr));
nlhdr -> nlmsg_len = NLMSG_LENGTH(strlen(text_line));
nlhdr -> nlmsg_pid = getpid(); /**/ /* self pid */
nlhdr -> nlmsg_flags = 0 ;
iov.iov_base = ( void * )nlhdr;
iov.iov_len = nlhdr -> nlmsg_len;
msg.msg_name = ( void * ) & daddr;
msg.msg_namelen = sizeof (daddr);
msg.msg_iov = & iov;
msg.msg_iovlen = 1 ;
ret = sendmsg(sd, & msg, 0 );
if (ret == - 1 ) ... {
perror( " sendmsg error: " );
}
}
close(sd);
return 0 ;
}