【C语言】Infiniband驱动__mlx4_init_one函数

news2024/11/18 23:45:15

一、注释

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通常拥有自己的队列、滤波器和其他硬件资源,允许它们独立于物理硬件的剩余部分进行数据包处理和其他网络功能。然而,虚拟功能的配置和使用通常需要硬件支持,并在硬件层面上实现精确控制和资源隔离。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】从C到C++、从面向过程到面向对象(类与对象)

文章目录 C入门知识C与C的关系1. 类的引入&#xff1a;从结构体到类2. 类的声明和定义3. 类的作用域4. 类的访问限定符5. 面向对象特性之一&#xff1a;封装6. 类的实例化&#xff1a;对象7. 计算类对象的内存大小8. 成员函数中暗藏的this指针9. 类的六个默认生成的成员函数9.1…

【二叉树】Leetcode 226. 翻转二叉树【简单】

翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 解题思路 二叉树翻转操作是指将二叉树中每个节点的左右子树进行交换。具体…

[Android]模拟器登录Google Play失败

问题&#xff1a; 模拟器登录Google Play失败&#xff0c;提示couldnt sign in there was a problem communicating with google servers. try again later. 原因&#xff1a; 原因是模拟器没有连接到互联网&#xff0c;打开模拟器中Google浏览器进行搜索一样不行。 解决&am…

虚拟机如何在原有磁盘上扩容

虚拟机未开启状态–菜单栏–虚拟机–快照–拍摄快照–拍摄快照– 菜单栏–虚拟机–快照–快照管理器–点击刚刚的快照1–删除–是– 文件–新建或者打开–硬盘&#xff08;以本人Win 10.64.3GL为例&#xff09;–虚拟机设置–硬件– 硬盘&#xff08;SATA&#xff09;–磁盘实…

赛氪网亮相中国人工智能产业发展联盟会议,共筑赛事生态新篇章

2024年3月14日至15日&#xff0c;备受瞩目的中国人工智能产业发展联盟&#xff08;AIIA&#xff09;第十一次全体会议在海南海口盛大召开。作为人工智能领域的重要交流与合作平台&#xff0c;此次会议吸引了300余位联盟成员单位代表齐聚一堂&#xff0c;共襄盛举。在这场人工智…

梵宁教育:助力大学生掌握设计新技能

在当今数字化、信息化的社会里&#xff0c;设计技能的重要性日益凸显。设计不仅仅局限于美术或艺术领域&#xff0c;它已经渗透到各行各业&#xff0c;从产品外观到用户界面&#xff0c;从品牌形象到营销策划&#xff0c;设计无处不在。对于大学生而言&#xff0c;掌握设计新技…

ts enum elementUI 表格列表中如何方便的标识不同类型的内容,颜色区分 enum ts + vue3

ts enum elementUI 表格列表中如何方便的标识不同类型的内容&#xff0c;颜色区分 enum ts vue3 本文内容为 TypeScript 一、基础知识 在展示列表的时候&#xff0c;列表中的某个数据可能是一个类别&#xff0c;比如&#xff1a; enum EnumOrderStatus{"未受理" …

python如何获取word文档的总页数

最近在搞AI. 遇到了一个问题&#xff0c;就是要进行doc文档的解析。并且需要展示每个文档的总页数。 利用AI. 分别尝试了chatGPT, 文心一言&#xff0c; github copilot&#xff0c;Kimi 等工具&#xff0c;给出来的答案都不尽如人意。 给的最多的查询方式就是下面这种。 这个…

时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测

时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测 目录 时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测&#xff08;完整源码和数据…

kali系统下载,并在VMware免安装式打开kali虚拟机

Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution 上面连接为官网连接 此安装过程也适合其他虚拟机的安装&#xff0c;也就是你曾经安装过的虚拟机&#xff0c;会保存一推文件&#xff0c;而这推文件&#xff0c;还可以用VMware再次打开。

Unity中如何实现草的LOD

