引言
理解一个系统运行之前,我们需要先了解有关计算机信息的基础概念。
概览
狭义上的计算机结构是:CPU,内存,外部设备,外部设备包含输入输出和外部外存储器以及网络适配器。
而从广义上来看,计算机可以用抽象化的概念进行解释,则可以简单讲计算机分为三部分,第一部分是应用程序,这些程序依托于环境和载体,而第二部分就是运行程序的载体,负责管理系统调用,进程切换和设备驱动这些工作,同时担任重要的硬件抽象的角色,为应用程序掩盖掉复杂的底层维护工作,最后一部分是硬件,这部分就是我们狭义的理解计算机的部分,这里不多介绍。
在硬件设备重复执行以下步骤:
- 输入设备或者网络适配器发起请求
- 读取内存命令,CPU上执行,把结果写入负责保存到内存区域
- 内存的数据写入HDD、SDD等存储器
程序大致分为下面几种:
- 应用程序:让用户直接使用,为用户提供帮助程序,例如计算机等办公软件,计算机上的办公软件,智能手机和其他应用。
- 中间件:辅助程序运行等软件,比如WEB服务器和数据库
- OS:控制硬件,为应用程序和中间件提供运行环境,Linux 叫做OS。
用户模式和内核模式
用户模式:用户进程访问的时候都是用户模式,用户模式是受到保护或者说权限受限的,只能够使用内核分配的内存和CPU进行操作,失去使用权则会处于等待的情况,但是用户进程的一大特点是用户的空间只能用户进程使用,所以一旦有用户进程崩溃了,内核可以去把它给清理掉。因此增强系统的鲁棒性。
而在 内核模式下运行的代码对 CPU 和内存具有无限的使用权限,这个强大的权限使得内核可以轻易腐化和崩溃整个系统,同时内核使用的空间是只能被内核访问的,其他任何用户都无权访问。
特点对比
内核级线程特点 | 用户级线程的特点 |
1.进程中的一个线程被阻塞,内核能调度同一进程的其他线程(就绪态)占有处理器运行。 2.多处理器环境中,内核能同时调度同一进程的多线程,将这些线程映射到不同的处理器核心上,提高进程的执行效率。 3.应用程序线程在用户态运行,线程调度和管理在内核实现。线程调度时,控制权从一个线程改变到另一线程,需要模式切换,系统开销较大 |
1.线程切换不需要内核模式,能节省模式切换开销和内核资源。 2.允许进程按照特定的需要选择不同的调度算法来调度线程。调度算法需要自己实现。 3.由于其不需要内核进行支持,所以可以跨OS运行。 4.不能利用多核处理器优点,OS调度进程,每个进程仅有一个ULT能执行 5.一个ULT阻塞,将导致整个进程的阻塞。 |
对于用户线程阻塞在后续使用一项叫做Jacketing技术来 使用Jacketing技术把阻塞式系统调用改造成非阻塞式的。当线程陷入系统调用时,执行jacketing程序,由jacketing程序来检查资源使用情况,以决定是否执行进程切换或传递控制权给另一个线程。 也就是说Jacketing技术实现了当用户线程阻塞的时候更加灵活的进行切换,防止一个线程的阻塞导致多线程阻塞的情况。
用户模式切换到内核模式:
一般是发生了缺页中断或者无法处理的系统异常情况下出现,此时内核会剥夺用户进程的控制权进行处理, 并且执行一些内核的修复操作,比如缺页中断申请内存并且分配新的页表给进程,这种机制为请求分页的处理方式,更多细节可以查看Linux 进程调度器入门 - 掘金 (juejin.cn)查看有关进程调度的内容。
用户模式切换到内核模式一般会经历下面的步骤:
- CPU模式切换
- 保存当前的进程状态到核心栈。
- 中断异常程序调度处理
内核模式切换到用户模式:
当中断异常处理调度程序完成之后,内核模式会逐渐转为用户模式运行,此时用户线程回从核心栈找回当前到进程状态,并且CPU运行模式也会执行为用户模式。
和Windows简单对比:
下面我们类比Windows系统的内核模式以及用户模式的切换,来看看微软的官方文档是如何介绍的:
用户模式:进程享受专用的虚拟地址空间,和Linux类似的 在用户模式下运行的处理器无法访问为操作系统保留的虚拟地址。
内核模式:在内核模式下运行的所有代码都共享单个虚拟地址空间。
其实简单对比一下就会发现其实实现机制都是类似的,只不过内部实现代码不同和细节不同而已,基本的特性都是相似的。
模式切换的优劣对比: 其实这两个模式用最抽象的概念就是应用程序和系统程序之间的差别,因为对于用户来说模式切换是透明的,基本使用过电脑程序都经历过应用程序崩溃引发系统崩溃的情况,此时就是一种用户模式到内核模式的切换。
用户模式的好处在于访问和空间占用都是受到内核管控的,但是有一个很大的问题是一旦出现中断异常就会发生用户模式和内核模式的切换,在通常情况下看起来没什么问题,但是随着进程需要的内存越来越大,每一次切换对于系统带来影响都是非常严重的。
在很多高级编程语言编造的大型项目中,因为模式切换带来的问题也并不罕见,更多情况下是用户编写的低质量或者问题代码出现资源浪费导致的问题。
C标准库
C标准库在很早就被国际设立为标准规范,哪怕过去这么多年依然有很多标准通用,而C标准库中最核心的组件是glibc,之前介绍过glibc是用户进程像内核申请内存的关键函数,使用glibc申请内存之后,再使用mmap函数申请具体的内存,这部分内容可以阅读[[010105 - Linux内存管理]]进行了解。
glibc通过系统调用向
mmap
申请大量的内存空间作为内存池,程序则调用malloc
根据内存池分配出具体的内存使用,如果进程需要再次获取内存则需要再次通过mmap获取内存并且再次进行分配操作。
另外高级的编程语言同样依赖或者基于C标准库,比如Python的ppidloop
就是如此,以及C++对于C标准库的进一步扩展等。
OS提供的程序
下面列举一些OS提供的程序:
- 初始化系统:init
- 变更系统运作方式:sysctl、nice、sync
- 文件操作:touch、mkdir
- 文本数据处理:grep、sort、uniq
- 性能测试:sar、iostat
- 编译:gcc
- 脚本语言运行环境:perl、python、ruby
- shell:bash
- 视窗系统:x
另外系统调用通常也可以分为下面几种,过去的笔记都有进行相关整理,这里就不多介绍了: