【C语言】Infiniband驱动__mlx4_init_one函数

一、注释

Linux内核驱动程序中的部分,属于Mellanox网卡驱动mlx4的初始化过程。

// Mellanox 以太网驱动主程序代码
static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data,
               struct mlx4_priv *priv)
{
    int err; // 错误码变量
    int nvfs[MLX4_MAX_PORTS + 1] = {0, 0, 0}; // 存储每个端口的VF数量
    int prb_vf[MLX4_MAX_PORTS + 1] = {0, 0, 0}; // 存储每个端口要检查(probe)的VF数量
    const int param_map[MLX4_MAX_PORTS + 1][MLX4_MAX_PORTS + 1] = {
        // 参数映射表,用来导航nvfs和prb_vf数组
        {2, 0, 0}, {0, 1, 2}, {0, 1, 2} };
    unsigned total_vfs = 0; // 总的VF数量
    unsigned int i;
    // 从命令行或模块参数中获取每个端口的VF数量和检查数量的参数个数(argc)
    int num_vfs_argc = mlx4_get_argc(num_vfs.dbdf2val.tbl, pci_physfn(pdev));
    int probe_vfs_argc = mlx4_get_argc(probe_vf.dbdf2val.tbl, pci_physfn(pdev));

    // 输出初始化信息到日志
    pr_info(DRV_NAME ": Initializing %s\n", pci_name(pdev));

    // 尝试启用PCI设备并分配所需的资源
    err = mlx4_pci_enable_device(&priv->dev);
    if (err) {
        dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
        return err;
    }

    // 通过param_map数组,根据参数个数来设置nvfs数组中对应端口的VF数量
    for (i = 0; i < num_vfs_argc; i++) {
        total_vfs += nvfs[param_map[num_vfs_argc - 1][i]]; // 计算总的VF数量
        int *cur_nvfs = &nvfs[param_map[num_vfs_argc - 1][i]]; // 当前端口的VF数量的指针

        // 获取每个端口的VF数量
        mlx4_get_val(num_vfs.dbdf2val.tbl, pci_physfn(pdev), i,
                 cur_nvfs);
        // 判断获取的VF数量是否有效
        if (*cur_nvfs < 0) {
            dev_err(&pdev->dev, "num_vfs module parameter cannot be negative\n");
            err = -EINVAL;
            goto err_disable_pdev;
        }
    }
    // 针对probe_vf的参数进行类似的操作
    // ... (之后部分的代码相似逻辑,略去了详细的注释以避免过长)

    // 以下包含初始化设备、请求PCI资源、设置PCI DMA掩码、初始化设备固件特性等步骤
    // 如果在初始化流程中的任何步骤遇到错误,都会跳转到相应的错误处理代码,释放资源,并返回错误码

    return 0; // 如果一切正常,则返回0表示成功

// 错误处理部分,分别对应于初始化过程中可能出错的几个阶段:
err_catas:
    mlx4_catas_end(&priv->dev);

err_crdump:
    mlx4_crdump_end(&priv->dev);

err_release_regions:
    pci_release_regions(pdev);

err_disable_pdev:
    mlx4_pci_disable_device(&priv->dev);
    return err;
}

这里需要注意的是,上述代码片段已经经过一定程度的简化和摘录,并未反映整个函数的完整功能。这些代码是在内核级别操作硬件设备,涉及到PCI设备初始化、资源申请、参数配置以及错误处理等多个复杂的过程。在真实环境中,理解和修改这些代码通常需要具备一定的内核编程经验和对硬件设备的深入了解。

用于初始化Mellanox MLX4驱动中一个PCI设备的函数。下面是该段代码的完整中文注释:

