news 2026/1/21 7:51:41

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/1/19 6:33:38

养老院信息|基于springboot + vue养老院信息管理系统(源码+数据库+文档)

养老院信息 目录 基于springboot vue养老院信息系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 基于springboot vue养老院信息系统 一、前言 博主介绍&#xff1a…

作者头像 李华
网站建设 2026/1/19 22:13:10

优化业务流程的营销智脑创新案例

本文围绕营销智脑在优化业务流程中的应用展开&#xff0c;阐释了其如何通过技术整合实现智能化的企业管理。营销智脑通过数据分析与客户服务的互动&#xff0c;为企业提供实时的数据支持&#xff0c;使决策更加科学化。在众多案例中&#xff0c;不同企业利用这一工具&#xff0…

作者头像 李华
网站建设 2026/1/19 22:18:59

企业级快速开发平台ruoyi-vue-pro:如何用30天完成传统3-6个月的项目

在数字化转型浪潮中&#xff0c;企业面临的最大挑战不是技术本身&#xff0c;而是如何在有限时间和预算内构建稳定、可扩展的业务系统。传统开发模式下&#xff0c;一个完整的企业级系统需要3-6个月才能上线&#xff0c;而基于ruoyi-vue-pro平台&#xff0c;这一周期可缩短至30…

作者头像 李华
网站建设 2026/1/19 22:25:11

如何快速掌握OAM Application Scopes:云原生应用边界管理的终极指南

如何快速掌握OAM Application Scopes&#xff1a;云原生应用边界管理的终极指南 【免费下载链接】spec Open Application Model (OAM). 项目地址: https://gitcode.com/gh_mirrors/spec3/spec 在现代云原生架构中&#xff0c;微服务组件数量爆炸式增长带来了前所未有的管…

作者头像 李华
网站建设 2026/1/20 7:24:47

Claude Code Router智能路由实战指南:5步构建多模型AI工作流

还在为单一AI模型的能力限制而烦恼&#xff1f;或者面对高昂的API成本感到压力&#xff1f;Claude Code Router为您提供了完美的解决方案。这个开源项目让您能够在不使用Anthropics官方账号的情况下&#xff0c;将Claude Code路由到其他LLM提供商&#xff0c;实现多模型智能调度…

作者头像 李华
网站建设 2026/1/19 14:42:08

开源免费!蝴蝶号下载工具 无需安装 绿色无广告

宝子们&#xff01;wx_channels_download是款超贴心的蝴蝶号专属下载器&#xff5e; 它在GitHub上开源免费&#xff0c;还是绿色单文件版&#xff0c;不用复杂安装&#xff0c;双击就能直接打开使用呀&#xff5e;宝子们注意啦&#xff5e; 打开wx_channels_download后&#xf…

作者头像 李华