参考
- Linux内核版本和发行版本
- Linux架构
- 用户态和内核态的简单理解「建议收藏」
- CPU、操作系统内核、ARM内核与架构的理解【科普】
- 一文看懂指令集是什么
- 一文搞懂X86架构和ARM架构的区别
- 线程和进程的区别是什么?
- 如何理解内核线程、内核进程?
讲解
知识点 | 详细解释 | 备注 |
---|---|---|
内核 | **目的:**为了管理硬件,让硬件动起来 **本质:**就是一段计算机程序 **作用:**屏蔽了调用各硬件资源的细节,包括CPU、内存空间、硬盘接口、网络接口等等 **定位:**操作系统的核心组件(心脏) | uname -a 或者cat /proc/version |
系统调用 | 目的: 为了【方便】调用内核 **本质:**将内核提供的接口【抽象为更易用的函数】,类似C这种高级语言具有抽象性,而不是汇编语言(不同硬件会有差异) **作用:**不需要了解内核的复杂结构就能使用内核,Linux具有200多个系统调用 定位:【系统调用是操作系统的最小功能单位】 | |
上面两部分就构成了我们熟知的【操作系统】——【内核+系统调用=操作系统】 | ||
发行版本 | 在内核的基础上,开发不同应用程序,组成的一个完整的操作系统。目前估计各种发行版本有数十种 —— 可以理解为机器动起来的同时,提供很多优质软件(如windows 的office、 mac 的keynote,这些软件的集合可以叫作套件) 【不同的发行版本提供的套件不同,其中LSTM 版本叫长期演进版,就是一直维护,又bug会进行修复】 【操作系统 + 不同厂商提供的多种软件(该厂商进行维护) = 不同发行版本】 | cat /etc/issue ;lsb_release -a;cat /etc/redhat-release |
系统调用移植性差的原因 | windows、linux内核的系统调用是不同的,比如:同一个功能提供给用户的函数名、参数【都不相同】,会出现从一个系统移植到另一个系统无法正常运行 | |
库函数 | **目的:**为了解决【系统调用移植性差】的问题,同时封装windows和linux系统调用,对用户提供一个的函数(接口) **定位:**系统调用在内核地址空间执行,而库函数是在用户地址空间调用 | ISO C标准库,POSIX标准等 |
shell | **作用:**可以看作一种特殊的应用,称为【命令行解释器】 **原理:**每个shell命令相当于一个小功能,比如 ls 命令就是【查看功能】,【命令行解释器】意思就是:将 shell 命令(如ls命令)【翻译(解释)成】操作系统能听懂的系统调用,之后便可进行执行 **用途:**通过【shell 命令的组合】可以完成一些【稍复杂的功能】,这个组合称之为【shell脚本】 | |
编译型语言(二进制文件) | 有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。 这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。 | 优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等 |
解释型语言(源码) | 有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。 这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器 | 优点是使用灵活、部署容易、【跨平台性好】,非常适合 Web 开发以及小工具的制作 |
指令集 | 表示着CPU可执行的指令的集合,【每条指令对应着计算机硬件可理解的01字符串】(比如 加法指令 对应 01000010101010 等) | |
指令集的种类 | X86:复杂指令集(CISC:Complex Instruction Set Computer) ARM:精简指令集(RISC:Reduced Instruction Set Computer) 可以这么说,X86指令集中的指令是复杂的,一条很长指令就可以很多功能,而ARM指令集的指令是很精简的,需要几条精简的短指令完成很多功能。 可理解为: 一条复杂指令的作用 = 多条精简指令叠加的作用 | |
微架构(CPU) | 微架构 —— CPU 的架构(CPU中电路、硬件结构等) 指令集代表着CPU的执行逻辑方式,但同一指令集在不同公司设计下可能有不同的CPU硬件结构 | |
x86和ARM硬件架构 | 通常所说x86和ARM架构机器,指的就是他们的指令集不同 因x86和ARM 基于不同指令集实现,因此软件的迁移,需要进行改动适配 如 x86 上的软件使用复杂指令集,那么迁移到 ARM 机器上,就需要适配为使用精简指令集 | |
内核和内核态 | 内核 —— 控制硬件的程序 内核态 —— 指的是执行过程中正在运行内核程序,而不是用户程序 | |
用户态和内核态 | 在CPU指令中,有些指令是非常危险的,如果错用将导致系统崩溃,比如清内存、设置时钟等,所以CPU将指令分为特权指令和非特权指令。 对于危险指令,只允许操作系统及其相关模块使用,普通应用程序只能使用不危险的指令。 操作系统启动时对内存进行了划分,操作系统的数据都是存放于内核空间的,用户进程的数据是存放于用户空间的。 处于用户态级别的程序只能访问用户空间,而处于内核态级别的程序可以访问用户空间和内核空间。 | |
两种状态切换耗费时间的原因 | 内核为上层应用使用CPU、存储、IO等硬件资源提供了接口,即系统调用,如果一个应用程序需要用到内核资源,就需执行系统调用的相关语句。 (1)当执行系统调用时,首先会保存现场,然后通过系统调用到内核态执行,最后内核态变为用户态执行用户程序时,需要恢复用户态的现场。在进入内核的时候需要保存用户态的寄存器,在内核态返回用户态的时候会恢复这些寄存器的内容。 (2)每个线程都有两个栈:用户栈和内核栈,当执行系统调用时,线程就会由用户栈切换到内核栈,而内核代码对用户不信任,需要进行额外的检查。 | |
何种情况导致切换 | 线程间的切换(上下文):内核级线程上下文切换时,前一个线程(切换前最后时刻的数据和指令)会被保存到内核中,而切换回来时需要从内核空间中读取数据恢复现场。 Syncronized锁的获取和释放:使用内核中monitor对象 | |
为什么线程切换会导致用户态与内核态的切换 | 因为线程的调度是在内核态运行的,而线程中的代码是在用户态运行 可能也不尽然,线程分用户级线程和内核级线程,用户级线程的切换就是由应用程序本身进行的,并不需要进入内核 | |
用户态切换到内核态 | 当程序在用户态需要申请外部资源(如声卡、网卡、U盘、磁盘)时,需要从用户态切换到内核态,主要有3种情况需要申请外部资源: (1)系统调用 举例: 创建线程时,需要通过pthread函数库调用内核来创建 (2)中断 举例: 当外围设备完成用户请求的操作后,会想CPU发送中断信号。这时CPU会暂停执行下一条指令(用户态)转而执行与该中断信号对应的中断处理程序 (3)异常:某些异常如缺页异常只能由内核进行处理 | |
进程和线程 | 进程是资源分配的最小单位,线程是CPU调度的最小单位 进程是拥有自己的资源(内存等等),而线程不拥有资源,只是能使用资源 | |
进程 | 管理好自己的资源 在一定的环境下,把静态的程序代码运行起来,通过使用不同的资源,来完成一定的任务。比如说,进程的环境包括环境变量,进程所掌控的资源,有中央处理器,有内存,打开的文件,映射的网络端口等等 一个系统中,有很多进程,它们都会使用内存。为了确保内存不被别人使用,每个进程所能访问的内存都是圈好的。一人一份,谁也不干扰谁。还有内存的分页,虚拟地址我就不深入探讨了。这里给大家想强调的就是,进程需要管理好它的资源 | |
线程 | 作为进程的一部分 扮演的角色就是怎么利用中央处理器去运行代码。这其中牵扯到的最重要资源的是中央处理器和其中的寄存器,和线程的栈(stack)。这里想强调的是,线程关注的是中央处理器的运行,而不是内存等资源的管理。 当只有一个中央处理器的时候,进程中只需要一个线程就够了。随着多处理器的发展,一个进程中可以有多个线程,来并行的完成任务。比如说,一个web服务器,在接受一个新的请求的时候,可以大动干戈的fork一个子进程去处理这个请求,也可以只在进程内部创建一个新的线程来处理。线程更加轻便一点。线程可以有很多,但他们并不会改变进程对内存(heap)等资源的管理,线程之间会共享这些资源。 | |
用户级线程 | 一、概述 用户级线程的实现就是把整个线程实现部分放在用户空间中,内核对线程一无所知,内核看到的就是一个单线程进程。注:对于实现的是用户级线程的操作系统而言,CPU 调度的基本单位看起来像是进程(因为在内核看来,这些进程都是单线程的,所以对单线程的调度就像是在调度进程一样)。 二、优点 1. 整个用户级线程的切换发生在用户空间,这样的线程切换至少比陷入内核要快一个数量级(不需要陷入内核、不需要上下文切换、不需要对内存高速缓存进行刷新,这就使得线程调度非常快捷) 2. 用户级线程有比较好的可扩展性,线程能够利用的表空间和堆栈空间比内核级线程多,这是因为在内核空间中内核线程需要一些固定的表格空间和堆栈空间,如果内核线程的数量非常大,就会出现问题,因为内核空间也是很有限的。 3. 创建和销毁线程、线程切换代价等线程管理的代价比内核线程少得多, 因为保存线程状态的过程和调用程序都只是本地过程 三、缺点 1. 一个线程阻塞,会阻塞该进程中其他所有的线程。比如:线程发生 I/O 或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程,缺页中断也会产生类似的问题。 2. 如果一个线程开始运行,那么该进程中其他线程就不能运行,除非第一个线程自动放弃 CPU 。因为在一个单独的进程内部,没有时钟中断,所以不能用轮转调度(轮流)的方式调度线程。 3. 还有一个缺点就是对于线程的调度方面,说是缺点是因为,不像内核线程的调度那样,由时钟中断来控制,在用户线程中我们必须自己手动编写调度的代码,这会极大的增加我们代码的复杂度。同时也是一种优点,那是因为,线程调度的控制权掌握在了我们自己手中,我可以自己定制相应的调度策略。 | |
内核级线程 | 一、概述 在内核中有一个用来记录系统中所有线程的线程表(TCB,进程表PCB),当某个线程希望创建一个新线程或撤销一个已有线程时,它进行一个系统调用,这个系统调用通过对线程表的更新完成线程的创建或撤销工作。 现在主流的操作系统对于线程的实现,大多采用这种线程模型,比如 Windows、Linux 等。 二、优点 1. 当一个线程阻塞时,内核根据选择,可以运行同一个进程中的其它线程或其他进程中的线程。 2. 由内核来调(借助于时钟中断),开发者无须关心调度的实现。 三、缺点 1. 在内核中创建和撤销线程的开销比较大,速度慢 2. 在用户级线程中,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。 3. 在内核级线程中,**线程管理的所有工作都由内核完成,应用程序没有进行线程管理的代码,**只有一个到内核级线程的编程接口。内核为进程及内部的每个线程维护上下文信息,调度也在内核基于线程架构的基础上完成。 |