// drivers\net\ethernet\mellanox\mlx4\main.c
// 定义一个函数用于初始化MLX4设备
static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data,
                           struct mlx4_priv *priv)
{
    int err; // 保存错误状态
    // 为每个端口初始化虚拟功能(VF)数量为0
    int nvfs[MLX4_MAX_PORTS + 1] = {0, 0, 0};
    // 初始化一个数组,用于存储每个端口将要激活的VF数量
    int prb_vf[MLX4_MAX_PORTS + 1] = {0, 0, 0};
    // 参数映射表,用于将num_vfs和probe_vf参数和实际端口对应起来
    const int param_map[MLX4_MAX_PORTS + 1][MLX4_MAX_PORTS + 1] = {
        {2, 0, 0}, {0, 1, 2}, {0, 1, 2}};
    unsigned total_vfs = 0; // 总的VF数量
    unsigned int i; // 循环计数器
    // 从命令行参数获取VF的数量,并存储相关端口的VF数量
    int num_vfs_argc =
        mlx4_get_argc(num_vfs.dbdf2val.tbl, pci_physfn(pdev));
    // 获取希望进行探测的VF数量
    int probe_vfs_argc =
        mlx4_get_argc(probe_vf.dbdf2val.tbl, pci_physfn(pdev));
    // 记录设备正在初始化
    pr_info(DRV_NAME ": Initializing %s\n", pci_name(pdev));
    // 允许PCI设备
    err = mlx4_pci_enable_device(&priv->dev);
    if (err) {
        // 如果无法启动PCI设备,则记录错误并退出
        dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
        return err;
    }
    // 遍历num_vfs相关端口,并计算总VF数量
    for (i = 0; i < num_vfs_argc;
         total_vfs += nvfs[param_map[num_vfs_argc - 1][i]], i++) {
        int *cur_nvfs = &nvfs[param_map[num_vfs_argc - 1][i]];
        // 获取当前端口的VF数量
        mlx4_get_val(num_vfs.dbdf2val.tbl, pci_physfn(pdev), i,
                     cur_nvfs);
        // 如果该数量为负,则记录错误信息然后禁用PCI设备
        if (*cur_nvfs < 0) {
            dev_err(&pdev->dev, "num_vfs module parameter cannot be negative\n");
            err = -EINVAL;
            goto err_disable_pdev;
        }
    }
    // 遍历希望探测的VF的端口
    for (i = 0; i < probe_vfs_argc; i++) {
        int *cur_prbvf = &prb_vf[param_map[probe_vfs_argc - 1][i]];
        // 获取当前端口希望探测的VF数量
        mlx4_get_val(probe_vf.dbdf2val.tbl, pci_physfn(pdev), i,
                     cur_prbvf);
        // 如果该数量为负,则记录错误信息然后禁用PCI设备
        if (*cur_prbvf < 0) {
            dev_err(&pdev->dev, "probe_vf module parameter cannot be negative\n");
            err = -EINVAL;
            goto err_disable_pdev;
        }
    }
    // 检查探测的VF数量是否不大于每个端口的VF数量
    for (i = 0; i < ARRAY_SIZE(nvfs); i++) {
        if (prb_vf[i] > nvfs[i]) {
            dev_err(&pdev->dev, "probe_vf module parameter cannot be greater than num_vfs\n");
            err = -EINVAL;
            goto err_disable_pdev;
        }
    }
    // 检查总VF数量是否超出硬件所支持的最大数目
    if (total_vfs > MLX4_MAX_NUM_VF) {
        dev_err(&pdev->dev,
                "Requested more VF's (%d) than allowed by hw (%d)\n",
                total_vfs, MLX4_MAX_NUM_VF);
        err = -EINVAL;
        goto err_disable_pdev;
    }
    // 检查每个端口的VF数量是否超出单个端口所支持的最大数目
    for (i = 0; i < MLX4_MAX_PORTS; i++) {
        if (nvfs[i] + nvfs[2] > MLX4_MAX_NUM_VF_P_PORT) {
            dev_err(&pdev->dev,
                    "Requested more VF's (%d) for port (%d) than allowed by driver (%d)\n",
                    nvfs[i] + nvfs[2], i + 1,
                    MLX4_MAX_NUM_VF_P_PORT);
            err = -EINVAL;
            goto err_disable_pdev;
        }
    }
    // 检查基址寄存器(BARs,Base Address Register)是否存在
    if (!(pci_dev_data & MLX4_PCI_DEV_IS_VF) &&
        !(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
        dev_err(&pdev->dev, "Missing DCS, aborting (driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
                pci_dev_data, pci_resource_flags(pdev, 0));
        err = -ENODEV;
        goto err_disable_pdev;
    }
    // 检查第二个BAR是否存在
    if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
        dev_err(&pdev->dev, "Missing UAR, aborting\n");
        err = -ENODEV;
        goto err_disable_pdev;
    }

    // 请求PCI资源
    err = pci_request_regions(pdev, DRV_NAME);
    if (err) {
        dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n");
        goto err_disable_pdev;
    }

    // 设置设备为PCI主设备
    pci_set_master(pdev);

    // 尝试设置64位DMA掩码
    err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
    if (err) {
        dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
        // 如果设置64位DMA掩码失败,则尝试设置32位DMA掩码
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
            dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
            goto err_release_regions;
        }
    }
    // 类似地,尝试设置64位一致性DMA掩码
    err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
    if (err) {
        dev_warn(&pdev->dev, "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
        // 如果设置失败,则尝试设置32位一致性DMA掩码
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
            dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, aborting\n");
            goto err_release_regions;
        }
    }

    // 设置DMA段的最大大小为1GB,这是固件支持的限制
    dma_set_max_seg_size(&pdev->dev, 1024 * 1024 * 1024);

    // 检查该设备是否是一个虚拟功能(VF)
    if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
        // 如果是物理功能(PF),通常我们会跳过VF,除非有明确的要求来探测它们
        if (total_vfs) {
            unsigned vfs_offset = 0;

            // 遍历所有VF,查找与当前设备匹配的VF
            for (i = 0; i < ARRAY_SIZE(nvfs) &&
                 vfs_offset + nvfs[i] < extended_func_num(pdev);
                 vfs_offset += nvfs[i], i++)
                ;
            if (i == ARRAY_SIZE(nvfs)) {
                err = -ENODEV;
                goto err_release_regions;
            }
            // 如果本地VF编号大于要探测的VF编号,就跳过这个VF
            if ((extended_func_num(pdev) - vfs_offset)
                > prb_vf[i]) {
                dev_warn(&pdev->dev, "Skipping virtual function:%d\n",
                         extended_func_num(pdev));
                err = -ENODEV;
                goto err_release_regions;
            }
        }
    }

    // 初始化核心转储和错误检测机制
    err = mlx4_crdump_init(&priv->dev);
    if (err)
        goto err_release_regions;
    err = mlx4_catas_init(&priv->dev);
    if (err)
        goto err_crdump;

    // 调用mlx4_load_one来加载并初始化一台MLX4设备
    err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv, 0);
    if (err)
        goto err_catas;

    return 0;

// 如果中间过程出现错误,则会进行错误处理并释放资源
err_catas:
    mlx4_catas_end(&priv->dev);

err_crdump:
    mlx4_crdump_end(&priv->dev);

err_release_regions:
    pci_release_regions(pdev);

err_disable_pdev:
    mlx4_pci_disable_device(&priv->dev);
    return err;
}

