系统调用
用户进程与内核进行交互的一组接口;
需要C库支持,通过使用C库定义的函数来访问系统调用
系统调用、中断、异常是访问内核的三种方式。
- 系统调用号:关联系统调用
- 系统调用的性能:
- linux很短的上下文切换时间
- 系统调用处理程序和每个系统调用都非常简洁
- 系统调用处理程序:用户空间的程序使用软中断来通知内核执行系统调用
通过exa寄存器传递系统调用号给内核;
给用户空间的返回值也通过寄存器传递(x86系统:存放在eax寄存器上);
建立系统调用的好处:
- 系统调用创建容易且使用方便
- Linux系统调用性能很高
- 保证了系统的安全性和稳定性:用户程序不能直接操作内核地址空间。
系统调用和库函数的区别:
- 库函数是0-n个系统调用的封装
- 函数库调用是调用函数库中的一个程序,而系统调用是调用系统内核的一个服务
- 函数库调用属于过程调用,开销较小,系统调用需要有用户态和内核态之间的切换开销
- 库函数调用是在用户地址空间执行,而系统调用是在内核地址空间执行
问题:
- 围绕系统调用号来讲,包括系统预分配、再固化、注册到每个需要支持的体系结构、维护困难(只能在主内核树维护)、功能简单的话便是大材小用
内存管理
页:内核把物理页作为内存管理的基本单位
内存管理单元(MMU):管理内存并把虚拟地址转换为物理地址的硬件,以页为单位进行处理。
struct page:使用了大量的联合体union来保证struct page结构体足够小
-
区:Linux把系统的页划分成区,形成不同的内存池,这样可以根据用途进行分配。
ZONE_DMA:DMA使用的页
ZONE_NORMAL:正常可寻址的页
ZONE_HIGHMEM:动态映射的页
kmalloc():返回在物理连续的一段以字节为单位的内核内存
kfree():与kmalloc()配对使用,避免内存泄漏
-
vmalloc():分配的内存虚拟地址连续,物理地址无需连续
分配非连续的物理内存块,再“修正”页表,把内存映射到逻辑地址空间的连续区域中
-
kmalloc和vmalloc
大部分内核代码选用kmalloc分配内存的原因,主要出于性能考虑。
vmalloc为了把物理上不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。并且,通过vmalloc获得的页必须一个一个进行映射,会导致比直接映射产生更大的TLB抖动。
内核常用的内存分配方式:使用某个低级页分配器或者kmalloc
当创建和撤销很多大的数据结构,可以建立slab高速缓存来提升性能
进程地址空间
用户空间中进程的内存,成为进程地址空间。
-
内存区域:
- 代码段:可执行文件代码的内存映射
- 数据段:可执行文件的已初始化的全局变量和静态变量
- BSS段:未初始化的全局变量和静态变量
- 堆:任何匿名的内存映射(如malloc分配的内存)
- 栈:进程用户空间栈(进程内核栈独立存在并由内核维护)
内核同时使用了mmap(链式结构)和mm_rb(红黑树)来描述同一块内存区域,在可以同时遍历整个节点同时,保证了查找节点的效率
-
查找内存地址所对应的内存区域:
- 查看mmap_cache缓存
- 未命中时则选择搜索mm_rb红黑树结构,未找到则返回NULL
linux使用三级页表完成地址转换(虚拟地址转换成物理地址)
brk sbrk
定时器和时间管理
实际时间:开机后,内核初始化从RTC读取,放入到xtime变量中。系统读写 xtime 时用的就是顺序锁
-
定时器
-
静态定时器:执行周期性的工作
更新系统运行时间
更新实际时间
检查当前进程是否用尽了自己的时间片,如果用尽,需要重新调度。
-
-
动态定时器
动态创建的定时器,使用后销毁。一般在内核代码中使用的基本都是动态定时器
-
HZ:
节拍率(HZ)是时钟中断的频率,表示的一秒内时钟中断的次数。比如 HZ=100 表示一秒内触发100次时钟中断程序。
-
jiffies
jiffies用来记录自系统启动以来产生的总节拍数。比如系统启动了 N 秒,那么 jiffies就为 N×HZ
-
时间中断处理程序
时钟中断处理程序作为系统定时器而注册到内核中,体系结构的不同,可能时钟中断处理程序中处理的内容不同
-
-
定时器执行流程
定义:定时器在内核中用一个链表来保存的,链表的每个节点都是一个定时器
生命周期: