news 2026/5/25 21:24:38

Linux 内核中常见地址的设计原理及其API使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 内核中常见地址的设计原理及其API使用

在Linux内核中,地址类型和它们的正确使用是理解内核内存管理的关键。本文将详细解释各种地址类型及其用法。

一、Linux内核中的主要地址类型

  1. 物理地址 (Physical Address)
phys_addr_t// 平台无关的物理地址类型

· 定义:CPU总线上的实际内存地址,对应RAM芯片上的物理位置

· 使用场景:DMA操作、硬件寄存器映射、物理页面管理

· 包含的头文件:#include <linux/types.h>

· 示例:

phys_addr_tphys_addr=page_to_phys(page);// 将struct page转换为物理地址dma_addr_tdma_handle=dma_map_single(dev,virt_addr,size,direction);
  1. 虚拟地址/内核逻辑地址 (Virtual/Kernel Logical Address)
void*// 通用指针unsignedlong// 用于计算的地址值

· 定义:内核直接映射区域的地址(通常是3GB/4GB分割中的1GB内核空间)

· 特点:

· 与物理地址有固定的线性映射关系(通常:virt = phys + PAGE_OFFSET)

· 可以通过virt_to_phys()和phys_to_virt()与物理地址相互转换

· 始终有效,可直接解引用(不需要特殊访问函数)

· 示例:

void*virt_addr=kmalloc(size,GFP_KERNEL);// 返回内核逻辑地址unsignedlongaddr=(unsignedlong)virt_addr;
  1. 内核虚拟地址 (Kernel Virtual Address)
void__iomem*// I/O内存的虚拟地址

· 定义:通过vmalloc()或ioremap()创建的地址,不一定有连续的物理映射

· 特点:

· 需要通过iowrite32()/ioread32()等函数访问(I/O内存)

· 或直接访问(vmalloc分配的内存)

· 示例:

// I/O内存映射void__iomem*io_addr=ioremap(phys_addr,size);u32 value=ioread32(io_addr+offset);iounmap(io_addr);// vmalloc分配void*vm_addr=vmalloc(size);memcpy(vm_addr,src,len);vfree(vm_addr);
  1. 用户空间地址 (User Space Address)
void__user*// 用户空间地址(需要特殊处理)

· 定义:用户进程虚拟地址空间中的地址

· 特点:

· 在内核中不能直接解引用

· 必须通过专门的复制函数访问

· 使用__user标记有助于静态分析工具(如Sparse)检查

· 包含的头文件:#include <linux/uaccess.h>

· 示例:

longcopy_to_user(void__user*to,constvoid*from,unsignedlongn);longcopy_from_user(void*to,constvoid__user*from,unsignedlongn);// 在系统调用中的典型用法staticssize_tmy_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos){charkernel_buf[256];// ... 填充kernel_buf ...if(copy_to_user(buf,kernel_buf,min(count,sizeof(kernel_buf))))return-EFAULT;returnbytes_copied;}
  1. 总线地址 (Bus Address)
dma_addr_t// 设备看到的DMA地址

· 定义:设备进行DMA操作时使用的地址

· 特点:可能与物理地址不同(存在IOMMU时)

· 包含的头文件:#include <linux/dma-mapping.h>

· 示例:

dma_addr_tdma_handle;void*cpu_addr=dma_alloc_coherent(dev,size,&dma_handle,GFP_KERNEL);// 将dma_handle传递给设备进行DMAdma_free_coherent(dev,size,cpu_addr,dma_handle);
  1. 各类地址及其kernel API

  1. 地址类型层次

  1. 内核地址空间布局

32位系统

高地址

0xFFFFFFFF ±-----------------+

| 内核代码/数据 | <- 内核自身代码

0xFFC00000 ±-----------------+

| KMAP_ATOMIC区 | <- 每个CPU的原子映射槽(4KB/CPU)

0xFEBFFFFF ±-----------------+

| 固定映射区 | <- 特殊用途固定映射

0xFE000000 ±-----------------+

| 持久映射区 | <- kmap持久映射(4MB)

0xF8000000 ±-----------------+

| vmalloc区 | <- vmalloc动态分配 | |

0xF7FFFFFF ±-----------------+

| 空洞 |

0xC0000000 ±-----------------+ <- PAGE_OFFSET(3GB)

| 直接映射区 | <- 896MB物理内存的线性映射 | (低端内存) | | |

0x00000000 ±-----------------+

低地址

64位系统

0xFFFFFFFFFFFFFFFF ±-----------------+

| 规范地址空洞 |

0xFFFF800000000000 ±-----------------+

| 内核模块 | | vmalloc区 | | vmemmap区 | <- 稀疏内存模型结构 | 直接映射所有物理内存 | <- 可映射TB级内存

0xFFFF800000000000 ±-----------------+

