深入理解Linux内核

深入理解Linux内核

第一章 绪论

GNU项目

1. Linux和其他类Unix内核的比较

2. 硬件的依赖性

3. Linux版本

4.操作系统的基本概念

内核(kernel)、过程(procedure)

操作系统的主要目标

  • 与硬件部分相互作用,为所有包含在硬件平台的底层可编程不见提供服务

  • 为运行在计算机系统的应用程序提供一个执行环境

类Unix操作系统在用户应用程序前把计算机物理组织相关的所有底层细节隐藏起来,当程序想使用硬件资源时,必须向操作系统发出一个请求。内核对这个请求进行评估,如果允许使用这个资源,那么,内核代表应用程序与相关的硬件部分进行交互。为实现这种机制,现代操作系统依靠特殊的硬件特性来禁止用户程序直接与底层硬件部分打交道,或者直接访问任意的物理地址。硬件为CPU引入了至少两种不同的执行模式:用户程序的非特权模式和内核的特权模式。Unix把它们分别称为用户态(User Mode)和内核态(Kernel Mode)

4.1 多用户系统

并发:竞争各种资源、独立:每个应用程序执行自己的任务

特点:

  • 认证机制,核实用户身份

  • 一个保护机制,防止有错误的用户程序妨碍其他应用程序在系统中运行

  • 一个保护机制,防止有恶意的用户程序干涉或窥视其他用户的活动

  • 记账机制,限制分配给每个用户的资源数

4.2 用户和组

用户标识符(User ID)组标识符(Group ID)

root 超级用户(superuser)

4.3 进程

定义:执行程序的一个实例

4.4 内核体系结构

单模块

5. Unix文件系统概述

5.1 文件

以一列字节序列组成的信息载体,内核不解释文件的内容。

5.2 硬链接和软链接

5.3 文件类型

  • 正规文件

  • 目录

  • 符号链接

  • 面向块的设备文件

  • 面向字符的设备文件

  • 管道(pipe)和命名管道(named pipe)(FIFO)

  • 套接字(socket)

5.4 文件描述符和索引节点

文件系统用来管理文件的所有信息包含在一个叫做索引节点的数据结构中。

索引节点至少包含:

  • 文件类型

  • 与文件相关的硬链接个数

  • 以字节为单位的文件长度

  • 设备描述符

  • 用来在文件系统中标识文件的索引节点号

  • 文件拥有者的UID

  • 文件的GID

  • 几个时间标记

  • 访问权限和文件模式

5.5 访问权限和文件模式

文件潜在的用户分为三种类型:

  • 作为这个文件的拥有者

  • 同组用户,不包括所有者

  • 所有剩下的用户

访问权限:读、写及执行

文件访问权限就用九种不同的二进制来标记,还有三种附加的标记:suid(Set User ID)、sgid(Set Group ID)、sticky(用来定义文件模式)

5.6 文件操作的系统调用

打开一个文件

fd = open(path, flag, mode)

访问一个打开的文件
read() write() 
newoffset = lssek(fd, offset, whence)
关闭文件
res = close(fd)
更名及删除文件
res = rename(oldpath, newpath)

res = unlink(pathname)

6. Unix内核概述

6.1 进程/内核模式

  • 所有标准的Unix内核都仅仅利用了内核态和用户态

  • 程序在用户态不能直接访问内核数据结构或内核的程序

  • 进程是动态的实体,在系统中只有有限的生存期

  • 内核本身不是一个进程,而是进程的管理者

  • 除了用户进程,Unix还有几个所谓内核线程的特权进程

    • 以内核态运行在内核地址空间

    • 不与用户直接交互,因此不需要终端设备

    • 在系统启动时创建,然后一直处于活跃状态一直到系统关闭

用户态和内核态的切换

激活内核例程:

  • 进程调用系统调用

  • 正在执行进程的CPU发出一个而异常信号

  • 外围设备向CPU发出一个中断信号以通知一个事件的发生

  • 内核线程被执行

6.2 进程实现

进程描述符包含有关进程当前状态信息

当内核暂停一个进程的执行时,就把几个相关处理器寄存器的内容保存在进程描述符中,包括:

  • 程序计数器(PC)和栈指针(SP)寄存器

  • 通用寄存器

  • 浮点寄存器

  • 处理器控制寄存器

  • 跟踪进程对RAM访问的内存管理寄存器

6.3 可重入内核

可重入内核:若干个进程可以同时在内核态下执行

内核控制路径 :内核处理系统调用、异常或中断所执行的指令序列

6.4 进程地址空间

每个进程都有它的私有地址空间,用户态下运行的进程涉及到私有栈、数据区和代码区。内核态运行时,进程访问内核的数据区和代码区,但使用另外的私有栈

每个进程访问一个私有地址空间但有时进程之间也共享部分地址空间。

如果程序由几个用户使用,只被装入内存一次,指令由所有用户共享,数据不被共享。

进程间也能共享部分地址空间,以实现进程间通信,即共享内存。

6.5 同步和临界区

同步机制:如果内核控制路径对某个内核数据结构进行操作时被挂起,那么,其他的内核控制路径就不应该再对该数据进行操作,除非它已经被重新设置为一致性状态。

