《Linux Device Drivers》第十五章 内存映射和DMA——note

arm Linux中dma的cache管理 一路跟踪,dma_map_single()会最终调用到arch_sync_dma_for_device(), dma_unmap_single()会最终调用到arch_sync_dma_for_cpu(), 和arch_sync_dma_for_cpu_all()。而arch_sync_dma_for_cpu_all()对Arm64是空函数。arch_sync_dma_for_device/arch_sync_dma_for_cpu的定义在文件arch\arm64\mm\dma-mapping.c中。 阅读详情
  • 简介
    • 许多类型的驱动程序编程都需要了解一些虚拟内存子系统如何工作的知识
    • 当遇到更为复杂、性能要求更为苛刻的子系统时,本章所讨论的内容迟早都要用到
    • 本章的内容分成三个部分
      • 讲述mmap系统调用的实现过程
      • 讲述如何跨越边界直接访问用户空间的内存页
      • 讲述了直接内存访问(DMA)I/O操作,它使得外设具有直接访问系统内存的能力
  • Linux的内存管理
    • 地址类型
      • Linux是一个虚拟内存系统,这意味着用户程序所使用的地址与硬件使用的物理地址是不等同的
      • 有了虚拟内存,在系统中运行的程序可以分配比物理内存更多的内存,甚至一个单独的进程都能拥有比系统物理内存更多的虚拟地址空间
      • 下面是一个Linux使用的地址类型列表
        • 用户虚拟地址
          • 这是在用户空间程序所能看到的常规地址
        • 物理地址
          • 该地址在处理器和系统内存之间使用
        • 总线地址
          • 该地址在外围总线和内存之间使用,通常它们与处理器使用的物理地址相同
        • 内核逻辑地址
          • 内核逻辑地址组成了内核的常规地址空间
          • 在大多数体系架构中,逻辑地址和与其相关联的物理地址不同,仅仅在它们之间存在一个固定的偏移量
          • kmalloc返回的内存就是内核逻辑地址
        • 内核虚拟地址
          • 和内核逻辑地址的相同之处在于,它们都将内核空间的地址映射到物理地址上
          • 内核虚拟地址与物理地址的映射不必是线性的一对一的
          • 所有的逻辑地址都是内核虚拟地址,但是很多内核虚拟地址不是逻辑地址
          • vmalloc分配的内存具有一个虚拟地址
      • <asm/page.h>
        • __pa()
          • 返回其对应的物理地址
        • __va()
          • 将物理地址逆向映射到逻辑地址,但这只对低端内存页有效
    • 物理地址和页
      • 物理地址被分成离散的单元,称之为页
      • <asm/page.h>
        • PAGE_SIZE
      • 目前大多数系统都使用每页4096个字节
    • 高端与低端内存
      • 使用32位系统只能在4GB的内存中寻址
      • 内核将4GB的虚拟地址空间分割为用户空间和内核空间,一个典型的分割是将3GB分配给用户空间,1GB分配给内核空间
      • 低端内存
        • 存在于内核空间上的逻辑地址内存
      • 高端内存
        • 那些不存在逻辑地址的内存
    • 内存映射和页结构
      • <linux/mm.h>
      • struct page
        • atomic_t count;
          • 对该页的访问计数。当计数值为0时,该页将返回给空闲链表
        • void *virtual;
          • 如果页面被映射,则指向页的内核虚拟地址;如果未被映射则为NULL
        • unsigned long flags;
          • 描述页状态的一系列标志
          • PG_locked表示内存中的页已经被锁住
          • PG_reserved表示禁止内存管理系统访问该页
      • struct page *virt_to_page(void *kaddr);
      • struct page *pfn_to_page(int pfn);
        • 针对给定的页帧号,返回page结构指针
      • void *page_address(struct page *page);
        • 返回页的内核虚拟地址
      • <linux/highmem.h>
        • <asm/kmap_types.h>
        • void *kmap(struct page *page);
          • 对于低端内存页来说,返回页的逻辑地址
          • 对于高端内存,在专用的内核地址空间创建特殊的映射
        • void kunmap(struct page *page);
        • void *kmap_atomic(struct page *page, enum km_type type);
        • void kunmap_atomic(void *addr, enum km_type type);
    • 页表
      • 处理器必须使用某种机制同,将虚拟地址转换为相应的物理地址,这种机制被称为页表
      • 它基本上是一个多层树形结构,结构化的数据中包含了虚拟地址到物理地址的映射和相关的标志位
    • 虚拟内存区
      • 虚拟内存区(VMA)用于管理进程地址空间中不同区域的内核数据结构
      • 进程的内存映射包含下面这些区域
        • 程序的可执行代码区域
        • 多个数据区,其中包含初始化数据、非初始化数据以及程序堆栈
        • 与每个活动的内存映射对应的区域
      • /proc/<pid>/maps
        • start-end perm offset major:minor inode image
      • vm_area_struct结构
        • <linux/mm.h>
        • struct vm_area_struct
          • unsigned long vm_start;
          • unsigned long vm_end;
          • struct file *vm_file;
          • unsigned long vm_pgoff;
          • unsigned long vm_flags;
          • struct vm_operations_struct *vm_ops;
          • void *vm_private_data;
        • struct vm_operations_struct
          • void (*open) (struct vm_area_struct *vma);
          • void (*close) (struct vm_area_struct *vma);
          • struct page *(*nopage) (struct vm_area_struct *vma, unsigned long address, int *type);
          • int (*populate) (struct vm_area_struct *vm, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
    • 内存映射处理
      • <linux/sched.h>
        • struct mm_struct
      • current->mm

  • mmap设备操作
    • 内存映射可以提供给用户程序直接访问设备内存的能力
    • 映射一个设备意味着将用户空间的一段内存与设备内存关联起来
    • 像串口和其他面向流的设备就不能进行mmap抽象
    • 必须以PAGE_SIZE为单位进行映射
    • mmap方法是file_operations结构的一部分
    • mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
    • int (*mmap) (struct file *filp, struct vm_area_struct *vma);
    • 有两种建立页表的方法
      • 使用remap_pfn_range函数一次全部建立
      • 通过nopage VMA方法每次建立一个页表
    • 使用remap_pfn_range
      • int rempa_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot);
      • int io_remap_page_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long phys_addr, unsigned long size, pgprot_t prot);
      • vma
        • 虚拟内存区域
      • virt_addr
        • 重新映射时的起始用户虚拟地址
      • pfn
        • 与物理内存对应的页帧号,虚拟内存将要被映射到该物理内存
        • 页帧号只是将物理地址右移PAGE_SHIFT位
      • size
        • 以字节为单位
      • prot
        • 新VMA要求的“保护(protection)”属性
    • 一个简单的实现
      • drivers/char/mem.c
      • remap_pfn_range(vma, vma->vm_start, vm_.vm_pgoff, vma->vm_end – vma->vm_start, vma->vm_page_prot)
    • 为VMA添加操作
      • struct vm_operations_struct simple_remap_vm_ops = {.open = simple_vma_open, .close = simple_vma_close,}
    • 使用nopage映射内存
      • 如果要支持mremap系统调用,就必须实现nopage函数
      • struct page *(*nopage) (struct vm_area_struct *vma, unsigned long address, int *type);
      • get_page(struct page *pageptr);
      • static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma)
      • {
        • unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
        • if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC))
          • vm->vm_flags |= VM_IO
        • vm->vm_flags |= VM_RESERVED;
        • vm->vm_ops = &simple_nopage_vm_ops;
        • simple_vma_open(vma);
        • return 0;
      • }
      • struct page *simple_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
      • {
        • struct page *pageptr;
        • unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
        • unsigned long physaddr = address – vma->vm_start + offset;
        • unsigned long pageframe = physaddr >> PAGE_SHIFT;
        • if (!pfn_valid(pageframe))
          • return NOPAGE_SIGBUS;
        • pageptr = pfn_to_page(pageframe);
        • get_page(pageptr);
        • if (type)
            • type = VM_FAULT_MINOR;
        • return pageptr;
      • }
    • 重新映射RAM
      • 对remap_pfn_range函数的一个限制是:它只能访问保留页和超出物理内存的物理地址
      • remap_pfn_range不允许重新映射常规地址
      • 使用nopage方法重新映射RAM
        • 使用vm_ops->nopage一次处理一个页错误
    • 重新映射内核虚拟地址
      • page = vmalloc_to_page(pageptr);
      • get_page(page);
  • 执行直接I/O访问
    • 如果需要传输的数据量非常大,直接进行数据传输,而不需要额外地从内核空间拷贝数据操作的参与,这将会大大提高速度
    • 设置直接I/O的开销非常巨大
      • 使用直接I/O需要write系统调用同步执行
      • 在每个写操作完成之前不能停止应用程序
    • <linux/mm.h>
      • int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm-area_struct **vmas);
        • tsk
          • 指向执行I/O的任务指针,该参数几乎是current
        • mm
          • 指向描述被映射地址空间的内存管理结构的指针
          • 对驱动程序来说,该参数总是current->mm
        • force
          • 如果write非零,对映射的页有写权限
          • 驱动程序对该参数总是设置为0
        • pages
          • 如果调用成功,pages中包含了一个描述用户空间缓冲区page结构的指针列表
        • vmas
          • 如果调用成功,vmas包含了相应VMA的指针
    • 使用直接I/O的设备通常使用DMA操作
    • 一旦直接I/O操作完成,就必须释放用户内存页
    • <linux/page-flags.h>
      • void SetPageDirty(struct page *page);
    • void page_cache_release(struct page *page);
    • 异步I/O
      • <linux/aio.h>
      • ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);
      • ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);
      • int (*aio_fsync) (struct kiocb *iocb, int datasync);
      • int is_sync_kiocb(struct kiocb *iocb);
      • int aio_complete(struct kiocb *iocb, long res, long res2);
  • 直接内存访问
    • DMA是一种硬件机制同,它允许外围设备和主内存之间直接传输它们的I/O数据,而不需要系统处理器的参与
    • 使用这种机制可以大大提高与设备通信的吞吐量
    • DMA数据传输概览
      • 有两种方式引发数据传输
        • 软件对数据的请求
          • 当进程调用read,驱动程序函数分配一个DMA缓冲区,并让硬件将数据传输到这个缓冲区中,进程处于睡眠状态
          • 硬件将数据写入到DMA缓冲区中,当写入完毕,产生一个中断
          • 中断处理程序获得输入的数据,应答中断,并且唤醒进程,该进程现在即可读取数据
        • 硬件异步地将数据传递给系统
          • 硬件产生中断,宣告新数据的到来
          • 中断处理程序分配一个缓冲区,并且告诉硬件向哪里传输数据
          • 外围设备将数据写入缓冲区,完成后产生另外一个中断
          • 处理程序分发新数据,唤醒任何相关进程,然后执行清理工作
    • 分配DMA缓冲区
      • 使用DMA缓冲区的主要问题是:当大于一页时,它们必须占据连接的物理页,这是因为设备使用ISA或者PCI系统总线传输数据,而这两种方式使用的都是物理地址
      • DIY分配
        • get_free_pages函数可以分配多达几M字节的内存,但是对较大数量的请求,甚至是远少于128KB的请求也通常会失败,这是因为此时系统内存中充满了内存碎片
        • 当内核不能返回请求数量的内存或需要超过128KB内存时,除了返回-ENOMEM,另外一个方法是在引导时分配内存或是为缓冲区保留顶部物理RAM
        • 还有一个方法是使用GFP_NOFAIL分配标志来为缓冲区分配内存
    • 总线地址
      • 使用DMA的设备驱动程序将与连接到总线接口上的硬件通信,硬件使用的是物理地址,而程序代码使用的是虚拟地址
      • <asm/io.h>
        • unsigned long virt_to_bus(volatile void *address);
        • void *bus_to_virt(unsigned long address);
    • 通用DMA层
      • 内核提供了一个与总线体系架构无关的DMA层
      • <linux/dma-mapping.h>
      • 处理复杂的硬件
        • int dma_set_mask(struct device *dev, u64 mask);
          • 该掩码显示与设备能寻址能力对应的位
          • 如果dma_set_mask返回0,则对该设备不能使用DMA
      • DMA映射
        • 一个DMA映射是要分配的DMA缓冲区与为该缓冲区生成的、设备可访问地址的组合
        • DMA映射建立了一个新的结构类型——dma_addr_t来表示总线地址
        • 根据DMA缓冲区期望保留的时间长短,PCI代码区分两种类型的DMA映射
          • 一致性DMA映射
            • 这种类型的映射存在于驱动程序生命周期中
            • 一致性映射的缓冲区必须可同时被CPU和外围设备访问
            • 建立和使用一致性映射的开销是很大的
          • 流式DMA映射
            • 通常为单独的操作建立流式映射
            • 内核开发者建议尽量使用流式映射,然后再考虑一致性映射
              • 在支持映射寄存器的系统中,每个DMA映射使用总线上的一个或者多个映射寄存器
              • 在一些硬件中,流式映射可以被优化,但优化的方法对一致性映射无效
      • 建立一致性DMA映射
        • void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
          • 返回值是缓冲区的内核虚拟地址
          • 与其相关的总线地址,保存在dma_handle中
        • void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle);
      • DMA池
        • DMA池是一个生成小型、一致性DMA映射的机制
        • <linux/dmapool.h>
          • struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);
            • allocation不为零,表示内存边界不能超越allocation
          • void dma_pool_destroy(struct dma_pool *pool);
          • void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);
            • 返回的DMA缓冲区的地址是内核虚拟地址
          • void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);
      • 建立流式DMA映射
        • 当建立流式映射时,必须告诉内核数据流动的方向
        • enum dma_data_direction
          • DMA_TO_DEVICE
          • DMA_FROM_DEVICE
          • DMA_BIDIRECTIONAL
          • DMA_NONE
        • dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
        • void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);
        • 有几条非常重要的原则用于流式DMA映射
          • 缓冲区只能用于这样的传送,即其传送方向匹配于映射时给定的方向wfhg
          • 一旦缓冲区被映射,它将属于设备,而不是处理器
          • 在DMA处于活动期间内,不能撤销对缓冲区映射,否则会严重破坏系统的稳定性
        • void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
        • void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
      • 单页流式映射
        • dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction direction);
        • void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, enum dma_data_direction direction);
      • 分散/聚焦映射
        • 这是一种特殊的流式DMA映射
        • 假设有几个缓冲区,它们需要与设备双向传输数据
        • 有几种方式能产生这种情形
          • 从raedv或者writev系统调用产生
          • 从集群的磁盘I/O请求产生
          • 从映射的内核I/O缓冲区中的页面链表产生
        • 许多设备都能接受一个指针数组的分散表,以及它的长度,然后在一次DMA操作中把它们全部传输走
        • 映射分散表的第一步是建立并填充一个描述被传送缓冲区的scatterlist结构的数组
        • <linux/scatterlist.h>
        • struct scatterlist
          • struct page *page;
          • unsigned int length;
          • unsigned int offset;
        • int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
          • nents是传入的分散表入口的数量
          • 返回值是要传送的DMA缓冲区数
        • 驱动程序应该传输由dma_map_sg函数返回的每个缓冲区
        • dma_addr_t sg_dma_address(struct scatterlist *sg);
        • unsinged int sg_dma_len(struct scatterlist *sg);
        • void dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);
          • nents一定是先前传递给dma_map_sg函数的入口项的数量
        • void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
        • void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
      • PCI双重地址周期映射
        • 通常DMA支持层使用32位总线地址,其为设备的DMA掩码所约束
        • PCI总线还支持64位地址模式,既双重地址周期(DAC)
        • 如果设备需要使用放在高端内存的大块缓冲区,可以考虑实现DAC支持
        • <linux/pci.h>
        • int pci_dac_set_dma_mask(struct pci_dev *pdev, u64 mask);
          • 返回0时,才能使用DAC地址
        • dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction);
          • direction
            • PCI_DMA_TODEVICE
            • PCI_DMA_FROMDEVICE
            • PCI_DMA_BIDIRECTIONAL
        • void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction);
        • void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction);
    • ISA设备的DMA
      • ISA总线允许两种DMA传输:本地(native)DMA和ISA总线控制(bus-master)DMA
      • 本地DMA使用主板上的标准DMA控制器电路来驱动ISA总线上的信号线
      • ISA总线控制DMA完全由外围设备控制
      • 有三种实现涉及到ISA总线上的DMA数据传输
        • 8237 DMA控制器(DMAC)
        • 外围设备
          • 当设备准备传送数据时,必须激活DMA请求信号
        • 设备驱动程序
          • 需要驱动程序完成的工作很少,它只是负责提供DMA控制器的方向、总线地址、传输量的大小等等
      • 注册DMA
        • <asm/dma.h>
          • int request_dma(unsigned int channel, const char *name);
            • 返回0表示执行成功
          • void free_dma(unsigned int channel);
      • 与DMA控制器通信
        • unsigned long claim_dma_lock();
        • 必须被装入控制器的信息包含三个部分:RAM的地址、必须被传输的原子项个数以及传输的方向
        • void set_dma_mode(unsigned int channel, char mode);
          • mode
            • DMA_MODE_READ
            • DMA_MODE_WRITE
            • DMA_MODE_CASCADE
              • 释放对总线的控制
        • void set_dma_addr(unsigned int channel, unsigned int addr);
        • void set_dma_count(unsigned int channel, unsigned int count);
        • void disable_dma(unsigned int channel);
        • void enable_dma(unsigned int channel);
        • int get_dma_residue(unsigned int channel);
          • 返回还未传输的字节数
        • void clear_dma_ff(unsigned int channel);