| 用户空间 | <- 128TB用户地址空间

0x00007FFFFFFFFFFF ±-----------------+

| 用户空间空洞 |

0x0000000000000000 ±-----------------+

二、关键转换函数和宏

物理地址 ↔ 虚拟地址转换

#include<asm/io.h>// 内核逻辑地址转换(仅限直接映射区域)phys_addr_tphys=virt_to_phys(virt_addr);void*virt=phys_to_virt(phys_addr);// 页帧号转换structpage*page=virt_to_page(virt_addr);void*virt=page_to_virt(structpage*page);// 通用映射(适用于任何物理地址)void__iomem*ioremap(phys_addr_tphys_addr,size_tsize);voidiounmap(void__iomem*addr);

用户空间地址访问

#include<linux/uaccess.h>// 基本复制函数unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongn);unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongn);// 验证用户空间指针是否可访问intaccess_ok(constvoid__user*addr,size_tsize);// 获取/设置单个值intget_user(x,ptr);// 从用户空间获取值intput_user(x,ptr);// 写入用户空间// 字符串操作longstrncpy_from_user(char*dst,constchar__user*src,longcount);longstrnlen_user(constchar__user*s,longn);

DMA地址转换

#include<linux/dma-mapping.h>// 一致性DMA映射(缓存一致)void*dma_alloc_coherent(structdevice*dev,size_tsize,dma_addr_t*dma_handle,gfp_tflag);voiddma_free_coherent(structdevice*dev,size_tsize,void*cpu_addr,dma_addr_tdma_handle);// 流式DMA映射(需要同步)dma_addr_tdma_map_single(structdevice*dev,void*ptr,size_tsize,enumdma_data_directiondir);voiddma_unmap_single(structdevice*dev,dma_addr_taddr,size_tsize,enumdma_data_directiondir);// DMA同步voiddma_sync_single_for_cpu(structdevice*dev,dma_addr_taddr,size_tsize,enumdma_data_directiondir);voiddma_sync_single_for_device(structdevice*dev,dma_addr_taddr,size_tsize,enumdma_data_directiondir);

API使用与选择决策树

地址转换API使用

谢谢关注,后续会持续分享关于AI,GPU,Linux开发,操作系统,图形学,高性能计算,芯片行业讯息。欢迎感兴趣的伙伴关注微信公众号参与讨论沟通:

请关注公众号获取完整系统资料


请关注微信公众号:颇锐克科技共享


图片
上下文地址转换API
图片
三、实际使用示例

示例1:混合使用各种地址类型

#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/uaccess.h>#include<linux/slab.h>#include<linux/io.h>staticintmy_device_mmap(structfile*filp,structvm_area_struct*vma){structmy_device*dev=filp->private_data;unsignedlongoffset=vma->vm_pgoff<<PAGE_SHIFT;unsignedlongphys=dev->phys_base+offset;unsignedlongvsize=vma->vm_end-vma->vm_start;unsignedlongpsize=dev->mem_size-offset;// 将物理地址映射到用户空间if(vsize>psize)return-EINVAL;returnremap_pfn_range(vma,vma->vm_start,phys>>PAGE_SHIFT,vsize,vma->vm_page_prot);}staticssize_tmy_device_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*pos){structmy_device*dev=filp->private_data;char*kernel_buf;intret;// 1. 在内核空间分配缓冲区(内核逻辑地址)kernel_buf=kmalloc(count,GFP_KERNEL);if(!kernel_buf)return-ENOMEM;// 2. 从用户空间复制数据(用户空间地址 -> 内核空间地址)if(copy_from_user(kernel_buf,buf,count)){kfree(kernel_buf);return-EFAULT;}// 3. 处理数据...// 4. 可能需要进行DMA操作dma_addr_tdma_addr=dma_map_single(dev->device,kernel_buf,count,DMA_TO_DEVICE);// 设置设备寄存器(使用I/O映射地址)iowrite32(dma_addr,dev->io_addr+REG_DMA_ADDR);iowrite32(count,dev->io_addr+REG_DMA_LEN);// 5. 清理dma_unmap_single(dev->device,dma_addr,count,DMA_TO_DEVICE);kfree(kernel_buf);returncount;}

示例2:简单的字符设备驱动程序

#include<linux/fs.h>#include<linux/uaccess.h>#defineBUFFER_SIZE1024staticchardevice_buffer[BUFFER_SIZE];staticssize_tdevice_read(structfile*filp,char__user*buffer,size_tlength,loff_t*offset){intbytes_to_copy;intbytes_copied=0;// 验证用户空间缓冲区是否可访问if(!access_ok(buffer,length))return-EFAULT;bytes_to_copy=min(length,(size_t)BUFFER_SIZE);// 从内核缓冲区复制到用户空间if(bytes_to_copy){if(copy_to_user(buffer,device_buffer,bytes_to_copy))return-EFAULT;bytes_copied=bytes_to_copy;}returnbytes_copied;}

