Netlink0004 --- 多播机制的用法

Linux内核netlink机制 - 用户空间和内核空间数据传输 Linux内核netlink机制,数据传输介绍及代码实现。 阅读详情

在上一篇博文中我们所遇到的情况都是用户空间作为消息进程的发起者,Netlink还支持内核作为消息的发送方的情况。这一般用于内核主动向用户空间报告一些内核状态,例如我们在用户空间看到的USB的热插拔事件的通告就是这样的应用。

先说一下我们的目标,内核线程每个一秒钟往一个多播组里发送一条消息,然后用户空间所以加入了该组的进程都会收到这样的消息,并将消息内容打印出来。

Netlink地址结构体中的nl_groups是32位,也就是说每种Netlink协议最多支持32个多播组。如何理解这里所说的每种Netlink协议?在里预定义的如下协议都是Netlink协议簇的具体协议,还有我们添加的NETLINK_TEST也是一种Netlink协议。

#define NETLINK_TEST 20 /* 用户添加的自定义协议 */

 在我们自己添加的NETLINK_TEST协议里,同样地,最多允许我们设置32个多播组,每个多播组用1个比特表示,所以不同的多播组不可能出现重复。你可以根据自己的实际需求,决定哪个多播组是用来做什么的。用户空间的进程如果对某个多播组感兴趣,那么它就加入到该组中,当内核空间的进程往该组发送多播消息时,所有已经加入到该多播组的用户进程都会收到该消息。

 再回到我们Netlink地址结构体里的nl_groups成员,它是多播组的地址掩码,注意是掩码不是多播组的组号。如何根据多播组号取得多播组号的掩码呢?在af_netlink.c中有个函数:

static u32 netlink_group_mask(u32 group)
{
    return group ? 1 << (group - 1) : 0;
}

也就是说,在用户空间的代码里,如果我们要加入到多播组1,需要设置nl_groups设置为1;多播组2的掩码为2;多播组3的掩码为4,依次类推。为0表示我们不希望加入任何多播组。理解这一点很重要。所以我们可以在用户空间也定义一个类似于netlink_group_mask()的功能函数,完成从多播组号到多播组掩码的转换。最终用户空间的代码如下:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>

#define MAX_PAYLOAD 1024 // Netlink消息的最大载荷的长度

unsigned int netlink_group_mask(unsigned int group)
{
    return group ? 1 << (group - 1) : 0;
}

int main(int argc, char* argv[])
{
    struct sockaddr_nl src_addr;
    struct nlmsghdr *nlh = NULL;
    struct iovec iov;
    struct msghdr msg;
    int sock_fd, retval;

    // 创建Socket
    sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
    if(sock_fd == -1){
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = PF_NETLINK;
    src_addr.nl_pid = 0; // 表示我们要从内核接收多播消息。注意:该字段为0有双重意义,另一个意义是表示我们发送的数据的目的地址是内核。
    src_addr.nl_groups = netlink_group_mask(atoi(argv[1])); // 多播组的掩码,组号来自我们执行程序时输入的第一个参数

    // 因为我们要加入到一个多播组,所以必须调用bind()。
    retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
    if(retval < 0){
        printf("bind failed: %s", strerror(errno));
        close(sock_fd);
        return -1;
    }

    // 为接收Netlink消息申请存储空间
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    if(!nlh){
        printf("malloc nlmsghdr error!\n");
        close(sock_fd);
        return -1;
    }

    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
    iov.iov_base = (void *)nlh;
    iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);

    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    // 从内核接收消息
    printf("waitinf for...\n");
    recvmsg(sock_fd, &msg, 0);
    printf("Received message: %s \n", NLMSG_DATA(nlh));
    
    close(sock_fd);

    return 0;
}

可以看到,用户空间的程序基本没什么变化,唯一需要格外注意的就是Netlink地址结构体中的nl_groups的设置。由于对它的解释很少,加之没有有效的文档,所以我也是一边看源码,一边在网上搜集资料。有分析不当之处,还请大家帮我指出。

  内核空间我们添加了内核线程和内核线程同步方法completion的使用。内核空间修改后的代码如下:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <net/netlink.h> 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Koorey King");

struct sock *nl_sk = NULL;
static struct task_struct *mythread = NULL; //内核线程对象

