第一章 计算机软硬件基本结构
引言:
本章是对于计算机软硬件的基本了解和回顾,包括大致如下内容:
ü CPU与外部件的连接
ü 计算机软硬件层次结构
ü SMP与多核
ü 操作系统,虚拟空间,物理空间
ü 多信 PV原语的理解
ü 对于线程和进程的理解,和深入
1. 1 Hello,world引出的几个问题
1) 程序为什么要被编译器编译了之后才可以运行/p>
2) 编译器在把C语言程序转换成可以执行的机器码的过程中做了什么,怎么做的/p>
3) 最后编译出来的可执行文件里面是什么了机器码还有什么们怎么存放的,怎么组织的/p>
4) #include
5) 不同的编译器(Microsoft VC, GCC)和不同的硬件平台(x86, SPARC,MIPS, ARM),以及不同的操作系统(Windows,Linux, UNIX, Solar), 最终编译出来的结果一样吗什么/p>
6) Hello World 程序是怎么运行起来的操作系统是怎么装载它的它从哪儿开始执行,到哪儿结束ain函数之前发生了什么main函数结束以后又发生了什么/p>
7) 如果没有操作系统,Hello World可以运行吗果要在一台没有操作系统的机器上运行Hello World需要什么该怎么实现/p>
8) printf是怎么实现的为什么可以有不定数量的参数什么能够在终端上输出字符串/p>
9) Hello World程序在运行时,它在内存中是什么样子的/p>
1.2万变不离其宗——硬件
1.2.1计算机硬件主要组成:
中央处理器(CPU,CentralProcessing Unit)
内存(Memory)
I/O输入/输出(Input/Output)
1.2.2南桥北桥芯片(South Bridge/North Bridge)
随着CPU频率的提高,内存跟不上CPU速度,于是产生了内存频率一致的系统总线,为了协调CPU,内存,和高速图形设备设计了高速的北桥芯片,交换数据。设计了南桥芯片,处理低速设备。
北桥芯片(South Bridge)
北桥主要控制 CPU内存显卡等高速设备。也就是说CPU想和其他任何部分通信必须经过北桥。北桥芯片中通常集成的还有内存控制器等,用来控制与内存的通信。现在的主板上已经看不到北桥了,它的功能已经被集成到CPU当中了。
南桥芯片(North Bridege)
南桥芯片主要控制I/O输入输出流,如USB,PCI总线等低速设备。CPU要想访问外设必须经过南桥芯片。
FSB总线(Front Side Bus):
即前端总线,CPU和北桥之间的桥梁,CPU和北桥传递的所有数据必须经过FSB总线,可以这么说FSB总线的频率直接影响到CPU访问内存的速度。
PCI总线(Peripheral ComponentInterconnect):
PCI总线即外设部件互连标准,是一种高性能局部总线,其不受CPU限制,构成了CPU和外设之间的高速通道。比如现在的显卡一般都是用的PCI插槽,PCI总线传输速度快,能够很好地让显卡和CPU进行数据交换。
2. SMP与多核
为了提高CPU频率,于是想从另一个角度提高CPU的速度,便增加CPU的数量
对称多处理器(SymmetricMulti-Processor)
一个计算机上汇集了一组处理器(多CPU)。
各CPU之间共享内存子系统以及总线结构。虽然同时使用多个CPU,但是从管理的角度来看,它们的表现就像一台单机一样。
多核处理器(Multi-core processor)
一枚处理器中集成两个或多个完整的计算引擎(内核),此时处理器能支持系统总线上的多个处理器,由总线控制器提供所有总线控制信 和命令信 。
1.3站得高,望得远——软件体系
1.计算机软件体系结构采用层的结构
接口(Interface)
每个层次之间互相通信,必须有一个通信协议,将其称为接口。
接口上层:接口的使用者; 接口下层:接口的提供者
应用程序(Application)
开发工具和应用程序在该层结构中属于一个层次,因为他们共用一个接口—API
应用程序编程接口(Application Programming Interface)
是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。类似于头文件
运行库(Runtime Library)
使用操作系统提供的系统调用接口。是一个经过封装的程序模块,对外提供接口,只要知道接口参数就可以自由使用。我们把常用的功能都封装在一个个“库文件”中(通常以DLL形式提供),只需要在使用时调用的就可以。常见的运行库有 C/C++ 运行库、VB 运行库、Delphi 运行库、.NET运行库、Java 运行库等等
系通调用接口(System Call Interface)
运行库使用操作系统提供的系统调用接口,其实现往往以软中断的形式
操作系统内核(Operating System)
内核层对于硬件层来说是硬件接口的使用者,而硬件则是接口的定义者。
操作系统内核是指大多数操作系统的核心部分。它由操作系统中用于管理存储器、文件、外设和系统资源的那些部分组成。操作系统内核通常运行进程,并提供进程间的通信。
硬件规格(Hardware Specification)
各种硬件接口标准
1.4操作系统做什么
操作系统位于底层硬件与用户之间,是两者沟通的桥梁。用户可以通过操作系统的用户界面,输入命令。操作系统则对命令进行解释,驱动硬件设备,实现用户要求
1.4.1不要让CPU打盹
多道程序(Multiprigramming)
提高CPU的利用率。在多道程序环境下,多个程序共享计算机资源当某个程序等待I/O操作时,CPU可以执行其他程序,大大提高CPU的利用率。
分时系统(Time-Share System)
一种联机的多用户交互式的操作系统。一般采用时间片轮转的方式使一台计算机为多个终端服务。对每个用户能保证足够快的响应时间,并提供交互会话能力。
多任务系统(Multi-Tasking)
操作系统直接接管了所有硬件资源,所有应用程序以进程的方式运行在在比操作系统权限更低的级别,并且每个进程拥有自己独立的地址空间,(相互隔离),CPU由操作系统根据优先级统一分配。宏观上看,这些程序都正在执行;微观上看,这些程序所对应的诸进程正在交替地执行,因为任何一个时刻只能有一个进程在处理机(CPU)上执行的。
1.4.2 设备驱动
硬件驱动程序(DeviceDriver)
一种可以使计算机和设备通信的特殊程序。相当于硬件的接口,操作系统只有通过这个接口,才能控制硬件设备的工作,假如某设备的驱动程序未能正确安装,便不能正常工作
硬盘:
硬盘—>盘片—>面—>磁道—>扇区
1.5内存不够怎么办
操作系统的多任务性使得CPU在多个进程之间共享,从进程的角度来看,就是一个进程独占一个CPU,IO抽象模型也很好地实现了IO设备的共享,那么内存的分配与共享就交给虚拟存储器来管理了。
早期计算机中,程序是直接运行在物理内存上的,就是程序运行时访问的都是PA(物理地址),我们必须同时在内存中运行多个程序,那么有限的RAM空间如何分配呢/p>
直接分配的策略有如下问题:
1.地址空间不隔离,所有程序直接访问PA,内存空间没有隔离,恶意修改数据有可能造成安全问题。
2.内存使用率低下,程序之间换进换出(RAM和Disk之间),因为这里还没有分段和分页机制。
3.程序运行的地址不确定。
1.5.1隔离
首先引入虚拟地址的概念,把程序给出的地址看成是VA(虚拟地址),通过映射机制(MMU+paging)把VA翻译成PA,这样只需管理这个VA到PA的映射过程就可以保证程序之间的PA区域不会重叠。
每个进程都有自己独立的VA空间,每个进程只能访问自己的虚拟地址空间,这样做到进程隔离。
1.5.2分段(Segmentation)
程序所需要的内存空间大小的虚拟空间映射到每个物理地址空间中,一一映射,它能解决隔离问题,不同的物理地址空间,但是分段没有解决内存使用率低下的问题,分段对内存区域的映射是按照程序为单位的,如果内存不足,被换入换出到磁盘的都是整个程序,造成大量的磁盘访问,粒度较大。根据程序的局部性原理,一个程序运行时,在某个时间段内,它只是频繁使用到了一部分数据和指令,更小粒度的内存分割和映射方法—->分页机制。提高内存使用率。
1.5.3分页(Paging)
我们可以把进程的虚拟地址空间按页分割,一般一个page的大小为4KB或者8KB,把常用的数据和代码装载到RAM中,把不常用的代码和数据保存在Disk中,等到需要使用的时候通过page fault调入RAM,这时候如果没有空闲的页,那么会使用页面替换算法牺牲一个页来覆盖(LRU最近最少使用算法)。
通过页映射机制,可以保存程序数据,我们可以给每个页设置权限属性(一些标志位)来控制该页的访问权限(内核还是用户)还有RWX权限等。分页需要MMU(MemoryMangement Unit)硬件单元,该单元集成在CPU内部。
1.6线程(Thread)
1.6.1线程基础
1.定义:
线程(Thread),是CPU资源分配的最小单位,是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
进程(Process), 是CPU资源分配的基本单位。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
线程与进程的区别可以归纳为以下4点:
? 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
? 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
? 调度和切换:线程上下文切换比进程上下文切换要快得多。
? 在多线程OS中,进程不是一个可执行的实体。
2. 线程的访问权限
线程可以访问进程内存中的所有数据,如果知道其他线程的堆栈地址,甚至可以访问其他线程中的数据。
3. 线程调度与优先级
当线程数量小于处理器数量的时候,线程总是“并发”执行的。当单处理器对应多线程的时候,“并发”是一种模拟的状态。(操作系统会让这些多线程程序轮流执行),这样每个线程看起来就像是同时执行(有点类似于数学中的微元法
线程调度(ThreadSchedule)
在处理器上切换不同的线程的行为。线程通常至少包括三种状态:
? 运行(Running):线程正在执行;
? 就绪(Ready):可以立即运行,但是CPU已经被占用;
? 等待(Waiting):此时线程正在等待某一事件(通常是I/O同步)发生,无法执行
BUT,百度百科定义和这个有点差异,在JAVA中,
2. 五种进程调度算法的总结:
(1)时间片轮转调度算法(RR):给每个进程固定的执行时间,根据进程到达的先后顺序让进程在单位时间片内执行,执行完成后便调度下一个进程执行,时间片轮转调度不考虑进程等待时间和执行时间,属于抢占式调度。优点是兼顾长短作业;缺点是平均等待时间较长,上下文切换较费时。适用于分时系统。
(2)先来先服务调度算法(FCFS):根据进程到达的先后顺序执行进程,不考虑等待时间和执行时间,会产生饥饿现象。属于非抢占式调度,优点是公平,实现简单;缺点是不利于短作业。
(3)优先级调度算法(HPF):在进程等待队列中选择优先级最高的来执行。
(4)多级反馈队列调度算法:将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,先按优先级调度,优先级相同的,按时间片轮转。优点是兼顾长短作业,有较好的响应时间,可行性强,适用于各种作业环境。
(5)高响应比优先调度算法:根据“响应比=(进程执行时间+进程等待时间)/ 进程执行时间”这个公式得到的响应比来进行调度。高响应比优先算法在等待时间相同的情况下,作业执行的时间越短,响应比越高,满足段任务优先,同时响应比会随着等待时间增加而变大,优先级会提高,能够避免饥饿现象。优点是兼顾长短作业,缺点是计算响应比开销大,适用于批处理系统。
? 优先级调度的环境下,现成的优先级改变一般有三种方式
1. 用户指定优先级
2. 根据等待的频繁程度提高或者降低优先级
3. 长时间得不到执行从而提升优先级
4. 根据等待实践区分的两种线程
IO密集型线程(IO Bound Thread)
频繁等待 I/O,input 和output,在输入输出方面消耗大。IO密集型线程是针对于IO操作比CPU计算操作占用更多的CPU时间来说的。比如:数据库服务器,这个服务器中的线程的职责就是进行阻塞的IO操作。当线程因为IO阻塞而进入阻塞状态后,该线程的调度被操作系统内核立即停止,不再占用CPU时间片段,而其他IO线程能立即被操作系统内核调度,等IO阻塞操作完成后,原来阻塞状态的线程重新变成就绪状态,而可以被操作系统调度。所以,像数据库服务器中的IO密集型线程来说,线程的数量就应该适量的多点。
CPU密集型线程(CPU Bound Thread)
很少等待,CPU使用频率高,消耗大。CPU密集型线程的作用是进行无阻塞的逻辑运算的线程。比如:RPG游戏中的游戏逻辑服务器(也叫地图服务器)里面的主逻辑线程,这个主逻辑线程需要进行大量的无阻塞的逻辑处理。当被操作系统内核调度的时候,这个线程就在给定的CPU执行时间内进行无阻塞的操作。如果这个游戏逻辑服务器还创建更多的线程来进行逻辑操作的话,这样的行为是得不尝失的。因为,本进程的线程越多,而本进程获取的CPU执行时间又是固定的,导致了进程内每个线程的执行时间很短。所以,像游戏逻辑服务器中的线程应该属于CPU密集型线程。所以线程不宜过多。
4.可抢占线程与不可抢占线程
线程在用尽时间片后,强制剥夺继续执行的权利,进入就绪状态,这个过程叫做抢占(Preemption)
线程的调度有抢占式或者非抢占的模式。
详情:https://www.cnblogs.com/chllovegeyuting/archive/2012/10/13/2722251.html
5. Linux的多线程
Linux内核中并不存在真正意义上的线程概念,Linux将所有的提醒实体(无论是线程还是进程)都成为任务(Task),每一个任务概念上都类似于一个单线程的进程——具有内存空间,执行实体,文件资源等。
在Linux下,用以下方法可以创建一个新的任务。
1.6.2线程安全
多线程程序,可访问的全局变量和堆栈,随时都可能被其他的线程改变,因此,多线程程序在并发的时候,一致性非常重要。
1. 竞争与原子操作
原子(Atomic)操作 不会被线程调度机制打断的操作
2. 同步与锁
同步(Synchronization)
一个线程访问数据未结束时,其他线程不得对同一个数据进行访问。则对数据的访问便原子化了(不可被打断)。
锁(Lock)
同步的最常见的方法。锁是一种非强制的机制,每一个线程在访问资源之前先获取(Acquire)锁,在结束数据访问之后释放(Release)锁。在锁被占用的时候,获取锁,线程会等待,直到锁被释放。
线程同步的几种方式:
二元信 量binary Semaphore
|
信 量 Semaphore |
互斥量 Mutex
|
临界区 Critical Section
|
条件变量 Condition Variable |
读写锁 Read-Write Lock |
两种状态: 占用/非占用 适合只能被唯一一个线程独占访问的资源,当而源信 量被占用时,其信 量被置为占用状态,其他的试图获取的线程进入等待状态,直至被释放 |
PV原语 P(进入) 信 量 S – 1 S–1>=0继续执行 S-1
V(释放) 信 量S +1 S+1>0,继续执行 S+1 |
仅同时允许一个线程访问,谁获取互斥量,谁就要释放这个锁。 但二元信 量,在整个系统可以被任意线程获取并释放。 |
获取锁:进入临界区 释放锁:离开临界区 作用范围仅限于本进程,其他进程无法获取该锁。 |
使用条件变量,可以让许多线程一起等待某个时间的发生,当事件发生时候(条件变量被唤醒),所有线程可以一起恢复执行 |
一次只有一个线程可以占有写模式的读写锁。 当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进行加锁, 它必须直到所有的线程释放锁. |
3. 可重入与线程安全
可重入(Reentrant) 表示一个函数没有执行完成。,一个可重入函数在多线程环境下可以放心使用。
一个函数要被重入一般有两种情况:
? 多个线程同时执行
? 函数自身调用自身(递归)
一般具有以下特点:
(1) 不使用任何(局部)静态或全局的const常量;
(2) 不返回任何(局部)静态或全局的const指针;
(3) 仅依赖于调用方提供的函数;
(4) 不以来任何单个资源的锁(MUTEX等)
(5) 不调用任何不可重入的函数
4.过度优化
1.6.3多线程内部情况
1.线程的实现曾有3种模型:
? 多对一(M:1)的用户级线程模型
? 一对一(1:1)的内核级线程模型
? 多对多(M:N)的两级线程模型
上面的x对y(x:y)即x个用户线程对应y个内核调度实体(KernelScheduling Entity,这个是内核分配CPU的对象单位)。
多对一用户线级程模型
多对一线程模型中,线程的创建、调度、同步的所有细节全部由进程的用户空间线程库来处理。用户态线程的很多操作对内核来说都是透明的,因为不需要内核来接管,这意味不需要内核态和用户态频繁切换。线程的创建、调度、同步处理速度非常快。当然线程的一些其他操作还是要经过内核,如IO读写。这样导致了一个问题:当多线程并发执行时,如果其中一个线程执行IO操作时,内核接管这个操作,如果IO阻塞,用户态的其他线程都会被阻塞,因为这些线程都对应同一个内核调度实体。在多处理器机器上,内核不知道用户态有这些线程,无法把它们调度到其他处理器,也无法通过优先级来调度。这对线程的使用是没有意义的!
一对一内核极线程模型
一对一模型中,每个用户线程都对应各自的内核调度实体。内核会对每个线程进行调度,可以调度到其他处理器上面。当然由内核来调度的结果就是:线程的每次操作会在用户态和内核态切换。另外,内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响。但该模型的实用性还是高于多对一的线程模型。
多对多两极线程模型
多对多模型中,结合了1:1和M:1的优点,避免了它们的缺点。每个线程可以拥有多个调度实体,也可以多个线程对应一个调度实体。听起来好像非常完美,但线程的调度需要由内核态和用户态一起来实现。可想而知,多个对象操作一个东西时,肯定要一些其他的同步机制。用户态和内核态的分工合作导致实现该模型非常复杂。NPTL曾经也想使用该模型,但它太复杂,要对内核进行大范围改动,所以还是采用了一对一的模型。
拓展知识:
(在学习过程中附加)
IOS七层模型
单片内核
此次学习要求:
1. 了解专业名词,以及其英文全称
2. 在学习之前简历思维导图,清楚整个层次体系
3. 增加拓展内容
4. 在每一章节之前写引言,包括本章节牵扯到的知识
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!