Linux 内核开发 - 进程空间

1.1 虚拟内存

Linux 是一个多任务的系统,如果每个任务都独立的占用内存,则实际的物理内存将很快消耗殆尽,实际上对于前台正在运行的任务来说,所需要要的内存并不多,很多任务基本不需要运行,也就没有必要一直占用内存,虚拟内存技术采用硬盘来充当一部分内存,当内存不足时就将不需要在内存中使用的数据搬移到硬盘中去,当任务需要运行时又将硬盘中的数据搬回物理内存。

虚拟内存技术不仅起到了保护操作系统的作用,而且使得用户程序可以使用到比实际物理内存更大的地址空间,屏蔽了实际物理内存对用户地址空间的影响。

1.2 地址空间的划分

对于一个进程而言,Linux 系统将虚拟地址空间划分为用户空间与内核空间,用户空间占用3G(0x0~0xBFFFFFFF),而内核空间占用1G(0xC0000000 ~ 0xFFFFFFFF)。

Linux 是一个多用户的系统,每个用户就是一个进程,享有独立的地址空间,但是它们是共享内核空间,不同的进程之间进行切换的时候,内核空间的是不变的。

1.3 用户空间

Linux 应用程序时在用户空间运行,只有当产生中断或者当出现系统调用的时候进程才会从用户态切换到内核态。完成中断操作或者系统调用后又回到用户态。

1.4 内核空间

Linux 的核心程序运行在内核空间,这是为了保护核心程序不受劣质应用程序的影响而崩溃,而且能起到保护数据的作用。因为应用程序不具有直接控制硬件的权限,只有内核程序才能拥有系统的最高权限。

1.5 内核内存的分配与释放

在创建进程fork()、动态非配内存malloc()时分配的内存都只是虚拟内存,而不是物理内存,之后在实际访问分配的虚拟地址时,才会由“请页机制”产生“缺页”异常,进入实际分配页框的程序。

“缺页”异常是是虚拟内存赖以生存的基础,他会告诉内核为进程分配物理页,并建立页表,这时虚拟地址才映射到实际的物理地址。

在应用程序中,使用的是malloc来动态分配内存,而在内核中使用的是kmalloc来分配内存,kmalloc的原型是

#include<linux/slab.h>

#include<linux/gfp.h>

void *kmalloc(size_t size , int flags);

参数:

size:需要分配内存的大小

flags:分配标志,控制kmalloc的行为

以下是flages 可能的标志。

最常用的GFP_KERNEL,他表示内存分配(最终总是调用get_free_pages来实现实际的分配,这就是,这就是GFP前缀的由来)是代表运行在内核空间的进程执行的。使用GFP_KERNEL容许kmalloc在分配空闲内存时候如果内存不足容许把当前进程睡眠以等待。因此这时分配函数必须是可重入的。如果在进程上下文之外如:中断处理程序、tasklet以及内核定时器中这种情况下current进程不该睡眠,驱动程序该使用GFP_ATOMIC。

  • lGFP_ATOMIC

用来从中断处理和进程上下文之外的其他代码中分配内存. 从不睡眠.

  • GFP_KERNEL

内核内存的正常分配. 可能睡眠.

  • GFP_USER

用来为用户空间页来分配内存; 它可能睡眠.

  • GFP_HIGHUSER

如同 GFP_USER, 但是从高端内存分配, 如果有. 高端内存在下一个子节描述.

  • GFP_NOIO

类似 GFP_KERNEL,但禁止任何 I/O 初始化

  • GFP_NOFS

类似 GFP_KERNEL,但不允许执行任何文件系统调用

  • __GFP_DMA

这个标志要求分配在能够 DMA 的内存区. 确切的含义是平台依赖的并且在下面章节来解释.

  • __GFP_HIGHMEM

这个标志指示分配的内存可以位于高端内存.

  • __GFP_HIGH

这个标志标识了一个高优先级请求, 它被允许来消耗甚至被内核保留给紧急状况的最后的内存页.

  • __GFP_REPEAT

当它有困难满足一个分配. __GFP_REPEAT 意思是" 更尽力些尝试" 通过重复尝试 -- 但是分配可能仍然失败

  • __GFP_NOFAIL