从BARs(Base Address Register)的检查开始,后面代码的另一个注释:

    // 检查基址寄存器(BARs)是否存在,如果是PF且0号BAR不是内存类型则报错并退出
    if (!(pci_dev_data & MLX4_PCI_DEV_IS_VF) &&
        !(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
        dev_err(&pdev->dev, "Missing DCS, aborting (driver_data: 0x%x, pci_resource_flags(pdev, 0):0x%lx)\n",
                pci_dev_data, pci_resource_flags(pdev, 0));
        err = -ENODEV;
        goto err_disable_pdev;
    }
    // 检查2号基址寄存器,如果不是内存类型则报错并退出
    if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
        dev_err(&pdev->dev, "Missing UAR, aborting\n");
        err = -ENODEV;
        goto err_disable_pdev;
    }

    // 为PCI设备请求寄存器空间
    err = pci_request_regions(pdev, DRV_NAME);
    if (err) {
        dev_err(&pdev->dev, "Couldn't get PCI resources, aborting\n");
        goto err_disable_pdev;
    }

    // 设置PCI设备为主设备(Bus Master)
    pci_set_master(pdev);

    // 设置PCI设备的DMA遮罩为64位
    err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
    if (err) {
        dev_warn(&pdev->dev, "Warning: couldn't set 64-bit PCI DMA mask\n");
        // 如果无法设置为64位,则尝试设置为32位
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
            dev_err(&pdev->dev, "Can't set PCI DMA mask, aborting\n");
            goto err_release_regions;
        }
    }
    // 设置一致性(consistent)DMA遮罩为64位
    err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
    if (err) {
        dev_warn(&pdev->dev, "Warning: couldn't set 64-bit consistent PCI DMA mask\n");
        // 如果无法设置为64位,则尝试设置为32位
        err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
        if (err) {
            dev_err(&pdev->dev, "Can't set consistent PCI DMA mask, aborting\n");
            goto err_release_regions;
        }
    }

    // 设置DMA传输的最大段大小为1GB
    dma_set_max_seg_size(&pdev->dev, 1024 * 1024 * 1024);

    // 检测当前设备是否为虚拟功能(VF)
    if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
        // 如果是PF且有VF需要处理,则跳过所有VF,除非明确指定要探测它们
        if (total_vfs) {
            unsigned vfs_offset = 0;

            // 计算跳过的VF数量
            for (i = 0; i < ARRAY_SIZE(nvfs) &&
                 vfs_offset + nvfs[i] < extended_func_num(pdev);
                 vfs_offset += nvfs[i], i++)
                ;
            if (i == ARRAY_SIZE(nvfs)) {
                err = -ENODEV;
                goto err_release_regions;
            }
            // 如果要探测的VF超出范围,则跳过这个VF
            if ((extended_func_num(pdev) - vfs_offset)
                > prb_vf[i]) {
                dev_warn(&pdev->dev, "Skipping virtual function:%d\n",
                         extended_func_num(pdev));
                err = -ENODEV;
                goto err_release_regions;
            }
        }
    }

    // 初始化crash dump所需资源
    err = mlx4_crdump_init(&priv->dev);
    if (err)
        goto err_release_regions;
    // 初始化设备故障检测
    err = mlx4_catas_init(&priv->dev);
    if (err)
        goto err_crdump;

    // 加载设备所需资源和配置
    err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv, 0);
    if (err)
        goto err_catas;

    // 初始化成功,返回0
    return 0;

    // 下面是错误处理部分,执行清理工作并返回错误码
err_catas:
    mlx4_catas_end(&priv->dev);

err_crdump:
    mlx4_crdump_end(&priv->dev);

err_release_regions:
    pci_release_regions(pdev);

err_disable_pdev:
    mlx4_pci_disable_device(&priv->dev);
    return err;
}

这个函数主要分为以下几个部分:
1. 变量声明和初始化:定义错误变量`err`,并且初始化一些数组比如`nvfs`和`prb_vf`来保存端口的虚拟功能数量和探测虚拟功能数量。
2. 设备初始化前置检查:包括打印信息,检查PCI设备是否能够被激活,检查命令行参数(如果`num_vfs`或`probe_vf`参数是负数,则报错)。
3. 检查虚拟功能(VF)数量的合法性:验证总VF数量和每个端口的VF数量是否超过硬件和驱动程序允许的最大VF数量。
4. 基址寄存器(BARs)检查:确保设备具有必要的I/O内存资源。
5. PCI资源请求和DMA配置:请求PCI资源,设置PCI总线主模式,配置DMA掩码(用于64位或32位DMA映射)和最大DMA段大小。
6. 特殊设备处理:如果PCI设备是一个虚拟功能,而且没有明确的探测要求,那就跳过一些设置步骤。
7. 设备启动:调用多个MLX4系列函数,比如`mlx4_crdump_init`,`mlx4_catas_init`,`mlx4_load_one`来加载和初始化设备。
8. 错误处理:一系列的错误处理标签(`err_catas`,`err_crdump`,`err_release_regions`,`err_disable_pdev`),分别对应不同阶段的初始化失败处理,包括释放资源和禁用设备等。
每一个部分都精心设计以确保驱动装载在正确初始化硬件之前进行了多项检查,包括参数有效性检查、资源分配、以及其他硬件兼容性检查。任何环节失败,都会执行适当的资源释放和错误回报,并中止初始化过程。

