码迷,mamicode.com
首页 > 其他好文 > 详细

操作系统基础

时间:2016-05-19 21:34:29      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:操作系统   内核   api   系统调用   abi   

  对于大多数人而言,软件就是一个能够完成某些功能的应用程序。我们在日常的生活和工作中,只要使用计算机类的设备,就不可避免的会使用各种各样的软件。尤其是现在手持智能终端设备的广泛流行,使得应用程序(APP)的概念更加的深入人心,甚至以前从来没有接触过编程的人也能很流畅的说出这样的术语来。


  其实,软件的的确确就是由程序员使用某种编程语言编写出来的,但是程序编写出来之后,是如何被使用的呢?这个恐怕就不是每个人都知道,也不是每个人都能说的清楚的事情了。大家可能会很纳闷,标题不是《操作系统基础》么?怎么说了这么半天的软件呢?大家莫着急,接下来,跟大家一起来聊一聊计算机应用程序的运行原理,从而引出操作系统的概念及其功能。


  首先,值得大家注意的是:程序是由指令和数据构成的。从另外一个角度来讲,我们也可以认为程序是由算法和数据结构构成的。不管程序员使用何种编程语言编写出来的程序,都是以最为简易和原始的纯文本文件的形式存放于计算机或者类似设备的外部存储器之中的。而所谓的指令和数据是一并存储在这些文件中的。这些程序源代码,是利用一种更加接近我们人类的自然语言的语言编写的。软件的运行离不开硬件建构和硬件环境,也就是说,软件必须在某个特定架构的硬件系统上运行才可以。而所谓的软件的运行,一般是指,将保存软件程序执行功能的文件从磁盘或其他外部存储器中加载至内存,分别存放在指令段和数据段,然后再由CPU调用指令短信息,通过指令指针寄存器找到位于内存中某个位置的数据并对该数据实施运算操作,最终输出结果的过程。整个软件的运行都是在内存和CPU之间徘徊的。也就是说,想要一个程序运行起来,就必须得能够让程序加载到内存,并且能够被CPU处理才行。


  而我们的计算机是数字设备(所谓的数字设备,大家可以简单理解为能够进行数字信息的运算和处理的设备,而且根据数字计算机只能处理二进制的数据。),无法理解这些高级程序源代码中所应用的人类的自然语言,因此,程序必须经过处理之后编码成二进制流才能被底层硬件所执行。但是硬件设备非常的底层,硬件所提供的功能也非常的底层,提供给用户的接口也是非常的简单和丑陋的,所以,为了方便大多数的程序员可以基于底层硬件进行程序开发,硬件制造商会把这些把这些丑陋的接口抽象成更加具体和容易使用的汇编接口。但是汇编接口仍然非常的底层,每个程序员想要将这些使用高级语言编写的程序在硬件上运行起来,就必须对这些硬件程序再开发一个能够顺利完成汇编功能的程序——驱动程序。这无疑会加大程序员的开发负担。而且,对于同一个硬件架构平台上的不同应用程序来说,其底层的支持应该是相同的,所以驱动程序也应该是相同的,因此,每个程序都需要单独开发驱动程序就显得多余而且是完全没有必要的。于是就有人站出来,专门为这些底层硬件开发驱动程序,然后共享出来,使得所有程序员在实施程序开发的时候,不需要自行编写这段代码,而只是在需要的时候调用这些驱动程序就可以了。这样一来,大大简化了程序员开发的过程,缩短了程序开发的周期,也为程序的稳定运行提供了保证,毕竟不是每个程序员写的驱动程序都是那么稳定的。至此,程序员开发的程序,在调用了驱动程序之后,就可以访问硬件了,然后再汇编成二进制流就可以被CPU直接处理了。这样一来,每个程序就是一个计算机的运行的基本单位,这个运行中的程序,我们不再称呼他为程序,而是称他为进程(Process)。


  但是,问题还没结束。在早期的Mainframe主机上,通常都是大家拿着自己的显示器和键盘,连接到专用的终端接口上来使用计算机计算资源。而整个主机上也只有一段连续的内存空间,通常也只有一颗CPU,那么如果多个用户同时使用该主机,并且都发起了一个进程的话,计算机该如何处理呢?其实最核心的问题就是:多个进程是如何占有和使用内存和CPU资源的。其实,在早期的计算机上是不支持多任务的,因此,某个终端上启动一个程序,如果这个时候计算机正在处理其他的进程,那么刚刚计划启动的进程就只能等到那个更早执行更早占用系统资源的其他进程的执行结束之后,才能被加载至内存,进而被CPU计算执行。因此我们称这种进程处理的方式为单任务处理。


  但是这样的进程处理方式,使得计算机执行效率太低,如果有某个终端发起了一个很大的进程,可能会占用非常长的CPU处理时间,那么这段时间其他的程序就只能等待,甚至都无法发起执行,无法载入内存。因此就有人提出"多任务"的设想,即:可以在一台计算机,利用有限的计算机系统资源,同时处理多个任务。但是,这样一来就必须要将有限的系统内存和CPU计算资源划分给多个进程才可以,而且为了保证各个进程之间互相不踩踏,互不倾轧,互不影响,就必须要有一套完善的资源分配和监控的程序来完成这个任务,而且这个监控程序应该是一个通用程序,必须是具有相当的公信度的程序,而这个程序就是后来我们大家所说的操作系统。但是这个所谓的操作系统并不是现在大家印象中的操作系统的概念,这只是一个狭义的操作系统,我们通常将其称之为kernel(内核)。


  kernel这个监控程序只是负责把底层的硬件驱动起来,并且把底层硬件所提供的各种资源虚拟化(如:把内存空间切割成N段,分给多个程序使用,从而也实现了内存资源的复用,我们称为空间复用;再比如把CPU的工作时间分片,供多个程序轮流使用,基于时序完成复用,我们称为时序复用。),以期能够将资源分配给多个进程并监控这些进程是如何使用这些资源的。同时,如何启动一个程序,如何关闭一个程序等工作也都是由监控程序完成的。他取得了整个硬件的控制权,并且把硬件的本来的接口的面目虚拟成了软件的样子。


  这些资源复用所带来的结果就是:能够把一套独立的完整的硬件系统资源切分成N个部分。站在这个角度来讲,我们可以认为每一个程序都独立占有计算机资源(CPU的计算资源和内存的存储资源),只不过每个程序都只是占有了整个计算机硬件系统完整资源的一部分罢了。


  而站在这个进程的角度来看,自己对资源的使用是通过那个监控程序来实现的,因此,该进程会认为自己是除了那个监控软件之外的唯一的运行在这个硬件系统上的进程,他自己是唯一的占有了除了那个监控程序消耗的CPU和内存资源之外所有的硬件系统资源的程序。他无法理解还有其他进程存在这一事实。每个程序都是这样在工作的。这是因为监控程序为每个进程虚拟出一个绝对私有的美好家园。



  kernel到底负责做什么呢?现在我们可以做出这样的总结了:

    第一、驱动底层硬件使得硬件可以被轻松访问;

    第二、将底层硬件资源抽象成简单易用的资源,以方便程序调用;

    第三、管理各进程的运行,把有限的资源合理分配给进程并予以监控,让其彼此之间相安无事。


  我们通常所说一个完整的操作系统由内核(Kernel)和各种应用程序(Applications)组成。内核运行在硬件系统之上,屏蔽了底层硬件复杂逻辑和丑陋接口,将其虚拟化为更加加单易用的接口,以方便程序调用,因此这些抽象出来的接口,我们称之为"系统调用(System Call)"。因此程序员在开发应用程序的时候就必须要清楚的知道一个操作系统内核中包含了哪些调用接口,当编写代码的过程中需要用到那个系统调用的时候,直接在程序中调用即可。但是内核中的系统调用数量足有数百个,对于程序员来讲,想完全了解所有的接口的功能以及调用方式也是异常辛苦的事情。于是,就有一些人将那些经常被调用的接口再次进行了封装,我们将这些被封装起来的接口称之为"库(Libraries)",而库是更加接近程序的最终形态的一种接口。这样一来,程序员在开发程序的时候,既可以选择基于系统调用来编写程序,也可以选择基于库调用来进行程序编写程序,于这样就为程序员们创造了更加自由,更加便利的开发环境。而现在在Linux开发领域,库成为了一种标准接口,因为基于库的程序开发速度更快,效率更高。这些库也称为API(Application Program Interface)。


  如果从程序员的开发视角来看,有一个程序员所开发的成员使用了Linux的API,那么这个程序在Windows里面是否可以运行呢?如果想要将一个在Linux开发的程序在Windows里面运行起来,或者反之在Windows下开发的程序在Linux里面运行起来,就需要将这些不同操作系统的API统一起来,也就是说在操作系统中提供可以相互兼容的库调用。互联网中定义了一个规范,叫POSIX(Portable Operating System,可移植操作系统)。任何一个遵守POSIX规范的API而编写的程序代码,可以在不同的操作系统上通用。


  如果站在一个程序的运行视角来看,或者站在一个应用者的视角来讲,库是二进制文件,内核是二进制文件,程序是二进制文件,他们都是可以运行的,但是库一般是不能直接运行的,而是在被某个其他的二进制程序调用的时候才会运行。也就意味着,一个程序是否能够运行,要看该程序所依赖调用的二进制库二者是否兼容,这样一来,就又有了另外一个接口,即ABI(Applications Binary Interface),这个接口是应用者运行程序时面对的接口。


  编程接口兼容并不意味着二进制接口兼容,任何一个应用程序只有转码成为二进制格式以后才可以使用的。这需要一个编译器编译。如果我们将程序在Windows上编译,编译器将转码为适用于Windows所支持的ABI的运行格式,所以放到Linux里面是运行不了的;同理,在Linux可以编译,而编译成二进制以后,只能兼容Linux的ABI接口,而无法放到Windows里面运行了。所以,在程序员接口上能兼容,在运行接口上未必能兼容,而且一般情况下也不可能兼容的。


  这些就是操作系统的组成结构。


  接下来聊一下操作系统的功能,通常情况下,操作系统都会具备以下基本功能:

    1.驱动程序

    2.进程管理

    3.安全管理

    4.网络功能

    5.内存管理

    6.文件系统


  这么多的功能没有一个是帮用户完成具体的任务的,所以,为了让用户能够使用操作系统来实现其所需的功能,操作系统只有内核是不够的,在操作系统之上还应该有具体的应用程序,需要哪种功能就应该提供哪种功能的应用程序即可。在诸多的应用程序中,有一种特殊的应用程序——访问界面程序(也称为访问接口程序)。  访问界面程序大致可以分为两大类,即:

    GUI:Graphic User Interface,即图形用户界面

    CLI:Command Line Interface,即CLI


  就像我们常用的Windows,装完之后有一个桌面,这个桌面就是Windows为用户提供的GUI。在这个桌面上我们可以用鼠标实现点击操作。亦或者像现在用的手机,操作接口就是触摸屏,我们也可以认为这是一种GUI,这种GUI可以通过手指的触摸、点击、划动的方式实现操作。在触摸屏出现之前,我们对计算机的操作往往只有键盘和鼠标。而如果不是GUI,鼠标变得无用了,只要有键盘就可以了。那么是GUI的操作效率高还是CLI的操作效率高呢?我想凡是使用过CLI的人都应该清楚的知道一点:CLI效率要高得多。其实现在就连微软公司也将GUI从Windows系统中剥离出来了,从2008开始,Windows也提供了一款CLI,那就是PowerShell了。为什么有些人认为CLI操作比较慢呢?其主要原因就在于通常有这样想法的人是没有使用过CLI或者畏惧使用文件界面,觉得CLI的操作太难,又是命令又是参数的,相反GUI的话,只需要鼠标点击即可完成大部分的操作。但事实是:在CLI下面一个命令即可完成的操作,在GUI下可能需要数次点击加各种目录的切换才能完成,因此说,GUI比较容易上手,但是操作效率其实很低。要学会使用GUI,很短的时间就可以,三五分钟;如果想深入使用,三五天就可以了。而CLI的入门曲线比较陡峭,对CLI来讲,恐怕仅仅是学会使用,没有三五十天恐怕是很困难的。但一旦入门,你就会发现,基于CLI的操作是相当的简单,比那些把复杂逻辑隐藏在GUI背后的视窗系统要容易的多。使用起来几乎是透明的,任何一个环节出现问题,都可以随时查找问题并能够及时纠正这些问题。从这个角度来说,Linux是比Windows更简单更易用的操作系统。如果说难度的话,Linux只是入门比较难而已。


  不管是哪种类型的访问界面都是一种应用程序,包括GUI。对Linux而言,桌面仅仅是一款应用程序而已。没有这个应用程序Linux依然可以运行的很好,有了这个应用程序反而可能运行的不好了,这个GUI使得Linux背负了一个重重的枷锁,系统的资源不得不被GUI无情的占用,而且由于GUI程序存在不稳定性,使得GUI随时可能崩溃。


  访问界面反正就是一种应用程序,没有什么其他特殊的地方,因此对于Linux而言,可用的选择就有很多。

    GUI:

      GNOME:使用C语言研发的,开发环境叫gtk。GNU

      KDE:使用C++语言研发的,开发环境叫qt

      XFCE:嵌入式环境中的轻量级图形界面

    CLI:bash zsh csh sh tcsh ksh 

    此外,接口还包括telnet、ssh、共享桌面等各种类型的远程连接访问界面

 

  那么对于操作系来说,这些访问界面是否是必须的呢?如果对于Windows来讲, 可以说这是必须的,但是对于Linux来讲的话,倒也未必。我们在操作系统内核之上安装这样的操作界面程序,主要的目的是为了让用户可以方便的去使用存放于磁盘上的各类其他应用程序,如:游戏,播放器等。那么问题就来了,是不是其他的应用程序必须要通过这种方式才能 启动呢?答案显然是否定的,在任何一款操作系统中,启动应用程序的方式通常由两种:即手动启动和自动运行。


  所谓手动启动就是在操作系统完全启动,用户登录之后通过鼠标点击或命令执行的方式来启动应用程序的方式。一般通过这种方式启动的进程是必须要提供用户访问界面的,用户登录的身份验证过程就是打开访问界面的必要步骤,也只有在成功打开访问界面以后,才可以实现手动启动程序的操作。用户对计算机的任何操作,其实归根到底都是通过进程来代理的。换句话说,每次操作的执行主体并不是用户本身,而是那些被运行起来的进程。到底该运行那个程序,将那个程序启动为进程,是由用户来决定的;但是一旦进程启动以后,进程到底做了些什么,完成了哪些操作,得到了哪些结果,进行和何种形式的输出等问题统统由进程自己决定,其后将不需要用户的参与。当然如果用户想要强行中止进程的执行还是可以的,但是也仅限于此而已。


  所谓自动运行就是让操作系统在启动的过程中启动某些应自动用程序的方式,如果某台服务器上的所有关键应用都是通过这种方式启动的,这是没有必要再提供用户访问界面的。所以我们可能会发现,很多机房里面有很多的机柜,机柜里面一排排的服务器主机,可是看不到显示器和键盘等这些IO设备,这是什么原因呢?很简单,因为这些只要开机,所有的关键部署都会自动运行的,更重要的是,这些服务器都是支持远程连接的。将一个程序设置为在操作系统启动后能够自动运行为一个进程之后,我们就无需人为介入了,而这个服务器也可以照样自动运行。所以访问界面对于交互式的应用场景是必要的。但对于服务器来讲,访问界面未必是必需的。除了刚开始安装配置的环节之外,一旦所有的部署都配置好了,只要机房不断电,一个Linux服务器连续运行数年也是很正常的。甚至于从装机开始,我们把所有的安装过程、配置文件都基于我们的运维工具来自动实现的话,服务器装完操作系统,配好IP地址,所有该做的配置通过运维工具自动就推送过去了,或者让服务器自动拉过去,所有的过程,我们从头到尾不需要参与一下,这就是运维人的最高境界——自动化运维。


本文出自 “运维者的家” 博客,请务必保留此出处http://zhaotianyu.blog.51cto.com/132212/1775155

操作系统基础

标签:操作系统   内核   api   系统调用   abi   

原文地址:http://zhaotianyu.blog.51cto.com/132212/1775155

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!