嵌入式学习路线全解析:从C语言到Linux实战,系统掌握开发核心 嵌入式系统作为专用计算机系统,其核心在于针对特定功能进行优化设计,广泛应用于智能设备、工业控制等领域。其技术原理涉及从底层硬件寄存器操作到上层应用逻辑的完整栈,要求开发者具备软硬件协同能力。掌握嵌入式开发的技术价值在于能够构建高可靠性、实时响应的智能终端,是物联网、边缘计算等前沿领域的基石。应用场景覆盖从简单的单片机控制到复杂的嵌入式Linux设备开发。本文基于【STM32】和【FreeRTOS】两大热词,系统梳理了从C语言基础、单片机裸机开发到RTOS应用、嵌入式Linux实战的渐进式学习路径,为初学者提 阅读详情

相关推荐

没有IOMMU的DMA操作-swiotlb(转)

因为DMA受32位访问的限制,所以只能访问0x0000_0000到0xFFFF_FFFF地址空间的内存,再加上DMA需要访问连续的物理内存,故coherent pool,cma,buddy,swiotlb必须保证在0x0000_0000~0xFFFF_FFFF以内的连续物理空间。我们知道DMA映射有两种方式,一种是一致性映射 dma_alloc_coherent,一种是流式映射 dma_map_single (dma_map_sg可以映射多个dma buffer)。