二、讲解

这段代码是运行在Linux内核环境下,负责初始化某型号的Mellanox以太网卡的PCI设备的函数。其主要作用是配置PCI设备以及准备硬件操作所需资源。下面我将逐部分进行讲解。
首先,来看下函数的声明:

static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data,
               struct mlx4_priv *priv)
{
    //...
}

这个函数是一个`static`的内部函数,意味着它只能在定义它的文件中被调用。它接受三个参数:PCI设备指针`pdev`、PCI设备的数据`pci_dev_data`和`mlx4`私有数据结构体`priv`的指针。
接下来是功能说明:
1. 函数一开始首先输出日志,表明它正在初始化该PCI设备。
2. 函数尝试使PCI设备可用(`mlx4_pci_enable_device`),并检查是否成功。
3. 函数遍历解析通过模块参数`num_vfs`和`probe_vf`给出的Virtual Functions (VFs)数量。这些参数用于处理虚拟化环境下的多个虚拟网络接口。
4. 检查通过`num_vfs`和`probe_vf`模块参数指定的VF数量的有效性,它们必须是非负的,并且`probe_vf`不能超过`num_vfs`指定的值。
5. 总VF的数量不能超过硬件支持的最大值`MLX4_MAX_NUM_VF`。
6. 检查PCI设备的BAR 0和BAR 2是否包含了必要的IO资源,如果没有,则报错。
7. 向PCI子系统请求PCI资源,并设置设备为bus master。
8. 尝试设置64位或32位DMA掩码。DMA掩码限制了设备发起DMA请求时能够访问的内存地址范围。
9. 设置DMA传输最大段大小为1GB(固件的限制)。
10. 检测该设备是否为虚拟功能(VF),并基于`total_vfs`等数据做进一步处理。
11. 初始化核心转储和CATAS错误处理机制。
12. 调用`mlx4_load_one`加载驱动并初始化硬件。
如果过程中遇到错误,函数会执行一系列的清理工作,包括解除对PCI资源的占用(`pci_release_regions`),禁用PCI设备(`mlx4_pci_disable_device`),以及与核心转储和CATAS相关的结束工作。
最后,函数会根据处理过程中是否有错误发生来返回相应的结果。如果成功执行,返回0,失败则返回错误码。
整个函数体现了Linux内核中对硬件设备初始化的一个典型处理过程,其中包括错误检查、资源分配、DMA设置和错误处理等多个环节。

三、虚拟功能(VF)

在计算机云计算和网络技术中,虚拟功能(VF)是一种虚拟化技术,特别是在服务器虚拟化和网络功能虚拟化(NFV)领域使用。VF是运行在物理硬件上的软件抽象,它允许单个物理资源(如网络接口卡NIC)被多个虚拟机或容器安全地共享,每个虚拟机/容器获得自己的VF实例。
VF的一个常见实践是通过PCI传递直接将硬件资源分配给虚拟机或容器。这种技术通常称为单根I/O虚拟化(SR-IOV)。在SR-IOV中,一个物理功能(PF)可以有多个虚拟功能(VFs),其中每个VF都是一个完全独立的接口。这些VFs可以直接分配给不同的虚拟机或容器,无需经过主机操作系统的网络栈处理。结果,VFs能够提供接近于原生硬件性能的I/O吞吐量和较低的延迟。
例如,在网络接口卡(NIC)上,一个物理NIC可以拥有多个VF,每个虚拟机或容器可以直接控制一个VF,就好像它是它们自己的专用NIC一样。这允许虚拟机/容器绕过传统的虚拟化网络层,直接和网络硬件通信,从而大幅度减少CPU使用率和网络延迟,提高性能。
此外,VFs通常拥有自己的队列、滤波器和其他硬件资源,允许它们独立于物理硬件的剩余部分进行数据包处理和其他网络功能。然而,虚拟功能的配置和使用通常需要硬件支持,并在硬件层面上实现精确控制和资源隔离。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/492137.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【vue3学习笔记(二)】(第141-143节)初识setup;ref函数_处理基本类型;ref函数_处理对象类型

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 本篇内容对应课程第141-143节 课程 P141节 《初识setup》笔记 1、setup是所有组合式API“表演的舞台”&#xff0c;组件中所用到的所有数据、方法、监视数据、生命周期钩子等都需要配置在setup中。 2、setup的两种返回值&…