最佳实践和注意事项

  1. 始终使用正确的类型:

    · 用户空间指针 → void __user *

    · I/O内存指针 → void __iomem *

    · DMA地址 → dma_addr_t

  2. 地址验证:

    // 对于用户空间指针if(!access_ok(VERIFY_READ,user_ptr,size))return-EFAULT;// 对于I/O映射地址if(!request_mem_region(phys_addr,size,"my_device"))return-EBUSY;
  3. 错误处理:

    · 复制函数返回未复制的字节数(0表示成功)

    · 映射函数可能返回NULL或错误码

    · 始终检查返回值

  4. 内存屏障:

    // 在访问设备内存时需要iowrite32(value,addr);wmb();// 写内存屏障
  5. 架构注意事项:

    · 32位 vs 64位系统地址大小不同

    · 大端序 vs 小端序系统

    · 使用#ifdef CONFIG_64BIT等条件编译

四、调试工具

  1. 地址打印:

    printk(KERN_INFO"Virtual address: %p\n",virt_addr);printk(KERN_INFO"Physical address: %pa\n",&phys_addr);printk(KERN_INFO"User address: %p\n",user_addr);
  2. 地址检查:

    #include<linux/vmalloc.h>// 检查地址是否在内核空间if(!is_vmalloc_addr(addr))// 是直接映射地址// 检查地址是否是有效用户空间地址if(!access_ok(VERIFY_READ,addr,size))// 无效地址

理解这些地址类型及其正确用法对于编写稳定、安全的内核代码至关重要。错误使用地址类型可能导致内核崩溃、安全漏洞或硬件损坏。

谢谢关注,后续会持续分享关于AI,GPU,Linux开发,操作系统,图形学,高性能计算,芯片行业讯息。欢迎感兴趣的伙伴关注微信公众号参与讨论沟通:

请关注公众号获取完整系统资料


请关注微信公众号:颇锐克科技共享


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 4:37:45

用ComfyUI打造专属AI滤镜:定制化图像风格生成方案

用ComfyUI打造专属AI滤镜&#xff1a;定制化图像风格生成方案 在广告公司做视觉设计的第三年&#xff0c;我终于不再被“上次那个色调怎么调的&#xff1f;”这种问题困扰了。过去每次客户说“就那种感觉&#xff0c;但再明亮一点”&#xff0c;团队就得翻聊天记录、试十几组参…

作者头像 李华
网站建设 2026/5/26 0:30:14

免费PCB设计查看神器:Altium文件浏览器完整使用指南

免费PCB设计查看神器&#xff1a;Altium文件浏览器完整使用指南 【免费下载链接】AltiumDesignerViewer Altium Designer Viewer是一款高效且易于使用的查看工具&#xff0c;专为设计工程师和团队成员打造&#xff0c;旨在无需进行任何注册或激活的情况下&#xff0c;轻松浏览和…

作者头像 李华
网站建设 2026/5/26 4:13:07

ImageSharp色彩变换:揭秘数字图像调色的数学魔法

ImageSharp色彩变换&#xff1a;揭秘数字图像调色的数学魔法 【免费下载链接】ImageSharp :camera: A modern, cross-platform, 2D Graphics library for .NET 项目地址: https://gitcode.com/gh_mirrors/im/ImageSharp 你是否曾好奇&#xff0c;那些令人惊艳的滤镜效果…

作者头像 李华
网站建设 2026/5/26 4:37:44

5分钟掌握Quick Tabs:Chrome标签管理终极指南

5分钟掌握Quick Tabs&#xff1a;Chrome标签管理终极指南 【免费下载链接】quick-tabs-chrome-extension A quick tab list and switch plugin inspired by the intelliJ IDEA java IDE 项目地址: https://gitcode.com/gh_mirrors/qu/quick-tabs-chrome-extension 还在为…

作者头像 李华
网站建设 2026/5/25 6:32:17

解锁Photoshop AVIF图像格式:完整安装与使用指南

解锁Photoshop AVIF图像格式&#xff1a;完整安装与使用指南 【免费下载链接】avif-format An AV1 Image (AVIF) file format plug-in for Adobe Photoshop 项目地址: https://gitcode.com/gh_mirrors/avi/avif-format 想要让Photoshop支持下一代图像格式AVIF吗&#xf…

作者头像 李华
网站建设 2026/5/26 4:44:49

终极指南:如何用SMUDebugTool快速解决Ryzen处理器性能瓶颈?

终极指南&#xff1a;如何用SMUDebugTool快速解决Ryzen处理器性能瓶颈&#xff1f; 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目…

作者头像 李华