全方位剖析 Linux 操作系统,太全了(11)


Linux 系统中的同步下面来聊一下 Linux 中的同步机制 。早期的 Linux 内核只有一个 大内核锁(Big Kernel Lock,BKL)  。它阻止了不同处理器并发处理的能力 。因此,需要引入一些粒度更细的锁机制 。
Linux 提供了若干不同类型的同步变量,这些变量既能够在内核中使用,也能够在用户应用程序中使用 。在地层中,Linux 通过使用 atomic_set 和 atomic_read 这样的操作为硬件支持的原子指令提供封装 。硬件提供内存重排序,这是 Linux 屏障的机制 。
具有高级别的同步像是自旋锁的描述是这样的,当两个进程同时对资源进行访问,在一个进程获得资源后,另一个进程不想被阻塞,所以它就会自旋,等待一会儿再对资源进行访问 。Linux 也提供互斥量或信号量这样的机制,也支持像是 mutex_tryLock 和 mutex_tryWait 这样的非阻塞调用 。也支持中断处理事务,也可以通过动态禁用和启用相应的中断来实现 。
Linux 启动下面来聊一聊 Linux 是如何启动的 。
当计算机电源通电后,BIOS会进行开机自检(Power-On-Self-Test, POST),对硬件进行检测和初始化 。因为操作系统的启动会使用到磁盘、屏幕、键盘、鼠标等设备 。下一步,磁盘中的第一个分区,也被称为 MBR(Master Boot Record) 主引导记录,被读入到一个固定的内存区域并执行 。这个分区中有一个非常小的,只有 512 字节的程序 。程序从磁盘中调入 boot 独立程序,boot 程序将自身复制到高位地址的内存从而为操作系统释放低位地址的内存 。
复制完成后,boot 程序读取启动设备的根目录 。boot 程序要理解文件系统和目录格式 。然后 boot 程序被调入内核,把控制权移交给内核 。直到这里,boot 完成了它的工作 。系统内核开始运行 。
内核启动代码是使用汇编语言完成的,主要包括创建内核堆栈、识别 CPU 类型、计算内存、禁用中断、启动内存管理单元等,然后调用 C 语言的 main 函数执行操作系统部分 。
这部分也会做很多事情,首先会分配一个消息缓冲区来存放调试出现的问题,调试信息会写入缓冲区 。如果调试出现错误,这些信息可以通过诊断程序调出来 。
然后操作系统会进行自动配置,检测设备,加载配置文件,被检测设备如果做出响应,就会被添加到已链接的设备表中,如果没有相应,就归为未连接直接忽略 。
配置完所有硬件后,接下来要做的就是仔细手工处理进程0,设置其堆栈,然后运行它,执行初始化、配置时钟、挂载文件系统 。创建 init 进程(进程 1 ) 和 守护进程(进程 2) 。
init 进程会检测它的标志以确定它是否为单用户还是多用户服务 。在前一种情况中,它会调用 fork 函数创建一个 shell 进程,并且等待这个进程结束 。后一种情况调用 fork 函数创建一个运行系统初始化的 shell 脚本(即 /etc/rc)的进程,这个进程可以进行文件系统一致性检测、挂载文件系统、开启守护进程等 。
然后 /etc/rc 这个进程会从 /etc/ttys 中读取数据,/etc/ttys 列出了所有的终端和属性 。对于每一个启用的终端,这个进程调用 fork 函数创建一个自身的副本,进行内部处理并运行一个名为 getty 的程序 。
getty 程序会在终端上输入
login:等待用户输入用户名,在输入用户名后,getty 程序结束,登陆程序 /bin/login 开始运行 。login 程序需要输入密码,并与保存在 /etc/passwd 中的密码进行对比,如果输入正确,login 程序以用户 shell 程序替换自身,等待第一个命令 。如果不正确,login 程序要求输入另一个用户名 。
整个系统启动过程如下

全方位剖析 Linux 操作系统,太全了

文章插图
 
Linux 内存管理Linux 内存管理模型非常直接明了,因为 Linux 的这种机制使其具有可移植性并且能够在内存管理单元相差不大的机器下实现 Linux,下面我们就来认识一下 Linux 内存管理是如何实现的 。
基本概念每个 Linux 进程都会有地址空间,这些地址空间由三个段区域组成:text 段、data 段、stack 段 。下面是进程地址空间的示例 。
全方位剖析 Linux 操作系统,太全了

文章插图
 
数据段(data segment) 包含了程序的变量、字符串、数组和其他数据的存储 。数据段分为两部分,已经初始化的数据和尚未初始化的数据 。其中尚未初始化的数据就是我们说的 BSS 。数据段部分的初始化需要编译就期确定的常量以及程序启动就需要一个初始值的变量 。所有 BSS 部分中的变量在加载后被初始化为 0。


推荐阅读