第一章——计算机系统初始

第1章—计算机系统概述

基本介绍

基本组成

计算机从顶层角度来看,由处理器、存储器、输入输出设备组成,这些设备之间通过某种关系互联起来,分享数据,协同解决计算任务,实现许多功能,其主要包含四个组成构件:

  • 处理器

执行计算任务 ,执行数据处理功能,当只有一个处理器时,他被叫做中央处理器(CPU)处理器和存储器交换数据,在处理器中有两个与内存相关的寄存器,一个是存储地址寄存器(Memory Address Register, MAR)其确定下一次读写的存储器地址,另一个存放从存储器中读取过来或者将要被写入存储器中的数据叫存储器缓冲寄存器(Memory Buffer Register,MBR),与之同理的是,输入/输出地址寄存器(IO Address Register,IO AR)确定一个特定的输入输出设备,而输入/输出缓冲寄存器(IO Buffer Register,IO BR)确定cpu与这个特定的输入输出设备之间要交换的数据,寄存器有着比内存更快的访问速率,有些数据可以存放再寄存器中,但是寄存器的容量也比较小,没有内存那么大

  • 存储器

存储器存储程序运行时的数据,数据是易失性的,这就意味着断电之后,存储器中存储的数据会丢失,别名内存,又叫主存储器或者实存储器,而磁盘中存储的数据是计算机关机后不会丢失的,内存模块由一系列单元组成,由顺序编号的地址定义,每个单元存放着一个二进制数据,可以解释为指令或者数据

  • I/O设备

I/O设备包括存储数据的硬盘,输入文字的键盘、鼠标、显示信息的屏幕,都是IO设备,IO设备也有内存缓冲区,存储着输入输出的设备,直到断电或者被发送出去为止

  • 总线

总线将存储器、处理器、IO设备连接起来,使它们能交换数据,进行通信

在这里插入图片描述


处理器寄存器

  • 用户可见寄存器

优先使用此寄存器,可以减少使用机器语言或汇编语言的程序员对内存的访问次数,高级语言的优化编译器会决定哪些变量适合放在寄存器中,哪些变量又放在内存中,用户寄存器一般是对所有程序都可用的,通常可以分为数据寄存器、地址寄存器、条件码寄存器

  • 数据寄存器:这类寄存器用于存放函数调用时产生的数据,可以被程序员分配给各种函数,一般是通用的,可以被执行任何数据操作的机器指令引用,但是也有限制,比如对浮点数的运算就是使用专用的寄存器.
  • 地址寄存器:存放数据和指令的内存地址,或者用于存放计算完整地址的有效地址或部分地址,这也是一个通用的寄存器,可以用来以某种特定的方式寻址
  • 变址寄存器: 一种最常用的寻址方式,通过给一个基值加上偏移量从而定位到相应的为止,即(base_address + offset)
  • 段指针:分段寻址方式,存储器划分成段,每段由长度不等的字块组成,每一段包含特定的短号和段内偏移量,这种方式需要用一个寄存器保存段的基地址,可能存在多个这样的寄存器
  • 栈指针:如果对用户可见的栈进行寻址,应该有一个专门的寄存器指向栈顶,这样就可以使用不包含地址域的指令,如入栈和出栈
  • 控制和状态寄存器

控制和状态寄存器并不是所有应用程序都可以使用,是用于控制处理器的操作,主要被具有特权的操作系统例程使用,其对用户是不可见的,一部分可以在内核态下被某些机器指令所访问

  • 程序计数器: 即将取指令的地址
  • 指令寄存器: 近期取的指令内容

处此之外,所有的处理器还应该包含一个或一组寄存器,通常称为程序状态字(Program Status Word,PWS),其包含状态信息,通常包含条件码和其它状态信息如中断允许/禁止位,内核态/用户态位

条件码是处理器位硬件操作结果设计的位

中断寄存器:在处理器中,通常有一组中断寄存器,每个指向一个中断处理例程

同时寄存器还可以用于IO操作的控制

设计者必须决策在昂贵、高速的寄存器中存放多少控制信息、在相对偏移、低速的内存中存放多少控制信息

指令的执行