gjioui123的博客 2623

深度阐述Linux x86-64 IOMMU的原理

深度介绍IOMMU软硬件的介绍

存储随笔 3479

PCI设备的DMA映射操作详解

上周认真学习了LDD3第15章直接内存访问部分,这周调试PCI的网卡和视频采集卡,结合代码对DMA映射有了进一步的理解,这里按照LDD315章的顺序总结一下,记一下笔记,以后忘了再来翻,本人刚毕业1年菜鸟,理解不到的地方还希望大神指点!   根据LDD3说法,DMA是一种硬件机制,是说硬件具有这种仲裁能力,在cpu不干预的情况下设备可以作为主设备来对内存的直接读写访问,这样可以大大提高大数据流的

做一个有技术追求的人 1万+

IOMMU/SMMUV3代码分析(9)函数dma_map_single()和dma_map_page()

对于函数dma_map_single()和函数dma_map_page(),它们的操作类似,对一块物理内存进行映射,且最终会调用dma_map_page_attrs()。它们的差别为,dma_map_single()将一块物理连续内存进行映射,而dma_map_page()将一页物理内存进行映射。 与之前dma_map_sg()类似,同样分为直接映射和使能SMMU情况。 1 DMA直接映射情况 与dma_map_sg()类似,dma_map_pag...