1&#xff09;Unity中如何实现草的LOD 2&#xff09;用Compute Shader处理图像数据后在安卓机上不能正常显示渲染纹理 3&#xff09;关于进游戏程序集加载的问题 4&#xff09;预制件编辑模式一直在触发自动保存 这是第379篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热…

【Linux】线程的概念{虚拟地址堆区细分/缺页中断/页/初识线程/创建线程/优缺点}

文章目录 1.前导知识1.1 虚拟地址空间的堆区1.2 缺页中断1.3ELF文件格式1.4页/页框/页帧/页表/MMU1.5虚拟地址到物理地址 2.初识Linux线程2.1之前所学的进程2.2线程的引入2.3如何理解线程2.4如何理解轻量级进程 3.创建线程3.1pthread_create()函数3.2程序测试3.3Makefile怎么写…

力扣--并查集1631.最小体力消耗路径

这题将图论和并查集联系起来。把数组每个位置看成图中的一个节点。 这段代码的主要思路是&#xff1a; 遍历地图中的每个节点&#xff0c;将每个节点与其相邻的下方节点和右方节点之间的边加入到边集合中&#xff08;因为从上到下和从下到上他们高度绝对值一样的&#xff0c;…

文件IO的方式读取jpeg图片的分辨率

1、读取jpeg图片分辨率的两种方式 1.1 使用libjpeg库 可以使用libjpeg库读取JPEG图像文件&#xff0c;并获取图像的分辨率&#xff08;宽度和高度&#xff09;&#xff0c;简单demo示例如下&#xff1a; #include <stdio.h> #include <jpeglib.h>int main() {st…

6、ChatGLM3-6B 部署实践

一、ChatGLM3-6B介绍与快速入门 ChatGLM3 是智谱AI和清华大学 KEG 实验室在2023年10月27日联合发布的新一代对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型&#xff0c;免费下载&#xff0c;免费的商业化使用。 该模型在保留了前两代模型对话流畅、部署门槛低等众多…

GPT2从放弃到入门(四)

引言 体验地址&#xff1a;https://huggingface.co/spaces/greyfoss/gpt2-chatbot 上篇文章我们通过Gradio作为前端轻松地连接到训练好的Chatbot&#xff0c;本文介绍如何分享你创建好的模型给你的朋友。 当你训练好的模型推送到Huggingface Hub上后&#xff0c;其实还可以进一…

How to convert .py to .ipynb in Ubuntu 22.04

How to convert .py to .ipynb in Ubuntu 22.04 jupyter nbconvertp2j 最近看到大家在用jupyter notebook&#xff0c;我也试了一下&#xff0c;感觉还不错&#xff0c;不过&#xff0c;也遇到了一些问题&#xff0c;比方说&#xff0c;我有堆的.py文件&#xff0c;如果要一个一…

YOLOv8融入低照度图像增强算法---传统算法篇

YOLOv8n原图检测YOLOv8n增强后检测召回率和置信度都有提升 前言 这篇博客讲讲低照度,大家都催我出一些内容,没想到这么多同学搞这个,恰好我也做过这方面的一些工作,那今天就来讲解一些方法,低照度的图像增强大体分“传统算法”和“深度学习算法”; 目前低照度的图像增…

sqlite3嵌入式开发板命令行方式使用

如何在编译嵌入式版本的sqlite3&#xff0c;请看我上一篇文章 sqlite3 交叉编译-CSDN博客 一、sqlite3命令行方式使用 假如我将编译好的嵌入式的sqlite3放置在如下路径&#xff1a; 进入bin目录进行操作 1.运行sqlite3 运行sqlite3有两种方式 1&#xff09;直接在内存里面…

Wireshark使用相关

1.wireshark如何查看RST包 tcp.flags.reset1 RST表示复位&#xff0c;用来异常的关闭连接&#xff0c;在TCP的设计中它是不可或缺的。发送RST包关闭连接时&#xff0c;不必等缓冲区的包都发出去&#xff08;不像上面的FIN包&#xff09;&#xff0c;直接就丢弃缓存区的包发送R…