【MMC子系统】六、MMC块设备层
Jan 19, 2024
本文阅读量次我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
Linux内存管理 | 六、物理内存分配——伙伴系统 # 上一章,我们了解了物理内存的布局以及Linux内核对其的管理方式,页(page)也是物理内存的最小单元,Linux内核对物理内存的分配主要分为两种:一种是整页的分配,采用的是伙伴系统,另一种是小内存块的分配,采用的是slab技术。 下面我们先来看看什么是伙伴系统! 1、伙伴系统(Buddy System) # Linux系统中,对物理内存进行分配的核心是建立在页面级的伙伴系统之上。Linux内存管理的页大小为4KB,把所有的空闲页分组为11个页块链表,每个链表分别包含很多个大小的页块,有 1、2、4、8、16、32、64、128、256、512 和 1024 个连续页的页块,最大可以申请 1024 个连续页,对应 4MB 大小的连续内存。每个页块的第一个页的物理地址是该页块大小的整数倍。 如下图所示: 第 i 个页块链表中,页块中页的数目为 2^i。——仔细理解这个页块的含义。 在struct zone结构体中,有下面定义 struct free_area free_area[MAX_ORDER]; #define MAX_ORDER 11 free_area:存放不同大小的页块 MAX_ORDER:就是指数 当向内核请求分配 (2^(i-1),2^i] 数目的页块时,按照 2^i 页块请求处理。如果对应的页块链表中没有空闲页块,那我们就在更大的页块链表中去找。当分配的页块中有多余的页时,伙伴系统会根据多余的页块大小插入到对应的空闲页块链表中。 举个例子: 例如,要请求一个 128 个页的页块时,先检查 128 个页的页块链表是否有空闲块。如果没有,则查 256 个页的页块链表;如果有空闲块的话,则将 256 个页的页块分成两份,一份使用,一份插入 128 个页的页块链表中。如果还是没有,就查 512 个页的页块链表;如果有的话,就分裂为 128、128、256 三个页块,一个 128 的使用,剩余两个插入对应页块链表。 上面的这套机制就是伙伴系统所做的事情,它主要负责对物理内存页面进行跟踪,记录哪些是被内核使用的页面,哪些是空闲页面。 2、页面分配器(Page Allocator) # 由上一章我们知道,物理内存被分为了几个区域:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM,其中前两个区域的物理页面与虚拟地址空间是线性映射的。 页面分配器主要的工作原理如下: 如果页面分配器分配的物理页面在ZONE_DMA、ZONE_NORMAL区域,那么对应的虚拟地址到物理地址映射的页目录已经建立,因为是线性映射,两者之间有一个差值PAGE_OFFSET。 如果页面分配器分配的物理页面在ZONE_HIGHMEM区域,那么内核此时还没有对该页面进行映射,因此页面分配器的调用者,首先在虚拟地址空间的动态映射区或者固定映射区分配一个虚拟地址,然后映射到该物理页面上。 以上就是页面分配器的原理,对于我们只需要调用相关接口函数就可以了。 页面分配函数主要有两个:alloc_pages和__get_free_pages,而这两个函数最终也会调用到alloc_pages_node,其实现原理完全一样。 下面我们从代码层面来看页面分配器的工作原理 更多干货可见:高级工程师聚集地,助力大家更上一层楼! 3、gfp_mask # 我们先来了解一下gfp_mask,它并不是页面分配器函数,而只是这些页面分配函数中一个重要的参数,是个用于控制分配行为的掩码,并可以告诉内核应该到哪个zone中分配物理内存页面。 ...
【一文秒懂】Linux远程调试工具——gdbserver # 1、介绍 # 对于开发者来说,调试必不可少。 对于开发PC软件,通常系统已经继承了调试工具(比如Linux系统的GDB),或者IDE直接支持对程序的调试。 而对于开发嵌入式软件来说调试的手段比较有限,很多开发者仅有的调试手段依然是最原始的打印(我也是其中之一)。 当然除了打印调试之外还有通过gdb+gdbserver来调试,gdbserver在目标系统中运行,gdb则在宿主机上运行。 简而言之,gdbserver 是一个程序,它允许宿主机可以通过网络,远程调试目标板。 2、如何使用 # 2.1 编译器准备 # 这里就不再详细讲解编译器的安装什么的了,网上一大把! #直接安装 sudo apt-get install gcc-arm-linux-gnueabihf #源码安装 $ tar zxvf gdb-7.12.tar.gz $ cd gdb-7.12/ $ ./configure --target=arm-linux --prefix=$PWD/__install $ make $ make install 编译完成后,最终会生成gdbserver 的可执行程序,这个就是我们要使用的工具。 2.2 目标机准备 # 我们将gdb_server可执行程序放置目标板上。 再将我们要调试的程序放置目标板上,如helloworld 使用gdb_server进行调试,使用方法如下: gdbserver 192.168.xx.xx:1234 ./helloworld 192.168.xx.xx:IP地址信息 1234:自定义端口号 ./helloworld:运行要仿真的程序 此时gdbserver监听端口号1234,并等待客户端连接。 2.3 宿主机准备 # 在宿主机(Ubuntu)上,使用gdb调试 远程连接目标机 运行程序 $ gdb (gdb) target remote 192.168.xx.xx:1234 Remote debugging using :1234 c #运行 target remote:远程连接到指定IP的端口 ...
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
我的圈子: 高级工程师聚集地 创作理念:专注分享高质量嵌入式文章,让大家读有所得! 亲爱的读者,你好: 感谢你对我的专栏的关注和支持,我很高兴能和你分享我的知识和经验。如果你喜欢我的内容,想要阅读更多的精彩技术文章,可以扫码加入我的社群。 欢迎关注【嵌入式艺术】,董哥原创!
【一文秒懂】Linux内核死锁检测 # 最近遇到了一个驱动上面的BUG,导致终端敲命令都无响应,最终导致内核触发了hung_task_timeout… 为什么会出现这种情况?该如何排查? 1、死锁 # 死锁指两个或更多进程或线程因相互等待对方释放资源而互相阻塞,从而导致系统中所有的进程或线程都无法继续运行的情况。 一个典型的死锁场景包括以下几个角色: 资源:系统内的某个文件、某个设备、共享的内存区域等。 进程/线程:进程或线程需要访问某个资源来完成其工作,但其当前无法取得该资源的控制权,因为该资源已被其他进程或线程占用。 饥饿:由于进程无法获取其需要的资源,它不能继续前进或完成操作。如果没有正确的措施来处理和解决死锁,进程可能会一直等待,直到设备或整个系统崩溃。 2、常见的死锁方式 # 常见死锁的2种方式: AA锁:包括重复上锁和上下文切换引起的上锁,即一个线程,持有A锁,还未释放,又去请求A锁 AB-BA死锁:一个F1线程,持有A锁,再去获取B锁,而一个F2线程持有B锁,再去获取A锁,这个时候处于的死锁状态 常见的死锁有以下4种情况: 进程重复申请同一个锁,称为AA死锁。例如,重复申请同一个自旋锁;使用读写锁,第一次申请读锁,第二次申请写锁。 进程申请自旋锁时没有禁止硬中断,进程获取自旋锁以后,硬中断抢占,申请同一个自旋锁。这种AA死锁很隐蔽,人工审查很难发现。 两个进程都要获取锁L1和L2,进程1持有锁L1,再去获取锁L2,如果这个时候进程2持有锁L2并且正在尝试获取锁L1,那么进程1和进程2就会死锁,称为AB-BA死锁。 在一个处理器上进程1持有锁L1,再去获取锁L2,在另一个处理器上进程2持有锁L2,硬中断抢占进程2以后获取锁L1。这种AB-BA死锁很隐蔽,人工审查很难发现。 内核提供了Lockdep来检测死锁的异常情况 3、Lockdep 内核配置 # CONFIG_LOCKDEP: CONFIG_DEBUG_LOCK_ALLOC:检查内核是否错误地释放被持有的锁。 CONFIG_PROVE_LOCKING:允许内核报告死锁问题。 CONFIG_DEBUG_LOCKDEP :在死锁发生,内核报告相应的死锁 CONFIG_LOCK_STAT:追踪锁竞争的点,解释的更详细 4、Lockdep 初探 # lockdep操作的基本单元并非单个的锁实例,而是锁类(lock-class)。比如,struct inode结构体中的自旋锁i_lock字段就代表了这一类锁,而具体每个inode节点的锁只是该类锁中的一个实例。对所有这些实例,lockdep会把它们当作一个整体做处理,即把判断粒度放大,否则对可能有成千上万个的实例进行逐一判断,那处理难度可想而知,而且也没有必要. lockdep跟踪每个锁类的自身状态,也跟踪各个锁类之间的依赖关系,通过一系列的验证规则,以确保锁类状态和锁类之间的依赖总是正确的。另外,锁类一旦在初次使用时被注册,那么后续就会一直存在,所有它的具体实例都会关联到它。 锁的几个状态: ever held in STATE context –> 该锁曾在STATE上下文被持有过 ever head as readlock in STATE context –> 该锁曾在STATE上下文被以读锁形式持有过 ever head with STATE enabled –> 该锁曾在启用STATE的情况下被持有过 ever head as readlock with STATE enabled –> 该锁曾在启用STATE的情况下被以读锁形式持有过 5、Lockdep 检查规则 # 单锁状态规则如下: (1)一个软中断不安全的锁类也是硬中断不安全的锁类。 ...