flyingnosky的专栏 9495

PCIe-2.2.7 Memory, I/O, and Configuration Request Rules in No-Flit Mode/Flit Mode & MSI/MSI-X

分层Requester ID管理RC层:管理总线号分配和重配置设备层:捕获并应用分配的Bus/Device NumberSwitch层:保持Transaction ID不变,使用正确ID生成自己的请求。

weixin_44700185的博客 103

6 赫斯曼网管软件industrial hivision用户管理

文章目录前言一、打开Industrial HiVision服务器二、在用户管理界面添加策略三、使用可选策略四、接受策略总结 前言 安装 Industrial HiVision 后,所有人都能广泛使用程序。安全起见,请进行相 关配置,对访问权限进行限制。 本文主要介绍用户管理基于某种角色模型。在访问角色中定义角色名称,通过分配访问权限给这些角色分配角色功能。例如,给管理人员自然要分配可用的权限,而观察员尽管可以登录,但没有能够进行更改的权限。Industrial HiVision 提供以下访问角色: 

t98746194的博客 1381

【Linux内核】内存管理——缓存一致性问题

CPU访问外设的数据寄存器和状态寄存器时,也是需要将寄存器的值映射到内存上进行读写,外设寄存器的值由硬件修改,也可以由CPU读写,因此外设和CPU对寄存器映射的内存访问也是异步的。在没有写回之前,核心B的cache中的同个数据还没改,当B去访问它时则会与A不一致。因为DMA是直接内存访问,对内存的访问不需要经过CPU,因此如果DMA修改了内存上的CPU访问过的数据,而CPU的cache中并不知道内存已经修改了,CPU读写内存的时候还是cache上的旧数据,造成缓存不一致问题。

