本文主要是介绍嵌入式学习-驱动开发前奏-lesson2-内存管理与进程管理子系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
内存管理子系统
在内核相关一课中,已经介绍过,linux内核一共有7个子系统:
1.SCI
2.Pm
3.MM
4.Arch
5.Vfs
6.Network stack
7.DD
在以后的驱动学习章节中,主要是与 MM PM VFS 这三个相关。
在本章节中则主要讲解MM(内存管理子系统)
1)内存管理模型
根据上面的图,可以得知,内存的管理主要是做两个方面的工作:
1:物理内存分配
2:虚拟地址与物理地址的映射
2)地址的映射
上面这幅图是地址空间映射图,将其分为三个部分:
1:虚拟地址空间分布
2:虚拟地址转化为物理地址
3:物理内存分配
1.虚拟地址空间分布
根据上图可知,4G的虚拟地址空间又可分为3G的用户空间和1G的内核空间。
1G的内核空间又可分为4部分:
①、直接映射区,最大空间为896MB (896MB以下的内存称之为低端内存,以上则称之为高端内存) 然后减去3G 就可以直接得到物理地址
②、vmalloc区(既可以访问低端区域,也可以访问高端区域)
③、永久内核映射(固定访问高端内存)
④、固定映射的线性地址(和一些特殊的寄存器有关)
2.虚拟地址向物理地址转化
主要是上图第二部分完成的,将其简化为下图
具体过程:
1.cr3基地址加上虚拟地址的22–32位指向页目录的某一个地址(cr3指向页目录的基地址)
2.指向页目录的那个地址又指向页表的基地址,然后该地址加上虚拟地址的12–21位指向页表的某一个地址,也就是物理页的基地址
3.物理页的基地址加上虚拟地址的0–11,就得到物理存储单元的地址
3.物理内存的分配
linux使用虚拟地址管理方式,只有真正访问物理地址,才分配物理内存
上图,当使用malloc或者vmalloc时,会分配一个虚拟地址,并没有真正访问到物理内存,当需要使用物理内存的时候,会产生一个 请页异常 然后从 空闲页框 中 ,查看是否有空闲的物理内存,当有空闲的时候,机会分配一个物理内存,从而建立起访问关系。
但是当使用kmalloc的时候,slab管理器会直接将虚拟地址和物理地址建立关系。
进程管理子系统
一、进程相关概念
1)进程与程序
程序
存放在磁盘上的一系列代码和数据的可执行映像,是一个静止的实体
进程
是一个执行中的程序,它是动态的实体
2)进程四要素
- 有一段程序供其执行。这段程序不一定是某个进程所专有,可以与其他进程共用。
- 有进程专用的内核空间堆栈。
- 在内核中有一个task_struct数据结构,即通常所说的“进程控制块(pcb)”。有了这个数据结构,进程才能成为内核调度的一个基本单位接受内核的调度。
- 有独立的用户空间。
3) Linux进程状态
linux进程的状态可以将其范围上面三个部分,就绪、执行、阻塞。这是一种典型的三态图,将其进一步细分,
1.TASK_RUNNING(就绪、执行)
进程正在被CPU执行,或者已经准备就绪,随时可以执行。当一个进程刚被创建时,就处于TASK_RUNNING状态。
2.TASK_INTERRUPTIBLE(属于三态中的阻塞态)
处于等待中的进程,待等待条件为真时被唤醒,也可以被信号或者中断唤醒。
3. TASK_UNINTERRUPTIBLE
处于等待中的进程,待资源有效时唤醒,但不可以由其它进程通过信号(signal)或中断唤醒。
4. TASK_KILLABLE
Linux2.6.25新引入的进程睡眠状态,原理类似于TASK_UNINTERRUPTIBLE,但是可以被致命信号(SIGKILL)唤醒。
5. TASK_TRACED
正处于被调试状态的进程。
6. TASK_DEAD
进程退出时(调用do_exit),所处的状态。
4) Linux进程描述
在Linux内核代码中,线程、进程都使用结构task_struct(sched.h)来表示,它包含了大量描述进程/线程的信息,其中比较重要的有:
pid_t pid; //进程号
long state; //进程状态
int prio; //进程优先级
在内核源代码sched.h中有关于此结构体的详细描述
二、进程调度
所谓的进程调度就是从就绪的进程中选出最适合的一个来执行。
主要分为:
1、调度策略
2、调度时机
3、调度步骤
1) 调度策略
SCHED_NORMAL(SCHED_OTHER):普通的分时进程
SCHED_FIFO :先入先出的实时进程
SCHED_RR:时间片轮转的实时进程(间隔一定时间,不管是否执行完,换下一个进程运行同样的时间)
SCHED_BATCH:批处理进程
SCHED_IDLE: 只在系统空闲时才能够被调度执行的进程
2 )调度时机
什么时候发生调度?
即schedule()函数什么时候被调用?
schedule()函数在linux内核中完成调度过程
1主动式调度
在内核中直接调用schedule()。当进程需要等待资源等而暂时停止运行时,会把自己的状态置于挂起(睡眠),并主动请求调度,让出CPU。
范例:
1. current->state = TASK_INTERRUPTIBLE; (处于阻塞态)
2. schedule();
2被动式调度
被动式调度又名:抢占式调度。分为:用户态抢占(Linux2.4、Linux2.6)和内核态抢占(Linux2.6)。
2.1用户态抢占
用户抢占发生在:
从系统调用返回用户空间。
从中断处理程序返回用户空间。
内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,即发生用户抢占。
**当某个进程耗尽它的时间片时,会设置need_resched标志
**当一个优先级更高的进程进入可执行状态的时候,也会设置need_resched标志。
2.2内核态抢占
用户态抢占缺陷
进程/线程一旦运行到内核态,就可以一直执行,直到它主动放弃或时间片耗尽为止。这样会导致一些非常紧急的进程或线程将长时间得不到运行,降低整个系统的实时性。
改进方式
允许系统在内核态也支持抢占,更高优先级的进程/线程可以抢占正在内核态运行的低优先级进程/线程。
内核抢占可能发生在:
**中断处理程序完成,返回内核空间之前。
**当内核代码再一次具有可抢占性的时候,如解锁及使能软中断等。
在支持内核抢占的系统中,某些特例下是不允许抢占的:
** 内核正在运行中断处理。
** 内核正在进行中断上下文的Bottom Half(中断的底半部)处理。硬件中断返回前会执行软中断,此时仍然处于中断上下文中。
** 进程正持有spinlock自旋锁、writelock/readlock读写锁等,当持有这些锁时,不应该被抢占,否则由于抢占将可能导致其他进程长期得不到锁,而让系统处于死锁状态。
** 内核正在执行调度程序Scheduler。抢占的原因就是为了进行新的调度,没有理由将调度程序抢占掉再运行调度程序。
2.3 抢占计数
为保证Linux内核在以上情况下不会被抢占,抢占式内核使用了一个变量preempt_count,称为内核抢占计数。这一变量被设置在进程的thread_info结构中。每当内核要进入以上几种状态时,变量preempt_count就加1,指示内核不允许抢占。每当内核从以上几种状态退出时,变量preempt_count就减1,同时进行可抢占的判断与调度。
3) 调度步骤
Schedule函数工作流程如下:
1. 清理当前运行中的进程;
2. 选择下一个要运行的进程;
3. 设置新进程的运行环境;
4. 进程上下文切换 。
菜鸟一枚,如有错误,多多指教。。。
这篇关于嵌入式学习-驱动开发前奏-lesson2-内存管理与进程管理子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!