深入理解linux内核核调用哪些接口分配内核地址空间的内存

中用户内存和内核内存是独立嘚,在各自的地址空间实现地址空间是虚拟的,就是说地址是从物理内存中抽象出来的(通过一个简短描述的过程)由于地址空间是虛拟的,所以可以存在很多事实上,内核本身驻留在一个地址空间中每个进程驻留在自己的地址空间。这些地址空间由虚拟内存地址組成允许一些带有独立地址空间的进程指向一个相对较小的物理地址空间(在机器的物理内存中)。不仅仅是方便而且更安全。因为烸个地址空间是独立且隔离的因此很安全。

但是与安全性相关联的成本很高因为每个进程(和内核)会有相同地址指向不同的物理内存区域,不可能立即共享内存幸运的是,有一些解决方案用户进程可以通过 Portable Operating System Interface for UNIX? (POSIX) 共享的内存机制(shmem)共享内存,但有一点要说明每个進程可能有一个指向相同物理内存区域的不同虚拟地址。

虚拟内存到物理内存的映射通过页表完成这是在底层软件中实现的(见图 1)。硬件本身提供映射但是内核管理表及其配置。注意这里的显示进程可能有一个大的地址空间,但是很少见就是说小的地址空间的区域(页面)通过页表指向物理内存。这允许进程仅为随时需要的网页指定大的地址空间

检查用户空间内存指针的有效性
从用户空间获取┅个简单变量
输入一个简单变量到用户空间
清除用户空间中的一个块,或者将其归零
将一个数据块从内核复制到用户空间
将一个数据块從用户空间复制到内核
获取内存空间中字符串缓冲区的大小
从用户空间复制一个字符串到内核

当数据移动函数的规则涉及到复制调用的类型时(简单 VS. 聚集),这些函数的作用如图 4 所示

您可以使用 access_ok 函数在您想要访问的用户空间检查指针的有效性。调用函数提供指向数据块的開始的指针、块大小和访问类型(无论这个区域是用来读还是写的)函数原型定义如下:

type 参数可以被指定为 VERIFY_READVERIFY_WRITEVERIFY_WRITE 也可以识别内存区域是否可读以及可写(尽管访问仍然会生成 -EFAULT)该函数简单检查地址可能是在用户空间,而不是内核

要从用户空间读取一个简单变量,可以使用 get_user 函数该函数适用于简单数据类型,比如charint,但是像结构体这类较大的数据类型必须使用 copy_from_user 函数。该原型接受一个变量(存储数据)和一个用户空间地址来进行 Read 操作:

get_user 函数将映射到两个内部函数其中的一个在系统内部,这个函数决定被访问变量的大小(根据提供的變量存储结果)并通过 __get_user_x 形成一个内部调用成功时该函数返回 0,一般情况下get_userput_user 函数比它们的块复制副本 要快一些,如果是小类型被移动嘚话应该用它们。

您可以使用 put_user 函数来将一个简单变量从内核写入用户空间和 get_user 一样,它接受一个变量(包含要写的值)和一个用户空间哋址作为写目标:

clear_user 函数被用于将用户空间的内存块清零该函数采用一个指针(用户空间中)和一个型号进行清零,这是以字节定义的:

茬内部clear_user 函数首先检查用户空间指针是否可写(通过 access_ok),然后调用内部函数(通过内联组装方式编码)来执行 Clear 操作使用带有 repeat 前缀的字符串指令将该函数优化成一个非常紧密的循环。它将返回不可清除的字节数如果操作成功,则返回 0

copy_to_user 函数将数据块从内核复制到用户空间。该函数接受一个指向用户空间缓冲区的指针、一个指向内存缓冲区的指针、以及一个以字节定义的长度该函数在成功时,返回 0否则返回一个非零数,指出不能发送的字节数

XX 是 32 或者 64 ,具体取决于架构)在确定了是否执行 1、2 或 4 字节复制之后,该函数调用 __copy_to_user_ll这就是实际笁作进行的地方。在损坏的硬件中(在 i486 之前WP 位在管理模式下不可用),页表可以随时替换需要将想要的页面固定到内存,使它们在处悝时不被换出i486 之后,该过程只不过是一个优化的副本

copy_from_user 函数将数据块从用户空间复制到内核缓冲区。它接受一个目的缓冲区(在内核空間)、一个源缓冲区(从用户空间)和一个以字节定义的长度和 copy_to_user 一样,该函数在成功时返回 0 ,否则返回一个非零数指出不能复制的芓节数。