Ethan-Code's blog 2190

Windows Server 2022域控环境下的MDT部署工具安装与配置避坑指南

本文详细介绍了在Windows Server 2022域控环境中安装和配置MDT(Microsoft Deployment Toolkit)的全流程,包括ADK与Windows PE的精准安装、MDT高级配置、部署工作流构建及性能优化技巧。通过实战指南帮助IT管理员实现自动化系统部署,提升企业IT运维效率,特别适用于需要静默下发系统的大规模部署场景。

weixin_28481133的博客 436

杂谈 linux DMA内存

这块物理块会记录在 cma_areas[cma_area_count] 中,然后 dma_contiguous_default_area 指向这块物理块。不同的mask,对应从不同的zone获取内存。2 dma_contiguous_default_area 没货了,则优先从dma zone获取page。最后将64为的物理地址对应到32位的dma地址,phys_attr_t转化位dma_attr_t。注意了,开始找的地方是从dma的边界往下(arm64_dma_phys_limit)。

m0_37797953的博客 1363

Ansys Zemax导入CAD文件避坑指南:STL/STEP/IGES格式选择与性能优化

本文深度解析了在Ansys Zemax中导入CAD文件的核心要点与性能优化策略。针对STL、STEP、IGES等主流格式,详细对比了其几何表示、精度与适用场景差异,并提供了基于模型特征的格式选择决策树。文章重点分享了体元、弦公差等高级参数的调优技巧,以及处理复杂装配体和避免精度陷阱的实战指南,旨在帮助光学工程师提升仿真效率与结果可靠性。

