[实战] linux驱动框架与驱动开发实战

linux驱动框架与驱动开发实战

  • Linux驱动框架与驱动开发实战
    • 一、Linux驱动框架概述
      • 1.1 Linux驱动的分类
      • 1.2 Linux驱动的基本框架
    • 二、Linux驱动关键API详解
      • 2.1 模块相关API
      • 2.2 字符设备驱动API
      • 2.3 内存管理API
      • 2.4 中断处理API
      • 2.5 PCI设备驱动API
    • 三、Xilinx XDMA驱动开发详解
      • 3.1 XDMA概述
      • 3.2 XDMA驱动开发步骤
        • 步骤1:定义PCI设备ID
        • 步骤2:定义驱动主结构体
        • 步骤3:实现PCI probe函数
        • 步骤4:实现文件操作接口
        • 步骤5:实现中断处理
        • 步骤6:实现DMA传输
        • 步骤7:实现remove函数
        • 步骤8:定义PCI驱动结构体并注册
      • 3.3 步骤总结
    • 四、XDMA驱动测试与调试
      • 4.1 加载驱动模块
      • 4.2 测试DMA传输
      • 4.3 常见问题调试
    • 五、性能优化技巧
      • 5.1 使用分散/聚集DMA
      • 5.2 实现零拷贝
      • 5.3 使用DMA池
    • 六、总结

Linux驱动框架与驱动开发实战

一、Linux驱动框架概述

Linux驱动是操作系统内核与硬件设备之间的桥梁,它使得硬件设备能够被操作系统识别和管理。Linux内核提供了一套完善的驱动框架,开发者可以基于这些框架开发各种硬件设备的驱动程序。

1.1 Linux驱动的分类

Linux驱动主要分为以下几类:

  1. 字符设备驱动:以字节流形式进行数据读写,如键盘、鼠标等
  2. 块设备驱动:以数据块为单位进行读写,如硬盘、SSD等
  3. 网络设备驱动:用于网络通信的设备,如网卡
  4. 其他特殊类型:如USB驱动、PCI驱动等框架驱动

Linux驱动模型分层:

用户空间
系统调用接口
VFS虚拟文件系统
字符设备驱动
块设备驱动
硬件设备

1.2 Linux驱动的基本框架

无论哪种类型的驱动,Linux都提供了相应的框架和接口。一个典型的Linux驱动包含以下组成部分:

  1. 模块加载和卸载函数module_init()module_exit()
  2. 文件操作接口file_operations结构体
  3. 设备注册与注销register_chrdev()等函数
  4. 中断处理request_irq()和中断处理函数
  5. 内存管理kmalloc(), ioremap()等函数
  6. 同步机制:自旋锁、信号量、互斥锁等

二、Linux驱动关键API详解

2.1 模块相关API

module_init(init_function);  // 指定模块加载时执行的函数
module_exit(exit_function);  // 指定模块卸载时执行的函数
MODULE_LICENSE("GPL");       // 声明模块许可证
MODULE_AUTHOR("Author");     // 声明模块作者
MODULE_DESCRIPTION("Desc"); // 声明模块描述

2.2 字符设备驱动API

// 注册字符设备
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops);// 注销字符设备
void unregister_chrdev(unsigned int major, const char *name);// 文件操作结构体
struct file_operations {struct module *owner;loff_t (*llseek) (struct file *, loff_t, int);ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);int (*open) (struct inode *, struct file *);int (*release) (struct inode *, struct file *);// 其他操作...
};

2.3 内存管理API

// 内核内存分配
void *kmalloc(size_t size, gfp_t flags);
void kfree(const void *objp);// 物理地址映射
void *ioremap(phys_addr_t offset, unsigned long size);
void iounmap(void *addr);// 用户空间与内核空间数据拷贝
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n);

2.4 中断处理API

// 申请中断
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev);// 释放中断
void free_irq(unsigned int irq, void *dev_id);// 中断处理函数原型
irqreturn_t irq_handler(int irq, void *dev_id);

2.5 PCI设备驱动API

// PCI设备ID表
static const struct pci_device_id ids[] = {{ PCI_DEVICE(VENDOR_ID, DEVICE_ID) },{ 0, }
};
MODULE_DEVICE_TABLE(pci, ids);// PCI驱动结构体
static struct pci_driver pci_driver = {.name = "xdma_driver",.id_table = ids,.probe = xdma_probe,.remove = xdma_remove,// 其他回调...
};// 注册PCI驱动
pci_register_driver(&pci_driver);// 注销PCI驱动
pci_unregister_driver(&pci_driver);