程序由存储在内存中的一组指令组成,指令处理包含两个步骤,取指令和执行指令,执行指令可能包含多个小步骤,这取决于指令自身,一个单一的指令需要的处理被称为指令周期,用两个步骤表示即取指令和执行指令,仅当发生某些意外错误、关机、或者执行到停机的指令时,程序才会停止

在这里插入图片描述

程序计数器程序计数器里面保存着下一次需要执行的指令地址,无特殊情况,程序计数器在每次取指令后往后递增一,但是有些指令也可能会导致程序计数器跳转到一个特定的地址位置

指令被取出来放到指令寄存器中,指令的组成包含操作数和地址,处理器解析指令,并执行相应操作,大体上操作可以分为:

在这里插入图片描述

  • 处理器—存储器:数据从存储器到处理器或者从处理器到存储器

  • 处理器—IO设备:处理器和IO设备之间的数据传输,数据可以从IO设备到处理器,也可以从处理器到IO设备

  • 数据处理:处理器执行与数据有关的算术操作和逻辑操作

  • 控制:改变程序的执行顺序,比如可以控制程序计数器的指令跳转到特定位置

有这么一个执行例子:假如需要将地址位940的数据和地址为941的数据相加,存放到941中去,其执行流程为,设PC为程序计数器、AC为累加数据存储器,IR为指令寄存器

  1. PC中的地址位为300,指令内容送入IR中去,然后PC中地址往下移变成301,处理器解析内容,明白了指令的意思,将地址位940的数据0010放入累加数据存储器中去
  2. PC中的地址位为301,指令送入IR中,PC下移,为302,处理器解析指令寄存器中的指令,发现是从地址位941取出数据0001与AC中的数据相加放入再次放入AC中,即0011
  3. PC中的地址位为302,指令送入IR中,PC下移,处理器解析指,将AC中的数据存放到地址位941中,即3被存放到了原来0001的位置中,达到了执行的目的

本次操作一共用去三个指令周期,如果使用更复杂功能的指令集,可能会用到更少的指令周期,大多数现代处理器都具包含多个地址的指令,因此指令执行可能包含多个地址的访问

与内存的交互一样,处理器与IO设备也具有同样的IO操作,甚至有些时候,IO可以直接与内存进行数据交换,以减少处理器的负担,这一个过程叫做(Direct Memory Access,DMA)

在这里插入图片描述

中断

中断是一种提高处理器效率的方法,在一次指令执行过程中,比如向一台打印机写入数据时,每次写入都需要等待打印机接受完毕之后再进行处理,这一时间区段处理器处于空闲时期,由于处理器的处理速度十分快,在这一空闲时期,可能处理器又可以执行上千条指令,为了利用这一部分空挡时间,引入中断是十分必要的,中断有如下分类

类别说明
程序中断在某些条件下由指令执行的结果产生,如算术溢出、除数为0、试图执行一条非法指令以及访问到用户不允许的存储器位置
时钟中断由处理器内部的计时器产生,允许操作系统以一定的规律执行函数
I/O中断由I/O控制器产生,用于发信号通知一个操作的正常完成和各种错误条件
硬件故障中断由注入掉电或者存储器奇偶错误之类的故障产生

这里有一个示例:比如1GHz CPU的CPU,每秒大约执行10的9次方条指令,硬盘喂7200转/分,大约旋转半周的时间为4ms,处理器比这快400万倍,说白了,中断就是假如你在烧水,烧水需要10分钟,如果没有中断,就相当于这十分钟的时间内你只能等待水烧开,而不能去做其它的事,但是有了中断,就相当于你可以在这十分钟之内做一些其它的事,比如洗碗,拖地,刷抖音…

当外部设备做好服务的准备,即当水烧开了时,外部设备会向处理器发送一个中断请求信号,处理器此时做出相应,暂停当前程序的处理,转去先处理其它事情,处理完毕之后,再回来处理手头上的事

从用户程序的角度看,中断打断了程序的正常执行的序列,处理器和操作系统负责挂起当前程序,在中断执行完毕后,在同一个地方恢复执行

在这里插入图片描述

为适应中断,指令周期增加了中断阶段,虽然会增加指令读取的数量,但是在IO设备让处理器等待的时间相比,这些指令的执行完全可以忽略不计

