//xia仔k:操作系统入门与实践-参透技术本质
正文
什么是操作系统?
现代计算机系统由一个或多个处置器、主存、打印机、键盘、鼠标、显现器、网络接口以及各种输入/输出设备构成。
普通而言,现代计算机是一个复杂的系统。假如每位应用程序员都不得不控制系统的一切细节,那就不可能再编写代码了。所以,计算机装置了一层软件,称为操作系统,它的任务是为用户程序提供一个更好、更简单、更明晰的计算机模型,并管理方才提到的一切设备。
这是一个操作系统的简化图,最下面的是硬件,硬件包括芯片、电路板、磁盘、键盘、显现器等我们上面提到的设备,在硬件之上是软件。大局部计算机有两种运转形式:内核态 和 用户态,软件中最根底的局部是操作系统,它运转在 内核态 中。操作系统具有硬件的访问权,能够执行机器可以运转的任何指令。软件的其他局部运转在 用户态 下。
计算机硬件引见
操作系统与运转操作系统的内核硬件关系亲密。操作系统扩展了计算机指令集并管理计算机的资源。因而,操作系统因而必需足够理解硬件的运转,这里我们先扼要引见一下现代计算机中的计算机硬件。
从概念上来看,一台简单的个人电脑能够被笼统为上面这种类似的模型,CPU、内存、I/O 设备都和总线串联起来并经过总线与其他设备停止通讯。
CPU
CPU 是计算机的大脑,它主要和内存停止交互,从内存中提取指令并执行它。一个 CPU 的执行周期是从内存中提取第一条指令、解码并决议它的类型和操作数,执行,然后再提取、解码执行后续的指令。反复该循环直到程序运转终了。
由于访问内存获取执行或数据要比执行指令破费的时间长,因而一切的 CPU 内部都会包含一些存放器来保管关键变量和暂时结果。因而,在指令集中通常会有一些指令用于把关键字从内存中加载到存放器中,以及把关键字从存放器存入到内存中。还有一些其他的指令会把来自存放器和内存的操作数停止组合,例如 add 操作就会把两个操作数相加并把结果保管到内存中。
除了用于保管变量和暂时结果的通用存放器外,大多数计算机还具有几个特殊的存放器,这些存放器关于程序员是可见的。其中之一就是 程序计数器(program counter),程序计数器会指示下一条需求从内存提取指令的地址。提取指令后,程序计数器将更新为下一条需求提取的地址。
内存
计算机中第二个主要的组件就是内存。理想状况下,内存应该十分快速(比执行一条指令要快,从而不会拖慢 CPU 执行效率),而且足够大且廉价,但是目前的技术手腕无法满足三者的需求。于是采用了不同的处置方式,存储器系统采用一种分层次的构造:
顶层的存储器速度最高,但是容量最小,本钱十分高,层级构造越向下,其访问效率越慢,容量越大,但是造价也就越廉价。
存放器
存储器的顶层是 CPU 中的存放器,它们用和 CPU 一样的资料制成,所以和 CPU 一样快。程序必需在软件中自行管理这些存放器(即决议如何运用它们)
高速缓存
位于存放器下面的是高速缓存。当应用程序需求从内存中读取关键词的时分,高速缓存的硬件会检查所需求的高速缓存行能否在高速缓存中。假如在的话,那么这就是高速缓存命中(cache hit)。高速缓存满足了该恳求,并且没有经过总线将内存恳求发送到主内存。高速缓存命中通常需求破费两个时钟周期。缓存未命中需求从内存中提取,这会耗费大量的时间。高速缓存行会限制容量的大小由于它的造价十分昂贵。
主存
在上面的层次构造中再下一层是主存,这是内存系统的主力军,主存通常叫做 RAM(Random Access Memory)。一切不能再高速缓存中得到满足的内存访问恳求都会转往主存中。
除了主存之外,许多计算机还具有少量的非易失性随机存取存储器。它们与 RAM 不同,在电源断电后,非易失性随机访问存储器并不会丧失内容。ROM(Read Only Memory) 中的内容一旦存储后就不会再被修正。它十分快而且廉价。
磁盘
下一个层次是磁盘(硬盘),磁盘同 RAM 相比,每个二进制位的本钱低了两个数量级,而且经常也有两个数量级大的容量。磁盘独一的问题是随机访问数据时间大约慢了三个数量级。
I/O 设备
CPU 和存储器不是操作系统需求管理的全部,I/O 设备也与操作系统关系亲密。能够参考上面这个图片,I/O 设备普通包括两个局部:设备控制器和设备自身。控制器自身是一块芯片或者一组芯片,它可以控制物理设备。它可以接纳操作系统的指令,例如,从设备中读取数据并完成数据的处置。
在许多状况下,实践控制设备的过程是十分复杂而且存在诸多细节。因而控制器的工作就是为操作系统提供一个更简单(但依然十分复杂)的接口。也就是屏蔽物理细节。任何复杂的东西都能够加一层代理来处理,这是计算机或者人类社会很普世的一个处理计划。
计算机启动过程
那么有了上面一些硬件再加上操作系统的支持,我们的计算机就能够开端工作了,那么计算机的启动过程是怎样的呢?下面只是一个扼要版的启动过程:
在每台计算机上有一块双亲板,也就是母板,母板也就是主板,它是计算机最根本也就是最重要的部件之一。主板普通为矩形电路板,上面装置了组成计算机的主要电路系统,普通有 BIOS 芯片、I/O 控制芯片、键盘和面板控制开关接口、指示灯插接件、扩大插槽、主板及插卡的直流电源供电接插件等元件。
在母板上有一个称为 根本输入输出系统(Basic Input Output System, BIOS)的程序。在 BIOS 内有底层 I/O 软件,包括读键盘、写屏幕、磁盘I/O 以及其他过程。往常,它被保管在闪存中,它是非易失性的,但是当BIOS 中发现错误时,能够由操作系统停止更新。
在计算机启动(booted)时,BIOS 开启,它会首先检查所装置的 RAM 的数量,键盘和其他根底设备能否已装置并且正常响应。接着,它开端扫描 PCIe 和 PCI 总线并找出连在上面的一切设备。即插即用的设备也会被记载下来。假如现有的设备和系统上一次启动时的设备不同,则新的设备将被重新配置。
最后,BIOS 经过尝试存储在 CMOS 存储器中的设备清单尝试启动设备。
操作系统概念
大局部操作系统提供了特定的根底概念和笼统,例如进程、地址空间、文件等,它们是需求了解的中心内容。
地址空间
每台计算机都有一些主存用来保管正在执行的程序。在一个十分简单的操作系统中,仅仅有一个应用程序运转在内存中。为了运转第二个应用程序,需求把第一个应用程序移除才干把第二个程序装入内存。
复杂一些的操作系统会允许多个应用程序同时装入内存中运转。为了避免应用程序之间互相干扰(包括操作系统),需求有某种维护机制。固然此机制是在硬件中完成,但却是由操作系统控制的。
通常,每个进程有一些能够运用的地址汇合,典型值从 0 开端直到某个最大值。一个进程可具有的最大地址空间小于主存。在这种状况下,即便进程用完其地址空间,内存也会有足够的内存运转该进程。
但是,在许多 32 位或 64 位地址的计算机中,分别有 2^32 或 2^64 字节的地址空间。假如一个进程有比计算机具有的主存还大的地址空间,而且该进程希望运用全部的内存,那该怎样处置?在早期的计算机中是无法处置的。但是如今有了一种虚拟内存的技术,操作系统能够把局部地址空间装入主存,局部留在磁盘上,并且在需求时来回交流它们。
输入输出
一切的计算机都有用来获取输入和产生输出的物理设备。毕竟,假如用户不能通知计算机该做什么,且在计算机完成了所请求的工作之后竟不能得到结果,那么计算机还有什么用途呢?有各品种型的输入输出设备,包括键盘、显现器等等,对这些设备的管理全靠操作系统。
维护
计算机中含有大量的信息,用户希望可以对这些信息中有用而且重要的信息加以维护,这些信息包括电子邮件、商业方案等,管理这些信息的平安性完整依托操作系统来保证。例如,文件提供受权用户访问。
比方 UNIX 操作系统,UNIX 操作系统经过对每个文件赋予一个 9 位二进制维护代码,对 UNIX 中的文件完成维护。该维护代码有三个位子段,一个用于一切者,一个用于与一切者同组(用户被系统管理员划分红组)的其他成员,一个用于其别人。每个字段中有一位用于读访问,一位用于写访问,一位用于执行访问。这些位就是著名的 rwx位。例如,维护代码 rwxr-x--x 的含义是一切者能够读、写或执行该文件,其他的组成员能够读或执行(但不能写)此文件、而其别人能够执行(但不能读和写)该文件。
系统调用
多数现代操作系统都有功用相同但是细节不同的系统调用,引发操作系统的调用依赖于计算机本身的机制,而且必需用汇编代码表达。任何单核CPU 计算机一次执行执行一条指令。假如一个进程在用户态下运转用户程序,例如从文件中读取数据。那么假如想要把控制权交给操作系统控制,那么必需执行一个异常指令或者系统调用指令。操作系统紧接着需求参数检查找出所需求的调用进程,然后执行系统调用,把控制权移交给系统调用下面的指令。大致来说,系统调用就像是执行了一个特殊的过程调用,但是只要系统调用可以进入内核态,而过程调用则不能进入内核态。
为了可以理解详细的调用过程,下面我们以 read 办法为例来看一下调用过程。像上面提到的那样,会有三个参数,第一个参数是指定文件、第二个是指向缓冲区、第三个参数是给定需求读取的字节数。就像简直一切系统调用一样,它经过运用与系统调用相同的称号来调用一个函数库,从而从C程序中调用:read。
count = read(fd,buffer,nbytes);
系统调用在 count 中返回实践读出的字节数。这个值通常与 nbytes 相同,但也可能更小。比方在读过程中遇到了文件尾的状况。
假如系统调用不能执行,不论是由于无效的参数还是磁盘错误,count 的值都会被置成 -1,然后在全局变量 errno 中放入错误信号。程序应该进场检查系统调用的结果以理解能否出错。
下面,我们会列出一些常用的系统调用,POSIX 系统调用大约有 100 多个,它们之中最重要的一些调用见下表:
进程管理
调用
阐明
pid = fork()
创立与父进程相同的子进程
pid = waitpid(pid, &statloc,options)
等候一个子进程终止
s = execve(name,argv,environp)
交换一个进程的中心映像
exit(status)
终止进程执行并返回状态
文件管理
调用
阐明
fd = open(file, how,...)
翻开一个文件运用读、写
s = close(fd)
关闭一个翻开的文件
n = read(fd,buffer,nbytes)
把数据从一个文件读到缓冲区中
n = write(fd,buffer,nbytes)
把数据从缓冲区写到一个文件中
position = iseek(fd,offset,whence)
挪动文件指针
s = stat(name,&buf)
获得文件状态信息
目录和文件系统管理
调用
阐明
s = mkdir(nname,mode)
创立一个新目录
s = rmdir(name)
删去一个空目录
s = link(name1,name2)
创立一个新目录项 name2,并指向 name1
s = unlink(name)
删去一个目录项
s = mount(special,name,flag)
装置一个文件系统
s = umount(special)
卸载一个文件系统
其它
调用
阐明
s = chdir(dirname)
改动工作目录
s = chmod(name,mode)
修正一个文件的维护位
s = kill(pid, signal)
发送信号给进程
seconds = time(&seconds)
获取从 1970 年1月1日至今的时间
上面的系统调用参数中有一些公共局部,例如 pid 系统进程 id,fd 是文件描绘符,n 是字节数,position 是在文件中的偏移量、seconds 是流逝时间。