1.2
cpu发展至今在制造工艺方面已经达到了物理极限,除非cpu制造工艺有新的突破,否则cpu的频率将会一直被目前4GHZ的“天花板所限制”。
在频率短期无法提高的情况下,人们想到了用cpu的数量来提高计算的计算速度。于是出现了多核计算机。
最常见的一种形式是 对称多处理器(Symmertrical Multi-Processing),理论上速度的提高与cpu的数量呈正比,但事实并非如此,因为我们呢的程序并不能总是能够分解呈螺杆完全不相干的子问题。
当然,在最常见的大型数据库、网络服务器上,他们要同时处理大量的请求,并且请求相互独立,这时候多处理器就能大显身手了。
多核处理器(Multi-core Processor),SMP的简化版,多核处理器之间共享比较昂贵的缓存,这样可以降低制造成本,多核和SMP在缓存共享等方面有细微差别,使得程序在优化上可以有针对性的进行
1.3
系统软件分两块
-平台性的,比如操作系统内核,驱动程序,运行库和系统工具
-用于程序开发的,比如编译器,汇编器,链接器等开发工具盒开发库
计算机软件体系结构
Applications: Development Tools:
Web Browserc/c++ Complier
Video Player Assembler
Word Processor Library Tools Debug Tools Development Libraries
.......... ..............
-------------------------Operating System API-------------------------------------------
Runtime Library
--------------------------------System call --------------------------------------------------
Operating System Kernel
------------------------------Hardware Specification------------------------------------
Hardware
-每个层次之间相互通信,要通信需协议---》接口
-接口的下面一层是接口的提供者,它定义接口,接口的上层是使用者,使用该接口实现所需功能
-每个中间层都是对他下面一层的包装和拓展,中间层的存在使得应用程序和硬件之间保持相对的独立
-开发工具与应用程序属于同一层,使用 API
-API的提供者是运行库,什么样的运行库提供什么样的API Linux-->Glibc 提供POSIX的API
-运行库使用操作系统提供的系统调用接口,系统调用接口以软件中断方式提供
-Linux使用 0x80中断作为系统调用接口
-Windows使用0x2E号中断作为系统调用接口
-硬件接口的定义决定了操作系统内核--->驱动程序如何操作硬件,如何与硬件通信,这种接口被称为硬件规格,操作系统和驱动程序开发者通过阅读硬件规格文档所规定的各种硬件编程接口来编写操作系统和驱动程序
1.4操作系统做什么
功能: --提供抽象的接口
--管理硬件资源
1.4.1 不要让cpu打盹
--多道程序(Multiprogramming):用一个监控程序,当某个程序无需使用cpu 监控程序将另外正在等待cpu的程序启动
--提高了cpu的利用率
--调度侧率太粗糙,不分轻重缓急
--分时系统(Time-Sharing System):每个程序运行一段时间后主动让出cpu给其他程序
-如果一个程序在进行一个很耗时的计算,一直霸占着cpu不放,操作系统也没办法,比如一个程序进入while(1)死循环,那么整个系统都将停止
--多任务(Multi-tasking)系统:
--OS接管所有硬件资源,本身运行在受硬件保护的级别。
--所有程序以进程的方式运行,级别比OS低,有独立的地址空间
--OS控制cpu,按进程优先级分配cpu,给一定的运行时间----抢占式:0S可以剥夺cpu资源给它认为最需要的进程
1.4.2 设备驱动
--设备驱动程序可以看成是操作系统的一部分,往往跟内核一起运行在特权级,与内核间有一定的独立性这使得
--驱动程序有比较好的灵活性,不同硬件对应不同的驱动程序所以
--设备驱动程序由设备生产厂商提供 原则是
-OS开发者为硬件生产商提供一系列接口和框架,按此原则开发的驱动程序都可以在OS上使用
--以一个文件的读取为例子:
知识:
--每个磁盘有多个盘片,每个盘片分两面,每面按照同心圆划分若干磁道,每个磁道分若干扇区
--由于不同的磁道长度不同,所拥有的扇区不同 所以
--使用LBA(Logical Block Address) 的方式,整个硬盘的所有扇区从0开始编号,一直到最后一个扇区,扇区的编号叫做逻辑扇区号
--当我们给出一个逻辑的扇区号时,硬盘将其转化成实际的盘面、磁道等位置
例子:在linux下有一个文件 /home/uscr/test.dat 长度为8000字节,创建它 的时候 ext3文件系统有可能这么做:
--前4096字节存储在1000--1007号扇区,8个扇区共4096字节
--后3904字节存储在2000--2007号扇区,8个扇区共4096字节,3904个有效字节
--文件系统保存了这些文件的存储结构,负责维护这些数据结构、保证磁盘中的扇区能有效组织利用
--Linux中,要读取这个文件的前4kb时 具体的步骤如下
--使用read的系统调用
--文件系统接到read请求,判断数据的具体位置(扇区号)
--文件系统向磁盘驱动发一个读取扇区号为1000开始的8个扇区的请求
--磁盘驱动程序收到请求,向硬盘发出硬件命令
(向硬件发送I/O的常见方式:读写I/O端口寄存器来实现,x86平台上,有65536个硬件端口寄存器,被分配到不同的I/O端口地址
cpu提供 "in" 'out" 指令来实现对硬件端口的读写')
--对IDE接口来说,有两通道:IDE0、IDE1,每个通道可连接两个设备(Master 和 Slave) 一个PC最多有4个IDE设备
--假设我们的文件位于IDE0.Master 硬盘上 ID0通道I/O端口地址为:0x1f0-0x1f7 0x376-0x377 通过这些端口地址能与IDE硬盘通信
--以读取1000号扇区开始的8个扇区为例:
--0x1f3-0x1f6 4个字节的端口地址用来写入LBA地址,1000---0x00 00 03 e8 ==>f3 f4 f5 f6
--0x1f2 地址用来写索要读取的扇区数 8
--0x1f7执行操作的命令码 读操作==>0x20
--所以我们要执行的指令为:
out 0x1f3,0x00
out 0x1f4,0x00
out 0x1f5,0x03
out 0x1f6.0xe8
out 0x1f2,0x08
out 0x1f7,0x20
--硬盘接收到这个命令以后,执行相应的操作,讲数据读取到实现设置好的内存地址(内存地址也是通过类似的命令方式设置)
1.5 内存不够怎么办(如何将计算机上有限的物理内存分配给多个程序使用)
--如果直接按照将要执行的程序在内存中找一块足够大的连续的内存地址空间执行的方法来管理内存的话
--地址空间不隔离
--内存使用率低
--程序运行的地址不确定性
--解决办法:增加中间层-->使用一种间接地地址访问方法
--把程序给出的地址看做 虚拟地址
--通过映射,讲虚拟地址转换成实际的物理地址
SO:只要控制好虚拟到实际的映射过程,就可以保证任意一个程序访问的物理内存与另外一个不重叠,达到地址空间的隔离效果
1.5.2 分段(Segmentation)
把一段与程序所需要的内存空间大小的虚拟空间映射到某个地址空间
--这样做:
解决了上面提到的问你的第一和第三个
--程序A和程序B被映射到凉快不同的物理空间区域,它们之间没有任何重叠
--对于每个程序来说,无论被分配到的物理地址是那一块区域,对于程序来说都是透明的,它们不需要关心物理地址的变化
--但是这样做并不能提到内存的利用率
分段对内存区域的映射还是按照程序为单位,如果内存不足,被换入换出到磁盘的都是整个程序
1.5.3 分页(Paging):利用程序的局部性原理
--分页的基本方法:--把地址空间人为的等分成固定大小的页(有硬件决定,或硬件支持多种分页大小而由操作系统来决定)
--我们将物理空间和虚拟空间按照 两者的页的大小相同的方法来划分(一般为4KB)
--虚拟地址到物理地址的转换:
---------------- --------------- ----------------------------
cpu -----Virtual Address---> MMU ------> Physical Address------> Physical Memory
---------------- --------------- ---------------------------
1.6 众人拾拆火焰高
1.6.1 线程基础
什么是线程
--线程(Thread),有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元
--标准的线程组成:线程ID 当前指令(pc) 寄存器集合和堆栈组成
--通常意义,一个进程由一个活多个线程组成,各个线程之间共享程序的内存(包括代码段、数据段、堆等)及一些进程级的资源(如打开文件和信号)
--使用多线程的原因如下:
--某个操作可能陷入等待,等待的线程进入睡眠状态无法执行。多线程执行可以有效利用等待实践。如等待网络的响应
--多CPU或多核计算机本身具备同时执行多个线程的能力,多线程能发挥计算机的全部计算能力
--相对于多进程应用,多线程在数据共享方面的效率更高
线程的访问权限
--线程的访问非常自由,可访问进程内存里的所有数据
--也拥有自己的私有存储空间
--栈,一般情况下认为是私有数据
--线程局部存储:某些操作系统为线程单独提供的私有空间
--寄存器(包括pc),是执行流的基本数据,为线程私有
线程的调度与优先级
--单处理器对应多线程的情况下,并发是一种模拟出来的状态,操作系统让这些多线程程序轮流执行、每次执行一小段时间,看起来就如同同时执行
--如此不断的切换进程就叫 线程调度(Thread Schedule) 衍生出三种线程状态:
--运行(Running):线程正在执行-->运行时间为 时间片(Time Slice),用完时进入就绪状态
--就绪(Ready):线程可以立刻运行 单cpu被占用
--等待(Waiting):线程正在等待某一事件(通常是I/O或同步)发生
--调度的基本原则:
--运行中的线程用 a.完时间片-->就绪状态 b.用完之前开始等待-->等待状态
--当一个线程离开运行状态,调度系统钻泽一个其他的 就绪线程 继续执行
--处于等待的线程的等待时间发生后-->就绪状态
--在Windows 和 Linux中,线程的优先级不仅可以用户手动设置,系统还会根据不同该线程的表现自动调整优先级
-总结优先级调度的环境下,线程的优先级改变的手段:
--用户指定优先级
--根据进入等待状态的频繁程度提升或降低优先级
--长时间得不到执行而被提升优先级
抢占线程和不可抢占线程
--抢占:线程在用尽时间片后被强制剥夺继续执行的权利 进入就绪状态,即之后执行的别的线程抢占当前线程
Linux的多线程
--Linux对多线程的支持频发,Linux内核中并不存在真正意义的线程
--Linux讲所有的执行实体(无论线程进程)都称为任务(Task)
--每个任务任务概念上都类似于一个单线程的进程
--共享了同一个内存空间的多个任务构成一个进程,任务成了进程里的线程
--linux中 一下方法创建一个新的任务
--fork 复制当前进程
--exec 使用新的可执行映像覆盖当前可执行映像
--clone 创建子进程并从指定位置开始执行
关于fork exec clone的更详细介绍--不理解
1.6.2 线程安全
竞争与原子操作
--多个线程同时访问一个共享数据可能造成很恶劣的后果
--我们把单指令的操作称为 原子的(Atomic),无论如何,单挑指令的执行是不可以被打断的
--为了避免出错,很多体系结构提供一些常用的原子操作指令
--在复杂的情况下,比如要保证一个复杂的数据结构更改后的原子性,原子操作就力不从心
同步与锁
---为了避免多个线程同时读写同一个数据而产生的不可预料的后果,需要将各个线程对同一个数据的访问同步(Synchronization)
--同步:一个线程访问一个数据未结束的时候,其他线程不能访问--->对数据的访问被原子化
--最常见方法:锁(Lock),锁是非强制机制,线程在访问数据或资源之前首先试图获取锁,访问结束后师释放锁。获取不到十时,线程会等待
--二元信号亮(binary Semapore):最简单的一种锁。占用和非占用两种状态。适合只能被唯一线程独占访问的资源
--