【MySQL】4.MySQL日志管理与数据库的备份和恢复

备份的目的只要是为了灾难恢复&#xff0c;备份还可以测试应用&#xff0c;回滚数据&#xff0c;修改和查询历史数据&#xff0c;审计等 日志在备份、恢复中起着重要作用 一、数据库备份的重要性 在生产环境中&#xff0c;数据的安全性至关重要 任何数据丢失都可能产生严重的…

数组的常见算法

数组的常见算法 数值型数组特征值统计 这里的特征值涉及到&#xff1a;平均值、最大值、最小值、总和等 举例1&#xff1a;数组统计&#xff1a;求总和、均值 public class TestArrayElementSum {public static void main(String[] args) {int[] arr {4,5,6,1,9};//求总和、…

Android 性能优化实例分享-内存优化 兼顾效率与性能

背景 项目上线一段时间后,回顾重要页面 保证更好用户体验及生产效率&#xff0c;做了内存优化和下载导出优化&#xff0c;具体效果如最后的一节的表格所示。 下面针对拍摄流程的两个页面 预览页 导出页优化实例进行介绍&#xff1a; 一.拍摄前预览页面优化 预览效果问题 存在…

实时通讯技术实现

实时通讯技术实现 前言 在CS架构中&#xff0c;经常会有实时通信的需求。客户端和服务端建立连接&#xff0c;服务端实时推送数据给客户端。本文介绍几种常见的实现方式&#xff0c;希望能给读者们一点点参考。 实时通讯的主要实现技术 长轮询(Long Polling) WebSocket 服务器发…

搜索(find a way, Pots)

Find a way 思路&#xff1a;这一题看似简单其实不然&#xff0c;有很多特办条件&#xff0c;如果当这个M和Y都在KFC的时候就会导致步数为0 &#xff0c;或者可以这样说&#xff1a;只有两人都能到达才能算入答案。 我们可以使用bfs来写这道题&#xff0c;这不过这道题目不需…

ChatGPT,来一份3·28雷布斯米时捷上市发布会即时发言稿

你新招了一个秘书。上班第一天&#xff0c;你对他说&#xff1a;“3月28号我可能会受邀参加雷老板的米时捷’上市发布会&#xff0c;届时我可能会有十分钟的发言机会&#xff0c;你现在准备一篇演讲稿。” 秘书问你有何指导意见&#xff1f; 你自己都不知说啥子&#xff0c;能…

探秘 RabbitMQ 的设计理念与核心技术要点

目录 一、消息中间件介绍 1.1 消息中间件的作用 二、RabbitMQ 2.1 核心概念 2.2 生产者发送消息过程 2.3 消费者接收消息过程 2.4 RabbitMQ 为何要引入信道(channel) 2.5 消费模式 一、消息中间件介绍 消息队列中间件&#xff08;message queue middleWare, MQ&#xff09;指…

前端发版上线出现白屏问题

目录 路由配置问题资源缓存问题首屏加载过慢 &#xff1a;喂&#xff0c;你的页面白啦&#xff01; 出现上线白屏的问题有很多&#xff0c;如&#xff1a;配置错误、缓存问题、浏览器兼容问题&#xff0c;根据不同情况去解决。 路由配置问题 问题描述&#xff1a; 在vue开发…

ValueError: Cannot load file containing pickled data when allow_pickle=False