weixin_29202687的博客 436

用ESP32-S3和USB摄像头DIY一个无线图传监控(附完整代码和接线图)

本文详细介绍了如何利用ESP32-S3开发板和USB摄像头打造一套高性价比的无线图传监控系统。从硬件选型、接线图到代码实现,提供了完整的DIY方案,特别适合家庭监控、宠物看护等场景。通过优化WiFi传输和双核任务分配,实现了稳定的视频流传输,整套方案成本控制在200元以内。

weixin_33701251的博客 519

手撕ARM64启动栈(一):QEMU + TF-A → OP-TEE → U-Boot → Linux 全链路总览

ARM 官方提供的参考固件实现,本文用作 BL1/BL2/BL31:开源 TEE 实现,本仓库用作 BL32,运行在 S-EL1,通过 opteed(SPD)与 BL31 交互,提供 TA 运行环境U-Boot:本仓库用作 BL33/Normal World bootloader,通过 ARM semihosting(,非网络/非fw_cfg)从宿主机文件系统读取内核与 initrd,bootm跳转内核。

Fifty的博客 495

深入解析EDID中Display Descriptor Block的配置技巧与实战应用

本文深入解析EDID中Display Descriptor Block的配置技巧与实战应用。通过剖析像素时钟、有效像素、消隐区与同步脉冲等核心参数,并结合1280x720@60Hz的实战配置案例,指导开发者如何正确为定制硬件或非标准显示器编写“身份证”,避免黑屏、分辨率异常等问题,确保显示设备完美点亮。