告诉分配器不要失败; 它尽最大努力来满足要求. 使用 __GFP_NOFAIL 是强烈不推荐的

  • __GFP_NORETRY

告知分配器立即放弃如果得不到请求的内存.

1.6 按页分配与释放

如果内核模块需要分配大块内存,使用面向页的分配技术会更好。

#include<linux/gfp.h>

  • get_zeroed_page(unsigned int flags)

返回新页面的指针,并将页面清零。

  • __get_free_page(unsigned int flags);

申请一个页面,返回新页面的指针,但不清零页面。

  • __get_free_pages(unsigned int flags ,unsigned int order)

分配并返回一个纸箱内存区第一个字节的指针,内存区可能是一个或者多个页长,但是没有清零(物理上连续)。

order 是你在请求的或释放的页数的以 2 为底的对数(即, log2N). 例如, 如果你要一个页 order 为 0, 如果你请求 8 页就是 3. 如果 order 太大(没有那个大小的连续区可用), 页分配失败. get_order 函数, 它使用一个整数参数, 可以用来从一个 size 中提取 order(它必须是 2 的幂)给主机平台. order 允许的最大值是 10 或者 11 (对应于 1024 或者 2048 页), 依赖于体系. 但是, 一个 order-10 的分配在除了一个刚刚启动的有很多内存的系统中成功的机会是小的。

  • void free_pages(unsigned long addr , unsigned long order);

释放释放的页内存。

  • void free_page(unsigned long addr);

释放释放的页内存,需要注意的是如果释放的系统内存与分配的内存不一致会导致系统错误。

1.7 内核空间的内存分布

内核空间是有内核进行映射的,它不会跟着进程变化,是固定的。

什么是高端内存:

在x86结构中,内核被分为三个区块,区域分布如下(Linux 与x86类似):

ZONE_DMA        内存开始的16MB

ZONE_NORMAL       16MB~896MB

ZONE_HIGHMEM       896MB ~ 结束

896M以上被称为高端内存,896M以下被称为低端内存。

  • 直接映射区(Direct Memory Region)

从0xC0000000(3G)开始的最多896的内存区域被称作直接映射区,这是因为该区域的线性地址和物理存在直接的线性转换关系:

线性地址 = 0xc0000000 + 物理地址

比如物理地址为0x100000 ~ 0x200000 的线性地址就是0xc0100000 ~ 0xc0200000

直接映射区的内存可以通过kmalloc直接分配。

  • 动态映射区()

该区域的地址通过vmalloc来进行分配,需要注意的是vmalloc所分配出内存区的线性地址连续,但是物理内存区域是不一定是连续的。它是通过页表的方式将各个空闲的页连接起来使用,所以效率要比kmalloc 要低很多。不过vmalloc所分配出来的地址可能出于高端内存、也可能出于低端内存。

  • 永久内存映射区(PKMap Region)

该区域可访问高端内存。访问方法是使用alloc_page(_GFP_HIGHMEM)分配高端内存页或者使用kmap函数将分配到的高端内存映射到该区域。

永久映射区常用的全局变量:

PKMAP_BASE:永久映射空间的起始地址。永久映射空间为4M。所以它最多能映射4M/4K=1024个页面。

pkmap_page_table:永久映射空间对应的页目录。我们来看一下它的初始化:

pkmap_page_table = pte_offset_kernel(pmd_offset(pgd_offset_k

(PKMAP_BASE), PKMAP_BASE), PKMAP_BASE);

实际上它就是PKMAP_BASE所在的PTE

LAST_PKMAP:永久映射空间所能映射的页面数。在没有开启PAE的情况下被定义为1024

highmem_start_page:高端内存的起始页面

pkmap_count[PKMAP]:每一项用来对应映射区域的引用计数

  • 固定映射区(Fixing Mapping Region)

该区域和4G的顶端只有4k的隔离带,其每个地址项都服务于特定的用途,如ACPI_BASE等。

时间: 2024-12-28 09:21:10

Linux 内核开发 - 进程空间的相关文章

Linux内核开发 — 进程控制