问题描述 遇到报错&#xff1a;ValueError: Cannot load file containing pickled data when allow_pickleFalse 解决方案 经过查阅有人说是与numpy的版本有关&#xff0c;但是还是不要轻易改变环境中的版本&#xff0c;不一定哪个地方就会报错。这里放个解决方案&#xff1a;…

【详细讲解yarn的安装和使用】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

探索MongoDB:发展历程、优势与应用场景

一、MongoDB简介 MongoDB 始于 2007 年&#xff0c;由 Dwight Merriman、Eliot Horowitz 和 Kevin Ryan –&#xff08;DoubleClick 的主理团队&#xff09;共同创立。 DoubleClick 是一家互联网广告公司&#xff08;现隶属于 Google&#xff09;&#xff0c;公司团队开发并利…

数字化转型的密码:传统企业转型路径的深度剖析

引言&#xff1a;数字经济浪潮下的新资产形态 传统行业&#xff0c;如零售、金融、教育等&#xff0c;在如今这场数字化浪潮中&#xff0c;同样需要积极拥抱变革&#xff0c;探索适合自身的转型之路。本文将结合制造业的转型经验&#xff0c;深入探讨传统行…

电脑开机0x0000007B蓝屏怎么办?

电脑开机0x0000007B蓝屏怎么办啊?相信很多用户的电脑都有遇到过蓝屏的问题,最近有用户电脑一开机就蓝屏,并且显示0x0000007B错误代码,原本想通过安全模式进行修复,结果发现安全模式进不去,不知道该怎么解决。这可能与我们的内存或硬盘有关,尝试设置一下硬盘模式,看看是…

嵌入式软件工程师都需要安装哪些软件

文章目录 一、编程软件1.keil2.vscode①Chinese&#xff1a;中文②C/C、C/C Extension Pack③CMake、CMake Tools等代码调试运行的工具④Remote-SSH等&#xff0c;关于远程登录linux服务器的插件 3.Pycharm和Anaconda&#xff0c;用来写python脚本和配置环境&#xff0c;PYQT上…

【Python】搭建 Python 环境

目 录 一.安装 Python二.安装 PyCharm 要想能够进行 Python 开发&#xff0c;就需要搭建好 Python 的环境 需要安装的环境主要是两个部分&#xff1a; 运行环境: Python开发环境: PyCharm 一.安装 Python (1) 找到官方网站 (2) 找到下载页面 选择 “Download for Windows”…

[蓝桥杯 2022 省 A] 求和

[蓝桥杯 2022 省 A] 求和 题目描述 给定 n n n 个整数 a 1 , a 2 , ⋯ , a n a_{1}, a_{2}, \cdots, a_{n} a1​,a2​,⋯,an​, 求它们两两相乘再相加的和&#xff0c;即 S a 1 ⋅ a 2 a 1 ⋅ a 3 ⋯ a 1 ⋅ a n a 2 ⋅ a 3 ⋯ a n − 2 ⋅ a n − 1 a n − 2 ⋅ a…

C语言:动态内存管理(malloc,calloc,realloc,free)

目录 前言 malloc函数 free函数 calloc函数 realloc函数 前言 在这一章节将讲解动态内存分配&#xff0c;它可以在程序的堆区创建一块内存&#xff0c;在这块内存中存什么值就是由自己决定的了 开辟的空间有两个特点&#xff1a; 1. 空间开辟的大小是固定的 2. 数组在…

零基础学习挖掘PHP网站漏洞

教程介绍 本套课程&#xff0c;分为三个阶段&#xff1a;第一阶段&#xff1a;基础篇 学习PHP开发的基础知识&#xff0c;对PHP常见的漏洞进行分析&#xff0c;第二阶段&#xff1a;进阶篇 实战PHP漏洞靶场&#xff0c;了解市面上的PHP主流网站开发技术&#xff0c;并对市面上…

JAVA获取免费天气

JAVA调用天气代码示例 前沿&#xff1a;最近在开发任务中需要获取每日的实时天气和天气预报&#xff0c;要求还是免费的。在网络上搜索了一下免费的API并有了以下思路 免费API网址&#xff1a;https://dev.qweather.com/docs/api/grid-weather/grid-weather-now/ 调用格林天…