操作系统基础概念大扫盲 - 操作系统系列(一)(持续更新,争取完整)
文章目录
前言
本文旨在扫盲一些概念,对每个概念可能不会深入讲解,博主希望这篇文章不仅是操作系统各种概念的集合体,也是博主自己理解一些东西之后的交流桥梁,算是跟大家一起扫盲,我把自己理解的东西还有一些各路大佬写的比较好的博客都写到了引用框里面了,感谢所有的这些博文作者,好多都是我学习过程中令我茅塞顿开的超级优秀的文章,如果我有说的不对的地方希望大家能在评论区帮我指正,谢谢呐,原创不易,有用的话还望留下赞或者评论。
1 - 什么是操作系统?
操作系统(Operation System, OS) 是指控制和管理整个计算机系统的硬件和软件资源,并合理的组织和调度计算机的工作和资源的分配,以提供给用户和其它软件方便的接口和环境,它是计算机系统中最基本的系统软件。
2 - 一个重要概念 — 抽象(自顶向下看操作系统)
抽象是管理复杂性的一个关键。
一个好的抽象可以把一个几乎不可能管理的任务划分为两个部分:
-
抽象体的定义和实现
-
调用抽象体解决问题
其实仔细思考,操作系统内核其实就是底层硬件的抽象,抽象出来后提供一些API接口供用户使用,使得电脑就像一个黑匣子,程序员只需会调用系统的API接口就可以开发运行在操作系统上的软件,再思考,其实面向对象编程思想的提出,也是抽象的一种体现,把复杂的问题抽象出来并解决是每个程序员毕生的工作。
3 - 操作系统是硬件资源的管理者(自底向上看操作系统)
现代的计算机包含CPU、存储器、磁盘、鼠标等等硬件资源,从此角度来看,操作系统的任务其实是在相互竞争的程序之间有序地控制这些硬件的资源分配。
资源管理其实包含以下两个方面:
- 时间上复用资源:一种资源在时间上复用时,不同程序/用户轮流使用。例子:CPU的时间片轮询算法。
- 空间上复用资源:一份资源,每个程序/用户都得到资源的一部分。例子:磁盘空间资源的管理。
4 - 操作系统的历史
1 - 百度百科
2 - 维基百科
5 - 操作系统基本概念
(一)进程
在所有操作系统中,一个重要的概念是进程(process)。
进程本质上是正在执行的一个程序,与每个进程相关的是进程的地址空间(addressspace),这是从某个最小值的存储位置(通常是零)到某个最大值存储位置的列表。
在这个地址空间中,进程可以进行读写。该地址空间中存放有可执行程序、程序的数据以及程序的堆栈。
与每个进程相关的还有资源集,通常包括寄存器(含有程序计数器和堆栈指针)、打开文件的清单、突出的报警、有关进程清单,以及运行该程序所需要的所有其他信息。
进程基本上是容纳运行一个程序所需要所有信息的容器。
进程的两种理解(动态容器、正在执行的程序)
- 理解一:**动态的一个容器。**每个进程都有其地址空间,一般情况下其中包括文本区域、数据区域、堆栈。
- 文本区存储进程需要执行的静态的代码
- 数据区是存放程序中已初始化的全局变量的一块内存区域,属于静态内存分配
- 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
- 栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区,属于动态内存分配。
- 理解二:一个正在执行的程序。程序是静态的,只有CPU利用操作系统赋予程序生命后,正在执行的程序才成为进程,成为一个活动的实体。
(二)线程
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如[Windows 7](https://baike.baidu.com/item/Windows 7)的线程,进行混合调度。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
一个进程可以有很多线程,每条线程并行执行不同的任务。
在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责I/O处理、人机交互而常被阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。 – 百度百科
其实归结为一句话就是:线程就是进程中一步一步执行的路径。
-
多线程:
-
如果一个进程同一时间多个线程并行,那么就是支持多线程的
-
线程作为调度和执行的单位,每个线程都有独立的运行栈区和程序计数器,线程切换的时间开销很小
-
一个进程中的多个线程共享相同的内存单元和内存地址空间。(多个线程可以访问堆区中相同的变量和对象,称为线程的通信,但是与此同时带来的就是线程操作共享系统资源可能带来的线程安全问题,比如多线程修改共享数据时,由于时间不同步而导致的数据不符合程序正常处理的结果。)
-
线程和进程的区别?
- 地址空间:线程共享本进程的地址空间,而进程之间是独立的地址空间(地址空间就是指比如64位计算机就有2^64字节的地址空间)
- 资源:线程共享本进程的资源(内存、CPU、I/O等),不利于资源的管理和保护,二进程之间是独立的,便于资源的管理和保护。
- 健壮性:多进程要比多线程健壮,一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是如果多线程中一个线程崩溃会导致整个进程死掉。
- 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口,执行开销大。线程不能独立执行,必须在应用程序中编写相关的多线程代码,由应用程序本身进行多线程执行控制,执行开销小。
- 切换开销:线程切换开销小于进程切换开销。(具体原因推荐看:blog1、blog2)
(三)并行和并发
- 并行:简单来说就是指同一时刻多个程序共同执行。只有多核CPU才能实现并行(单核CPU通过快速的线程切换,实现的是伪并行,效率肯定低于多核CPU),这时候不会发生CPU资源的抢占,各个程序之间各自用着不同的CPU资源,互不影响。
- 并发:简单来说就是指同一时刻多个程序共同发生。单核CPU由于一个时间单元内只能执行一个程序,所以多个程序共同发生的时候,CPU利用时间片调度算法(某个任务在设置好的时间片结束之前没有完成,那么该任务会被暂停并放弃CPU资源,等待下一轮循环再拿回CPU资源再做,此时的CPU资源被分配给另一个任务去使用)执行多个程序,由于时间片设置得当,导致微微的停顿人根本感受不到
举一个并发和并行的例子:互联网公司A有一个饮水机(单核CPU),员工们(进程)拿着自己的水杯排队去接水,这就是并发。互联网公司B有多个饮水机(多核CPU),员工们(进程)在不同的队伍排队接水,这就是并行。
单核CPU和多核CPU
- 单核CPU是假的多线程,在一个时间单元内,只能执行一个线程的任务,只不过CPU运算速度很快,人感受不到线程切换的时间。
- 多核CPU真正能实现多线程,提高运行效率
- 一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()
垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
(四)系统调用
- 基本概念:计算机的各种硬件资源是有限的,为了更好的管理这些资源,用户进程是不允许直接操作的,所有对这些资源的访问都必须由操作系统控制。为此操作系统为用户态运行的进程与硬件设备之间进行交互提供了一组接口,这组接口就是所谓的系统调用。
那么在应用程序和硬件之间设置这样一个接口层有什么优点呢?
- 把用户从学习硬件设备的低级编程特性中解放出来。
- 提高了系统的安全性,内核在满足某个请求之前就可以在接口级检查这个请求的正确性。
- 最重要的是,这些接口使得程序更具有可移植性,因为只要不同操作系统所提供的一组接口相同,那么在这些操作系统上就可以正确的编译和执行相同的程序。
系统调用实质上就是函数调用,只不过调用的是系统函数,处于内核态而已。
用户在调用系统调用时会向内核传递一个系统调用号,然后系统调用处理程序通过此号从**系统调用表**中找到相应的内核函数执行,最后返回。
(五)BIOS
BIOS是英文"Basic Input Output System"的缩略词,直译过来后中文名称就是"基本输入输出系统"。
BIOS其实有两种含义,分别是硬件上的BIOS芯片和软件上的BIOS设置程序。
主板上有BIOS芯片,BIOS芯片是一个ROM(Read Only Memory 只读存储器)芯片,然后BIOS芯片在主板出厂时就存储了BIOS设置程序,随着技术发展,现在的EEPROM(电可擦除可编程ROM)的诞生,可以对EEPROM进行重写。
BIOS芯片中主要存放:
●自诊断程序:通过读取CMOSRAM中的内容识别硬件配置,并对其进行自检和初始化;
● CMOS设置程序:引导过程中,用特殊热键启动,进行设置后,存入CMOS RAM中;
● 系统自举装载程序(也就是常说的BootLoader):在自检成功后将磁盘相对0道0扇区上的引导程序装入内存,让其运行以装入DOS系统;
● 主要I/O设备的驱动程序和中断服务:由于BIOS直接和系统硬件资源打交道,因此总是针对某一类型的硬件系统,而各种硬件系统又各有不同,所以存在各种不同种类的BIOS,随着硬件技术的发展,同一种BIOS也先后出现了不同的版本,新版本的BIOS比起老版本来说,功能更强。
(六)BootLoader
A bootloader is software that is responsible for booting a computer.
Boot Loader 是在操作系统内核运行之前运行的一段小程序(存储在BIOS芯片中)。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
在嵌入式操作系统领域,通常没有BIOS那样的固件程序(有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。
比如STM32嵌入式平台,现在已经有了完善的开发场景,利用HAL库和可视化工具STM32CubeMX即可开发,但是在互联网冲浪的时候我发现有大佬给STM32刷入了Arduino的BootLoader,太强了,我原本是个STM32选手,也是看到了这个之后才仔细玩了玩Arduino,这是那个国外大佬做的STM32duino-bootloader的Github Repo,喜欢的小伙伴可以康康:https://github.com/rogerclarkmelbourne
STM32的bootLoader讲解:https://blog.csdn.net/qq_30479727/article/details/63352897
由以上两个概念,推导出计算机的启动过程就是:开机 --> BIOS设置程序的运行 --> 进入bootloader程序 --> 引导操作系统内核
(七)中断向量表
中断:中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
简单来说,中断向量表存储的就是中断向量,中断向量是指向中断服务程序的指针,之所以叫做向量,我认为应该是因为它是一个指针,是具有指向功能的有方向的量。
(八)实模式和保护模式
- 实模式:如其名,就是指操作系统可以直接操作实际的物理地址的一种模式,早期CPU可以直接通过地址直接访问BIOS程序和外部硬件,这肯定是不安全的,对于非专业人员不太友好,一旦操作失误破坏底层程序就凉凉了。
- 保护模式:在保护模式下,其实就是在逻辑地址(用户操作的内存地址)和实际物理地址中间加层保护罩,这层保护罩就是全局描述符表(GDT),全局描述符表中含有一个个表项,每一个表项称为段描述符。而段寄存器在保护模式下存放的便是相当于一个数组索引的东西,通过这个索引,可以找到对应的表项。段描述符存放了段基址、段界限、内存段类型属性(比如是数据段还是代码段,注意一个段描述符只能用来定义一个内存段)等许多属性。
- 推荐博客:https://zhuanlan.zhihu.com/p/42309472
(九)原语
计算机进程的控制通常由原语完成。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中断。在操作系统中,某些被进程调用的操作,如队列操作、对信号量的操作、检查启动外设操作等,一旦开始执行,就不能被中断,否则就会出现操作错误,造成系统混乱。所以,这些操作都要用原语来实现 原语是操作系统核心(不是由进程,而是由一组程序模块组成)的一个组成部分,并且常驻内存,通常在管态下执行。原语一旦开始执行,就要连续执行完,不允许中断 。
原语通常由若干条指令组成,用来实现某个特定的操作。通过一段不可分割的或不可中断的程序实现其功能。原语是操作系统的核心,它不是由进程而是由一组程序模块所组成,是操作系统的一个组成部分,它必须在管态(一种机器状态,管态下执行的程序可以执行特权和非特权两类指令,通常把它定义为操作系统的状态)下执行,并且常驻内存,而个别系统有一部分不在管态下运行。原语和广义指令都可以被进程所调用,两者的差别在于原语有不可中断性,它是通过在执行过程中关闭中断实现的,且一般由系统进程调用。许多广义指令的功能都可用目态(一种机器状态,通常把它作为用户程序执行时的状态)下运行的系统进程完成,而不一定要在管态下完成,例如文件的建立、打开、关闭、删除等广义指令,都是借助中断进入管态程序,然后转交给相应的进程,最终由进程实现其功能。引进原语的主要目的是为了实现进程的通信和控制。
如何理解原语?为什么要使用原语?
先说一下原子操作的概念:
- 在多进程(线程)的操作系统中不能被其它进程(线程)打断的操作就叫原子操作,文件的原子操作是指操作文件时的不能被打断的操作。
- 原子还有一层意思,当该次操作不能完成的时候,必须回到操作之前的状态,原子操作不可拆分。
- 所有原子操作是同步的,而且不可被外部中断(内中断可以,不过一般是致命错误处理)。
- 也就是说,原子操作是中断安全的。
2.原语怎么理解:
原语其实就是一个用机器指令编写一个不可被中断的程序,其实就是执行原子操作的实际的函数或者过程。
3.为什么要使用原语?
其实就是在管理和控制进程的过程中,有些操作十分重要,如队列操作、对信号量的操作、检查启动外设操作等,如果它们被中断,操作系统可能就崩了,所以要使用原语。
参考资料
- 百度百科、维基百科
- 现代操作系统(原书第四版)—机械工业出版社
最后的最后
我是一个热爱IT技术和音乐的Dream Catcher,正在努力培养计算机的深度和广度认知,也会和大家伙儿分享我的音乐,大家伙儿多多关照 (๑❛ᴗ❛๑)
联系我的话,可以邮箱或者私信哦!!谢谢大家咯(*≧▽≦)
My Social Link:
我的个人博客站:https://blog.calvinhaynes.top/
我的知乎主页:https://www.zhihu.com/people/eternally-92-61
我的B站主页:https://space.bilibili.com/434604897
我的CSDN主页:https://blog.csdn.net/qq_45772333
我的邮箱:chx1006488386@163.com
我的Github主页:https://github.com/CalvinHaynes
我的码云主页:https://gitee.com/CalvinHaynes
喜欢我的文章的话,不妨留下你的大拇指,点个赞再走,您的支持是我创作的最大动力,也欢迎指正博客中存在的问题,谢谢呐(~ ̄▽ ̄)~