Saltar a contenido

Computer Architecture

Hoy en día, la mayoría de las computadoras modernas están construidas sobre lo que se conoce como la Von Neumann Architecture, que fue desarrollada en 1945 por Von Neumann para permitir la creación de "General-Purpose Computers" como los describió Alan Turing en ese momento. Alan Turing, a su vez, basó sus ideas en el concepto de "Programmable Computer" de Charles Babbage a mediados del siglo XIX. Cabe señalar que todas estas personas eran matemáticos.

Esta arquitectura ejecuta machine code para realizar algoritmos específicos. Principalmente consta de los siguientes elementos:

  • Central Processing Unit (CPU)
  • Memory Unit
  • Input/Output Devices
    • Mass Storage Unit
    • Keyboard
    • Display

Además, la CPU en sí misma consta de tres componentes principales:

  • Control Unit (CU)
  • Arithmetic/Logic Unit (ALU)
  • Registers

Von Neumann Architecture

Aunque es muy antigua, esta arquitectura sigue siendo la base de la mayoría de las computadoras modernas, servidores e incluso smartphones.

Los assembly languages trabajan principalmente con la CPU y la memoria. Por esta razón, es crucial entender el diseño general de la arquitectura de computadoras, para que cuando comencemos a usar instrucciones en assembly para mover y procesar datos, sepamos hacia dónde van, de dónde vienen y qué tan rápido/costoso es cada instrucción.

Además, la explotación binaria básica y avanzada requiere un entendimiento adecuado de la arquitectura de computadoras. Con desbordamientos de pila (stack overflows) básicos, solo necesitamos estar al tanto del diseño general. Una vez que empezamos a usar ROP y exploits en Heap, nuestro entendimiento debe ser más profundo. Ahora echemos un vistazo más detallado a algunos componentes esenciales.


Memory

La memoria de una computadora es donde se encuentran los datos e instrucciones temporales de los programas que se están ejecutando. La memoria de una computadora también se conoce como Primary Memory. Es la ubicación principal que utiliza la CPU para recuperar y procesar datos. Lo hace con mucha frecuencia (miles de millones de veces por segundo), por lo que la memoria debe ser extremadamente rápida para almacenar y recuperar datos e instrucciones.

Hay dos tipos principales de memoria:

  1. Cache
  2. Random Access Memory (RAM)

Cache

La memoria cache generalmente se encuentra dentro de la propia CPU y, por lo tanto, es extremadamente rápida en comparación con la RAM, ya que funciona a la misma velocidad de reloj que la CPU. Sin embargo, es muy limitada en tamaño y muy sofisticada y costosa de fabricar debido a su proximidad al núcleo de la CPU.

Dado que la velocidad de reloj de la RAM suele ser mucho más lenta que la de los núcleos de la CPU, además de estar lejos de la CPU, si la CPU tuviera que esperar a la RAM para recuperar cada instrucción, efectivamente estaría funcionando a velocidades de reloj mucho más bajas. Este es el principal beneficio de la memoria cache. Permite a la CPU acceder a las próximas instrucciones y datos más rápido que recuperándolos de la RAM.

Por lo general, hay tres niveles de memoria cache, dependiendo de su proximidad al núcleo de la CPU:

Level Description
Level 1 Cache Por lo general en kilobytes, la memoria más rápida disponible, ubicada en cada núcleo de la CPU. (Solo los registers son más rápidos).
Level 2 Cache Por lo general en megabytes, extremadamente rápida (pero más lenta que L1), compartida entre todos los núcleos de la CPU.
Level 3 Cache Por lo general en megabytes (más grande que L2), más rápida que la RAM pero más lenta que L1/L2. (No todas las CPU utilizan L3).

RAM

La RAM es mucho más grande que la memoria cache, con tamaños que van desde gigabytes hasta terabytes. La RAM también se encuentra lejos de los núcleos de la CPU y es mucho más lenta que la memoria cache. Acceder a datos desde direcciones de RAM requiere muchas más instrucciones.

Por ejemplo, recuperar una instrucción desde los registers toma solo un ciclo de reloj, recuperarla desde la cache L1 toma unos pocos ciclos, mientras que recuperarla desde la RAM toma alrededor de 200 ciclos. Cuando esto se hace miles de millones de veces por segundo, marca una gran diferencia en la velocidad general de ejecución.