三、Xilinx XDMA驱动开发详解

3.1 XDMA概述

Xilinx DMA (XDMA) 是一种高性能的DMA控制器,用于在FPGA和主机内存之间传输数据。XDMA驱动通常作为PCIe设备驱动实现,支持DMA传输、中断处理等功能。

PCIe
AXI总线
Host CPU
XDMA引擎
FPGA逻辑

其实现DMA传输流程如下:

User Kernel DMA引擎 write()系统调用 配置源地址/目标地址 传输完成中断 唤醒等待进程 User Kernel DMA引擎

3.2 XDMA驱动开发步骤

步骤1:定义PCI设备ID
#define PCI_VENDOR_ID_XILINX 0x10ee
#define PCI_DEVICE_ID_XDMA 0x7028static const struct pci_device_id xdma_pci_ids[] = {{ PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_XDMA) },{ 0, }
};
MODULE_DEVICE_TABLE(pci, xdma_pci_ids);
步骤2:定义驱动主结构体
struct xdma_dev {struct pci_dev *pdev;void __iomem *bar[MAX_BARS];  // PCI BAR空间映射int irq;                     // 中断号struct cdev cdev;            // 字符设备dev_t devno;                 // 设备号struct dma_chan *dma_chan;   // DMA通道// 其他设备特定数据...
};
步骤3:实现PCI probe函数

PCI设备探测流程:

Kernel PCIe设备 驱动 扫描PCI总线 返回Vendor/Device ID 调用probe()函数 Kernel PCIe设备 驱动

具体探测函数(probe)实现:

static int xdma_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{struct xdma_dev *xdev;int err, i;// 1. 分配设备结构体xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);if (!xdev)return -ENOMEM;xdev->pdev = pdev;pci_set_drvdata(pdev, xdev);// 2. 使能PCI设备err = pci_enable_device(pdev);if (err) {dev_err(&pdev->dev, "Failed to enable PCI device\n");goto fail;}// 3. 请求PCI资源err = pci_request_regions(pdev, "xdma");if (err) {dev_err(&pdev->dev, "Failed to request PCI regions\n");goto disable_device;}// 4. 映射BAR空间for (i = 0; i < MAX_BARS; i++) {if (!pci_resource_len(pdev, i))continue;xdev->bar[i] = pci_iomap(pdev, i, pci_resource_len(pdev, i));if (!xdev->bar[i]) {dev_err(&pdev->dev, "Failed to map BAR%d\n", i);err = -ENOMEM;goto release_regions;}}// 5. 设置DMA掩码err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));if (err) {err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));if (err) {dev_err(&pdev->dev, "No suitable DMA available\n");goto unmap_bars;}}// 6. 申请中断xdev->irq = pdev->irq;err = request_irq(xdev->irq, xdma_irq_handler, IRQF_SHARED, "xdma", xdev);if (err) {dev_err(&pdev->dev, "Failed to request IRQ\n");goto unmap_bars;}// 7. 初始化DMA引擎err = xdma_init_dma(xdev);if (err)goto free_irq;// 8. 注册字符设备err = xdma_setup_cdev(xdev);if (err)goto deinit_dma;dev_info(&pdev->dev, "XDMA driver loaded successfully\n");return 0;// 错误处理...
}// 初始化DMA引擎
static int xdma_init_dma(struct xdma_dev *xdev)
{dma_cap_mask_t mask;dma_cap_zero(mask);dma_cap_set(DMA_MEMCPY, mask);xdev->dma_chan = dma_request_channel(mask, NULL, NULL);if (!xdev->dma_chan) {dev_err(&xdev->pdev->dev, "Failed to get DMA channel\n");return -ENODEV;}return 0;
}// 设置字符设备
static int xdma_setup_cdev(struct xdma_dev *xdev)
{int err;dev_t devno;err = alloc_chrdev_region(&devno, 0, 1, "xdma");if (err < 0) {dev_err(&xdev->pdev->dev, "Failed to allocate device number\n");return err;}xdev->devno = devno;cdev_init(&xdev->cdev, &xdma_fops);xdev->cdev.owner = THIS_MODULE;err = cdev_add(&xdev->cdev, devno, 1);if (err) {dev_err(&xdev->pdev->dev, "Failed to add cdev\n");unregister_chrdev_region(devno, 1);return err;}return 0;
}
步骤4:实现文件操作接口
static const struct file_operations xdma_fops = {.owner = THIS_MODULE,.open = xdma_open,.release = xdma_release,.read = xdma_read,.write = xdma_write,.unlocked_ioctl = xdma_ioctl,.llseek = no_llseek,
};static int xdma_open(struct inode *inode, struct file *filp)
{struct xdma_dev *xdev = container_of(inode->i_cdev, struct xdma_dev, cdev);filp->private_data = xdev;return 0;
}static int xdma_release(struct inode *inode, struct file *filp)
{filp->private_data = NULL;return 0;
}static ssize_t xdma_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{struct xdma_dev *xdev = filp->private_data;// 实现DMA读取操作...return count;
}static ssize_t xdma_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{struct xdma_dev *xdev = filp->private_data;// 实现DMA写入操作...return count;
}static long xdma_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct xdma_dev *xdev = filp->private_data;switch (cmd) {case XDMA_IOCTL_START_DMA:// 启动DMA传输break;case XDMA_IOCTL_STOP_DMA:// 停止DMA传输break;case XDMA_IOCTL_GET_STATUS:// 获取DMA状态break;default:return -ENOTTY;}return 0;
}
步骤5:实现中断处理
static irqreturn_t xdma_irq_handler(int irq, void *dev_id)
{struct xdma_dev *xdev = dev_id;u32 status;// 读取中断状态寄存器status = ioread32(xdev->bar[0] + XDMA_IRQ_STATUS_REG);if (status & XDMA_IRQ_DONE) {// DMA传输完成中断complete(&xdev->dma_complete);}if (status & XDMA_IRQ_ERROR) {// DMA错误中断dev_err(&xdev->pdev->dev, "DMA error occurred\n");}// 清除中断状态iowrite32(status, xdev->bar[0] + XDMA_IRQ_STATUS_REG);return IRQ_HANDLED;
}
步骤6:实现DMA传输
static int xdma_do_transfer(struct xdma_dev *xdev, dma_addr_t src, dma_addr_t dst, size_t len)
{struct dma_async_tx_descriptor *tx;struct dma_device *dma_dev = xdev->dma_chan->device;enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;dma_cookie_t cookie;int err;// 准备DMA描述符tx = dma_dev->device_prep_dma_memcpy(xdev->dma_chan, dst, src, len, flags);if (!tx) {dev_err(&xdev->pdev->dev, "Failed to prepare DMA descriptor\n");return -EIO;}tx->callback = xdma_dma_callback;tx->callback_param = xdev;// 提交DMA传输cookie = dmaengine_submit(tx);err = dma_submit_error(cookie);if (err) {dev_err(&xdev->pdev->dev, "Failed to submit DMA transfer\n");return err;}// 触发DMA传输dma_async_issue_pending(xdev->dma_chan);// 等待传输完成if (!wait_for_completion_timeout(&xdev->dma_complete, msecs_to_jiffies(1000))) {dev_err(&xdev->pdev->dev, "DMA transfer timeout\n");dmaengine_terminate_all(xdev->dma_chan);return -ETIMEDOUT;}return 0;
}static void xdma_dma_callback(void *data)
{struct xdma_dev *xdev = data;complete(&xdev->dma_complete);
}
步骤7:实现remove函数
static void xdma_remove(struct pci_dev *pdev)
{struct xdma_dev *xdev = pci_get_drvdata(pdev);int i;// 1. 移除字符设备cdev_del(&xdev->cdev);unregister_chrdev_region(xdev->devno, 1);// 2. 释放DMA资源if (xdev->dma_chan)dma_release_channel(xdev->dma_chan);// 3. 释放中断free_irq(xdev->irq, xdev);// 4. 取消BAR空间映射for (i = 0; i < MAX_BARS; i++) {if (xdev->bar[i])pci_iounmap(pdev, xdev->bar[i]);}// 5. 释放PCI资源pci_release_regions(pdev);// 6. 禁用PCI设备pci_disable_device(pdev);// 7. 释放设备结构体devm_kfree(&pdev->dev, xdev);dev_info(&pdev->dev, "XDMA driver unloaded\n");
}
步骤8:定义PCI驱动结构体并注册
static struct pci_driver xdma_driver = {.name = "xdma",.id_table = xdma_pci_ids,.probe = xdma_probe,.remove = xdma_remove,
};static int __init xdma_init(void)
{return pci_register_driver(&xdma_driver);
}static void __exit xdma_exit(void)
{pci_unregister_driver(&xdma_driver);
}module_init(xdma_init);
module_exit(xdma_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Xilinx XDMA Driver");

3.3 步骤总结

上文以xilinx XDMA 为例介绍了Linux PCI设备驱动开发步骤,总结成流程图如下:

Yes
No
驱动模块加载
module_init调用
PCI设备探测 probe
探测成功?
资源分配
映射BAR空间
申请中断
初始化DMA引擎
注册字符设备
错误处理
模块退出
用户空间操作
open/read/write
ioctl控制
mmap内存映射
DMA传输处理
中断处理
数据传输完成
module_exit调用
释放资源
注销字符设备
释放DMA资源
解除BAR映射
释放中断
禁用PCI设备

四、XDMA驱动测试与调试

4.1 加载驱动模块

# 加载驱动
sudo insmod xdma.ko# 查看加载的模块
lsmod | grep xdma# 查看内核日志
dmesg | tail

4.2 测试DMA传输

可以使用简单的用户空间程序测试DMA功能:

// test_xdma.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define XDMA_DEV "/dev/xdma"
#define BUF_SIZE (1024 * 1024)  // 1MBint main()
{int fd = open(XDMA_DEV, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 分配测试缓冲区char *src = malloc(BUF_SIZE);char *dst = malloc(BUF_SIZE);if (!src || !dst) {perror("Failed to allocate buffers");close(fd);return -1;}// 填充源缓冲区memset(src, 0xAA, BUF_SIZE);memset(dst, 0, BUF_SIZE);// 写入数据到设备ssize_t written = write(fd, src, BUF_SIZE);printf("Written %zd bytes to device\n", written);// 从设备读取数据ssize_t readed = read(fd, dst, BUF_SIZE);printf("Read %zd bytes from device\n", readed);// 验证数据if (memcmp(src, dst, BUF_SIZE) {printf("Data verification failed!\n");} else {printf("Data verification passed!\n");}free(src);free(dst);close(fd);return 0;
}

4.3 常见问题调试

  1. PCI设备未识别

    • 检查lspci -nn确认设备ID是否正确
    • 确认内核配置中启用了PCI支持
  2. DMA传输失败

    • 检查DMA掩码设置
    • 确认物理地址是否正确
    • 检查DMA引擎是否支持所需操作
  3. 中断不触发

    • 确认中断号是否正确
    • 检查中断状态寄存器
    • 确认中断处理函数已正确注册

五、性能优化技巧

5.1 使用分散/聚集DMA

static int xdma_sg_transfer(struct xdma_dev *xdev, struct scatterlist *sg_src,struct scatterlist *sg_dst,int sg_count)
{struct dma_async_tx_descriptor *tx;struct dma_device *dma_dev = xdev->dma_chan->device;enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;dma_cookie_t cookie;int err;tx = dma_dev->device_prep_dma_sg(xdev->dma_chan, sg_dst, sg_count,sg_src, sg_count,flags);if (!tx) {dev_err(&xdev->pdev->dev, "Failed to prepare SG DMA descriptor\n");return -EIO;}tx->callback = xdma_dma_callback;tx->callback_param = xdev;cookie = dmaengine_submit(tx);err = dma_submit_error(cookie);if (err) {dev_err(&xdev->pdev->dev, "Failed to submit SG DMA transfer\n");return err;}dma_async_issue_pending(xdev->dma_chan);if (!wait_for_completion_timeout(&xdev->dma_complete, msecs_to_jiffies(1000))) {dev_err(&xdev->pdev->dev, "SG DMA transfer timeout\n");dmaengine_terminate_all(xdev->dma_chan);return -ETIMEDOUT;}return 0;
}

5.2 实现零拷贝

static int xdma_mmap(struct file *filp, struct vm_area_struct *vma)
{struct xdma_dev *xdev = filp->private_data;unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;unsigned long size = vma->vm_end - vma->vm_start;int ret;// 将BAR空间映射到用户空间if (offset >= pci_resource_len(xdev->pdev, 0) || size > pci_resource_len(xdev->pdev, 0) - offset) {return -EINVAL;}ret = remap_pfn_range(vma, vma->vm_start,(pci_resource_start(xdev->pdev, 0) + offset) >> PAGE_SHIFT,size, vma->vm_page_prot);if (ret)return -EAGAIN;return 0;
}

5.3 使用DMA池

// 初始化DMA池
xdev->dma_pool = dma_pool_create("xdma_pool", &xdev->pdev->dev,POOL_SIZE, POOL_ALIGN, 0);
if (!xdev->dma_pool) {dev_err(&xdev->pdev->dev, "Failed to create DMA pool\n");return -ENOMEM;
}// 从DMA池分配内存
void *buf = dma_pool_alloc(xdev->dma_pool, GFP_KERNEL, &dma_handle);
if (!buf) {dev_err(&xdev->pdev->dev, "Failed to allocate from DMA pool\n");return -ENOMEM;
}// 释放DMA池内存
dma_pool_free(xdev->dma_pool, buf, dma_handle);// 销毁DMA池
dma_pool_destroy(xdev->dma_pool);

六、总结

本文详细介绍了Linux驱动框架和关键API,并以Xilinx XDMA驱动为例,展示了Linux驱动开发的完整流程。关键点包括:

  1. 理解Linux驱动框架:掌握字符设备、块设备和网络设备驱动的基本结构
  2. 熟悉关键API:模块加载、文件操作、内存管理、中断处理等核心API
  3. PCI驱动开发:从设备发现到资源管理的完整流程
  4. DMA传输实现:包括标准DMA和分散/聚集DMA
  5. 驱动调试技巧:日志分析、用户空间测试程序等

通过XDMA驱动的实例,我们可以看到Linux驱动开发需要综合考虑硬件特性、内核API和性能优化等多个方面。希望本文能为Linux驱动开发者提供有价值的参考。

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

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

相关文章

1. hadoop 集群的常用命令

1.上传文件 1)hadoop fs -put words.txt /path/to/input/ 2)hdfs dfs -put words.txt /path/wc/input/ 2.获取hdfs中的文件 hadoop fs -get /path/wc/input/words.txt 3.合并下载多个文件 hadoop fs -getmerge /path/wc/input/words.txt /path/wc/input/words2.txt 4.查…

