Java笔记··By/蜜汁炒酸奶

信马由缰操作系统-闲话线程模型1

聊Java线程以及Golang协程前,我们先通过聊线程模型明确一些概念性的东西。

mindmaster_os_thread01.png

1 用户模式与内核模式

1.1 理论

在计算机中,我们想运行自己的程序都必须先保证操作系统是正常且自己的程序能在上面运行。

为了保证操作系统的正确运行,就必须区分操作系统的代码运行和用户代码的运行,从而限制用户代码的运行权限,避免其随意执行一些危险命令等改变或者破坏操作系统的相关运行。所以至少需要两种独立的运行模式:

  1. 用户模式( user mode ),也称用户态
  2. 内核模式( kernerl mode ),也称为内核态监视模式( supervisor mode )、系统模式( system mode )或者特权模式( privileged mode )。

大多数计算机系统采用硬件支持,以便区分各种执行模式。计算机硬件( 一般是CPU )通过一个模式位( mode bit ) 来表示当前模式:内核模式(0)用户模式(1)

  • 用户模式和内核模式是特指处理器执行模式的术语。
  • 内核模式下运行的代码可以完全控制 CPU,在用户模式下运行的代码有一定的限制。
  • 当计算机系统执行用户的应用程序时,系统处于用户模式
  • 当操作系统能够控制计算机时,它就处于内核模式
  • 如果用户的应用程序想从用户模式切换到内核模式,从而控制相关硬件做事情,必须通过系统调用
    • 当用户应用通过系统调用,请求操作系统服务时,必须从用户模式切换到内核模式,此时会暂停用户程序,转而执行对应的内核服务(此时操作系统获得计算机的控制权)。
    • 当内核中执行完成应用的相关需求后,通过系统调用切换回用户模式(从而将计算机的控制权交还给用户系统)。

用户模式到内核模式切换过程

由上简单说一下计算机系统的指令执行的生命周期(当然,步骤2和3在某个时间段可能会循环,最终肯定会回到3):

  1. 存在系统引导时(如电脑开机),硬件处于内核模式,操作系统接着加载。
  2. 然后在用户模式下执行用户应用程序。在将控制权交给用户程序前,系统会切换到用户模式(将模式位设置为1)。
  3. 一旦有陷阱( trap ,有些也称陷入,都是一个东西,本系列统一采用陷阱,它是一种由软件引起的中断)或者中断( interrupt ),硬件会从用户模式切换到内核模式(即将模式位设置为0)。此时操作系统能够再次控制计算机。

双重模式已经被大多数的当代操作系统支持,如 Windows(7等)、Unix 和 Linux 都利用了其优点,并为操作系统提供了更强的防护。

模式的概念可以扩展,从而超过两个,成为多重模式,此时CPU设置和检测模式时,会到多个位。如支持虚拟化( virtualization )技术的CPU有一种单独模式,用于表示虚拟机管理器( virtual Machine Manager, VMM )是否正在控制系统。这种模式的特权多于用户模式,少于内核模式。

除模式外,CPU 设计人员也能通过其它方式来区分执行权限。如 Intel 64系列的CPU有4种特权级别(privilege level)并支持虚拟化,但没有一个特定的虚拟化模式。

特权指令和非特权指令

双重模式中为了提供保护机制,将可能引起损害的机器指令当作特权指令( privileged instruction ),硬件只能在内核模式才能执行,其余指令为非特权指令( no-privileged instruction ):

  • 特权指令:在内核模式运行的指令。其对内存空间的访问限制不受限制,不仅能访问内核空间,也可能访问用户空间。
    • 切换到用户模式的指令
    • 执行启动外部设备、设置系统时钟时间、关中断、切换执行状态等操作的指令。
  • 非特权指令:在用户模式运行的指令。不能对系统中的硬件和软件直接访问,内存空间上只能访问用户空间。
    • 应用程序使用的指令都是非特权指令,只能完成一般性的操作和任务。

1.2 实现

上面说的主要是理论,但怎么将上面的理论变成现实,通过硬件支持实现这两个模式切换呢?

1.2.1 x86 权限模型