在这里插入图片描述

中断处理

在这里插入图片描述

多个中断的处理方式

1、当一个中断正在执行时,进制再发生其它中断,如果有其它中断进来,将其挂起,放入一个中断等候队列中,按照顺序执行中断,这样做的缺点是没有考虑不同中断事件的相对优先级和时间限制等要求
在这里插入图片描述

2、按照优先级,将不同的中断划定不同的优先级,当多个中断触发时,按照优先级执行中断,比如当前正在执行一个中断,突然来了一个优先级比较高的中断,那么处理器会打断当前中断的执行,将其PSW和PC以及执行数据压入栈中,开始执行优先级高的中断

在这里插入图片描述

即使使用了中断,处理器的利用效率仍然有可能未得到有效的利用,所以引入多道编程的方法,即多个程序一同执行,不同的程序之间有不同的优先级,使没有发生中断的程序或者优先级高的程序先执行,然后处理器在这之间进行切换

存储器的层次结构

存储器的设计目标包含三个问题:多大的容量?多快的速度?多贵的价格?

多大的容量这个问题是永无止境的,理论上来说是越大越好,因为越大容量的存储器,就可以开发越大容量的应用

多快的速度这个问题是为了适配处理器的速度,让它跟得上处理器的速度,达到最佳的性能

多贵的价格,这个不言而喻,往往存储器的选择会在这三者之中折中处理,这三个问题之间存在着一定的关系

  • 存取时间越快,每一个“位”的价格越高
  • 容量越大,每一个“位”的价格越低
  • 容量越大,存取速度越慢

为了解决设计面临的这些矛盾,所以引入存储器层次结构,不依赖单一的存储组件和技术,从上往下看

  • 每一个位的价格递减
  • 容量递增
  • 存取时间底层
  • 处理器访问存储器的频率递减

在这里插入图片描述

第四个条件有效的情况是局部性原理,在一个程序中,也许会包含多个循环语句,一旦某个程序进入循环,他就会重复访问一个小范围的结点集合,什么是访问局部性原理?即一个变量可能在较短的时间内被处理器访问多次,就比如下面的循环,变量i需要做两千万次累加,每一次的累加都需要涉及到取i、累加1、放i,这样的变量就适合存放到高速缓存中,假如处理器从高速缓存访问i一次的时间为0.1us,而从内存中访问i的时间为1us,那么整个循环下来,存放在高速缓存中只需要2s,而存放在内存中需要**20s!**这是多么明显的性能提升

int i = 1;
while(i < 20000000){
    i++;
}

对于寄存器、高速缓存、主存储器这些存储器一般来说是易失性的,断电后数据会擦除掉,又叫一级存储器,而对于磁盘这些才是真正用于保存数据的,叫二级存储器或者辅助存储器,硬盘也可以作为内存的扩展,即虚拟存储器,在软件中还可以额外增加存储层次,比如一部分内存作为缓冲区,用于临时保存从磁盘中读取的数据,留作备用,这种技术有时称为高速缓存,可以通过两种方法提高性能

  • 磁盘成簇写,采用次数少、数据量大的传输方式,而不是次数大、数据量小的传输方式,选择整批数据传输一次提高性能
  • 一些注定要 ”写出“ 的数据也许会在下一次存储到磁盘之前被程序访问到,在此情况下,数据能够迅速的从软件设置的磁盘高速换缓存中取出,而不是缓慢的从磁盘中取出

高速缓存

在全部指令周期中,处理器至少访问一次存储器,而且通常还要多次访问存储器用于取操作数或保存结果。处理器执行指令的速度显然受存储周的限制,长期以来,处理器和内存的速度不匹配,这个限制已经成为很严重的问题,而且处理器速度提高的速度一直快于存储器提高的速度,而采用先进的半导体技术构造内存又会造成太大的成本消耗,所以根据局部性原理,在处理器和内存之间构造一块容量小而访问速度快的存储器,即高速缓存

原理

高速缓存即用半导体技术复刻的一块容量大于寄存器,但是远小于内存,访问速度快,但是又比较便宜的存储器,里面复刻了内存中的某一部分数据,当处理器需要取数据时,首先会在高速缓存中寻找,如果高速缓存中有这个数据,则从这里取,如果没有的话,则会从内存中将这个数据读取到高速缓存中,再从高速缓存中读取使用

在这里插入图片描述

执行流程大致为:

在这里插入图片描述

高速缓存的设计

高速缓存的设计问题可以分为四个部分:高速缓存块大小、映射函数、替换算法、写策略

合适的块大小、是设计高速缓存的要求之一,其它的可以暂时了解,但是写策略还是得知道一下这玩意到底是个什么东西,写策略指的是,在程序运行的过程中,被读取到高速缓存中的存储槽中的内存块的某些变量,它可能会被处理器进行其它的操作,比如加1等等,这就导致了内存中对应数据与高速缓存中对应数据不一致的情况,所以高速缓存某个块被修改时,需要保证它被换出高速缓存之前把它写回内存,写策略即规定何时触发写操作。极端的情况下是每个块被更新时,就将所有的块写一遍,而另外一种情况时在块被更换时才进行写操作,后一种策略减少了存储器读写操作的次数,但是使内存处于一种过时的状态,这回妨碍多处理器操作以及IO模块直接的内存读取,所以一个优秀的写策略是设计一个好的高速缓存的重要组成部分!(这也就可以进一步去理解线程安全问题,以及实现线程安全的底层策略)

I/O通信技术

IO通信可能有三种技术:可编程IO、中断驱动IO、直接内存存取(DMA),其优异性能依次为:DMA > 中断驱动IO > 可编程IO

可编程I/O

在这里插入图片描述

当处理器正在执行一个程序并遇到一个I/O操作相关的指令时,它给相应I/O模块发送指令,相应I/O模块响应,执行动作,并设置IO相应寄存器的位,此过程不会进一步通知处理器,尤其是它不中断处理器,所以处理器在执行I/O操作指令后,需要定期检查I/O模块的状态,以确定I/O操作是否完成,这是一个十分耗时的处理

中断驱动I/O

在这里插入图片描述

中断驱动I/O指的是在处理器向I/O模块发送命令之后,I/O模块收到命令触发中断信号,相当于告诉处理器,我知道啦,你先去忙别的吧,我处理好了叫你,处理器就说,OKK我记录一下你的进度,就去忙别的去了

直接内存存取(DMA)

尽管中断处理I/O比简单的可编程I/O更有效,但是还是存在某些问题,任何的数据操作都需要经过处理器,这就导致了I/O出现的固有缺陷

  • I/O传送速度受限于处理器测试设备和提供服务的速度
  • 处理器忙于管理I/O传送的工作,必须执行很多指令以完成I/O传送

当需要传送大量的数据时,需要提供一种更有效的技术:直接内存存取(Direct Memory Access,DMA),DMA由系统总线中一个独立的模块完成,当处理器要读写一块数据时,处理器给DMA发送指令,包含以下信息:

  • 是否请求依次读或写
  • 涉及的I/O设备的地址
  • 开始读或写的存储器单元
  • 需要读写的字数

之后处理器继续干其它事了,将这件事情委托给DMA办了,发送命令后,DMA触发一个中断信号给处理器,只有在开始和结束这两个过程中处理器才会参与

处理器: 老弟,帮哥我个忙,从xxxIO设备搬几个字到内存那,那哥们因为少了几个数据,快急疯了,注意不要怎样怎样…

DMA:好嘞哥,你就放心交给我吧

处理器:那就交给你了,我去干别的事了

DMA:OKK

DMA:哥,事情办好了

处理器:强啊,老弟,辛苦了

DMA模块需要控制总线以便与存储器进行数据传送,由于在总线使用中存在竞争,当处理器需要使用总线时要等待DMA模块,但是这并不是一个中断,处理器没有保存上下文环境去做其他事情,但是由于占用了一部分总线资源,还是会对处理器的处理速度造成一定的影响

扩展

过程控制

控制过程调用和返回的最常用的技术就是使用栈

栈的实现,栈是一种先进先出的数据结构,是线性表的一种,可以在内存中申请一块连续的内存空间,在这个内存空间实现栈的操作需要三个地址

  • 栈指针:指向栈顶元素,往栈中压入元素,栈指针加一,元素出栈,栈指针减一
  • 栈底:指向栈底元素,即栈的开始边界
  • 栈界限:栈的上界,即栈最多能容纳的元素