//向用户空间发送消息的接口
void sendnlmsg(char *message/*,int dstPID*/)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;
    int len = NLMSG_SPACE(MAX_MSGSIZE);
    int slen = 0;

    if(!message || !nl_sk){
        return;
    }

    // 为新的 sk_buffer申请空间
    skb = alloc_skb(len, GFP_KERNEL);
    if(!skb){
        printk(KERN_ERR "my_net_link: alloc_skb Error./n");
        return;
    }

    slen = strlen(message)+1;

    //用nlmsg_put()来设置netlink消息头部
    nlh = nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0);

    // 设置Netlink的控制块里的相关信息
    NETLINK_CB(skb).pid = 0; // 消息发送者的id标识,如果是内核发的则置0
    NETLINK_CB(skb).dst_group = 5; //多播组号为5,但置成0好像也可以。

    message[slen] = '\0';
    memcpy(NLMSG_DATA(nlh), message, slen+1);

    //通过netlink_unicast()将消息发送用户空间由dstPID所指定了进程号的进程
    //netlink_unicast(nl_sk,skb,dstPID,0);
    netlink_broadcast(nl_sk, skb, 0,5, GFP_KERNEL); //发送多播消息到多播组5,这里我故意没有用1之类的“常见”值,目的就是为了证明我们上面提到的多播组号和多播组号掩码之间的对应关系
    printk("send OK!\n");
    return;
}

//每隔1秒钟发送一条“I am from kernel!”消息,共发10个报文
static int sending_thread(void *data)
{
     int i = 10;
     struct completion cmpl;
     while(i--){
            init_completion(&cmpl);
            wait_for_completion_timeout(&cmpl, 1 * HZ);
            sendnlmsg("I am from kernel!");
     }
     printk("sending thread exited!");
     return 0;
}

static int __init myinit_module()
{
    printk("my netlink in\n");
    nl_sk = netlink_kernel_create(NETLINK_TEST,0,NULL,THIS_MODULE);

    if(!nl_sk){
        printk(KERN_ERR "my_net_link: create netlink socket error.\n");
        return 1;
    }

    printk("my netlink: create netlink socket ok.\n");
    mythread = kthread_run(sending_thread,NULL,"thread_sender");
    return 0;
}

static void __exit mycleanup_module()
{
    if(nl_sk != NULL){
        sock_release(nl_sk->sk_socket);
}
printk("my netlink out!\n");
}

module_init(myinit_module);
module_exit(mycleanup_module);

关于内核中netlink_kernel_create(int unit, unsigned int groups,…)函数里的第二个参数指的是我们内核进程最多能处理的多播组的个数,如果该值小于32,则默认按32处理,所以在调用netlink_kernel_create()函数时可以不用纠结第二个参数,一般将其置为0就可以了。

 在skbuff{}结构体中,有个成员叫做"控制块",源码对它的解释如下:

struct sk_buff {
    /* These two members must be first. */
    struct sk_buff        *next;
    struct sk_buff        *prev;
    … …
    /*
     * This is the control buffer. It is free to use for every
     * layer. Please put your private variables there. If you
     * want to keep them across layers you have to do a skb_clone()
     * first. This is owned by whoever has the skb queued ATM.
     */
    char            cb[48];

    … …
}

当内核态的Netlink发送数据到用户空间时一般需要填充skbuff的控制块,填充的方式是通过强制类型转换,将其转换成struct netlink_skb_parms{}之后进行填充赋值的:

struct netlink_skb_parms
{
    struct ucred        creds;        /* Skb credentials    */
    __u32            pid;
    __u32            dst_group;
    kernel_cap_t        eff_cap;
    __u32            loginuid;    /* Login (audit) uid */
    __u32            sid;        /* SELinux security id */
};

  填充时的模板代码如下:

NETLINK_CB(skb).pid=xx;
NETLINK_CB(skb).dst_group=xx;

 这里要注意的是在Netlink协议簇里提到的skbuff的cb控制块里保存的是属于Netlink的私有信息。怎么讲,就是Netlink会用该控制块里的信息来完成它所提供的一些功能,只是完成Netlink功能所必需的一些私有数据。打个比方,以开车为例,开车的时候我们要做的就是打火、控制方向盘、适当地控制油门和刹车,车就开动了,这就是汽车提供给我们的“功能”。汽车的发动机,轮胎,传动轴,以及所用到的螺丝螺栓等都属于它的“私有”数据cb。汽车要运行起来这些东西是不可或缺的,但它们之间的协作和交互对用户来说又是透明的。就好比我们Netlink的私有控制结构struct netlink_skb_parms{}一样。