1a2s3d4f5g的博客 878

嵌入式开发资源地图:从官方文档到实战项目的体系化学习与避坑指南

嵌入式系统开发融合了硬件设计、底层软件编程和实时操作系统等核心技术,其核心在于通过微控制器或处理器对专用硬件进行高效、可靠的控制。理解其工作原理,需要掌握从电路基础、C语言编程到外设驱动、RTOS任务调度乃至嵌入式Linux系统构建的知识体系。这项技术的价值在于实现智能设备的自主运行与实时响应,广泛应用于物联网终端、工业控制、汽车电子和消费电子等领域。对于开发者而言,体系化的学习路径和高质量的资源导航至关重要。本文基于【STM32】和【嵌入式Linux】等核心热词,系统梳理了从官方文档阅读、开发环境配置到实

weixin_34161032的博客 359

汽车软件工程师必看:ASPICE开发流程全解析(附各阶段交付物清单)

本文全面解析了汽车行业广泛采用的ASPICE开发流程,详细介绍了从需求挖掘到系统架构设计、软件工程及测试验证各阶段的关键操作和交付物清单。针对汽车软件工程师,提供了实用的协作指南和工具推荐,帮助团队提升开发效率并降低项目风险,是掌握ASPICE标准的必备指南。

weixin_30733003的博客 643

031、速度前馈与加速度前馈

摘要: 本文通过追剪机调试案例,阐述了速度前馈和加速度前馈在运动控制中的关键作用。前馈不是高级技巧,而是解决动态响应问题的必要手段:速度前馈通过预测速度变化提前补偿,加速度前馈则针对惯量变化提供力矩补偿。实现时需注意信号平滑处理,避免噪声放大。前馈与PID分工明确,前馈承担主要动态响应,PID处理残余误差。调试应先调前馈再微调PID,前馈增益需计算而非仅凭参数调整,并允许适当残余误差。前馈作为开环补偿不会影响系统稳定性,但需配合平滑的指令生成器使用。(149字)

m0_50546716的博客 285

MPC8379E eTSEC MIB寄存器深度解析:嵌入式网络性能监控与故障诊断实战