经常被提到的就是 intel 的X86 CPU的权限模型。它提供了分层的权限机制,将区域分成了 Ring0~Ring3 这4个Ring(官方的说法是 PRIVILEGE LEVELS , 特权等级),数字越大权限越低。

  • Ring0 权限最高,操作系统用作内核模式,运行着操作系统(内核)的代码。这个环可以直接访问 CPU 和系统内存。
  • Ring1 和 Ring2 的规划是用来运行一些设备驱动,但实际上大部分操作系统并没有用到这两级。
    • 在公布的x86S架构中已经移除这两个层级。(原文参考:Removing ring 1 and 2 (which are unused by modern software) and obsolete segmentation features like gates.)
    • 虚拟化的一些技术会用到这两个级别,比如经典的半虚拟化技术Xen,Xen 在 ring0, guest kernel 在 ring1
  • Ring3 权限最低,操作系统用作用户模式,运行着用户的应用程序。如果需要访问磁盘,写文件等需要通过执行系统调用,CPU 的运行级别将从 Ring3 切换到 Ring0,之后跳转到对应代码上,由内核完成设备访问,再从 Ring0 切换到 Ring3,这个过程就是用户模式与内核模式的切换。

x86 的分层权限机制示意图

图中为与官方文档中图片标识一致,选用了 Level 这个词,后面文章均使用 Ring 指代图中的 Level一词。

1.2.2 RISC-V 权限模型

RISC(精简指令集计算机,Reduced Instruction Set Computer-RISC)和 CISC (复杂指令集计算机,Complex Instruction Set Computer-CISC)是CPU的两种架构。上面的 x86 的CPU架构就是基于 CISC 的。

RISC-V读作RISC Five,也即第五代精简指令处理器,intel 的 Nios V/m processor 便是基于此实现的。该模型支持三种特权级别

  • M:Machine ,最高级别的权限,可以做任何事情,相当于内核模式。
  • S:Supervisor,中等权限,可以执行多数的操作系统方法 (OS functions)。
  • U:User/Application,最低权限,运行用户程序,相当于用户模式。

想了解更多的可以自行搜索资料。

2 用户空间与内核空间

用户模式和内核模式是处理器相关的术语,实现独立于内核模式的用户模式的最常见方法涉及操作系统保护环,而保护环是使用 CPU 模式实现的(参考上面的 《x86 权限模型》部分)。

用户空间(user space)和 内核空间(kernel space)则是内存相关的术语,现代操作系统将虚拟内存分为了用户空间和内核空间,这种分离主要是为了提供内存保护和硬件保护,防止恶意或错误的软件行为。通常内核空间的程序以内核模式运行,用户空间的普通程序以用户模式运行

  • 内核空间是为内核保留的内存区域,被严格保留用于运行特权操作系统内核、内核扩展和大多数设备驱动程序。
  • 用户空间是为特定用户进程保留的内存区域,是应用软件和一些驱动程序执行的内存区域。每个用户空间进程通常在自己的虚拟内存空间中运行,除非明确允许,否则不能访问其他进程的内存。这是当今主流操作系统内存保护的基础,也是权限分离的基石。
  • 内核空间的访问受保护,因此用户应用程序无法直接访问,而用户空间则可由内核模式下运行的代码直接访问
/* copies user space usr_buf to kernel memory */
copy_from_user(k_mem, usr_buf, count);

/* copies kernel space buffer to user space usr_buf */
copy_to_user(usr_buf, buffer, rv);
1
2
3
4
5

下图中是典型的操作系统架构,更清晰的说明了用户空间与内核空间的职责划分。

典型的操作系统架构

内核为应用程序提供了一系列 API,这些 API 通常被称为 “系统调用”( System Calls )。这些 API 与普通的库 API 不同,因为它们是执行模式从用户模式切换到内核模式的边界。

  • 虽然内核应用程序的接口可以根据需求更改,但为了提供应用程序的兼容性,系统调用很少更改。
  • 系统调用是一种典型的陷阱(trap)。

内核代码本身在逻辑上可分为核心内核代码和设备驱动程序代码。

  • 设备驱动程序代码负责访问特定设备,而核心内核代码则是通用的。
  • 核心内核可进一步划分为多个逻辑子系统(如文件访问、网络、进程管理等)。

3 用户线程与内核线程