本章主要是以代码的角度分析进程的定义.状态.数据结构等概念. 进程的定义 进程是一段运行的程序,他是一个动态的可执行实体.而程序是代码和数据的集合,代码是一个静态的实体,程序是可以供多个进程使用,比如相同的应用程序可以在不同的计算机上运行而产生多个进程. 进程四要素 进程四要素主要是针对代码中对线程.进程的区别而言: l  有一段程序供其执行 l  有进程专用的内核堆栈空间 l  有内核控制块(有一个task_struct 数据结构),拥有内核控制块,才能被内核调度 l  有独立的用户空间  

ubuntu+systemtap进行Linux内核和用户空间开发测试

ubuntu+systemtap进行Linux内核和用户空间开发测试 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途.任何个人.媒体.其他网站不得私自抄袭:网络媒体转载请注明出处,增加原文链接,否则属于侵权行为.如有任何问题,请留言或者发邮件给sailing_9806#163.com) [摘要]本文主要介绍在ubuntu平台 + 自定义内核上如何安装systemtap工具包及

如何参与Linux内核开发(转)

本文来源于linux内核代码的Document文件夹下的Hoto文件.Chinese translated version of Documentation/HOWTO If you have any comment or update to the content, please contact theoriginal document maintainer directly.  However, if you have a problemcommunicating in English yo

深入Linux内核架构——进程管理和调度(上)

如果系统只有一个处理器,那么给定时刻只有一个程序可以运行.在多处理器系统中,真正并行运行的进程数目取决于物理CPU的数目.内核和处理器建立了多任务的错觉,是通过以很短的间隔在系统运行的应用程序之间不停切换做到的.由此,以下两个问题必须由内核解决:除非明确要求,否则应用程序不能彼此干扰:CPU时间必须在各种应用程序之间尽可能公平共享(一些程序可能比其他程序更重要).本篇博文主要涉及内核共享CPU时间的方法以及如何在进程之间切换(内核为各进程分配时间,保证切换之后从上次撤销其资源时执行环境完全相同)

Linux内核分析——进程的描述和进程的创建

Linux内核分析——进程的描述和进程的创建 20135111李光豫 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.实验内容 阅读理解task_struct数据结构http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235: 分析fork函数对应的内核处理过程sys_clone,理解创建一个新进

Linux内核学习-进程

先说几个术语: 一.Linux进程的五个段 下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的.重点:代码段.数据段.堆栈段,这是一个概念堆.栈.全局区.常量区,这是另一个概念1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像.代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作--它是不可写的.代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分

linux应用开发-进程管理

linux应用开发-进程管理 一 进程 一个组成部分是操作系统用来管理进程的内核对象,内核对象是系统用来存放进程的统计信息的地方 1 每个进程都有一个父进程 2 子进程终止,父进程会得到通知并能去的进程的退出状态 3 命名空间 类似有C++的namespace 4 进程组 每个进程都有一个进程组,都有一个进程组号=组长PID 进程组组长-groupleader 5 会话期session是一个或多个进程组的集合 setsid可以创建会话期 6 相关换算 2^10 = 1K 2^20 = 1M 2^

编码风格——linux内核开发的coding style

总结linux内核开发的coding style, 便于以后写代码时参考. 下面只是罗列一些规则, 具体说明可以参考: 内核源码(Documentation/CodingStyle) 01 - 缩进 缩进用 Tab, 并且Tab的宽度为8个字符 swich 和 case对齐, 不用缩进 switch (suffix) { case 'G': case 'g': mem <<= 30; break; case 'M': case 'm': mem <<= 20; break; cas

linux应用开发-进程控制理论

linux应用开发-进程控制理论 一 进程 进程是一个具有一定独立功能的程序的一次运行活动. 二 进程特点和状态 动态性 并发性 独立性 异步性 状态 就绪 执行 阻塞 三 调度算法 进程调度:按一定算法,从一组待运行的进程中选出一个来占有CPU运行. 先来先服务 短进程优先调度 高优先级优先调度 时间片轮转法 四 调度方式 在有了等待运行的进程和调度算法后,接下来需要讨论的问题是:什么时候调度? 按调度时机,调度可分为: 抢占式调度 非抢占式调度 五 死锁 多个进程抢占同一个资源而形成的一种僵