竞争状态

对全局变量的安全访问通过原子操作来保证。

临界区是这样一段代码,进入这段代码的进程必须完成,之后另一个进程才能够进入。

6.6 非抢占式内核

当进程在内核态执行时,它不能被任意挂起,也不能被另一个进程代替

6.7 禁止中断

单处理器的另一种同步机制:在进入临界区之前禁止所有硬件中断,离开时再重启中断。

6.8 信号量

信号量仅仅是一个数据结构相关的计数器。其组成:

  • 一个整数变量

  • 一个等待进程的链表

  • 两个原子方法:down()和up()

down信号量-1,小于0,将正在运行的进程加入信号量链表,然后阻塞进程。up信号量+1,大于等于0,激活信号量链表的一个或多个进程

6.9 自旋锁

自旋锁和信号量非常的相似,但没有进程链表;当一个进程发现锁被另一个进程锁着时,它就不停地“旋转”,执行一个紧凑的循环指令直到锁打开。

在单处理器下是无效的。当内核控制路径试图访问一个上锁的数据结构时,开始无休止的旋转,因此,内核控制路径可能因为正在修改受保护的数据结构而没有机会继续执行,也没有机会释放这个自旋锁,最后的结果就是系统挂起。

6.10 避免死锁

6.11 信号和进程间通信

Unix信号提供了系统事件报告给进程的一种机制

系统事件2种:

  • 异步通告

  • 同步错误或异常

POSIX标准定义了大约20种不同的信号,其中两种是用户定义的,可以当做用户态下进程通信和同步的原语机制。

对接收到的信号做出反应:

  • 忽略该信号

  • 异步地执行一个指定的过程(信号处理程序)

不指定方式,默认操作5种:

  • 终止进程

  • 将执行上下文和进程地址空间的内容写入一个文件,并终止进程

  • 忽略信号

  • 挂起进程

  • 如果进程曾被暂停,则恢复它的执行

进程间通信机制:信号量、消息队列及共享内存,统称为IPC

6.12 进程管理

fork()和_exit()分别用于创建一个新进程和终止一个进程,exec()类系统调用是装入一个新程序。

调用fork()进程是父进程,而新进程是子进程。

6.12.1 僵尸进程

wait4()系统调用允许进程等待,直到其中一个子进程结束;它返回已终止子进程的进程标识符。

waitpid()允许进程等待一个特殊的子进程。

6.12.2 进程组和登录会话

进程组:表示“作业”的抽象

登录回话:包含在指定终端已经开始工作会话的那个进程的所有后代进程——通常情况下,登录会话就是shell进程为用户创建的第一条命令。

6.13 内存管理

6.13.1 虚拟内存

作为一种逻辑层,处于应用程序的内存请求与硬件内存管理单元之间。虚拟内存的用途和优点:

  • 若干个进程可以并发地执行

  • 应用程序所需内存大于可用物理内存时也可以运行

  • 程序只有部分代码装入内存是进程可以执行它

  • 允许每个进程访问可用物理内存的子集

  • 进程可以共享库函数或程序的一个单独内存映像

  • 程序是可重定位的,也就是说,可以把程序放在物理内存的任何地方

  • 程序员可以编写与机器无关的代码,因为他们不必担心有关物理内存的组织结构

虚拟内存子系统的主要成分是虚拟地址空间。进程所用的一组内存地址不同于物理内存地址。当进程使用一个虚拟地址,内核和MMU协同定位其在内存中的实际物理位置。

CPU包含硬件电路自动把虚拟地址转换成物理地址,把可用的RAM划分成长度为4KB或8KB的页框,并且引入一组页表来指定虚拟地址和物理地址之间的对应关系。

6.13.2 随机访问存储器(RAM)的使用

所有的UNIX操作系统将RAM分为两个部分:

其中若干兆字节专门用于存放内核映像(内核代码和内核静态数据结构),其余部分由虚拟内存系统来处理。

6.13.3 内核内存分配器

内核内存分配器KMA:满足系统中所有部分对内存的请求。

基于不同算法技术提出的KMA包括:

  • 资源图分配算法

  • 2的幂次方空闲链表

  • McKusick-Karels分配算法

  • 伙伴(Buddy)系统

  • Mach的区域(Zone)分配算法

  • Dynix分配算法

  • Solaris的Slab分配算法

6.13.4 进程虚拟地址空间处理

包括进程可以引用的所有虚拟内存地址。

内核通常用一组内存区域描述符描述进程虚拟地址空间。

例:当进程通过exec()类系统调用开始某个程序的执行时,内核分配给进程的虚拟地址空间由以下内存区组成:

  • 程序的可执行代码

  • 程序的初始化数据

  • 程序的未初始化数据

  • 初始程序栈(即用户态栈)

  • 所需共享库的可执行代码和数据

  • 堆(由程序动态请求的内存)

现代Unix操作系统都采用所谓请求调页的内存分配策略。

6.13.5 高速缓存

物理内存的一大优势是用作磁盘和其他块设备的高速缓存。

6.14 设备驱动程序

内核通过设备驱动程序和I/O设备交互。

原文地址:https://www.cnblogs.com/luoxiao23/p/11078842.html