深刻Linux内核架构第一章笔记

1. Linux是多任务系统, 支持并发执行若干进程,系统同时真正运行的进程数目不超过CPU的数量,所以内核会按照时间间隔在不一样进程之间切换。 java

2.肯定那个进程运行多长时间的过程称为调度。 node

3.内核启动init进程做为第一个进程,该进程负责进一步的系统初始化操做,并显示登录提示符或登录界面。所以init是进程树的根,全部进程都直接或间接来源次进程。 linux

4. 进程不是内核支持的惟一一种程序执行方式,除此之外,还有线程。 算法

5. Linux将虚拟地址空间分为两部分,内核空间和用户空间。 编程

6. Intel 将处理器分为4个特权状态,ring0 ~ ring3, 可是Linux只使用了两种状态:用户态和核心态。 数组

7。用来将虚拟地址空间映射到物理地址空间的数据结构称为页表。 缓存

8。为了减小页表的大小并允许忽略不须要的区域,Linux将虚拟地址划分为多个部分,也就是多级页表。 安全

 

PGD (Page Global Directory) àPMD (Page Middle Directory)àPTE (Page Table Entry) àoffset 网络

9. 多级页表节省了大量的内存,可是它的缺点是须要逐级访问多个数组才能将虚拟地址转换为物理地址,耗时较长。因而CPU经过MMU来优化访问操做及利用TLB(Translation lookaside buffer)来加速。 数据结构

10. 内核不少时候须要分配连续页,为快速检测内存中的连续区域,通常状况下用伙伴系统,也就是空闲内存两两分组,每组中的两块内存称为伙伴。另一种方法就是slab。

11.slab主要针对常常释放并分配的内存对象,须要对象时,能够快速分配。虽然slab的性能很好,可是对嵌入式系统而言,开销太大,因而有了一个 slab 模拟层,名为 SLOB。这个 slab 的替代品在小型嵌入式 Linux 系统中具备优点,可是即便它保存了 512KB 内存,依然存在碎片和难于扩展的问题。在禁用 CONFIG_SLAB 时,内核会回到这个 SLOB 分配器中

 

Linux 的slab 可有三种状态:

  • 满的:slab 中的全部对象被标记为使用。
  • 空的:slab 中的全部对象被标记为空闲。
  • 部分:slab 中的对象有的被标记为使用,有的被标记为空闲。

    与传统的内存管理模式相比, slab 缓存分配器提供了不少优势。

  • 内核一般依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。
  • slab 缓存分配器经过对相似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。
  • slab 分配器还支持通用对象的初始化,从而避免了为同一目的而对一个对象重复进行初始化。
  • slab 分配器还能够支持硬件缓存对齐和着色,这容许不一样缓存中的对象占用相同的缓存行,从而提升缓存的利用率并得到更好的性能。

     

随着大规模多处理器系统和NUMA系统的普遍应用,slab分配器逐渐暴露出自身严重的不足:

  • 较多复杂的队列管理。在slab分配器中存在众多的队列,例如针对处理器的本地缓存队列,slab中空闲队列,每一个slab处于一个特定状态的队列之中。因此,管理太费劲了。
  • slab管理数据和队列的存储开销比较大。每一个slab须要一个struct slab数据结构和一个管理者kmem_bufctl_t型的数组。当对象体积较小时,该数组将形成较大的开销(好比对象大小为32字节时,将浪费1/8空间)。为了使得对象在硬件告诉缓存中对齐和使用着色策略,还必须浪费额外的内存。同时,缓冲区针对节点和处理器的队列也会浪费很多内存。测试代表在一个1000节点/处理器的大规模NUMA系统中,数GB内存被用来维护队列和对象引用。
  • 缓冲区回收比较复杂。
  • 对NUMA的支持很是复杂。slab对NUMA的支持基于物理页框分配器,没法细粒度的使用对象,所以不能保证处理器级的缓存来自同一节点(这个我暂时不太懂)。
  • 冗余的partial队列。slab分配器针对每一个节点都有一个partial队列,随着时间流逝,将有大量的partial slab产生,不利于内存的合理使用。
  • 性能调优比较困难。针对每一个slab能够调整的参数比较复杂,并且分配处理器本地缓存时,不得不使用自旋锁。
  • 调试功能比较难于使用。

12. SLOB是slab在小型嵌入式系统上的一个模拟器,目的是为了下降资源开销。