Keepalived+LVS+nginx高可用架构

注明&#xff1a;所有软件已经下载好&#xff0c;防火墙和SELinux已经全部关闭 一.搭建NFS 1.服务端 1.创建文件 [rootnfs ~]# mkdir -p /nfs/data 2、修改权限 [rootnfs ~]# chmod orw /nfs/data 3、写配置文件 [rootnfs ~]# cat /etc/exports /nfs/data 192.168.111.118(r…

深度学习处理文本(13)

我们使用基于GRU的编码器和解码器来在Keras中实现这一方法。选择GRU而不是LSTM&#xff0c;会让事情变得简单一些&#xff0c;因为GRU只有一个状态向量&#xff0c;而LSTM有多个状态向量。首先是编码器&#xff0c;如代码清单11-28所示。 代码清单11-28 基于GRU的编码器 fro…

HashMap 底层原理详解

1. 核心数据结构 JDK 1.7 及之前&#xff1a;数组 链表 JDK 1.8 及之后&#xff1a;数组 链表/红黑树&#xff08;链表长度 ≥8 时转红黑树&#xff0c;≤6 时退化为链表&#xff09; // JDK 1.8 的 Node 定义&#xff08;链表节点&#xff09; static class Node<K,V&g…

使用MySQL时出现 Ignoring query to other database 错误

