Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2092419
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2011-10-25 16:38:50

在 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 
+=   - - 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 ;
}

转自:http://blog.csdn.net/pulpdog/article/details/5956938

阅读(1652) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~