在嵌入式网络开发中,网络性能监控是保障系统稳定性的基石。其核心原理在于通过硬件计数器实时采集网络流量与错误数据,这通常基于RMON和IEEE 802.3 MIB等标准。这项技术的核心价值在于,它能以零CPU开销提供物理层与数据链路层的精准洞察,使工程师能从被动排障转向主动运维。其典型应用场景包括工业控制、通信网关等高可靠性领域,用于诊断网络延迟、丢包及吞吐量瓶颈。本文聚焦于**MPC8379E**处理器的**eTSEC MIB寄存器**,深入剖析其架构、关键计数器(如`RFCS`、`TLCL`)的实战意义,

dianpi2993的博客 406

YOLO系列算法改进 | C2PSA改进篇 | 融合FSD频域结构解耦模块 | 双域解耦感知,兼顾全局亮度与局部纹理,适应红外小目标与边缘部署场景 | ICME 2026

YOLO系列算法改进 | C2PSA改进篇 | 融合FSD频域结构解耦模块 | 双域解耦感知,兼顾全局亮度与局部纹理,适应红外小目标与边缘部署场景 | ICME 2026 (以YOLO26最新目标检测模型为基础,利用C2PSA_FSD创新模块进行改进)

2502_91881744的博客 315

Linux中DMA驱动空间映射(二)

在内存块中按照2的order次方字节来创建多个slab描述符,如16字节、32字节、64字节、128字节等大小,系统会 分别创建kmalloc-16、kmalloc-32、kmalloc-64等slab描述符,在系统启动时这在()函数中完成。例如,要分配30字节的一个小内存块,可以用“kmalloc(30,GFP_KERNEL)’’ 实现,之后系统会从kmalloc-32 slab描述符中分配一个对象。kmalloc:分配物理连续的内存地址(则虚拟地址自然连续,基于 slab)。

qq_44607924的博客 1988

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Linux x86-64 IOMMU详解(二)——SWIOTLB(软件IOMMU)

SWIOTLB概述 上一篇文章已经提到,IOMMU的核心功能就是,实现在low buffer和high buffer之间的sync,也就是内存内容的复制操作。 读者可能会想,内存的复制,在内核中,不就是调用memcpy()函数来实现的吗?没错,这就是本文要介绍的IOMMU的软件实现方式——SWIOTLB。之所以说是软件实现,是因为sync操作在底层正是调用memcpy()函数,这完全是软件实现的。......

qq_34719392的博客 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Dynamic DMA mapping Guide——linux内核doc译文

转载自:Dynamic DMA mapping Guide 一、前言 这是一篇指导驱动工程师如何使用DMA API的文档,为了方便理解,文档中给出了伪代码的例程。另外一篇文档dma-api.txt给出了相关API的简明描述,有兴趣也可以看看那一篇,这两份文档在DMA API的描述方面是一致的。 二、从CPU角度看到的地址和从DMA控制器看到的地址有什么不同? 在DMA API中涉及好...

steveliu->blog 845

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

linux-DMA-API : 使用通用设备的动态DMA映射

使用通用设备的动态DMA映射 ============================================ James E.J. Bottomley <James.Bottomley@HansenPartnership.com> 本文档描述了DMA API。 对于API的更温和的介绍(以及实际的例子),请参见Documentation/DMA-API-HOWTO.txt。 这个API被分成两部分。 第一部分描述了基本的API。第...

天使之翼 5814

linux之dma api -- 通用设备的动态dma映射,通用设备的动态DMA映射

通用设备的动态DMA映射本文描述DMAAPI。更详细的介绍请参看Documentation/DMA-API-HOWTO.txt。API分为两部分,第一部分描述API,第二部分描述可以支持非一致性内存机器的扩展API。你应该使用第一部分所描述的API,除非你知道你的驱动必须要支持非一致性平台。第一部分 DMAAPI为了可以引用DMA API,你必须 #include1-1 使用大块DMA一致性缓冲区...

weixin_39719165的博客 611
上一篇: 《Linux Device Drivers》第十四章 Linux 设备模型
下一篇: 《Linux Device Drivers》第十六章 块设备驱动程序——note
pingfengluo
博客等级 码龄14年 260粉丝 48原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值