Ignoring query to other database 错误 当在远程连接软件中输入MySQL命令出现该错误 导致错误原因是&#xff1a;登录mysql时账户名没有加上u 如果出现该错误&#xff0c;退出mysql&#xff0c;重新输入正确格式进入即可&#xff01;

哈尔滨工业大学:大模型时代的具身智能

大家好&#xff0c;我是樱木。 机器人在工业领域&#xff0c;已经逐渐成熟。具身容易&#xff0c;智能难。 机器人-》智能机器人&#xff0c;需要自主能力&#xff0c;加上通用能力。 智能机器人-》人类&#xff0c;这个阶段就太有想象空间了。而最受关注的-类人机器人。 如何…

Javascript代码压缩混淆工具terser详解

原始的JavaScript代码在正式的服务器上,如果没有进行压缩,混淆,不仅加载速度比较慢,而且还存在安全和性能问题. 因此现在需要进行压缩,混淆处理. 处理方案简单描述一下: 1. 使用 terser 工具进行 安装 terser工具: # npm 安装 npm install terser --save-dev# 或使用 yarn 安…

Java String 常用方法详解

目录 一、获取字符串信息(一)获取字符串长度(二)获取指定索引处的字符(三)获取子字符串二、字符串比较(一)比较字符串内容(二)忽略大小写比较三、字符串转换(一)转换为大写(二)转换为小写四、字符串查找(一)查找子字符串的位置(二)从指定位置开始查找五、字符…

Linux驱动开发练习案例

1 开发目标 1.1 架构图 操作系统&#xff1a;基于Linux5.10.10源码和STM32MP157开发板&#xff0c;完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪&#xff1b; 驱动层&#xff1a;为每个外设配置DTS并且单独封装外设驱动模块。其中电压ADC测试&#xff0c;采用linux内核…

leetcode-代码随想录-哈希表-赎金信

题目 题目链接&#xff1a;383. 赎金信 - 力扣&#xff08;LeetCode&#xff09; 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 maga…

精品可编辑PPT | “新基建”在数字化智慧高速公路中的支撑应用方案智慧建筑智慧交通解决方案施工行业解决方案

本文详细阐述了“新基建”在数字化智慧高速公路中的支撑应用方案&#xff0c;从政策背景出发&#xff0c;指出国家在交通领域的一系列发展规划和指导意见&#xff0c;强调了智慧交通建设的重要性。分析了当前高速公路存在的问题&#xff0c;如基础感知设施不足、协同水平低、服…

C语言求3到100之间的素数

一、代码展示 二、运行结果 三、感悟思考 注意: 这个题思路他是一个试除法的一个思路 先进入一个for循环 遍历3到100之间的数字 第二个for循环则是 判断他不是素数 那么就直接退出 这里用break 是素数就打印出来 在第一个for循环内 第二个for循环外

英语—四级CET4考试—蒙猜篇—匹配题

蒙猜方法一 匹配题的做题&#xff1a; 方法一&#xff1a; 首先&#xff0c;什么都不想&#xff0c;把问题中ing形式的&#xff0c;大写字母的&#xff0c;人名&#xff0c;地名&#xff0c;最后几个依次框起来。 然后&#xff0c;比如46题&#xff0c;口里默念meaningful lif…

股票日数据使用_未复权日数据生成前复权日周月季年数据

目录 前置&#xff1a; 准备 代码&#xff1a;数据库交互部分 代码&#xff1a;生成前复权 日、周、月、季、年数据 前置&#xff1a; 1 未复权日数据获取&#xff0c;请查看 https://blog.csdn.net/m0_37967652/article/details/146435589 数据库使用PostgreSQL。更新日…

系统与网络安全------Windows系统安全(6)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 共享文件夹 发布共享文件夹 Windows共享概述 微软公司推出的网络文件/打印机服务系统 可以将一台主机的资源发布给其他主机共有 共享访问的优点 方便、快捷相比光盘 U盘不易受文件大小限制 可以实现访问…

BN 层的作用, 为什么有这个作用?

BN 层&#xff08;Batch Normalization&#xff09;——这是深度神经网络中非常重要的一环&#xff0c;它大大改善了网络的训练速度、稳定性和收敛效果。 &#x1f9e0; 一句话理解 BN 层的作用&#xff1a; Batch Normalization&#xff08;批归一化&#xff09;通过标准化每一…

判断HiveQL语句为ALTER TABLE语句的识别函数

写一个C#字符串解析程序代码&#xff0c;逻辑是从前到后一个一个读取字符&#xff0c;遇到匹配空格、Tab和换行符就继续读取下一个字符&#xff0c;遇到大写或小写的字符a&#xff0c;就读取后一个字符并匹配是否为大写或小写的字符l&#xff0c;以此类推&#xff0c;匹配任意字…

基于编程的运输设备管理系统设计(vue+springboot+ssm+mysql8.x)

基于编程的运输设备管理系统设计&#xff08;vuespringbootssmmysql8.x&#xff09; 运输设备信息管理系统是一个全面的设备管理平台&#xff0c;旨在优化设备管理流程&#xff0c;提高运输效率。系统提供登录入口&#xff0c;确保只有授权用户可以访问。个人中心让用户可以查…

6.1 python加载win32或者C#的dll的方法

python很方便的可以加载win32的方法以及C#编写的dll中的方法或者变量&#xff0c;大致过程如下。 一.python加载win32的方法&#xff0c;使用win32api 1.安装库win32api pip install win32api 2.加载所需的win32函数并且调用 import win32api win32api.MessageBox(0,"…

前端精度计算:Decimal.js 基本用法与详解

一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库&#xff0c;它可以完美解决浮点数计算中的精度丢失问题。 官方API文档&#xff1a;Decimal.js 特性&#xff1a; 任意精度计算&#xff1a;支持大数、小数的高精度运算。 链式调用&#xff1a;…