在上面我们简单了解了用户空间与内核空间的概念,对应的会有两种线程的实现方式:用户线程和内核线程。

  • 用户线程(user thread):也称为用户级线程(user level thread,ULT),是在用户空间中实现的,其具体实现方式是使用中间系统实现,通常是使用一个线程库来实现。
  • 内核线程(kernel thread):也称为内核支持线程(kernel supported thread,KST),是在内核空间中实现的,其具体实现方式是使用系统调用实现,相应的会为每个线程设置一个TCB,内核根据 TCB 来感知该线程并控制。

目前一些数据库系统(如infomix)实现的是用户线程,Windows XP、Mac OS 和 Linux 等实现的是内核线程,Solaris 等系统中实现了这两种的组合。不管是进程还是线程,都需要直接或者间接的取得内核的支持。

3.1 内核线程的优缺点

内核线程的优点

  1. 在多处理器系统中,内核可以同时调度一个进程中的多个线程。
  2. 如果进程中的一个线程阻塞,内核可以调度该进程中的其它线程来占用处理器并运行,也可以调度其它进程中的线程。
  3. 内核线程具有很小的数据结构和堆栈,线程切换速度较快,资源消耗较小。
  4. 内核本身也可以使用多线程,提高系统的执行速度和效率。

内核线程的缺点用户的线程切换时,模式切换消耗较大。当从一个线程切换到另一个线程时,需要先从用户态切换到内核态进行,因为用户线程在用户态运行,而线程的管理和调度是在内核中实现的。

3.2 用户线程的优缺点

用户线程的创建、撤销等功能无需内核的支持,因而内核是完全不知道用户线程的存在。

优点

  1. 线程间的切换不需要切换到内核空间。对一个进程而言,其所有线程的管理数据结构以及管理线程切换的线程库均在用户空间,无需内核空间做任何干涉。
  2. 线程的调度算法可以是进程专用。在不影响OS调度的情况下,每个进程可以根据自身情况选择特定调度算法来管理和调度自己内部所有线程。
  3. 用户级线程的实现与OS平台无关,即使不支持线程的OS平台上也能实现线程机制。因为管理和调度线程的代码(即线程库)本身就在用户空间,属于用户程序的一部分,所有应用程序可以共享,从而甚至可以在原本不支持线程的OS平台实现线程机制。

缺点

  1. 存在系统调用的阻塞问题。在基于进程的OS中,大多数系统调用都会发生进程阻塞。因此,线程执行系统调度时,不仅该线程会阻塞,进程中的其它线程也会被阻塞。
  2. 多线程应用无法利用多核CPU可以进行多重处理的这一优点。内核每次给一个进程仅分配一个CPU,因此该进程中仅有一个线程能执行,在该线程放弃CPU之前,进程中的其它线程只能等待。

4 轻量级进程 LWP

这个概念目前看到有两种解释,这两种在一些时候需要结合语境分辨。

  1. 第一种是和传统进程对比,线程是轻量的。
    • 传统进程被称为重型进程(heavy-weight process. HWP),它相当于只有一个线程的任务。
    • 线程具有传统进程的很多特性,但又在进程之中,资源消耗等比较小,因此又称为轻型进程(light-weight process ,LWP)或进程元。
  2. 第二种是指许多系统实现线程模型时,常用来在用户和内核线程中间增加的一个数据结构(有些书中也叫中间系统),这种数据结构通常称为轻量级进程(LightWeight Process,LWP),有的书中也称核心线程

5 本地线程 native threads

线程通常被支持到操作系统中。我们把在这个级别工作的线程称为 “本地线程”(native threads,这里不是 Java 中的 ThreadLocal ),本地线程由操作系统内核管理和调度。

  • 其实是相对于 Green Thread 这种非操作系统内置实现的用户级别线程库而言的。

5. 参考资料

这里仅列出文章中未实时标注的参考资料。

  1. 《计算机操作系统(慕课版)》汤子瀛等
  2. 《操作系统概念(原书第9版)》
  3. 《深入理解计算机系统(原书第3版)》
  4. Intel® 64 and IA-32 Architectures Software Developer’s Manual
  5. User space and kernel space
  6. Basic operating systems terms and concepts
  7. Kernel Space Definition
预览
Loading comments...
0 条评论

暂无数据

example
预览