目前我们的例子中,将NETLINK_CB(skb).dst_group设置为相应的多播组号和0效果都是一样,用户空间都可以收到该多播消息,原因还不是很清楚,还请Netlink的大虾们帮我点拨点拨。

 编译后重新运行,最后的测试结果如下:

 注意,这里一定要先执行insmod加载内核模块,然后再运行用户空间的程序。如果没有加载mynlkern.ko而直接执行./test 5在bind()系统调用时会报如下的错误:

       bind failed: No such file or directory

因为网上有写文章在讲老版本Netlink的多播时用法时先执行了用户空间的程序,然后才加载内核模块,现在(2.6.21)已经行不通了,这一点请大家注意。

       小结:通过这三篇博文我们对Netlink有了初步的认识,并且也可以开发基于Netlink的基本应用程序。但这只是冰山一角,要想写出高质量、高效率的软件模块还有些差距,特别是对Netlink本质的理解还需要提高一个层次,当然这其中牵扯到内核编程的很多基本功,如临界资源的互斥、线程安全性保护、用Netlink传递大数据时的处理等等都是开发人员需要考虑的问题。

Cadence OrCAD常见引脚错误排查指南:从原理图到封装的完整解决方案 本文详细解析Cadence OrCAD设计中常见的引脚错误问题,提供从原理图到封装的系统化解决方案。涵盖错误诊断、元件库修复、封装管理及自动化处理技巧,帮助工程师高效解决ORCAP-36022等典型错误,确保设计流程顺畅。 阅读详情

相关推荐

Python Pygame 游戏开发中的状态机设计

在游戏开发中,一个游戏往往包含多个不同的场景和状态,例如主菜单、游戏关卡、暂停界面、游戏结束界面等。状态机设计的目的就是为了有效地管理这些不同的状态,使得游戏的逻辑更加清晰、易于维护和扩展。本文的范围主要聚焦于如何在 Python Pygame 这个游戏开发库中实现和应用状态机设计,涵盖了从基本概念到实际项目应用的各个方面。本文将按照以下结构进行组织:首先介绍状态机的核心概念和联系,包括其原理和架构;接着详细讲解核心算法原理和具体操作步骤,并通过 Python 代码进行说明;

AI Python 编程之道的博客 1236

用户空间和内核空间通讯之【Netlink 下】

关于Netlink多播机制的用法         在上一篇博文中我们所遇到的情况都是用户空间作为消息进程的发起者,Netlink还支持内核作为消息的发送方的情况。这一般用于内核主动向用户空间报告一些内核状态,例如我们在用户空间看到的USB的热插拔事件的通告就是这样的应用。        先说一下我们的目标,内核线程每个一秒钟往一个多播组里发送一条消息,然后用户空间所以加入了该组的进程都会收到这

积水成渊 1413

【没有网络,安装python包】如何从 PyPI 下载并安装 Plotly 的离线包

在某些情况下,您可能需要在没有互联网连接的环境中安装 Python 包,例如在飞机上、在某些受限制的网络环境中或在需要离线安装的场景下。以下是如何在 PyPI(Python Package Index)下载 Plotly 包并进行离线安装的详细步骤。

赖德发的博客 1102

netlink接口

1、netlink及相关接口说明 说到驱动和应用层进行数据通信,很多人就会想到使用传统的ioctl,诚然,很多时候应用层调用驱动进行参数配置和参数获取使用的也正是ioctl的方式,但这种方式有一种局限性,那就是只适合于数据量较小且操作不频繁的情况。否则,频繁操作io将大大影响设备的性能,甚至造成设备宕机。 最近在做一个关于wifi诊断需求时,需要记录wifi连接过程中的控制帧和管理帧,以及STA断开及断开原因。由于wifi驱动不断在进行数据帧和控制帧的交互,如果直接在驱动...

chylovecsd的博客 909

FIFO应用于UART串口(含代码)

FIFO(First in first out)即先入先出,可以把它理解成一个缓存器,对于收发两端速度不一致,位宽不一致的情况可以使用FIFO作为收发两端中间的“调节剂”使数据可以进行正常发送。

m0_58544042的博客 2747

AutoCAD2016安装破解教程

AutoCAD2016安装破解教程。本人亲自实验,破解成功,有效。以64位为例。 工具/原料   笔记本电脑 AutoCAD2016安装包 AutoCAD2016注册机(xf-adsk2016_x64.exe)链接:http://pan.baidu.com/s/1cAZQA windows7/8/8.1/10-32...

weixin_33804990的博客 1万+

linux socket recv函数如何判断收完一包_linux用户空间与内核空间通信——Netlink通信机制

一:什么是Netlink通信机制Netlink是linux提供的用于内核和用户态进程之间的通信方式。但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。那么Netlink有什么优势呢?一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netli...

weixin_39628380的博客 559

(超详细)Qt入门和配置Visual Studio的Qt环境

内容写了Qt入门,创建并编写第一个Qt程序,以及配置VS编写Qt程序的环境。(超级详细)

LY549826LY549264的博客 7677

RHEL6 安装ipvsadm-1.26

报错:1 [root@lvs1 ipvsadm-1.26]# makemake -C libipvsmake[1]: Entering directory `/tmp/ipvsadm-1.26/libipvs'gcc -Wall -Wunused -Wstrict-prototypes -g -fPIC -DLIBIPVS_USE_NL -DHAVE_NET_IP_VS_H -c -o lib...

weixin_33782386的博客 306

常见卫星图源下载教程

上面列出的8个常见的亚米级别数据源都可以通过xyz协议或者wmts/wms协议进行下载。这些协议是什么?地理信息数据为了方便网络传输,制定了一系列的地理数据传输标准(wms/wmts/xyz等)。协议特点优点缺点栅格数据支持矢量数据支持WMS通过HTTP请求获取GIS数据,服务器会将数据渲染成图像高度定制的地图;交互性强服务器渲染图像,响应时间较长支持有限支持WMTS提供预渲染的地图瓦片速度快,因为地图瓦片可以预先生成并在请求时快速提供瓦片的内容是预先确定的,无法进行定制支持。

锐多宝的地理空间 1万+

003、高通骁龙Spectra ISP架构深度解析:IFE/BPS/IPE流水线与Chromatix调优实战

摘要: 本文深入解析高通骁龙Spectra ISP架构,重点剖析IFE、BPS、IPE三阶段的流水线协作机制与Chromatix调优实战。通过夜景预览"水波纹"案例,揭示HDR对齐与TNR运动估计的关联性问题,指出ISP调优需关注模块间的时序与带宽约束。文章详解各模块核心功能:IFE负责RAW域处理(HDR合成/校正),BPS专注降噪与细节增强,IPE进行YUV域精处理。针对Chromatix调优,强调参数与寄存器映射关系,并给出LSC网格密度、TNR运动灵敏度、LTM光晕等典型问题的解决方案。最后提出实用

zzfmlxs的博客 228

IPVS

http://www.linuxvirtualserver.org/software/index.html

脚步6978 548

基于学术研究于NASA官网GPM卫星数据下载详细教程

基于学术研究于NASA官网GPM卫星数据下载详细教程

珞瑜的博客 8654

main 未定义 但是已经有main 函数了_Linux内核启动:main函数设置根设备并规划系统内存...

1. 楔子 从现在开始执行 main()函数! 目的就是让用户程序能够以"进程"的方式正常运行。能够实现这一目的的标准包括三方面的内容:1. 用户程序能够在主机上进行运算,1. 能够与外设进行交互1. 能够让用户以它为媒介进行人机交互。所以main函数需要完成对设备环境进行初始化,并激活第一个进程——进程 0。用户进程运行应该互不干扰,需要要靠系统给进程设计一套"边界" 来对其进行保护。 这套"边...

weixin_39955732的博客 407

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Netlink组播机制

文章目录数据结构协议对象: netlink_table传输控制块: netlink_sock组播发送控制信息: netlink_broadcast_data协议注册时套接字绑定时netlink_realloc_groups()更新签约信息: netlink_update_subscriptions()更新监听信息: netlink_update_listeners()套接字连接时套接字发送时do_one_broadcast()小结 数据结构 下面仅罗列了和组播机制相关的数据结构及其字段。 协议对象: net