13. 内存管理其实是一种关于权衡的零和游戏。您能够开发一种使用少许内存进行管理的算法,可是要花费更多时间来管理可用内存。也能够开发一个算法来有效地管理内存,但却要使用更多的内存。最终,特定应用程序的需求将促使对这种权衡作出选择。

每一个内存管理器都使用了一种基于堆的分配策略。在这种方法中,大块内存(称为 堆)用来为用户定义的目的提供内存。当用户须要一块内存时,就请求给本身分配必定大小的内存。堆管理器会查看可用内存的状况(使用特定算法)并返回一块内存。搜索过程当中使用的一些算法有 first-fit(在堆中搜索到的第一个知足请求的内存块 )和 best-fit(使用堆中知足请求的最合适的内存块)。当用户使用完内存后,就将内存返回给堆。这种基于堆的分配策略的根本问题是碎片(fragmentation)。

14. 给出了 slab 结构的高层组织结构。在最高层是 cache_chain,这是一个 slab 缓存的连接列表。这对于 best-fit 算法很是有用,能够用来查找最适合所须要的分配大小的缓存(遍历列表)。cache_chain 的每一个元素都是一个 kmem_cache 结构的引用(称为一个 cache)。它定义了一个要管理的给定大小的对象池。

15. proc 文件系统提供了一种简单的方法来监视系统中全部活动的 slab 缓存。这个文件称为 /proc/slabinfo,它除了提供一些能够从用户空间访问的可调整参数以外,还提供了有关全部 slab 缓存的详细信息。

16. 全局变量jiffies用来记录自系统启动以来产生的节拍的总数。启动时,内核将该变量初始化为0,此后,每次时钟中断处理程序都会增长该变量的值。一秒内时钟中断的次数等于Hz,因此jiffies一秒内增长的值也就是Hz。 系统运行时间以秒为单位,等于jiffies/Hz。

注意,jiffies类型为无符号长整型(unsigned long),其余任何类型存放它都不正确。

Jiffies_64 是64为系统。

17.系统调用是用户进程和内核交互的经典方法。主要包括进程管理, 信号,文件,目录和文件系统,保护机制及定时器函数。

18.万物皆文件,外设包括字符设备,块设备及网络设备。

19.Linux支持的文件系统有:Ext2, Ext3, ResierFS,XFS, VFAT等。

Ext2基于inode, 也就是说每一个文件都构造了一个单独的管理结构,称为inode。

VFS(Virtual File System or Virtual File Switch)将底层文件系的具体信息和应用层隔离开来。

20. 模块是个普通的程序,只不过运行在内核空间而不是用户空间。模块特性使得内核能够支持种类繁多的设备,而不会形成内核自身膨胀。

21.因为内核是基于页的内存映射来实现访问设备的,缓存也是按照页的组织缓存起来的,也就是页缓存。

22.内核提供的标准链表能够将任何类型的数据结构链接起来,固然这就意味着此链表不是类型安全的。此链表的机构存在于include/list.h下。

23. kobject是组成设备模型的基本结构。相似于java中的object类,是全部用来描述设备模型的数据结构的基类,它嵌入于全部的描述设备模型的容器对象中,例如bus,devices,drivers等。这些容器经过kobject连接起来,造成一个树状结构,这个树状结构与/sys中是一一对应的。须要注意的是,并非说每个kobject对象都须要在sysfs中表示,可是每个被注册到系统中的kset都会被添加到sysfs文件系统中,一个kset对象就对应一个/sys中的一个目录,kset中的每个kobject成员,都对应sysfs中一个文件或者一个目录。

目前为止,Kobject主要提供以下功能:

  • 经过parent指针,能够将全部Kobject以层次结构的形式组合起来。
  • 使用一个引用计数(reference count),来记录Kobject被引用的次数,并在引用次数变为0时把它释放(这是Kobject诞生时的惟一功能)。
  • 和sysfs虚拟文件系统配合,将每个Kobject及其特性,以文件的形式,开放到用户空间。

    Kobject是基本数据类型,每一个Kobject都会在"/sys/"文件系统中以目录的形式出现。

    Ktype表明Kobject(严格地讲,是包含了Kobject的数据结构)的属性操做集合(因为通用性,多个Kobject可能共用同一个属性操做集,所以把Ktype独立出来了)。注3:在设备模型中,ktype的命名和解释,都很是抽象,理解起来很是困难,后面会详细说明。

    Kset是一个特殊的Kobject(所以它也会在"/sys/"文件系统中以目录的形式出现),它用来集合类似的Kobject(这些Kobject能够是相同属性的,也能够不一样属性的)。