传统上,以及现在大多数机器中,栈底是保留的高端地址,而栈边是保留的低端地址

在这里插入图片描述

过程调用和返回

在过程调用中,最灵活的方法就是使用栈,当处理器执行一次调用时,不仅在栈中保存返回地址,而且保存被调用过程的参数,被调用过程从栈中访问参数,在返回前,返回参数也可以放在战中返回地址的下面。为一次过程调用保存的整个参数集合称为栈帧(stack frame),比如过程P中有局部变量x1,x2而在P的执行过程中又执行了过程Q,Q中有局部变量y1,y2,每一个过程的执行会重新创建一个栈帧,栈帧里面应当包含(执行时需要的参数、返回地址、返回参数类型、前一帧指针)

在这里插入图片描述

可重入过程

可重入过程指的是代码的一个副本可在同一段时间内可以被多个用户共享使用,可重入过程有两个特征

  • 程序代码不能修改其自身,每个用户的局部数据必须单独保存
  • 一个可重入过程可以被中断,由一个正在中断的程序调用,在返回过程仍然能被正确执行
LRU算法

LRU全称为(Least-Recently-Used)又叫缓存淘汰算法,是高速缓存替换算法的实现思路之一,当高速缓存中左右槽都被占满时,最新的被使用的块会被放到链表的最后,而最近没有被使用的块会被逐渐移动到链表的头部,当槽被占满时,每次添加新的结点会将头节点删除,而将新的结点添加到尾部

//数据组织
#include<iostream>
using namespace std;

template<class T>
struct Node{
    int chunk_name;  //块名称
    T value;  //块中存储的值或者指令
    Node<T> *next; //下一个结点

    Node(int chunk_name,T value):chunk_name(chunk_name),value(value){
        next = nullptr;
    }
};

高速缓存类

template<typename T>
class LruCache{
public:
    //初始化高速缓存槽
    LruCache(int pool_size){
        if(pool_size <= 0){
            throw std::runtime_error("the capacity of cache must > 0!");
        }
        this->pool_size = pool_size;
        chunk_count = 0;
        head = nullptr;
    }
    //添加块
    void addChunk(int chunk_name,T value){
        if(chunk_count < pool_size){
            //说明槽还没有被填满,直接在尾部添加结点
            addNode(chunk_name,value);
            chunk_count++;
        }else{
            auto p = head;
            head = head->next;
            delete p;
            addNode(chunk_name,value);
        }
    }

    //获取块的数据
    int getChunk(int chunk_name){
        //返回索引位置,并将其放入链表最后
        if(head == nullptr)
            return -1;
        else{
            //找到这个结点
            int idx = 1;
            auto p = head;
            if(p->chunk_name == chunk_name){
                head = head->next;
                addNode(p->chunk_name,p->value);
                delete p;
            }else{
                while(p != nullptr && p->next != nullptr && p->next->chunk_name != chunk_name){
                    p = p->next;
                }
                if(p!= nullptr && p->next != nullptr){
                    auto q = p->next;
                    auto k = q->next;
                    p->next = k;
                    if(k == nullptr){
                        p->next = new Node<T>(q->chunk_name,q->value);
                    }else{
                        while (k->next != nullptr){
                            k = k->next;
                            ++idx;
                        }
                        k->next = new Node<T>(q->chunk_name,q->value);
                    }
                    delete q;
                }else{
                    return -1;
                }
            }
            return idx;
        }
    }

    void printCache(){
        auto p = head;
        while(p != nullptr){
            std::cout<<p->chunk_name<<" ";
            p = p->next;
        }
        std::cout<<std::endl;
    }

private:
    void addNode(int chunk_name,T value){
        if(head == nullptr){
            head = new Node<T>(chunk_name,value);
        }else{
            auto p = head;
            while(p->next != nullptr){
                p = p->next;
            }
            p->next = new Node<T>(chunk_name,value);
        }
    }

private:
    int pool_size; //槽大小
    int chunk_count; //当前高速缓存存储块的数量
    Node<T> *head; //头节点
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值