Linux内核
中断
中断机制:
中断分为硬件中断和软件中断
- 硬件中断是由硬件中断控制芯片发出的
- 软中断是异常
代码实现:
asm.s, system_call.s, trap.c, fork.c, signal.c, sys.c
中断的工作流程
CPU的工作模式转换,寄存器拷贝压栈,中断异常向量表,保存正常运行函数的返回值, 跳转到对应的中断服务函数上运行, 模式复原, 跳转回正常函数
硬件中断前处理 asm.s
中断执行 trap.c
软件中断前 system_call.s
执行
fork.c signal.c exit.c sys.c
进程管理
系统时间:
CPU内部有个RTC,mktime函数计算从1970年1月1日0时的秒数
给mktime的参数是从RTC(CMOS)读出的,放到全局遍历里,为jiffies所用
jiffies
是系统时钟的滴答,一个滴答是10ms
dotimer
cpl 是内核中只是状态的变量:当前是内核态(0)还是用户态(1)
Linux内核设计与实现
进程管理
进程是处于执行期的程序,还包含其他资源。线程是在进程中活动的对象。Linux不对线程和进程特别区分,线程是一种特殊的进程。
现代操作系统进程有虚拟处理器和虚拟内存。
进程描述符
进程的列表放在一个任务队列的双向循环链表中。链表中的每一项是
task_struct
是进程描述符结构体
Linux 通过 slab 分配器分配 task_struct
的结构
进程描述符的存放
内核通过唯一的PID表示进程。PID是pid_t
(int)
类型,/proc/sys/kernel/pid_max
存放了进程数量上限。
通过current宏可以找到当前正在运行进程的进程标识符.
进程的状态
TASK_RUNNING
运行TASK_INTERRUPTIBLE
可中断TASK_UNINTERRUPTIBLE
不可中断__TASK_TRACED
被其他进程跟踪__TASK_STOPPED
停止
设置当前进程状态
可以用 set_task_state(task, state)
进程的家族树
所有进程都是 init 进程的后代,它PID为1,系统启动的最后阶段启动init进程。
每个进程有父进程,
也有零个或多个子进程。current->parent
可以访问父进程,current->children
是子进程的链表.
init 进程的进程符是 init_task
next 和 prev 可以获得前一个或者后一个进程。
进程创建
首先 fork()
拷贝当前进程创建一个子进程。子进程和父进程区别仅在PID,PPID
和一些资源和统计量。exec()
负责读取可执行文件,载入地址空间开始运行。
写时拷贝
不复制整个地址空间,只有在需要写入的时候,数据才复制。否则父子进程共享一个拷贝。
fork()
通过 clone() 实现 fork()
线程
Linux 把所有线程都当作进程实现。
创建线程
和创建进程类似,不过传递参数不一样
1 | clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0); |
进程是
1 | clone(SIGCHILD, 0); |
- CLONE FILES: 父子进程共享打开的文件
- CLONE FS: 共享文件系统信息
- CLONE VM: 共享地址空间
- CLONE SIGHAND: 共享信号处理函数,被阻断信号
内核线程
内核需要在后台完成一些操作,可以通过内核线程完成。它是独立运行在内核空间的标准线程。内核线程没有独立的地址空间。
ps -ef
可以看到内核线程
内核线程只能由内核线程创建, 在 linux/kthread.h
里
kthread_create()
可以创建内核线程
进程终结
发生在调用 exit()
时候。
进程调度
多任务
可分为非抢占式(cooperative multitasking) 和抢占式多任务 (preemptive multitasking)
Linux 有抢占式多任务,调度程序决定什么时候停止一个进程的运行,以便其他程序可以执行。这个强制挂起的动作叫抢占 (preemption)
进程在被抢占前可以运行的时间叫进程的时间片 (timeslice), 不过Linux没有采用时间片。
非抢占模式下,处非程序自己主动停止运行,否则会一直执行。进程主动挂起叫让步 (yielding)。
策略
I/O消耗型进程和处理器消耗型
IO消耗性是经常等待IO的, 比如GUI。处理器消耗型是一直运行的比如计算程序。
进程优先级
Linux有两种
- nice 值 从
[-20,19]
默认0,越低越优先 - 实时优先级,从
[0,99]
越高越优先,任何实时进程优先级都高于普通进程
Linux调度算法
调度器类
完全公平调度(CFS)针对普通进程的调度类,是SCHED_NORMAL
实现在 kernel/sched_fair.c
里面。
公平调度
CFG基于:进程调度的效果类似系统中有完美的多处理器。每个进程能获得
1/n
的处理器时间。
Linux的调度实现
在 kernel/sched_fair.c
里面
时间记账
CFS调度器结构体是 sched_entity
里面的
vruntime
存放进程虚拟运行时间。update_curr()
实现了记账。
进程选择
CFS挑选vruntime最小的进程运行。CFS使用红黑树组织可运行进程队列。取出里面最小的。
调度器入口
是 schedule()
选择哪个进程可以运行。先找到最高优先级的调度类,再问谁是下一个运行的进程。
睡眠和唤醒
被阻塞的进程处于不可执行状态。原因是等待一些事件。休眠通过等待队列进行处理。
抢占和上下文切换
从一个可执行进程切换到另一个可执行进程。由
context_switch()
负责。
实时调度策略
提供 SCHED_FIFO
和 SCHED_RR
RR在耗尽时间片后就不能再执行了。
与调度相关的系统调用
比如 nice()
设置nice值,sched_setparam()
设置优先级
sched_yield()
放弃处理器时间
系统调用
系统调用再用户空间和进程设备直接加了一个中间层。它为用户提供一种硬件的抽象接口。
POSIX
流行的编程接口时基于POSIX标准的,它提供一套基于Unix的可移植操作系统标准。Linux的系统调用作为C库的一部分提供。C库实现了Unix系统的主要API
系统调用
内存管理
页
内核把物理页作为内存管理的基本单位。内存管理单元是MMU.
内核用 struct page
来表示每个物理页。
1 | struct page { |
- flags 来存放页的状态
- count 存放页的引用次数, 如果是-1就是没有引用,在内存分配就可以使用它
- virtual 是页的虚拟地址
3. 内存区域的划分
Linux 内核将物理内存分为几个区域来管理:
- 低端内存(Low Memory):通常指低于 896 MB 的内存,主要用于用户空间进程。
- 高端内存(High Memory):通常指高于 896 MB 的内存,主要是用于内核空间。
- DMA 区域(Direct Memory Access):用于设备直接访问的内存区域。
内存分配机制
伙伴系统(Buddy System):Linux 内核采用了伙伴系统来进行物理内存的分配和回收。伙伴系统将物理内存划分为大小相同的块,每个块的大小为 2 的幂。每当一个块被分配或回收时,系统会检查是否能与其他相邻块合并或分裂,保持内存的高效利用。
页分配器(Page Allocator):内核使用页分配器管理物理内存的分配和回收。当进程或内核需要内存时,页分配器会在空闲页中分配所需的内存。
Slab 分配器(Slab Allocator):专门用于管理内核对象的内存分配,如进程控制块(PCB)、文件描述符等。Slab 分配器通过对象缓存池的机制减少了内存碎片,提高了内存的分配效率。
Slab 分配器(Slab Allocator)是 Linux 内核中用于高效管理内存的一种内存分配机制,特别是在管理内核对象(如进程控制块、文件描述符、网络缓存等)时,具有很大的优势。理解 Slab 分配器的动机,我们需要从以下几个方面来看:
1. 内核对象的内存分配特点
- 内核对象大小固定且数量众多:Linux
内核中的许多对象(如
task_struct
、file
、inode
等)大小固定,且数量非常多。如果每次为这些对象动态分配内存,会导致频繁的内存分配和释放,增加内存碎片的风险。 - 频繁的内存分配和释放:内核中需要不断创建和销毁这些对象,尤其是在高负载的系统上。传统的内存分配器(如伙伴系统)会因分配和释放过于频繁而产生内存碎片,影响系统性能。
2. Slab 分配器的工作原理
Slab 分配器通过为同一类内核对象预先分配一大块内存,并将其切分成若干块来管理每个对象。每个内核对象都会从一个称为“缓存”的内存块中分配,而这个缓存是专门为某种类型的对象准备的。
Slab 分配器的工作原理可以分为以下几个步骤:
- Slab(内存块):Slab 是一个内存区域,通常被分为多个对象的内存块。一个 Slab 可以包含多个内核对象实例。这些对象具有相同的大小,适用于某种特定的内核对象。
- Cache(缓存):Cache 是 Slab
分配器为每种类型的内核对象维护的一组 Slab。例如,为
task_struct
类型的内核对象分配一个缓存,为file
类型的内核对象分配另一个缓存。每种缓存会维护多个 Slab,这些 Slab 会随着内存使用的增加或减少而动态变化。 - 预分配与回收:当内核需要一个新对象时,Slab 分配器会从相应的缓存中取出一个空闲的 Slab。如果缓存中没有足够的空闲 Slab,分配器会分配一个新的 Slab。当对象不再需要时,Slab 分配器会将其回收到缓存池中。
3. Slab 分配器的优点
- 减少内存碎片:Slab 分配器通过将内核对象划分为大小固定的内存块来避免内存碎片问题。如果每个内核对象的大小相同,Slab 分配器能够在不产生碎片的情况下高效管理内存。
- 提高性能:由于 Slab 分配器预分配了内存块,内核对象的分配和回收变得非常快速,避免了每次动态分配内存时的开销。尤其是在需要频繁分配和销毁内核对象的情况下,Slab 分配器能够极大地提高性能。
- 缓存友好:Slab 分配器利用 CPU 缓存的特性,确保相同类型的内核对象被存储在内存的相邻位置。这提高了缓存的命中率,减少了访问延迟。
- 内存对齐:Slab 分配器确保每个分配的对象都按照适当的内存对齐要求进行分配,避免了性能问题。
- 高效的内存回收:Slab 分配器通过将已分配的对象按缓存进行管理,可以在内存回收时很高效地清理和复用内存,避免了传统内存分配机制中的频繁内存回收导致的性能下降。
4. 为什么 Linux 需要 Slab 分配器
- 内核内存需求特殊:与用户空间应用不同,内核的内存需求更为特殊,通常需要频繁地创建和销毁相同大小的对象。传统的内存分配机制在处理这些内存需求时,可能会产生较高的内存碎片和分配开销。
- 高效管理内核对象:Slab 分配器提供了一个专门为内核对象设计的高效管理方案,它通过缓存池和预分配机制,使得内核对象的分配与回收更加高效、减少了碎片化问题。
- 避免内存碎片化:在长时间运行的系统中,内存碎片化会影响系统性能。Slab 分配器的设计通过确保内核对象内存块大小一致,有效避免了碎片问题。
5. 与其他内存分配方式的对比
- 伙伴系统(Buddy System):伙伴系统适用于大块内存的分配和回收,而 Slab 分配器更适合内存需求较小、固定大小的内核对象。Slab 分配器在小对象的管理上更加高效。
- 页分配器(Page Allocator):页分配器是为了管理大块内存而设计的,而 Slab 分配器针对小型对象的内存管理进行优化。Slab 分配器能大大减少内存碎片,提升内存分配的速度。
总结
Slab 分配器的设计目的在于优化内核对象的内存分配,解决频繁分配和回收内存时可能出现的碎片化问题。通过预分配内存、将对象大小固定化并分配为“Slab”内存块,Slab 分配器提供了高效的内存分配和回收机制,降低了内存碎片,提升了性能。因此,Linux 内核引入 Slab 分配器,使得内核的内存管理更加高效、稳定。
- 内核对象大小固定且数量众多:Linux
内核中的许多对象(如
Linux内核完全注释
微型计算机组成原理

I/O端口寻址
CPU 要访问IO接口需要得到它们的地址,成为端口地址。它有两种方式:统一编址和独立编址
- 统一编址:和存储器公用
- 独立编址:有单独地址空间
IBM PC 采用独立编址的方式
/proc/ioports
文件可以查看地址的范围

接口的访问控制
PC机IO接口数据传输的方式
- 程序循环查询:循环查询设备控制器的状态变量来判断是否数据交互
- 中断处理:IO设备通过中断向CPU提出处理请求
- DMA: 用于批量数据传输
主存储器,BIOS和CMOS
主存储器
就是RAM内存。计算机上电后,物理内存被设置成从0开始的连续区域。除了0xA0000-0xFFFFF
和 0xFFFE000-0xFFFFFFF
是用于IO和BIOS外,其他都可以用作系统内存

BIOS
开机自检,建立各种配置表,中断向量表,硬盘参数表
上电时,CPU把代码段寄存器CS设置为 0xF000
基地址为
0xFFFF0000
段长度是64kb, IP 设置为 0xFFF0
所以第一行代码指向 0xFFFFFFF0
这里。这里是一个jmp.
BIOS大小通常1M-2M, 所以可以用Big Mode 把数据段寄存器设置为4G,来访问其他数据

CMOS
Complementary Metal Oxide Semiconductor 存储时钟信息,硬件配置信息
控制器和控制卡
中断控制器

开机时候,这个请求号会被设置成中断向量号

DMA控制器
可以让外部设备直接和内存交互,不经过cpu.
定时器/计数器
处理精确时间延迟
键盘控制器
扫描键盘按下和松开的码
有三套扫描集
- 原始XT键盘扫描集
- AT扫描集,现代键盘默认
- PS/2扫描集
键盘控制器会转换成XT扫描集
串行控制卡
计算机直接的语言是通信协议.
串行通信是在线路上一个bit一个bit传输的通信方式, 分为同步和异步.
- 异步:一个字符为一个单位
- 同步:多个字符或字节为一帧数据

为了实现串行通信,使用异步发送/接受控制芯片 UART 组成的串行控制器
显示控制
MDA 标准:仅支持黑白
CGA 标准: 有7种色彩和图形的显示方式

软盘和硬盘控制器
略
内核编程语言和环境
as86汇编器
as86 可以产生16位代码, 连接器 ld86
; 还有 GNU的汇编器
gas. 可以把低级汇编语言编译成机器码
1 | as [option] -o objfile srcfile |
objfile 通常有3个段 section, .text, .data, .bss
as86汇编程序
注释是 !, ;
1 | .global begtext, begdata, begbss, endtext enddata, endbss |
段寄存器
段寄存器(Segment Register)是 x86 架构中用来存储段地址的特殊寄存器。在早期的 x86 处理器中,内存地址是由 段地址(Segment Address)和 偏移量(Offset)两部分组成的,因此需要用段寄存器来指定段的基址。段寄存器和偏移量一起,确定了访问内存的具体位置。
物理地址 = (段寄存器的值 × 16) + 偏移量
x86 处理器有 6 个段寄存器,分别用于不同的目的:
- CS(Code Segment):代码段寄存器,指向当前执行代码的段。程序从这个段加载并执行指令。
- DS(Data Segment):数据段寄存器,通常用于访问程序中的全局数据、局部变量等。
- SS(Stack Segment):堆栈段寄存器,用于指向程序的栈(stack)。栈用于存储函数调用时的返回地址、局部变量等数据。
- ES(Extra Segment):额外段寄存器,通常用于某些特殊数据结构的存储,比如字符串操作、图像数据等。
- FS:通常用于操作系统的特定数据,像是线程信息、局部存储等。
- GS:与
FS
类似,也是用于操作系统的数据存储,通常用来访问线程局部存储(TLS)等。
编译链接
1 | as86 -0 -a -o boot.o boot.s # -0 是生成8086的16位目标程序, -a 生成和gnu as和ld部分兼容代码 |
Buchs
nasm
dd for windows
http://www.chrysocome.net/download
配置文件 bochsrc-fda.bxrc
路径需要更改,
a.img
是镜像文件,可以用 bximage
来生成
1 | megs: 32 |
测试代码
1 | start: |
编译:
1 | nasm test.asm -o test.bin |
写入
1 | dd if=test.bin of=a.img bs=512 count=1 conv=notrunc |
运行
1 | bochs -f bochsrc-fda.bxrc |
快速运行脚本
1 | nasm $1.asm -o $1.bin -l $1.lst |
debug.cfg
windows 没有这个
1 | b 0x7c00 |
80x86保护模式及其编程
标志寄存器EFLAGS

Linux内核体系结构
操作系统由硬件,系统内核,操作系统服务,和用户应用组成
内核分为进程调度模块,内存管理模块,文件系统模块,进程间通信模块,网络接口模块
物理内存
系统初始化时,分为几个区

SELinux by example
背景
操作系统中访问控制安全的演变
Discretionary Access Control (DAC) 个别用户,通常是资源的“拥有者”,可以指定谁可以或不可以访问该资源
Mandatory Access Control (MAC) 为了避免DAC的短板
SELinux 提供了一个灵活可配置的MAC机制
Reference monitor concept
在参考监视器中,操作系统将被动资源隔离成独立的对象,参考监视器机制(称为参考验证机制)随后通过应用一套访问控制规则所体现的安全策略来验证主体与对象之间的访问。

设计目标:
- Tamper-proof 不能被恶意更改
- Nonbypassable 不能逃离访问控制决策
- Verifiable 正确的并且实现可以被演示
DAC的问题
大多数DAC是基于用户身份。Linux里面有 owner-group-world permission mode
DAC 的弱点是不能识别人类和计算机程序。
MAC的起源
我们想要实现一种组织的安全策略,不受程序行为的影响。
Multilevel security (MLS) 每个主体和个体都有一个安全等级。
弱点是它非常严格,不灵活。
Type Enforcement
OS Security
- 身份认证与访问控制(Authentication and Access Control)
身份认证:确保系统只允许合法用户访问,防止未经授权的用户进入系统。常见的身份认证方法包括用户名/密码、双因素认证(2FA)、生物识别等。
访问控制
:通过访问控制策略限制用户和程序对系统资源(如文件、设备、进程等)的访问。访问控制模型包括:
- 自主访问控制(DAC):由资源的所有者控制访问权限。
- 强制访问控制(MAC):由系统根据策略强制限制访问权限。
- 基于角色的访问控制(RBAC):基于用户角色定义访问权限。
- 基于属性的访问控制(ABAC):基于属性(如用户、资源、环境等)来动态管理访问权限。
- 内核安全(Kernel Security)
内核防护
:内核是操作系统的核心,负责管理硬件资源和系统功能,因此它需要额外的保护措施。内核漏洞可能被恶意攻击者利用来提升权限或执行恶意操作。常见的防护措施包括:
- 内核空间和用户空间隔离:确保应用程序与内核代码分离,避免用户程序破坏系统核心功能。
- 内核完整性检查:使用技术(如数字签名、哈希校验)确保内核文件未被篡改。
- 内核漏洞修补:及时修复内核中的安全漏洞,以防被攻击者利用。
系统调用过滤:通过限制系统调用的使用,避免恶意程序通过系统调用执行危险操作。
- 进程和线程安全(Process and Thread Security)
- 进程隔离:每个进程应当被隔离,防止恶意进程通过共享内存或通信方式访问其他进程的敏感信息或控制其他进程。操作系统通过进程隔离确保进程间互不干扰,提供安全的执行环境。
- 沙盒(Sandboxing):在一个受限的环境中运行应用程序,使其无法对主系统造成威胁或进行不安全的操作。沙盒技术通常用于浏览器、虚拟机等场景。
- 内存保护:防止进程越权访问内存区域(例如通过堆溢出或栈溢出等漏洞攻击)。
- 文件系统安全(File System Security)
- 加密文件系统:通过加密技术保护存储在磁盘上的敏感数据。即使磁盘被盗,数据也无法被读取。
- 文件访问控制:通过访问控制列表(ACLs)或权限设置,限制谁可以访问、修改或删除文件。
- 审计和日志记录:记录所有对文件系统的访问,以便后期分析和审计,检测异常操作。
- 系统日志与审计(System Logging and Auditing)
- 日志记录:操作系统通常记录安全事件和用户行为。通过日志文件,管理员可以检测到潜在的安全威胁(如未经授权的访问尝试、文件篡改等)。
- 审计:审计涉及对系统活动的监控和记录,确保合规性并帮助追踪安全事件的根源。安全审计可以帮助管理员识别可疑活动,及时响应攻击。
- 恶意软件防护(Malware Protection)
- 病毒、蠕虫、木马防护:操作系统需要防止恶意软件的感染和传播。操作系统通过防病毒工具、反恶意软件程序、沙盒技术等防护措施,阻止恶意程序的执行。
- 恶意代码检测与清除:通过监控系统中的进程、文件和网络流量,检测潜在的恶意活动并清除病毒、木马等恶意软件。
- 执行控制:防止不受信任的程序或恶意代码执行,例如通过限制可执行文件的来源或数字签名验证。
- 网络安全(Network Security)
- 防火墙(Firewall):操作系统通常包含防火墙功能,用于过滤进入或离开网络的数据包,控制不同网络之间的通信。
- 入侵检测与防御系统(IDS/IPS):通过监控网络流量,检测并防止潜在的攻击行为(如网络扫描、拒绝服务攻击等)。
- 加密与安全通信:操作系统可以使用加密技术(如SSL/TLS)来确保数据传输的安全性,防止数据被中途篡改或窃取。
- 安全更新与补丁管理(Patch Management)
- 补丁管理:操作系统必须能够及时发布安全补丁,以修复已知的安全漏洞。通过自动更新系统,确保操作系统不受已知漏洞的攻击。
- 零日漏洞防护:对于尚未公开或尚未修补的漏洞,操作系统需要通过加强防护措施(如使用应用程序沙盒、防止特定攻击方式)来减少其被利用的风险。
- 虚拟化安全(Virtualization Security)
- 虚拟机安全:在虚拟化环境中,操作系统需要确保不同虚拟机之间的隔离,防止攻击者通过一个虚拟机攻击其他虚拟机或宿主机。
- 虚拟化监控:虚拟化平台(如Hypervisor)需要能够有效监控虚拟机的行为,并限制不安全的操作。
- 防止拒绝服务攻击(DoS/DDoS Prevention)
- 拒绝服务攻击(DoS)和分布式拒绝服务攻击(DDoS):操作系统需要能够识别并阻止大规模的流量攻击,防止系统资源被恶意消耗或瘫痪。
- 负载均衡和流量过滤:通过负载均衡、流量限制、速率控制等措施,防止过多请求消耗系统资源。
- 系统配置与硬化(System Hardening)
- 操作系统硬化:硬化是指通过禁用不必要的服务、关闭不必要的端口、删除不需要的账户、应用最小权限原则等措施,降低系统受攻击的面。
- 最小化攻击面:仅启用必需的功能和服务,减少操作系统暴露给攻击者的潜在入口。
PhD
工业博士:
题目是工业感兴趣的,sponsered by industrie
based in a company, full time
题目是应用向的,在university注册,有一个导师
教授和公司没有关系
可以自己找,也可以帮忙找
academic phd
公司会拥有你的成果
要联系公司是否可以作为博士论文
在早期就要确认是否可以发表
和工业专家联系多,市场倾向,成果对工业界很感兴趣,对就业有帮助。博士也作为工作经验.
如果博士毕业后想去公司,那么工业博士是好的
从经济层面差别很小
如果没有读出来,很容易在公司找工作。
和大学,教授的联系很少, 但和公司同事联系多。
非常可能在同一个公司得到工作
也可以去其他做相同事情的公司,
也可以去学术,但大部分去公司
based in university, funded from university or scholarship
题目是scientific的, 题目的选择比较多
更加理论的。对Post doc比较好
刚开始的
regular meeting with supervisor
build relationship
认识其他人在干什么
skill building
学习基本技能