Linux5.0内核深入剖析
依据Linux5.0 对核心模块做简单分析
Loopers
操作系统爱好者
展开
-
Linux Irq domain
本节学习下什么是irq domain, 以及irq domain的作用。可以参考内核文档IRQ-domain.txt为什么引入IRQ-Domain当早期的系统只存在一个interrupt-controller的时候,而且中断数目也不多的时候,一个很简单的做法就是一个中断号对应到interrupt-contoller的一个号,可以说是简单的线性映射而当一个系统中有多个interrup...原创 2020-04-28 14:03:30 · 2377 阅读 · 0 评论 -
中断处理流程梳理
在之前的ARMv8-A的异常文章中提到,ARMv8-A将中断也当做一种异常,中断分为IRQ和FIQ假设当前在EL0运行一个64位的应用程序,触发了一个EL0的IRQ中断,则处理器会做如下的操作将CPU的状态PSTATE保存到SPSR_EL1寄存器中,PC保存到ELR_EL1寄存器中 跳到异常的处理函数处则就会跳到ARM64对应的异常向量表/* * Exception ...原创 2020-04-28 11:30:12 · 1873 阅读 · 0 评论 -
分析匿名页(anonymous_page)映射
接着上一次malloc(探秘malloc是如何申请内存的)的dump信息继续分析。-006|do_anonymous_page(inline) | vma = 0xFFFFFFE314E27310 | tsk = 0xFFFFFFE300786640 | entry = (pte = 0x00E800026F281F53)-006|handle_pte_fau...原创 2020-04-28 11:11:24 · 2746 阅读 · 0 评论 -
探秘malloc是如何申请内存的
今天分析下malloc申请内存时都发生了什么,Let dot it我们都清楚malloc申请的内存不是立刻就建立虚拟地址和物理地址的映射的,当int *p = malloc(100*1024)执行这条指令之后,只是在用户空间给程序开辟一段100K左右的大小,然后就返回这段空间的首地址给程序员。当我们尝试第一次读或者写的时候,就会经过如下步骤的:CPU将此虚拟地址,送到MMU上去...原创 2020-04-28 10:49:26 · 3544 阅读 · 4 评论 -
手动玩转虚拟地址到物理地址转化
我们这次来根据dump动手来实际转化一个虚拟地址到物理地址,此次的地址不是线性地址映射。Target_Address_|________________logical|_physical______________| | C:FFFFFF8008015000| A:C549F000 上面0xFFFFFF8008015000就...原创 2020-04-28 10:34:31 · 1831 阅读 · 0 评论 -
学习ARM64页表转换流程
在引入虚拟地址概念以后,程序员和CPU看到的都是虚拟地址。当CPU尝试去访问某个虚拟地址的时候,这时候硬件单元MMU就会将此虚拟地址转化为物理地址,然后CPU再去访问。而在Linux中存储虚拟地址到物理地址转化的关系的表称为页表。目前最新的linux内核已经支持了5级页表。下图是一个4级页表的转化关系图。PGD(Page Global Directory)页全局目录 PU...原创 2020-04-28 10:23:59 · 3332 阅读 · 1 评论 -
Kmalloc申请内存源码分析
再上一节了解了SLUB是如何申请一个object的,其中涉及了从当前的freelist申请,以及kmem_cache_cpu->partital链表申请,以及到最后的kmem_cache_cpu→node中申请,如果上述三个步骤都没有申请到的话,就会重新创建一个新的slab,然后设置好freelist的指针,返回object使用。本节我们重点分析下Kmalloc的实现,其实在驱动中...原创 2020-04-27 18:16:26 · 2003 阅读 · 0 评论 -
SLUB分配一个object的流程分析
在上一节 我们清晰的知道了当调用kmem_cache_create之后系统会为我们分配一个名为slub_test的一个slab。这时候只是分配了kmem_cache,kmem_cache_cpu,kmem_cache_node结构,同时设置针对此object需要多少个page之类。我们这节将分析当申请一个object的时候,应该是如何的分配。还是之前的例子,继续来分析当调用kmem_cache...原创 2020-04-27 16:18:14 · 1131 阅读 · 1 评论 -
SLUB结构体创建及创建slab分析
在上一篇文章中我们通过一个简单的例子大概描述了如何创建SLUB缓存,如何分配一个object。本文详细描述下涉及的结构体,从结构体的描述中就可以大概理解slub的工作原理了。首先就是kmem_cache结构体/* * Slab cache management. */struct kmem_cache { struct kmem_cache_cpu __percpu ...原创 2020-04-26 16:28:38 · 1023 阅读 · 0 评论 -
SLUB的引入及举例说明
我们都知道Buddy分配器是按照页的单位分配的(Buddy系统分配器实现),如果我们需要分配几十个字节,几百个字节的时候,就需要用到SLAB分配器。SLAB分配器专门是针对小内存分配而设计的,比如我们驱动中常见的Kmalloc分配器就是通过SLAB分配器分配的内存。而SLAB分配器在linux系统中三种具体的实现:SLAB,SLUB,SLOB。目前内核代码中默认的SLAB分配器为SLUB算...原创 2020-04-26 11:06:57 · 1052 阅读 · 0 评论 -
page compaction代码分析之一
重要的数据结构/* * Determines how hard direct compaction should try to succeed. * Lower value means higher priority, analogically to reclaim priority. */enum compact_priority { COMPACT_PRIO_SYNC_F...原创 2020-04-23 15:56:54 · 499 阅读 · 0 评论 -
page compaction原理
参考:https://lwn.net/Articles/368869/先看一个例子:如果你的ubuntu机器经过长时间运行,比如1个月没关机,这时候你去看buddinfo和pagetypeinfo的信息。root@root# cat /proc/buddyinfoNode 0, zone DMA 3 3 1 1 ...原创 2020-04-22 18:25:58 · 626 阅读 · 1 评论 -
zone watermark水位控制
本节我们来分析下zone的水位控制,在zone那一节中,我们将重点放在了free_area中,故意没有分析zone中的水位控制,本节在重点分析zone中的水位控制。struct zone { /* zone watermarks, access with *_wmark_pages(zone) macros */ unsigned long watermark[NR_WM...原创 2020-04-21 17:55:52 · 2608 阅读 · 0 评论 -
快车道-分配页
内核提供如下函数用于分配页:alloc_pages(gfp_mask, order) //用于分配一个order阶数的页alloc_page(gfp_mask) //用于分配一页,最终调用的是alloc_pages(gfp_mask, 0)那我们就分析alloc_pages的具体实现static inline struct page *...原创 2020-04-20 18:05:01 · 420 阅读 · 0 评论 -
从备用类型总盗用steal page
在之前的文章中,当分配一页的时候从对应order的对应的迁移类型中freelist中分配一个空闲的页。但是也会出现此order的迁移类型中没有可用的page,这时候就会从备用的迁移类型中盗用pagestatic __always_inline struct page *__rmqueue(struct zone *zone, unsigned int order, int migratet...原创 2020-04-18 11:42:36 · 1666 阅读 · 1 评论 -
Buddy分配器之释放一页
在上面一节我们讲述了buddy分配器是如何分配一页的,本节我们在学习buddy分配器是如何释放一页的分配一页的算法:比如当前释放order=n的页获得当前释放order=n的页,对应的buddy,也就是兄弟页,俗话说先找到兄弟 找到兄弟buddy了之后,接下来就是看buddy和此页是否可以合并 检查buddy是否是空闲的页(通过检查page→private是否为0) 检查b...原创 2020-04-16 17:01:32 · 975 阅读 · 0 评论 -
Buddy(伙伴)系统分配器之分配page
Buddy分配器是按照页为单位分配和释放物理内存的,在Zone那一节文章中freearea就是通过buddy分配器来管理的。buddy分配器将空闲页面按照order的大小分配挂到不同的order链表中。比如order为0的链表下就挂载着大小为1个page的空闲页,order=4的链表下就挂载着大小为16个page的空闲页面。buddy分配器的算法是:当分配order=n的页面的时候...原创 2020-04-15 10:44:52 · 1750 阅读 · 0 评论 -
物理内存管理之zone详解
上一次说过了物理内存由node,zone,page三级结构来描述。而node是根据当前的系统是NUMA还是UMA系统。假设我们当前是UMA系统架构,则只有一个node。我们本节则重点学习下ZONE,重点是ZONE的数据结构,其中就可以看到ZONE中是如何管理我们page的,就会看到buddy分配器。struct zone { unsigned long _watermark[...原创 2020-04-11 13:45:14 · 4138 阅读 · 0 评论 -
物理内存是如何组织管理的
内存管理,相比大家都听过。但是内存管理到底是做什么呢?这就得从计算机刚出来的时候说起。计算机刚出来的时候内存资源很紧张,只有几十K,后来慢慢的到几百K,到周后来的512M,再到现在的几个G。真是因为内存资源的不足,在计算机的整个过程中衍生出各种各样的内存管理方法。而内存管理的终极目标就是合理的不浪费的使用物理内存。Linux针对如何合理的使用物理内存,软件上设计了多种的内存管理方法。今天我们就...原创 2020-04-10 11:35:40 · 1045 阅读 · 1 评论 -
VMA实战操练
在上篇文章根据crash学习用户空间程序内存布局涉及到了VMA的相关操作,本节通过一个简单的实例来深刻的学习下VMA是什么,以及VMA是如何组织的。先来看下VMA的结构体struct vm_area_struct { /* The first cache line has the info for VMA tree walking. */ unsigned long vm_s...原创 2020-04-09 16:44:26 · 948 阅读 · 0 评论 -
根据crash学习用户空间程序内存布局
在32位机器上,总共有4G大小的虚拟地址空间,其中0-3G是给应用程序使用,3-4G是给内核使用。在64位机器上,目前还不完全支持64位地址宽度,常见的地址长度有39(512GB)和48位(256TB),目前我使用的模拟器采用的是39位的地址宽度,这样的话用户空间和内核空间各占512GB的地址空间。当一个应用程序在用户跑起来的时候,它内部是如何正常运行的,通过一个简单的例子详细说明下...原创 2020-04-08 17:49:03 · 660 阅读 · 0 评论 -
根据crash学习ARM64虚拟地址空间布局
我们先来看一个出错的现场:[ 55.195101] Unable to handle kernel paging request at virtual address ffffdfc7be9c2100[ 55.195107] Mem abort info:[ 55.195109] ESR = 0x96000004[ 55.195112] Exception cl...原创 2020-04-08 11:02:49 · 11151 阅读 · 2 评论 -
GICv3驱动初始化
linux驱动支持GICv1, GICv2, GICv3, GICv4驱动,本节我们重点来描述下GICv3的驱动初始化,结合ARM-Cortex平台详细描述intc: interrupt-controller@666688888 { compatible = "arm,gic-v3"; #interrupt-cells = <3>; ...原创 2020-04-02 18:36:07 · 1496 阅读 · 0 评论 -
Android tombstone文件是如何生成的
本节内容我们聚焦到androidQ上,分析android中一个用于debug的功能,那就是tombstone,俗称“墓碑”。现实生活中墓碑一般是给死人准备的,而在android系统中“墓碑”则是给进程准备的。为何Android要设计出这样一个东西呢? 因为android系统是运行在Linux Kernel内核之上的,当内核出现异常,则内核异常机制会分辨出是什么原因,处理不了的直接panic。而...原创 2020-03-14 14:44:08 · 12441 阅读 · 1 评论 -
NULL指针的奇妙之旅
今天带大家了解下NULL指针是如何形成的? 当然了我们要深入到操作系统中去看看为何访问一个NULL指令会报Segment Fault的错误。想必大家在接触计算机时都写过NULL指针的程序,尤其是玩C语言的小伙伴们。比如刚初始化的一个int类型指针,还没有赋值的时候就往这个指针赋值,然后运行就会出现Segment Fault的错误。#include <stdio.h>int...原创 2020-03-11 16:55:56 · 1202 阅读 · 0 评论 -
CFS调度主要代码分析二
在上一篇文章中我们分析CFS的主要代码,设计的内容有:进程创建时调度器是如何初始化一个进程的 进程是如何添加到CFS运行队列中 当进程添加到CFS运行队列中,是如何选择下一个进程运行的本节在围绕一个进程的生命周期,继续分析一个进程是如何被抢占? 如果睡眠? 如何被调度出去的?Schedule_tick(周期性调度)周期性调度就是Linux内核会在每一个tick的时候会去更新当前进...原创 2020-03-07 19:39:16 · 654 阅读 · 0 评论 -
CFS调度主要代码分析一
在前面学习了CFS调度的原理和主要的数据结构,今天我们就来进入代码分析环节。当然了代码分析只看主要主干不看毛细,同时我们也是根据一个进程是如何被调度的思路来分析一些重要的代码。在分析代码之前,有一些小函数需要先分析下,俗话说万丈高楼平地起,这些小函数还是很重要的。calc_delta_faircalc_delta_fair函数是用来计算进程的vruntime的函数。在之前CFS原理篇了...原创 2020-03-01 14:49:04 · 1611 阅读 · 0 评论 -
CFS 调度器数据结构篇
在上一节我们了解了CFS的设计原理,包括CFS的引入,CFS是如何实现公平,CFS工作原理的。本小节我们重点在分析CFS调度器中涉及到的一些常见的数据结构,对这些数据结构做一个简单的概括,梳理各个数据结构之间的关系图出来。调度类CFS调度器是在Linux2.6.23引入的,在当时就提出了调度类概念,调度类就是将调度策略模块化,有种面向对象的感觉。先来看下调度类的数据结构,调度类是通过str...原创 2020-02-28 18:53:05 · 978 阅读 · 0 评论 -
CFS Scheduler(CFS调度器)
前面我们分享了O(n)和O(1)调度器的实现原理,同时也了解了各个调度器的缺陷和面临的问题。总的来说O(1)调度器的出现是为了解决O(n)调度器不能解决的问题,而O(1)调度器在Linux2.4内核的在服务器的变形是可行的,但是Linux2.4以后随着移动设备的逐渐普遍,面临的卡顿问题逐渐明晰,这才导致后来的CFS调度器的出现。今天我们来学习CFS调度器,再分析代码之前,我们可以先看下内...原创 2020-02-27 18:15:21 · 1932 阅读 · 0 评论 -
Linux O(1)调度器
前面我们学习了O(n)调度器的设计,以及它的核心算法。在这里复习下。O(n)调度器核心:O(n)调度器采用一个runqueue运行队列来管理所有可运行的进程,在主调度schedule函数中会选择一个优先级最高,也就是时间片最大的进程来运行,同时也会对喜欢睡眠的进程做一些补偿,去增加此类进程的时间片。当runqueue运行队列中无进程可选择时,则会对系统中所有的进程进行一次重新计算时间片的操...原创 2020-02-23 13:11:07 · 1314 阅读 · 0 评论 -
Linux O(n)调度器
前面我们学习了调度器的设计需要关注的几个点,在这里复习下:吞吐量(对应的是CPU消耗型进程) 响应速度(对应的是IO消耗型进程) 公平性,确保每个进程都可以有机会运行到 移动设备的功耗Linux中调度器的设计,引入的概念普通进程和实时进程使用优先级区分,0-99表示实时进程,100-139表示普通进程 实时进程采用两种调度策略SCHED_RR或者SCHED_FIFO 普通进程采...原创 2020-02-21 18:32:08 · 908 阅读 · 0 评论 -
进程调度开篇
在前面的几篇文章中,我们重点分析了如果通过fork, vfork, pthread_create去创建一个进程或者线程,以及后面说了在内核层面do_fork的实现。目前为止我们已经了解到一个进程是如何创建的。既然创建了一个进程,那这个进程肯定要去运行,执行它的使命。而进程何时被执行,在计算机系统中需要调度器来选择。所以我们从今天要开启一系列调度相关的知识了。为何要有调度器计算机设...原创 2020-02-20 18:32:40 · 493 阅读 · 1 评论 -
do_fork实现--下
昨天在do_fork实现–上中学习了do_fork创建的前半段,今天我们接着继续分析copy_Process函数分析了copy_fs, copy_files, copy_signal, copy_sighand, copy_mm,今天接着分析copy_thread, copy_thread是和架构相关的,需要到具体的ARCH目录下去看在分析copy_thread之前,我们先看几个知识...原创 2020-02-19 20:14:49 · 1191 阅读 · 0 评论 -
do_fork实现--上
在前面几节中讲述了如何通过fork, vfork, pthread_create去创建一个进程,或者一个线程。通过分析最终fork, vfork, pthread_create最终会通过系统调用clone去创建进程。今天我们就重点来分析clone之后的事情。为了学习今天这一节,前面的都是铺垫。既然fork, vfork, pthread_create都去调用到clone这个系统调用,...原创 2020-02-18 20:06:05 · 577 阅读 · 0 评论 -
Linux0号进程,1号进程,2号进程
本节我们将从linux启动的第一个进程说起,以及后面第一个进程是如何启动1号进程,然后启动2号进程。然后系统中所有的进程关系图做个简单的介绍0号进程0号进程,通常也被称为idle进程,或者也称为swapper进程。0号进程是linux启动的第一个进程,它的task_struct的comm字段为"swapper",所以也成为swpper进程。#define INIT_TASK_CO...原创 2020-02-17 20:07:26 · 8767 阅读 · 6 评论 -
ThreadInfo结构和内核栈的两种关系
本来本节是要学习内核启动的第一个进程的建立,也就是0号进程,也称idle进程,也称swapper进程。但是在学习第一个进程建立之前需要先学习threadinfo和内核栈的关系。目前内核存在两种threadinfo和内核的关系,接下来我们通过画图一一举例说明。ThreadInfo结构在内核栈中Threadinfo结构存储在内核栈中,这种方式是最经典的。因为task_struct结构...原创 2020-02-16 18:53:19 · 2620 阅读 · 4 评论 -
僵尸进程
之前在第一节进程的基本概念中讲到进程的状态的时候,提到进程的状态中存在一个僵尸状态。今天我们重点来分析下什么是僵尸进程,僵尸进程是如何产生的,以及僵尸进程如何灭掉,僵尸进程产生的原理。先来看一个在之前遇到的例子:Tasks: 779 total, 6 running, 753 sleeping, 0 stopped, 20 zombie Mem: 7.3G total, 6....原创 2020-02-15 17:59:34 · 675 阅读 · 0 评论 -
线程的创建以及线程的本质
上节详细学习了进程的创建,通过实例学习了fork和vfork的区别。本节将学习线程的创建,只涉及应用层的线程,内核线程的创建在后面学习。应用线程的创建应用线程的创建,想必大家都有所了解。使用pthread_create库函数来创建应用线程。通过一个简单的例子来看下。我们先来看下pthread_create的参数,通过man pthread_createNAME pt...原创 2020-02-14 15:44:00 · 907 阅读 · 0 评论 -
进程的创建fork vs vfork
上一篇文章学习了进程的基本概念,以及进程的状态,最后学习了Linux中是如何描述一个进程的。本节来学习Linux中进程是如何创建的,以及fork和vfork的区别。在大学的时候操作系统课程中我们都学过如何去创建一个进程,是通过fork系统调用命令来创建的。使用fork创建进程如下是一个简单的通过fork系统调用来创建子进程的例子#include <stdio.h>...原创 2020-02-12 18:13:12 · 907 阅读 · 0 评论 -
2020年学习规划
时隔两年已经没有写文章了,在这两年中收获了很多,但是发现到用的时候总是想不起来,所以打算从2020年开始继续培养写文章的好习惯。2020年过年刚好赶上了肺炎,一下子在家呆了快一个月了。在家实在无聊的时候翻阅了前几年买的Linux内核书籍,突然发现以前看不懂的内容突然可以看懂了,以前不理解的知识点,现在居然恍然大悟。所以下定决心,需要在2020年,再次踏上学习linux内核的道路。虽然学习li...原创 2020-02-07 11:16:37 · 733 阅读 · 3 评论