九天小哥的专栏 3437

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

netlink 通信广播demo

最近在做工程时,用到了内核间通信,需要把内核部分得到发送至用户空间。下面给出demo例子: 分为内核部分和用户空间部分: 首先在内核部分增加一个netlink消息类型 #define NETLINK_URL 23 创建内核的netlink socket static struct sock *nlfd; struct netlink_kernel_cfg cfg =

liangds的专栏 1264

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

linux 用户态和内核态通信之netlink机制介绍与实例

0、前言 由于开发需要,用户态要时刻监视内核态状态,以便检测上层应用知道磁盘插拔状态,以便动态管理磁盘。我们可以通过各种各样的用户态和内核态的IPC(interprocess communication)机制来实现。比如系统调用,ioctl接口,proc文件系统以及netlink socket来获取内核态状态信息。本文主要是netlink socket有关知识。 1、net...

阿Sir问道 849

交叉编译hostap时遇到 fatal error: netlink/genl/genl.h: No such file or directory 17

看见有的博主说安装libnl-3-dev和libnl-genl-3-dev可以解决问题,但是我的系统里已经安装了这两个库,系统提示不需要安装.因为genl.h 是安装在libnl-3-dev:/usr/include/libnl3/netlink/genl/genl. h。我的系统是x86-64的,iMX8的运行环境是arm64,所以需要进行交叉编译。然后突发奇想,就在.config 文件里面增加了这个路径。问题就可以完美解决了。

mason_yang_csdn的博客 1926

Linux驱动-Netlink通信

netlink通信用于用户层与内核之间的通信

妄想吃透Linux内核源码的xxs 1727

netlink实现驱动和应用层通信

##1、netlink及相关接口说明## &amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp

zouchun2016的博客 4266

hostapd致命错误:netlink/genl/genl.h: 没有此类文件或者目录

hostapd致命错误:netlink/genl/genl.h: 没有此类文件或者目录 问题: 我正在交叉编译hostapd的openwrt 。 我正在 fatal error: netlink/genl/genl.h: No such file or directory 我已经安装了 libnl-3.2.24. 如果我直接编译( 不交叉编译),那么hostapd就会正确编译而没有任何错误。 回答...

weixin_43845335的博客 1万+

linux内核与用户空间通信机制——Netlink通信

可以参考这个链接的文章了解:https://blog.youkuaiyun.com/zhao_h/article/details/80943226 netlink是一种用于内核态和用户态进程之间进行数据传输的特殊的IPC机制。它的特点是: (1) 用户态采用socket风格的API (2)除了预定义的协议类型之外,支持自定义协议类型 (3)异步通讯 (4)支持消息组播 (5)全双工(特别是支持内核...

weixin_39094034的博客 4554

linux 内核与用户空间通信之netlink使用方法

Linux中的进程间通信机制源自于Unix平台上的进程通信机制。Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上的各有所不同,前者形成了运行在单个计算机上的System V IPC,后者则实现了基于socket的进程间通信机制。同时Linux也遵循IEEE制定的Posix IPC标准,在三者的基础之上实现了以下几种主要的IPC机制:管道(Pipe)及命名管道(Named

haomcu的专栏 4万+

2020-04-25

nlmsg_put分析 添加netlink消息头部到netlink消息 参数 n,netlink消息 pid,netlink进程id或者NL_AUTO_PID seq,消息的序列号或者NL_AUTO_SEQ type,消息类型 payload,消息载体的长度 flags,消息标志 struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, ...

qq_41224661的博客 620

linux link 函数,Linux Netlink基本使用

好了,有了这个例子我们就大概知道用户态怎么使用Netlink了,至于我们没有用到的nl_groups等其他信息后面讲到再说,下面看下内核是怎么处理Netlink的。4. 内核 Netlink api4.1创建 netlink socketstruct sock *netlink_kernel_create(struct net *net,int unit,unsigned int groups,v...

weixin_39623805的博客 418

netlink套接字实现与使用方法详解(用户态/内核态)

https://zhuanlan.zhihu.com/p/269141945

zhaominyong的专栏 2806
上一篇: Netlink 0003 -- Netlink动手实践
下一篇: netlink 0005 -- Generic Netlink详解
废言Pro
博客等级 码龄16年 1092粉丝 142原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值