转载地址:http://www.poluoluo.com/server/201107/138420.html

在研究dahdi驱动的时候,见到了一些get_user,put_user的函数,不知道其来由,故而搜索了这篇文章,前面对linux内存的框架描述不是很清晰,描述的有一点乱,如果没有刚性需求,建议不用怎么关注,倒不如直接看那几个图片。对我非常有用的地方就是几个函数的介绍,介绍的比较详细,对应用有需求的可以着重看一个这几个函数。

Linux 内存

在 Linux

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

但是与安全性相关联的成本很高。因为每个进程(和内核)会有相同地址指向不同的物理内存区域,不可能立即共享内存。幸运的是,有一些解决方案。用户进程可以通过

Portable Operating System Interface for UNIX? (POSIX)

共享的内存机制(shmem)共享内存,但有一点要说明,每个进程可能有一个指向相同物理内存区域的不同虚拟地址。

虚拟内存到物理内存的映射通过页表完成,这是在底层软件中实现的(见图

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

图 1. 页表提供从虚拟地址到物理地址的映射

由于缺乏为进程定义内存的能力,底层物理内存被过度使用。通过一个称为 paging(然而,在 Linux 中通常称为

swap)的进程,很少使用的页面将自动移到一个速度较慢的存储设备(比如磁盘),来容纳需要被访问的其它页面(见图 2

)。这一行为允许,在将很少使用的页面迁移到磁盘来提高物理内存使用的同时,计算机中的物理内存为应用程序更容易需要的页面提供服务。注意,一些页面可以指向文件,在这种情况下,如果页面是脏(dirty)的,数据将被冲洗,如果页面是干净的(clean),直接丢掉。

图 2. 通过将很少使用的页面迁移到速度慢且便宜的存储器,交换使物理内存空间得到了更好的利用

MMU-less 架构

不是所有的处理器都有 MMU。因此,uClinux 发行版(微控制器 Linux)支持操作的一个地址空间。该架构缺乏 MMU

提供的保护,但是允许 Linux 运行另一类处理器。

选择一个页面来交换存储的过程被称为一个页面置换算法,可以通过使用许多算法(至少是最近使用的)来实现。该进程在请求存储位置时发生,存储位置的页面不在存储器中(在存储器管理单元

[MMU] 中无映射)。这个事件被称为一个页面错误 并被硬件(MMU)删除,出现页面错误中断后该事件由防火墙管理。该栈的详细说明见 图

3。

Linux 提供一个有趣的交换实现,该实现提供许多有用的特性。Linux

交换系统允许创建和使用多个交换分区和优先权,这支持存储设备上的交换层次结构,这些存储设备提供不同的性能参数(例如,固态磁盘 [SSD]

上的一级交换和速度较慢的存储设备上的较大的二级交换)。为 SSD

交换附加一个更高的优先级使其可以使用直至耗尽;直到那时,页面才能被写入优先级较低的交换分区。

图 3. 地址空间和虚拟 - 物理地址映射的元素

并不是所有的页面都适合交换。考虑到响应中断的内核代码或者管理页表和交换逻辑的代码,显然,这些页面决不能被换出,因此它们是固定的,或者是永久地驻留在内存中。尽管内核页面不需要进行交换,然而用户页面需要,但是它们可以被固定,通过

mlock(或

mlockall)函数来锁定页面。这就是用户空间内存访问函数的目的。如果内核假设一个用户传递的地址是有效的且是可访问的,最终可能会出现内核严重错误(kernel

panic)(例如,因为用户页面被换出,而导致内核中的页面错误)。该应用程序编程接口(API)确保这些边界情况被妥善处理。

内核 API

现在,让我们来研究一下用户操作用户内存的内核 API。请注意,这涉及内核和用户空间接口,而下一部分将研究其他的一些内存

API。用户空间内存访问函数在表 1 中列出。

表 1. 用户空间内存访问 API

函数

描述

access_ok

检查用户空间内存指针的有效性

get_user

从用户空间获取一个简单变量

put_user

输入一个简单变量到用户空间

clear_user

清除用户空间中的一个块,或者将其归零。

copy_to_user

将一个数据块从内核复制到用户空间

copy_from_user

将一个数据块从用户空间复制到内核

strnlen_user

获取内存空间中字符串缓冲区的大小

strncpy_from_user

从用户空间复制一个字符串到内核

正如您所期望的,这些函数的实现架构是独立的。例如在 x86 架构中,您可以使用

./linux/arch/x86/lib/usercopy_32.c 和 usercopy_64.c 中的源代码找到这些函数以及在

./linux/arch/x86/include/asm/uaccess.h 中定义的字符串。

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

图 4. 使用 User Space Memory Access API 进行数据移动

access_ok 函数

您可以使用 access_ok

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

access_ok( type, addr, size );

type 参数可以被指定为 VERIFY_READ 或 VERIFY_WRITE。VERIFY_WRITE

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

get_user 函数

要从用户空间读取一个简单变量,可以使用 get_user 函数,该函数适用于简单数据类型,比如,char 和

int,但是像结构体这类较大的数据类型,必须使用 copy_from_user

函数。该原型接受一个变量(存储数据)和一个用户空间地址来进行 Read 操作:

get_user( x, ptr );

get_user 函数将映射到两个内部函数其中的一个。在系统内部,这个函数决定被访问变量的大小(根据提供的变量存储结果)并通过

__get_user_x 形成一个内部调用。成功时该函数返回 0,一般情况下,get_user 和 put_user

函数比它们的块复制副本要快一些,如果是小类型被移动的话,应该用它们。

put_user 函数

您可以使用 put_user 函数来将一个简单变量从内核写入用户空间。和 get_user

一样,它接受一个变量(包含要写的值)和一个用户空间地址作为写目标:

put_user( x, ptr );

和 get_user 一样,put_user 函数被内部映射到 put_user_x 函数,成功时,返回 0,出现错误时,返回

-EFAULT。

clear_user 函数

clear_user

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

clear_user( ptr, n );

在内部,clear_user 函数首先检查用户空间指针是否可写(通过

access_ok),然后调用内部函数(通过内联组装方式编码)来执行 Clear 操作。使用带有 repeat

前缀的字符串指令将该函数优化成一个非常紧密的循环。它将返回不可清除的字节数,如果操作成功,则返回 0。

copy_to_user 函数

copy_to_user

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

0,否则返回一个非零数,指出不能发送的字节数。

copy_to_user( to, from, n );

检查了向用户缓冲区写入的功能之后(通过 access_ok),内部函数 __copy_to_user 被调用,它反过来调用

__copy_from_user_inatomic(在

./linux/arch/x86/include/asm/uaccess_XX.h 中。其中 XX 是 32 或者 64

,具体取决于架构。)在确定了是否执行 1、2 或 4 字节复制之后,该函数调用

__copy_to_user_ll,这就是实际工作进行的地方。在损坏的硬件中(在 i486 之前,WP

位在管理模式下不可用),页表可以随时替换,需要将想要的页面固定到内存,使它们在处理时不被换出。i486

之后,该过程只不过是一个优化的副本。

copy_from_user 函数

copy_from_user

函数将数据块从用户空间复制到内核缓冲区。它接受一个目的缓冲区(在内核空间)、一个源缓冲区(从用户空间)和一个以字节定义的长度。和

copy_to_user 一样,该函数在成功时,返回 0 ,否则返回一个非零数,指出不能复制的字节数。

copy_from_user( to, from, n );

该函数首先检查从用户空间源缓冲区读取的能力(通过 access_ok),然后调用 __copy_from_user,最后调用

__copy_from_user_ll。从此开始,根据构架,为执行从用户缓冲区到内核缓冲区的零拷贝(不可用字节)而进行一个调用。优化组装函数包含管理功能。

strnlen_user 函数

strnlen_user 函数也能像 strnlen 那样使用,但前提是缓冲区在用户空间可用。strnlen_user

函数带有两个参数:用户空间缓冲区地址和要检查的最大长度。

strnlen_user( src, n );

strnlen_user 函数首先通过调用 access_ok 检查用户缓冲区是否可读。如果是 strlen 函数被调用,max

length 参数则被忽略。

strncpy_from_user 函数

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

strncpy_from_user( dest, src, n );

由于从用户空间复制,该函数首先使用 access_ok 检查缓冲区是否可读。和 copy_from_user

一样,该函数作为一个优化组装函数(在 ./linux/arch/x86/lib/usercopy_XX.c 中)实现。

内存映射的其他模式

上面部分探讨了在内核和用户空间之间移动数据的方法(使用内核初始化操作)。Linux

还提供一些其他的方法,用于在内核和用户空间中移动数据。尽管这些方法未必能够提供与用户空间内存访问函数相同的功能,但是它们在地址空间之间映射内存的功能是相似的。

在用户空间,注意,由于用户进程出现在单独的地址空间,在它们之间移动数据必须经过某种进程间通信机制。Linux

提供各种模式(比如,消息队列),但是最着名的是 POSIX

共享内存(shmem)。该机制允许进程创建一个内存区域,然后同一个或多个进程共享该区域。注意,每个进程可能在其各自的地址空间中映射共享内存区域到不同地址。因此需要相对的寻址偏移(offset

addressing)。

mmap

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

函数通过 remap_pfn_range 内核函数实现,它提供设备内存到用户地址空间的线性映射。

结束语

本文讨论了 Linux

中的内存管理主题,然后讨论了使用这些概念的用户空间内存访问函数。在用户空间和内核空间之间移动数据并没有表面上看起来那么简单,但是

Linux 包含一个简单的 API 集合,跨平台为您管理这个复杂的任务。

linux 内核将两个设备相关联,linux用户空间和内核空间交换数据相关推荐

  1. 用户空间与内核空间,进程上下文与中断上下文[总结]【转】

    转自:http://blog.csdn.net/lizuobin2/article/details/51791863 本文转载自:http://www.cnblogs.com/Anker/p/3269 ...

  2. 谷歌:此Google帐户尚未与设备相关联安装应用程序之前请访问您设备上的googleplay商店应用程序怎么解决?

    部分用户使用谷歌google帐号登录googleplay应用商店,下载应用时遇到问题提示:此 Google 帐户尚未与设备相关联.安装应用程序之前,请访问您设备上的 Play 商店应用程序.(英文提示 ...

  3. linux 内核空间 sy,在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysf...

    级别: 初级 燚 杨 (), 计算机科学硕士 2006 年 2 月 16 日 本系列文章包括两篇,它们文详细地介绍了 Linux 系统下用户空间与内核空间数据交换的九种方式,包括内核启动参数.模块参数 ...

  4. linux内核空间和用户空间的是怎样区别的,如何交互,如何从用户空间进入内核空间

    linux驱动程序一般工作在内核空间,但也可以工作在用户空间.下面我们将详细解析,什么是内核空间,什么是用户空间,以及如何判断他们. Linux简化了分段机制,使得虚拟地址与线性地址总是一致,因此,L ...

  5. 嵌入式之linux用户空间与内核空间,进程上下文与中断上下文

    文章目录 前言 用户空间与内核空间 内核态与用户态 进程上下文和中断上下文 上下文 原子 进程上下文 中断上下文 进程上下文VS中断上下文 原子上下文 前言 之前在学习嵌入式linux系统的时候,一直 ...

  6. linux 物理内存用完了_Linux用户空间与内核空间(理解高端内存)

    Linux内核地址映射模型 x86 CPU采用了段页式地址映射模型.进程代码中的地址为逻辑地址,经过段页式地址映射后,才真正访问物理内存. 段页式机制如下图. Linux内核地址空间划分 通常32位L ...

  7. Linux Malloc分析-从用户空间到内核空间

    Linux Malloc分析-从用户空间到内核空间 本文介绍malloc的实现及其malloc在进行堆扩展操作,并分析了虚拟地址到物理地址是如何实现映射关系. ordeder原创,原文链接: http ...

  8. Linux 用户空间和内核空间

    最近在微信群里看到有人提这个问题,然后查了下资料,觉得这篇文章是写得最能让人看懂的,分享给大家. 欢迎大家评论说出自己的见解,让更多的人更容易理解这部分知识. 之前的相关文章 Linux内存,先看这篇 ...

  9. Linux用户空间与内核空间(理解高端内存)

    目录 Linux内核地址映射模型 Linux内核地址空间划分 Linux内核高端内存的由来 Linux内核高端内存的理解 Linux内核高端内存的划分 常见问题 小结 1.虚拟内核空间到物理空间的映射 ...

最新文章

  1. mysql 必读_MYSQL 调优和使用必读
  2. 腾讯2011.10.15校园招聘会笔试题
  3. 谈谈选用技术的原则,技术学习方法技巧,阅读代码的技巧及其它 MSF的一点心得...
  4. c语言第四版课后答案第三章3.4,算法与数据结构C语言版课后习题答案(机械工业出版社)第3,4章 习题参考答案...
  5. 电脑知识:电脑如何连接电视,在电视上显示?
  6. 对Linux课程内容的建议,Linux课程笔记 Day01 课程内容总结(示例代码)
  7. FFMPEG 库移植到 VC 需要的步骤
  8. 使用lvs实现负载均衡(V2.0)
  9. 用指针实现对二维数组元素的访问
  10. Dynamic 365 中创建编码规则
  11. kepware怎么读modbus/tcp数据_注塑机设备联网?EUROMAP 63?Kepware快速帮您搞定!
  12. @永和:为自己编码 --- 开源中国众包平台上线
  13. 读彼得林奇的成功投资有感
  14. 每周一书《用户故事地图》分享!设计、产品、开发必读!
  15. 【摘】请问make -j8 和make -j4 是什么意思?什么作用?
  16. 例题5.5 圆桌骑士 LA3523
  17. 第一章测试概述04-软件质量
  18. “AliOS之父”——阿里巴巴王坚博士
  19. php百度网盘解析源码,【教程】php实现百度网盘视频解析
  20. 最新荧惑网站渗透测试iAPP安卓源码+功能很多

热门文章

  1. centos7 redis5.0以前版本 集群部署示例 - 第一篇
  2. 【Java TreeMap】测试TreeMap的使用、Comparabe自定义类的自定义排序方式
  3. 网络协议之:socket协议详解之Datagram Socket
  4. Leet Code OJ 112. Path Sum [Difficulty: Easy]
  5. kotlin学习之函数(二)
  6. Effective Java之优先使用标准的异常(六十)
  7. hdu 1027 STL next_permutation
  8. algorithm头文件函数全集——史上最全,最贴心
  9. 1017 Queueing at Bank (25 分)_27行代码AC
  10. 【已解决】查看Python中已经安装的包