En el pasado, con direcciones de 32 bits, las direcciones de memoria estaban limitadas desde 0x00000000 hasta 0xffffffff. Esto significaba que el tamaño máximo posible de RAM era de 2^32 bytes, lo que equivale a solo 4 gigabytes, en cuyo punto se agotaban las direcciones únicas. Con direcciones de 64 bits, el rango ahora es hasta 0xffffffffffffffff, con un tamaño máximo teórico de RAM de 2^64 bytes, que es alrededor de 18.5 exabytes (18.5 millones de terabytes), por lo que no deberíamos quedarnos sin direcciones de memoria en el corto plazo.

Cuando se ejecuta un programa, todos sus datos e instrucciones se mueven desde la unidad de almacenamiento a la RAM para ser accedidos cuando los necesite la CPU. Esto ocurre porque acceder a ellos desde la unidad de almacenamiento es mucho más lento y aumentará los tiempos de procesamiento de datos. Cuando se cierra un programa, sus datos se eliminan o se marcan como disponibles para reutilización en la RAM.

Como podemos ver, la RAM se divide en cuatro principales segments:

Segment Description
Stack Tiene un diseño Last-in First-out (LIFO) y tiene un tamaño fijo. Los datos en él solo se pueden acceder en un orden específico mediante push y pop.
Heap Tiene un diseño jerárquico y, por lo tanto, es mucho más grande y versátil para almacenar datos, ya que los datos se pueden almacenar y recuperar en cualquier orden. Sin embargo, esto hace que el heap sea más lento que el Stack.
Data Tiene dos partes: Data, que se usa para mantener variables, y .bss, que se usa para mantener variables no asignadas (por ejemplo, memoria de buffer para asignación posterior).
Text Las principales instrucciones en assembly se cargan en este segmento para ser recuperadas y ejecutadas por la CPU.

Aunque esta segmentación se aplica a toda la RAM, cada aplicación recibe su Virtual Memory cuando se ejecuta. Esto significa que cada aplicación tendría sus propios stack, heap, data y text segments.


IO/Storage

Finalmente, tenemos los dispositivos de Input/Output, como el teclado, la pantalla o la unidad de almacenamiento a largo plazo, también conocida como Secondary Memory. El procesador puede acceder y controlar los dispositivos de IO utilizando Bus Interfaces, que actúan como 'autopistas' para transferir datos y direcciones, empleando cargas eléctricas para los datos binarios.

Cada Bus tiene una capacidad de bits (o cargas eléctricas) que puede transportar simultáneamente. Esto suele ser un múltiplo de 4-bits, llegando hasta 128-bits. Las Bus Interfaces también se utilizan generalmente para acceder a la memoria y otros componentes fuera de la CPU misma. Si observamos más de cerca una CPU o una motherboard, podemos ver las Bus Interfaces por todas partes:

bus

A diferencia de la memoria primaria, que es volátil y almacena datos e instrucciones temporales mientras los programas se están ejecutando, la unidad de almacenamiento guarda datos permanentes, como los archivos del sistema operativo o aplicaciones completas y sus datos.

La unidad de almacenamiento es la más lenta para acceder. Primero, porque están más alejadas de la CPU, acceder a ellas a través de Bus Interfaces como SATA o USB lleva mucho más tiempo para almacenar y recuperar los datos. También son más lentas en su diseño para permitir un mayor almacenamiento de datos. Mientras más datos haya que recorrer, más lentas serán.

Ha habido un cambio en los últimos años desde las unidades de almacenamiento magnético clásicas, como cintas o Hard Disk Drives (HDD), hacia Solid-State Drives (SSD). Esto se debe a que las SSD utilizan un diseño similar al de las RAM, empleando circuitos no volátiles que retienen los datos incluso sin electricidad. Esto hizo que las unidades de almacenamiento fueran mucho más rápidas para almacenar y recuperar datos. Sin embargo, dado que están alejadas de la CPU y conectadas a través de interfaces especiales, siguen siendo las unidades más lentas para acceder.


Speed

Como podemos ver en lo anterior, cuanto más alejado esté un componente del núcleo de la CPU, más lento será. Además, mientras más datos pueda almacenar, más lento será, ya que simplemente debe recorrer más información para recuperar los datos. La siguiente tabla resume cada componente, su tamaño y su velocidad:

Component Speed Size
Registers Fastest Bytes
L1 Cache Fastest, other than Registers Kilobytes
L2 Cache Very fast Megabytes
L3 Cache Fast, but slower than the above Megabytes
RAM Much slower than all of the above Gigabytes-Terabytes
Storage Slowest Terabytes and more

La velocidad aquí es relativa dependiendo de la velocidad de reloj de la CPU. Ahora que tenemos una idea general de la arquitectura del ordenador, discutiremos los Registers y la arquitectura de la CPU en la siguiente sección.