Linux内核中有大量的驱动,而这些驱动每每具备相似的结构,根据面向对象的思想,咱们就能够将这些共同的部分提取为父类,这个父类就是kobject,也就是驱动编程中使用的.ko文件的由来,下面这张图是我根据内核源码的kobject绘制的简单的UML图,从中能够看出,kobject包含了大量的设备必须的信息,而三大类设备驱动都须要包含这个kobject结构,也就是"继承"自kobject。一个kobject对象就对应sys目录中的一个设备。
内核源码中的kobject结构定义以下

//include/linux/kobject.h

struct kobject {

const char *name;

struct list_head entry;

struct kobject *parent;

struct kset *kset;

struct kobj_type *ktype;

struct kernfs_node *sd; /* sysfs directory entry */

struct kref kref;

#ifdef CONFIG_DEBUG_KOBJECT_RELEASE

struct delayed_work release;

#endif

unsigned int state_initialized:1;

unsigned int state_in_sysfs:1;

unsigned int state_add_uevent_sent:1;

unsigned int state_remove_uevent_sent:1;

unsigned int uevent_suppress:1;

};

这个结构中,

struct kobject
name表示kobject对象的名字,对应sysfs下的一个目录。
entry是kobject中插入的head_list结构,
parent是指向当前kobject父对象的指针,体如今sys结构中就是包含当前kobject对象的目录对象,
kset表示当前kobject对象所属的集合,
ktype表示当前kobject的类型。
sd用于表示VFS文件系统的目录项,是设备与文件之间的桥梁,sysfs中的符号连接就是经过kernfs_node内的联合体实现的。
kref是对kobject的引用计数,当引用计数为0时,就回调以前注册的release方法释放该对象。
state_initialized:1初始化标志位,在对象初始化时被置位,表示对象是否已经被初始化。
state_in_sysfs:1表示kobject对象在sysfs中的状态,在对应目录中被建立则置1,不然为0。
state_add_uevent_sent:1是添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。
state_remove_uevent_sent:1是删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备

kobject,kset是Linux设备管理中的基本结构体,但在实际操做中咱们几乎不会实际操做这些结构,由于他们自己并不具备针对某一个具体设备或驱动的信息,在Linux内核中,这两个结构都是被包含具体的设备结构中,好比cdev,gendisk等,从面向对象的角度考虑,就是每一类设备均可以看做这两个结构的子类。
经过上面的分析,咱们能够看出这三者之间的关系,并画出下面的结构框图,sysfs中的上目录结构就是根据kset之间的数据组织方式进行呈现的。

 

总结,Ktype以及整个Kobject机制的理解。 Kobject的核心功能是:保持一个引用计数,当该计数减为0时,自动释放(由本文所讲的kobject模块负责) Kobject所占用的meomry空间。这就决定了Kobject必须是动态分配的(只有这样才能动态释放)。 而Kobject大多数的使用场景,是内嵌在大型的数据结构中(如Kset、device_driver等),所以这些大型的数据结构,也必须是动态分配、动态释放的。那么释放的时机是什么呢?是内嵌的Kobject释放时。可是Kobject的释放是由Kobject模块自动完成的(在引用计数为0时),那么怎么一并释放包含本身的大型数据结构呢? 这时Ktype就派上用场了。咱们知道,Ktype中的release回调函数负责释放Kobject(甚至是包含Kobject的数据结构)的内存空间,那么Ktype及其内部函数,是由谁实现呢?是由上层数据结构所在的模块!由于只有它,才清楚Kobject嵌在哪一个数据结构中,并经过Kobject指针以及自身的数据结构类型,找到须要释放的上层数据结构的指针,而后释放它。 因此,每个内嵌Kobject的数据结构,例如kset、device、device_driver等等,都要实现一个Ktype,并定义其中的回调函数。同理,sysfs相关的操做也同样,必须通过ktype的中转,由于sysfs看到的是Kobject,而真正的文件操做的主体,是内嵌Kobject的上层数据结构! Kobject是面向对象的思想在Linux kernel中的极致体现,但C语言的优点却不在这里,因此Linux kernel须要用比较巧妙(也很啰嗦)的手段去实现,

相关文章
相关标签/搜索