应用程序与硬件的关系
从上一章讲起,无论是shell程序还是hello程序都没有直接访问键盘,显示器,磁盘这些硬件设备,真正操纵硬件的是操作系统,它是应用程序和硬件的中间层,所有的应用程序对硬件的操作必须通过操作系统来完成,如下图所示,这样设计有两个主要目的: 1.防止硬件被失控的应用程序滥用; 2.操作系统提供统一的机制来控制这些复杂的底层硬件。
操作系统
为了实现上述功能,操作系统引入了几个抽象的概念: 就是说, 1.文件是对IO设备的抽象 2.虚拟内存是对内存和磁盘IO的抽象 3. 进程是对处理器,内存以及IO设备的抽象。
对处理器,内存以及IO设备的抽象–进程
假设实例场景中只有两个并发的进程,shell进程和hello进程,一开始只有shell进程运行,她在等待命令行的输入,当我们通过shell进程加载hello进程时,shell进程通过系统调用来执行我们的请求,系统调用会将控制权从shell进程传递给操作系统,操作系统保存shell进程的上下文,然后创建一个新的hello进程及其上下文,然后将控制权转交给新的hello进程,hello进程执行完之后,操作系统就会恢复shell进程的上下文,并将控制权交给shell进程,之后shell进程继续等待下一个命令行的输入。如下图所示: 上面表达中的上下文是:操作系统会跟踪进程运行中所需要的所有状态信息,这种状态叫做上下文,例如当前PC和寄存器的值,以及内存中的内容等等。 现代操作系统中,一个进程实际上由多个线程组成,每一个线程都运行在进程的上下文中,共享代码和数据。由于网络服务器对并行处理的需求,线程成为越来越重重要的编程模型,下面有个示意图:
对内存和磁盘IO的抽象–虚拟内存
他为每一个进程提供了一个假象,就是每个进程都在独自占用整个内存空间,每一个进程看到的内存都是一样的,我们称之为 虚拟地址空间。 下面这一张图,就是linux系统中进程的虚拟地址空间: 从下往上看,地址是增大的。最下面是0地址,从下往上看的话, 1.第一个区域是用来存放程序的代码和数据的,这个区域的内容是从可执行目标文件中加载而来的;例如hello程序。对所有的进程来讲,代码都是从固定的地址开始。 2.第二个是读写区域,在C语言中,全局变量就是存放在这个区域。 3.第三个区域是堆,程序中malloc所申请的内存空间就在这个堆中,程序的代码和数据区在程序一开始的时候就被指定了大小,但是堆可以在运行时动态的扩展和收缩。 4.第四个区域是共享库的存放区域,这个区域主要存放像C语言的标准库和数学库这种共享库的代码和数据;例如hello程序中的printf()函数。 5.第5个区域叫做用户栈,函数调用的本质就是压栈,每一次当程序进行函数调用的时候,栈就会增长,函数执行完毕返回时,栈就会收缩,注意:栈的增长方向是从高地址到低地址。 6.第6个区域也就是最后一个区域,是为内核保留的区域。应用程序代码不能读写这个区域的数据,也不能直接调用内核中定义的函数,也就是说这个区域对应用程序是不可见的。
对IO设备的抽象–文件
linux系统的哲学思想是:一切皆为文件。 所有的IO设备,包括键盘,磁盘,显示器,甚至网络,这些都可以看成文件。系统中所有的输入和输出都可以通过读写文件来完成。例如,当程序员需要处理读写磁盘上的文件时,他们不需要了解具体的磁盘技术,同一个程序,可以在不同的磁盘技术上的不同的系统上运行。
|