zoukankan      html  css  js  c++  java
  • netlink

    用以实现用户进程与内核进程通信
    netlink 套接字的最大特点是对中断过程的支持

    内核与用户数据交换
    sysfs、sysctl、netlink、procfs、seq_file、debugfs和relayfs

    优点

    1. Netlink通过socket API;而ioctl和proc文件系统均需要通过程序加入相应的设备或文件
    2. Netlink使用socket缓存队列,是一种异步通信机制,而ioctl是同步通信机制,如果传输的数据量较大,会影响系统性能
    3. Netlink支持多播,属于一个Netlink组的模块和进程都能获得该多播消息
    4. Netlink允许内核发起会话,而ioctl和系统调用只能由用户空间进程发起

    用户空间

    int socket(int domain, int type, int protocol);

    domain:PF_NETLINK

    struct sockaddr_nl
    {
        sa_family_t    nl_family;
        unsigned short nl_pad;
        __u32          nl_pid;
        __u32          nl_groups;
    };
    
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    nl_pid:接收或发送消息的进程ID,如果希望内核处理消息或多播消息,就把该字段设置为 0,否则设置为处理消息的进程ID(线程使用pthread_self() << 16 | getpid())
    nl_groups:指定多播组。bind 函数把调用进程加入到该字段指定的多播组,如果设置为 0,表示调用者不加入任何多播组

    //netlink消息头
    struct nlmsghdr
    {
      __u32 nlmsg_len;   /* Length of message *///包含头
      __u16 nlmsg_type;  /* Message type*/
      __u16 nlmsg_flags; /* Additional flags */
      __u32 nlmsg_seq;   /* Sequence number */
      __u32 nlmsg_pid;   /* Sending process PID *///源端口
    };
    
    /*总长度*/
    #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
    /*字节对齐*/
    #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

    nlmsg_len:消息的总长度(包含消息头)。一般地,使用NLMSG_LENGTH来计算对齐后的总长度
    nlmsg_type:应用内部定义的消息类型

    #define NLMSG_NOOP              0x1     /* Nothing. */
    #define NLMSG_ERROR             0x2     /* Error */
    #define NLMSG_DONE              0x3     /* End of a dump */
    #define NLMSG_OVERRUN           0x4     /* Data lost */

    nlmsg_flags:附加在消息上的额外说明信息
    nlmsg_seq 和 nlmsg_pid:用于应用追踪消息,前者表示顺序号,后者为消息来源进程 ID

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

    举例

    #include <linux/types.h>
    #include <linux/netlink.h>
    
    #define NL_IMP2 31
    #define IMP2_U_PID 0 //消息类型
    
    static int skfd;
    skfd = socket(PF_NETLINK, SOCK_RAW, NL_IMP2);
    if(skfd < 0)
    {
        printf("can not create a netlink socket
    ");
        exit(0);
    }
    
    struct sockaddr_nl local;
    memset(&local, 0, sizeof(local));
    local.nl_family = AF_NETLINK;
    local.nl_pid = 0; //向内核发送
    local.nl_groups = 0;
    
    if(bind(skfd, (struct sockaddr*)&local, sizeof(local)) != 0)
    {
        printf("bind() error
    ");
        return -1;
    }
    
    struct nlmsghdr hdr;
    memset(&hdr, 0, sizeof(hdr));
    hdr.nlmsg_len = NLMSG_LENGTH(0); //没有多余的数据
    hdr.nlmsg_flags = 0;
    hdr.nlmsg_type = NL_T_XX; //自定义消息类型
    hdr.nlmsg_pid = getpid(); //发送者的PID
    
    sendto(skfd, &hdr, hdr.nlmsg_len, 0, (struct sockaddr*)&local, sizeof(local));
    //接收
    typedef struct
    {
        struct nlmsghdr hdr;
        struct packet_info icmp_info;
    }info;
    
    while(1)
    {
        len = sizeof(struct sockaddr_nl);
        rcvlen = recvfrom(skfd, &info, sizeof(info), 0, (struct sockaddr*)&local, &len);
    }

    内核空间

    struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len));

    unit:netlink协议类型
    input:当有消息到达,会被引用
    sk:和返回值一致

    在input中,你可以直接处理收到的数据,也可以不处理,在大量数据传输的情况下,在input中处理是不明智的,正确的方式应该是建立一个内核线程专门接收数据,没有数据的时候该内核线程睡眠,一旦有了数据,input回调函数唤醒这个内核线程就是了

    内核空间发送数据使用独立创建的sk_buff缓冲区

    #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))
    #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

    NLMSG_ALIGN(len):得到不小于len且字节对齐的最小数值
    NLMSG_LENGTH(len):计算数据部分长度为len时实际的消息长度
    NLMSG_SPACE(len):返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值

    #define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

    取得消息的数据部分首地址

    #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb))
    
    NETLINK_CB(skb).pid = 0;
    NETLINK_CB(skb).dst_pid = 0;
    NETLINK_CB(skb).dst_group = 1;

    pid:发送者进程ID,对于内核,它为0
    dst_pid:接收者进程ID,如果目标为组或内核,它为0
    dst_group:目标组地址,如果目标为某一进程或内核,它为0

    netlink_unicast()发布单播消息

    int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);

    pid:接收消息进程的pid
    nonblock:该函数是否为非阻塞。如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用时睡眠

    void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation);

    group:接收消息的多播组。如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或
    allocation:GFP_ATOMIC用于原子的上下文(即不可以睡眠);GFP_KERNEL用于非原子上下文

    #define NLMSG_PUT(skb, pid, seq, type, len)
    ({ if (skb_tailroom(skb) < (int)NLMSG_SPACE(len)) goto nlmsg_failure; __nlmsg_put(skb, pid, seq, type, len); })
    
    static __inline__ struct nlmsghdr *__nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq, int type, int len)
    {
        struct nlmsghdr *nlh;
        int size = NLMSG_LENGTH(len);
        nlh = (struct nlmsghdr*)skb_put(skb, NLMSG_ALIGN(size));
        nlh->nlmsg_type = type;
        nlh->nlmsg_len = size;
        nlh->nlmsg_flags = 0;
        nlh->nlmsg_pid = pid;
        nlh->nlmsg_seq = seq;
        return nlh;
    }

    程序中应该定义nlmsg_failure标签

    举例

    #include <linux/netfilter_ipv4.h>  
    #include <linux/netlink.h>
    #include <net/sock.h>
    
    nlfd = netlink_kernel_create(NL_IMP2, kernel_receive);
    if(!nlfd)
    {
        printk("can not create a netlink socket/n");
        return -1;
    }
    
    static void kernel_receive(struct sock *sk, int len)
    {
        do
        {
            struct sk_buff *skb;
            while((skb = skb_dequeue(&sk->receive_queue)) != NULL)
            {
                struct nlmsghdr *nlh = NULL;
                if(skb->len >= sizeof(struct nlmsghdr)
                {
                    nlh = (struct nlmsghdr *)skb->data;
                    if((nlh->nlmsg_len >= sizeof(struct nlmsghdr)) && (skb->len >= nlh->nlmsg_len))
                    {
                        //自定义消息
                        if(nlh->nlmsg_type == IMP2_U_PID)       
                        {
                            ;
                        }
                        //socket关闭
                        else if(nlh->nlmsg_type == IMP2_CLOSE)
                        {
                            ;
                        }
                    }
                }
            }
            kfree_skb(skb);
        }while(nlfd && nlfd->receive_queue.qlen);
    }
    
    if(nlfd)
    {
        sock_release(nlfd->socket);
    }
    //发送
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    struct packet_info *packet;
    
    /*计算消息总长*/
    size = NLMSG_SPACE(sizeof(*info));
    
    /*分配一个新的套接字缓存*/
    skb = alloc_skb(size, GFP_ATOMIC);
    
    /*初始化一个netlink消息首部*/
    nlh = NLMSG_PUT(skb, 0, 0, NL_T_XX, size-sizeof(*nlh));
    nlh->nlmsg_len = size;
    
    /*跳过消息首部,指向数据区*/
    packet = NLMSG_DATA(nlh);
    
    /*初始化数据区*/
    memset(packet, 0, sizeof(struct packet_info));
    
    /*填充待发送的数据*/
    packet->src = info->src;
    packet->dest = info->dest;
    
    /*设置控制字段*/
    NETLINK_CB(skb).dst_groups = 0;
    
    /*发送数据到目的pid*/
    ret = netlink_unicast(nlfd, skb, pid, MSG_DONTWAIT);

    2.6内核netlink
    从2.6.24开始,linux内部对netlink的实现机制和调用接口进行了很大的调整,特别是函数 netlink_kernel_create(),最新的参数有6个之多

    extern struct sock *netlink_kernel_create(struct net *net,
                              int unit, unsigned int groups,
                              void (*input)(struct sk_buff *skb),
                              struct mutex *cb_mutex,
                              struct module *module);

    net:使用&init_net,定义在linux/net/core/net_namespace.c 中,此外在linux/include/net/net_namespace.h中也有外部定义,直接作为参数使用即可
    unit:同上
    input:同上,但不需要自己调用调度的方法,让内核进程阻塞以便等待消息到来了
    module:THIS_MODULE,定义在module.h,表示当前模块

  • 相关阅读:
    js简单的下拉选中效果
    简单的JS+HTML+CSS达成图片轮播
    JQ延时对象
    javascript特效----碰壁运动
    PHP---封装
    PHP---面向对象
    PHP----基础
    MySQL查询实例
    JS下拉列表
    MySQL基础操作
  • 原文地址:https://www.cnblogs.com/zhangxuechao/p/11709821.html
Copyright © 2011-2022 走看看