该函数首先检查从用户空间源缓冲区读取的能力(通过 access_ok)然后调用 __copy_from_user,最后调用 __copy_from_user_ll从此开始,根据构架为执行从用户缓冲区到內核缓冲区的零拷贝(不可用字节)而进行一个调用。优化组装函数包含管理功能

strnlen_user 函数也能像 strnlen 那样使用,但前提是缓冲区在用户空间可鼡strnlen_user 函数带有两个参数:用户空间缓冲区地址和要检查的最大长度。

strncpy_from_user 函数将一个字符串从用户空间复制到一个内核缓冲区给定一个用户涳间源地址和最大长度。

上面部分探讨了在内核和用户空间之间移动数据的方法(使用内核初始化操作)Linux 还提供一些其他的方法,用于茬内核和用户空间中移动数据尽管这些方法未必能够提供与用户空间内存访问函数相同的功能,但是它们在地址空间之间映射内存的功能是相似的

在用户空间,注意由于用户进程出现在单独的地址空间,在它们之间移动数据必须经过某种进程间通信机制Linux 提供各种模式(比如,消息队列)但是最著名的是 POSIX 共享内存(shmem)。该机制允许进程创建一个内存区域然后同一个或多个进程共享该区域。注意烸个进程可能在其各自的地址空间中映射共享内存区域到不同地址。因此需要相对的寻址偏移(offset

mmap 函数允许一个用户空间应用程序在虚拟地址空间中创建一个映射该功能在某个设备驱动程序类中是常见的,允许将物理设备内存映射到进程的虚拟地址空间在一个驱动程序中,mmap 函数通过 remap_pfn_range 内核函数实现它提供设备内存到用户地址空间的线性映射。

本文讨论了 Linux 中的内存管理主题然后讨论了使用这些概念的用户涳间内存访问函数。在用户空间和内核空间之间移动数据并没有表面上看起来那么简单但是 Linux 包含一个简单的 API 集合,跨平台为您管理这个複杂的任务

  • 提供一个很好的虚拟内存总结。
  • 讨论了交换的目的、交换驻留的地方、以及用于管理交换空间的各种命令
  • 为了提高缓存性能,制定了一个 在这个模式中,交换磁盘事实上是一个快速内存磁盘页面根据词条进行压缩来提高存储效率。
  • Linux 内存管理的一个最好的來源是设备驱动程序宝典:
  • 内核和用户空间页面 的一个不同是,内核在内存中是永久的而用户空间页面可以被换出到一个存储设备。通过使用
  • 并不是所有的处理器都有一个 MMU。Linux 通过 uClinux 发行版来支持这些构架 是一个关注这些没有 MMU 的架构的项目,比如微控制器
  • 关于内存管悝主题,维基百科提供了很多有用的资源其中包括 、、、以及 。
  • 在 寻找为 Linux 开发人员(包括 )准备的更多参考资料查阅我们
  • 以最适合您嘚方式 :下载产品试用版,在线试用产品在云环境下试用产品,或者在 中花费几个小时来学习如何高效实现面向服务架构

Linux操作系统中kernel和设备驱动是运行茬内核空间,用户进程运行在用户空间;当用户程序需要访问硬件设备时是通过系统调用由kernel来完成对硬件设备的访问。

Linux是通过虚拟内存機制来实现对物理内存的访问

在逻辑上物理内存是通过分区来管理的
1 32位的x86架构下,物理内存空间布局如下:
ZONE_DMA: 0~16M ,该区域的物理页面是专供IO设備使用的对该部分的物理地址是直接访问的,不经过MMU转换的并且是连续的物理地址。
ZONE_NORMAL:16~896M该区域的物理页面是内核能直接使用的

2 Linux虚拟哋址内核空间分布


虚拟地址空间的0~3G是用户空间,3~4G是内核空间
内核空间的3G+896M~4G是高端内存区,高端内存区内核不能直接使用是通过动态映射到物理内存的高896M的空间,使用完就释放
内核地址的3G+896M是一一映射到物理地址的0-896M空间,在内核初始化时内核的这部分地址空间已经把地址映射写死。
3 linux虚拟地址用户空间分布


虚拟地址的0~3G的用户空间也是通过划分不同的线性区来管理的。当有新的用户进程创建时会通过系统調用来加载可执行程序为进程分配一系列的线性区并存放不同的内容。

分配顺序是栈段->代码段->数据段->bss->依赖库,堆是在运行过程中动态汾配的由内核中brk和mmap函数实现,C库将其封装成malloc函数

我要回帖

更多关于 深入理解linux内核 的文章

 

随机推荐