【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)数量为0int 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,查找与当前设备匹配的VFfor (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编号,就跳过这个VFif ((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传输的最大段大小为1GBdma_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超出范围,则跳过这个VFif ((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;// 初始化成功,返回0return 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.mzph.cn/news/771530.shtml

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

相关文章

【SSH配置公钥私钥免密登录】

SSH配置公钥私钥免密登录 SSH的安全机制一、修改远程主机ssh设置二、在windows客户端生成公钥私钥文件三、将客户端公钥追加到远程主机 .ssh/authorized_keys中参考链接 SSH的安全机制 SSH之所以能够保证安全&#xff0c;原因在于它采用了非对称加密技术(RSA)加密了所有传输的…

机载激光雷达标定板提高测量精度

机载激光雷达系统标定板&#xff08;LiDAR&#xff09;是一种高精度的遥感技术&#xff0c;它通过发射激光脉冲并接收反射回来的光波来测量目标物体的距离。这种技术在测量地形、建筑物、植被等方面有着广泛的应用。为了确保机载激光雷达系统的数据准确性和可靠性&#xff0c;需…

An Experimental Study of State-of-the-Art Entity Alignment Approaches论文阅读

最先进的实体对齐方法的实验研究综述 Title: An Experimental Study of State-of-the-Art Entity Alignment Approaches 日期: 2022 发表单位: IEEE github: https://github.com/DexterZeng/EAE 原文地址: https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber9174835 概括…

flutter 弹窗之系列二

自定义弹窗&#xff08;含底部抽屉&#xff09;Dialog class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;overrideState<MyHomePage> createState() > _MyHomePageState(); }class _MyHomePageState…

云手机:实现便携与安全的双赢

随着5G时代的到来&#xff0c;云手机在各大游戏、直播和新媒体营销中扮演越来越重要的角色。它不仅节约了成本&#xff0c;提高了效率&#xff0c;而且在边缘计算和云技术逐渐成熟的背景下&#xff0c;展现出了更大的发展机遇。 云手机的便携性如何&#xff1f; 云手机的便携性…

企业微信应用结合Cpolar内网穿透实现固定域名验证回调本地接口服务

文章目录 1. Windows安装Cpolar2. 创建Cpolar域名3. 创建企业微信应用4. 定义回调本地接口5. 回调和可信域名接口校验6. 设置固定Cpolar域名7. 使用固定域名校验 企业微信开发者在应用的开发测试阶段&#xff0c;应用服务通常是部署在开发环境&#xff0c;在有数据回调的开发场…

k8s入门到实战(四)—— k8s核心概念以及基本操作命令详细介绍

k8s 核心概念及操作命令 namespace&#xff08;命名空间&#xff0c;简称 ns&#xff09; k8s 资源创建的两种方式&#xff1a;使用命令行创建、使用 yaml 文件创建 什么是 ns 在 k8s 中&#xff0c;ns 是一种用于对集群资源进行逻辑分组和隔离的机制。它允许将 k8s 集群划…

Elasticsearch8 - Docker安装Elasticsearch8.12.2

前言 最近在学习 ES&#xff0c;所以需要在服务器上装一个单节点的 ES 服务器环境&#xff1a;centos 7.9 安装 下载镜像 目前最新版本是 8.12.2 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.12.2创建配置 新增配置文件 elasticsearch.yml http.host…

微信聊天记录删了怎样才能恢复?揭秘4个宝藏方法

在我们使用微信进行聊天的过程中&#xff0c;不可避免地会出现误删聊天记录的情况&#xff0c;这可能是因为手误、设备问题或其他原因导致的。而当重要的聊天记录不慎被删除时&#xff0c;往往会让人感到焦虑和困扰。然而&#xff0c;幸运的是&#xff0c;针对被删除的微信聊天…

Java基础--128陷阱

问题引入 Integer a 123; Integer b 123; System.out.println(ab); 结果为true。 但是如果代码如下 Integer a 1230;Integer b 1230;System.out.println(ab); 这个的结果就是false。 问题解决 当Integer a 123时&#xff0c;其实他底层自动转换成了Integer a Inte…

docker--Dockerfile (三)

1&#xff0c;Dcockerfile是什么 docker推荐使用dockerfile的定义文件和docker build命令来构建镜像。dockerfile使用基本的基于DSL&#xff08;面向领域语言&#xff09;语法的指令来构建Docker镜像。另一种创建Docker镜像的方式是使用docker commit&#xff0c;不推荐使用。 …

【python】flask执行上下文context,请求上下文和应用上下文原理解析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

SqlServer期末复习(数据库原理及应用)持续更新中

一、SQL语句 1.1 SQL语句知识引入 1.DDL语言(数据定义语言&#xff09;主要是进行定义/改变表的结构、数据类型、表之间的链接等操作&#xff0c;关键字CREATE、DROP、ALTER CREATE TABLE 表面( 列名1 数据类型&#xff0c; 列名2 数据类型&#xff0c; ) ALTER TABLE 表名&a…

MFC 打开类向导中方法时提示对com组件的调用返回了错误 HRESULT E_FAIL

解决&#xff1a;头文件中要分类&#xff0c;把virtual和afx_msg等放在一起&#xff0c;不要交叉错开。 MFC&#xff08;Microsoft Foundation Class&#xff09;中的virtual关键字用于声明虚函数。虚函数是C中实现多态的一种机制&#xff0c;它允许派生类重新定义基类中的虚函…

那位拿了多个Offer的大佬分享了最新Go面经

和大家分享一下我们 Go就业训练营 和 升职加薪星球 中战友们投稿的真实面经。 这是第一篇&#xff0c;计划还会再更新4篇最新Go面经&#xff0c;都是拿到Offer的那种&#xff01; 欢迎大家关注我的账号&#xff0c;关注之后不迷路。 先秀战绩 虽然不同的公司考察的侧重点不一…

每日必学Linux命令:mv命令

mv命令是move的缩写&#xff0c;可以用来移动文件或者将文件改名&#xff08;move (rename) files&#xff09;&#xff0c;是Linux系统下常用的命令&#xff0c;经常用来备份文件或者目录。 一&#xff0e;命令格式&#xff1a; mv [选项] 源文件或目录 目标文件或目录二&am…

计算机基础(中断、IO)

操作系统 设备交互 CPU 与 IO 设备交互过程 CPU 通过设备控制器&#xff08;驱动&#xff1f;&#xff09;与计算机外设进行交互。可以将控制器想象成编程语言中的接口&#xff0c;然后不同地计算机外设的控制器去实现这个接口&#xff0c;CPU 只需要调用接口而无需关注具体地…

如何使用生成式人工智能进行科学文献检索?

在学术写作过程中&#xff0c;查找文献是一个至关重要的步骤。文献检索不仅可以帮助我们了解研究领域的现状和发展&#xff0c;还可以为我们的论文提供理论支持和数据来源。然而&#xff0c;许多学者在查找文献时往往感到困惑和无所适从。本文将详细解释如何查找文献&#xff0…

柯桥专业会计培训|会计实操做账手工账电脑账出纳报税手把手教

开具纸质发票时&#xff0c;经常有小伙伴纠结发票开票人和复核人的问题。现在全国已施行数电票&#xff0c;这个问题还存在吗&#xff1f;一起来看看~ 暂未规定!! 开票人和复核人不应为同一人&#xff01; 目前&#xff0c;全国大部分城市已基本实现数电票的开票试点&#x…

Python学习:循环语句

Python循环语句 概念 循环语句是编程中常用的结构&#xff0c;用于多次执行相同或类似的代码块。Python中有两种主要的循环语句&#xff1a;for循环和while循环。 for循环&#xff1a; for循环用于遍历一个序列&#xff08;如列表、元组、字符串等&#xff09;中的元素&#x…