CH5-内存管理
内存管理为什么需要进行内存管理? 计算机的内存资源是有限的,而程序运行所需的内存空间需求是动态变化的。如果没有有效的内存管理机制,可能会导致内存浪费、过度使用或者内存泄漏等问题,这些问题会进一步引起程序运行缓慢甚至系统崩溃。 学习路线从硬件到最底层内存的分配算法–>到内核的内存分配算法–>应用程序与内核的交互–>到内存如何做磁盘的缓存 –> 内存如何和磁盘替换。 内存管理理论知识作用 内存空间的分配与回收。由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,提高编程效率。 地址转换。在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,把逻辑地址转换成相应的物理地址。 内存空间的扩充。利用虚拟存储技术或自动覆盖技术,从逻辑上扩充内存。 存储保护。保证各道作业在各自的存储空间内运行,互不干扰。 程序装入与链接 C语言的编译过程是将人类可读的源代码转换为机器可执行代码的过程,通常分为四个主要阶段,每个阶段由不同的工具处理: 预处理(Preprocessing) 输入:.c源文件(如 ma...
CH3-mmap详解与基于IMX6ULL嵌入式驱动开发实践
映射关系以下是针对 Linux 内存映射的四种组合及其应用场景的详细分析: 1. 文件共享映射(File Shared Mapping) 特点: 共享性:多个进程共享同一份物理内存,修改会立即反映到其他进程。 持久化:修改内容会写回磁盘文件(若文件支持)。 实现方式:通过 mmap 的 MAP_SHARED 标志实现。 内核机制:依赖文件系统的缓冲机制,所有修改需通过页缓存同步。 应用场景: 进程间通信(IPC):如共享文件的读写操作。 数据库缓存:将数据库文件映射到内存,多个进程直接操作内存实现高效访问。 多进程日志记录:多个进程共享同一日志文件的映射区域,减少磁盘 I/O。 示例代码: 12int fd = open("shared_file", O_RDWR);void *addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 2. 文件私有映射(File Private Mapping) 特点: 独占性:每个进程拥有独立的私有副本,修改仅影响...
CH2-malloc
malloc实际上,malloc并不是系统调用,而是C库里的函数,用于动态分配内存。malloc申请内存的时候,会有两种方式向操作系统申请堆内存。 **方式一:**通过==brk()系统调用==从堆分配内存 实现的方式很简单,就是通过brk()函数将「堆顶」指针向高地址移动,获得新的内存空间。如下图: **方式二:**通过==mmap()系统调用==在文件映射区域分配内存 malloc()源码里默认定义了一个阈值: 如果用户分配的内存小于128KB,则通过brk()申请内存; 如果用户分配的内存大于128KB,则通过mmap()申请内存; 特点: 当malloc通过brk系统调用申请内存时,释放该内存后,操作系统不会立即回收这部分空间;相反,它会被保留在malloc管理的内存池中,供后续分配请求直接复用,从而减少系统调用开销。 当malloc通过mmap系统调用申请内存时,释放该内存后,系统会立即将其归还给操作系统,实现物理内存的即时释放,避免资源长期占用。 malloc()分配虚拟...
CH1-进程虚拟空间
代码学习链接: Linux 源代码 (v6.15.5) - Bootlin Elixir 交叉引用器 理论知识学习链接:小林coding | Java面试学习 进程虚拟内存空间为了防止多进程运行时造成的内存地址冲突,内核引入了虚拟内存地址,为每个进程提供了一个独立的虚拟内存空间,使得进程以为自己独占全部内存资源。 内核根据进程运行的过程中所需要不同种类的数据而为其开辟了对应的地址空间。分别为: 用于存放进程程序二进制文件中的机器指令的代码段。 用于存放程序二进制文件中定义的全局变量和静态变量的数据段和BSS段。 那些在代码中被我们指定了初始值的全局变量和静态变量在虚以内存空间中的存储区域我们叫做数据段。 那些没有指定初始值的全局变量和静态变量在虚以内存空间中的存储区域我们叫做BSS段。这些未初始化的全局变量被加载进内存之后会被初始化为0值。 用于在程序运行过程中动态申请内存的堆。这里的堆指的是OS堆并不是VM中的堆。 用于存放这些动态链接库中的代码段,数据段,BSS段,以及通过mmp系统调用映射的共享内存区,在虚拟内存空间的存储区域叫做文件映射与匿名映射区。 用于存放函...
驱动开发-并发与竞争01原理
竞争的概念什么是竞争Linux 系统是个多任务操作系统,会存在多个任务同时访问同一片内存区域,这些任务可能会相互覆盖这段内存中的数据,造成内存数据混乱。针对这个问题必须要做处理,严重的话可能会导致系统崩溃。 Linux 系统并发产生的原因有下面几个主要原因: ①多线程并发访问,Linux 是多任务(线程)的系统,所以多线程访问是最基本的原因。 ②抢占式并发访问,从 2.6 版本内核开始,Linux 内核支持抢占,也就是说调度程序可以在任意时刻抢占正在运行的线程,从而运行其他的线程。 ③中断程序并发访问 ④SMP(多核)核间并发访问,现在 ARM 架构的多核 SOC 很常见,多核 CPU 存在核间并发访问。 ==所谓的临界区就是共享数据段,对于临界区必须保证一次只有一个线程访问==,也就是要保证临界区是原子访问的 什么是共享资源共享资源是指在同一时间内可以被多个执行单元(如进程、线程、中断处理程序、内核任务等)访问(读取或修改) 的任何数据、数据结构、硬件设备或状态信息。 关键特性: 并发访问可能性: 多个执行单元(几乎)同时或在时间上重...
驱动开发-并发与竞争02原子操作
原子操作原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作。 原子操作 API 函数Linux内核定义了叫做 atomic_t 的结构体来完成用于 32 位整数的原子操作。在使用中用原子变量来代替整形变量,此结构体定义在 include/linux/types.h 文件中,定义如下: 123typedef struct { int counter;} atomic_t; 用于 64 位整数的原子操作 (在支持 64 位原子操作的体系结构上)。 123typedef struct { long long counter;} atomic64_t; 初始化1234567//-------编译时初始化:-----ATOMIC_INIT(int i);// 静态初始化 atomic_t 变量为 iatomic_t v ATOMIC_INIT(0);// 示例atomic64_t v64 = ATOMIC64_INIT(0); // 64位初始化宏//------运行时初始化:-------atomic_t vatomic...
驱动开发-并发与竞争03自旋锁
自旋锁自旋锁的核心思想是 “忙等待” (Busy-Waiting)。当一个执行单元(CPU 核心、进程上下文线程、中断上下文等)尝试获取一个已经被其他执行单元持有的自旋锁时,它不会进入睡眠状态(阻塞),而是会在一个紧凑的循环中不断地检查锁的状态(”旋转”),直到锁被释放。 忙等待: 核心行为: 获取锁失败的执行单元在 CPU 上循环检查锁的状态 (while (lock_is_held);)。它持续占用着 CPU 核心,不做其他有用工作。 目的: 避免了进程上下文切换的开销(保存/恢复寄存器、更新数据结构、调度等)。在锁被持有时间非常短的情况下,忙等待的总开销可能小于睡眠唤醒的开销。 代价: 如果锁被持有时间较长,忙等待会浪费大量 CPU 周期,显著降低系统性能。因此,自旋锁只适用于临界区执行时间非常短的场景。 互斥性: 自旋锁保证在任意时刻,最多只有一个执行单元持有锁,从而确保对共享资源的互斥访问。 不可睡眠: 最重要规则: 在持有自旋锁期间,执行单元绝对不能睡眠(阻塞)或主动放弃 CPU(如调用 schedule(), kmalloc(GFP_KER...
驱动开发-并发与竞争04信号量
信号量信号量是 Linux 内核中一种允许进程进入睡眠状态等待资源的同步机制。它与自旋锁的“忙等待”形成鲜明对比,适用于**临界区执行时间可能较长、或者执行过程中可能发生阻塞(睡眠)**的场景。Linux 内核也提供了信号量机制,信号量常常用于控制对共享资源的访问。 其核心原理基于一个计数器和一个等待队列: 计数器 (count): 表示可用资源的数量。 当 count > 0 时,表示有资源可用,进程可以立即获取资源(减少计数器)并继续执行。 当 count = 0 时,表示资源已被占用完,后续试图获取资源的进程需要睡眠等待。 计数器初始值决定了信号量的类型: 初始值 = 1: 称为互斥信号量 (Mutex Semaphore) 或二进制信号量 (Binary Semaphore)。这是最常用的类型,用于实现互斥访问,保证同一时刻只有一个进程可以进入临界区。它本质上可以当作一个允许睡眠的锁来用。 初始值 = N (N > 1): 称为计数信号量 (Counting Semaphore)。用于控制对一类有多个实例的资源(如 N 个空闲缓冲区、N ...
驱动开发-并发与竞争05互斥锁
互斥锁提供比自旋锁更安全的互斥机制,支持睡眠等待,适用于可能阻塞的长临界区场景。 关键特性 强所有者模型: 仅持有者线程可解锁 严格禁止递归锁定 状态跟踪: owner 字段存储持有者的 task_struct 指针 + 状态标志位 12345[ 持有者地址 | flags ]// flags 位含义:// bit0: 锁是否被持有 (MUTEX_FLAG_HOLD)// bit1: 是否有等待者 (MUTEX_FLAG_WAITERS)// bit2: 是否启用乐观自旋 (MUTEX_FLAG_HANDOFF) 乐观自旋优化: 当 CONFIG_MUTEX_SPIN_ON_OWNER 启用时 若持有者正在运行,新竞争者短暂自旋而非立即睡眠 等待队列: 竞争失败线程加入 wait_list 并进入 TASK_UNINTERRUPTIBLE 状态 上下文限制严格 1234// 禁止在中断上下文使用void interrupt_handler() { mutex_lock(&lock); // 触发 BUG()} ...
文件系统-01初识文件系统
文件系统磁盘的格式化与挂载简化的Linux文件系统磁盘布局结构图如下: 虚拟机操作实验 首先我们通过添加一个磁盘文件 选择添加硬盘 选择SCSI(S)类型的磁盘 选择创建新的虚拟磁盘 设定磁盘大小为4MB,并且将虚拟磁盘存储为单个文件 点击完成 我们打开虚拟机终端,切换到管理员模式 1su 查看系统有哪些磁盘 1fdisk -l 我们可以看到我们添加的新磁盘为 /dev/sdb,接下来我们开始格式化添加的磁盘,我们将磁盘格式化为minix格式 1mkfs.minix /dev/sdb 根据上图我们可以看出格式化之后文件系统创建了 创建了 1376 个 inode(索引节点) 创建了 1376 个 inode(索引节点)。 inode数量 ≈ 磁盘大小 / 预期平均文件大小此处 4MB / 1376 ≈ 3KB/文件,适合存储大量小文件。 文件系统被划分为 4096 个数据块 块大小:根据后续参数计算为 1024 字节(1KB)(见下方推导)。 总磁盘空间:4096 块 × 1024 字节/块 = 4,194,304 字节 = 4MB,符合设...