Linux内存[翻译]

参考资料

Linux Agent 采集项说明 - 内存
Memory – Part 1: Memory Types
Memory – Part 2: Understanding Process memory

ipcs深入分析

虚拟内存

现代操作系统中, 每一个进程都都存活在各自的内存空间内. 但是操作系统并没有直接把内存地址对应硬件地址, 而是提供了一个硬件抽象层, 而为每一个进程创造了虚拟内存. 物理内存和虚拟内存之间的映射关系, 是由CPU 使用一个映射表转换得到的, 这个表由内核维护, 每一个进程在表中有一条记录.
虚拟内存有多个目的. 第一, 允许进程隔离. 一个进程只能够与虚拟内存中内存沟通. 这样它只能存取自己这个进程所关联的数据, 不能看到其他进程的内存数据. 第二, 硬件抽象. 内核可以自由的更改物理内存地址和虚拟内存的映射关系. 它也可以选择不给虚拟内存提供任何真实的物理内存, 除非真的需要. 而且它可以在虚拟内存长时间未使用, 而且物理内存吃紧的时候交换到硬盘上. 总的来说, 虚拟内存的存在使得内核更加自由, 唯一的限制是, 当程序需要读取内存的时候需要换算出原本数据写在哪儿. 第三, 它可以把不在RAM 中的存储单位当做内存. 这是mmap 和映射文件的原理. 可以把一个文件当做一段内存, 这样存取这个文件就像一段内存缓冲区. 这使得写代码比较简洁. 第四, 为了共享. 既然内核知道有哪些进程已经映射到虚拟内存中了, 就可以避免重复加载数据到内存, 而是重复利用指向物理内存的虚拟地址. 共享的结果就是内核使用COW (copy on write): 当两个进程使用同一份数据, 但是当其中一个更改了数据, 另一个进程不允许看到改变, 内核就会复制一份改变前的数据(类似Oracle 数据库的多版本机制).

fork()

最著名的COW 的案例就是fork(). 在类Unix 系统中, fork() 是一项系统调用, 可以复制现有的进程再造一个. 当fork() 返回时, 两个进程将处于相同的进度(打开的文件, 内存等等 相同). 由于COW 机制, fork() 并没有真的复制了一份进程的内存, 只有父进程改变的数据才在RAM 中复制. 由于大多数使用fork() 的场景中都立刻调用了exec(), 是的整个虚拟内存空间作废, COW 机制避免了父进程复制的内存完全无用.
另一个副作用是, fork() 用很低的成本创造了进程的私有内存的快照(snapshot). 如果你想在进程的内存中做一些操作, 但又害怕改坏了什么, 又不想去debug, 尽管fork()

Pages 分页

虚拟内存被拆分为Pages. 分页的大小由CPU 规定, 通常为4KiB. 这意味着内核中管理内存的粒度是4K. 当请求内存的时候, 内核返回的将是1 个或多个Pages, 当释放内存的时候也是以Page 为单位(Oracle 默认数据文件格式8K), 任何封装良好的API, 例如malloc, 都是在用户端发起的.
对每一个分配的Page, 内核保持了一组特权(permission): Page 可以被读, 写, 或者执行. 这些特权在分配内存的时候, 或者在之后调用mprotect() 的时候被设置. 没有被分配的Page 不能被存取.

内存类型

不是所有在虚拟内存中分配的内存都是一样的. 我们可以从两个方向分类. 第一类是内存是否是私有的或者共享的. 第二类是内存是否是file-backed. 这创造了一个4 个内存种类的分类.
private shared
anonymous
1. stack mmap(ANON, SHARED)
2. malloc()
3. mmap(ANON, PRIVATE)
4. brk()/sbrk()
file-backed 1. mmap(fd, PRIVATE) mmap(fd, SHARED)
2. binary/shared libraries

Private Memory

私有内存是进程独有的. 大多数程序使用的都是私有内存.
由于私有内存不能被其他进程存取, 所以私有内存服从COW. 副作用是, 即使内存是私有的, 多个进程也会共享一段物理内存去存储数据. 例如二进制文件和shared libraries.

Shared Memory

共享内存是设计用来实现进程间的通信. 当共享内存被修改, 其他进程可以看到改变.

Ananymous Memory

匿名内存全部在RAM 中. 但是内核不会在真正在RAM 写入之前分配地址.

File-backed and Swap

如果内存映射是file-backed, 数据是从磁盘上加载. 多数时候, 用的时候才加载. 但也可以控制内核提前加载. 物理内存不够用的时候, 内核会从RAM 移动一些数据到磁盘. linux 中, swap 是磁盘上一个特殊区域. 由于虚拟内存, swap pages 对进程来说完全是透明的.


去年来腾讯之后, 一直忙的云里雾里, 都没有空自己学点东西, 更别说写博客了. 当时在公司内网看到这个系列的linux 内存的文章, 粗枝大叶的翻译了第一二篇, 然后放在这里生尘灰, 半年都没有管他. 今天好不容易闲下来, 打开博客一看, 竟然有两千的阅读量, 真是意外啊. 所以我决定要重新润色一下这个系列的文章, 把没有完成的部分接着翻译下去, 才对得起看客们不小心点进来的时间啊!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux驱动开发是指在Linux操作系统下开发和编写设备驱动程序的过程。设备驱动程序是操作系统与硬件之间的桥梁,负责将操作系统的指令翻译成硬件可以理解的信号和命令,从而实现对硬件设备的控制和管理。 Linux驱动开发涉及到多个方面的知识和技术,包括设备文件的创建、设备的注册和初始化、中断处理、内存管理、驱动的加载与卸载等。下面分别对这些方面进行详细解释: 1. 设备文件的创建:在Linux系统中,每个设备都被视为一个特殊文件。驱动开发者需要通过创建设备文件来与硬件设备进行交互。这可以通过使用mknod命令或者udev规则来完成。 2. 设备的注册和初始化:驱动程序需要将自己注册到Linux内核中,以便操作系统能够正确地识别和调用驱动程序。这可以通过调用相关的注册函数(如platform_driver_register()或者misc_register())来完成。同时,驱动程序还需要对设备进行初始化,包括配置硬件寄存器、申请内存资源等。 3. 中断处理:中断是设备与CPU之间的一种异步通信机制,用于处理设备发生的事件。驱动程序需要注册中断处理函数,并在中断发生时执行相应的操作。在Linux中,可以通过request_irq()函数来申请中断,并通过irq_handler_t类型的函数来处理中断。 4. 内存管理:驱动程序需要管理设备所使用的内存资源,包括申请和释放内存。在Linux中,可以使用kmalloc()和kfree()函数来进行动态内存分配和释放。 5. 驱动的加载与卸载:驱动程序需要被加载到内核中才能生效。可以通过编译成内核模块的方式加载驱动,也可以将驱动编译进内核。加载驱动可以使用insmod命令,卸载驱动可以使用rmmod命令。 此外,驱动开发者还需要了解Linux内核的架构和相关的API接口,熟悉C语言和汇编语言编程,以及调试和排查驱动问题的技巧。 希望以上内容对你有所帮助!如有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值