Excepciones

3. Excepciones (eventos generados por software)

Excepciones.

A) Excepciones detectadas por el procesador

Cuando la CPU detecta una condición anómala durante la ejecución una instrucción. Estos se dividen a su vez en tres grupos, dependiendo de el valor del registro EIP (EIP es un registro apunta a la siguiente instrucción que debe ejecutarse en el flujo de ejecución del programa) que se guarda en la pila del modo Kernel cuando la unidad de control de la CPU plantea la excepción.

  1. Fault ,Fallos:Generalmente se puede corregir; una vez corregido, el programa puede reiniciar sin pérdida de continuidad continuando en el valor guardado de EIP siempre que sea capaz de corregir la condición anómala que provocó interrupción.
  2. Trap, Trampas: Informado inmediatamente después de la ejecución de las instrucciones de captura. Después de que el kernel devuelve el control al programa, se le permite continuar su ejecución sin pérdida de continuidad en el valor guardado de EIP. El uso principal de las trampas es para fines de depuración. El papel de la señal de interrupción en este caso es notificar al depurador que se ha ejecutado una instrucción específica (por ejemplo, se ha alcanzado un punto de interrupción brakepoint dentro de un programa).

  3. Abort, Aborta : Se produjo un error grave; la unidad de control tiene problemas y puede ser que no se puede almacenar en el registro EIP. Los abortos se utilizan para informar errores graves, como fallas de hardware y valores no válidos o inconsistentes en las tablas del sistema. Este El controlador no tiene más remedio que forzar la finalización del proceso afectado.


B) Excepciones programadas

Ocurren a petición del programador, deberían se pensadas por el programador, son activados por int (interrupciones) o int3.

La instrucción int3 es una instrucción de interrupción de software que se utiliza comúnmente para propósitos de depuración en programas en lenguaje ensamblador y en entornos de desarrollo de software. En arquitecturas x86 y x86-64, la instrucción int3 provoca una excepción de interrupción de software que suele ser manejada por un depurador.  

Las excepciones programadas son manejadas por la unidad de control como trampas; A menudo se les llama interrupciones de software. Las excepciones tienen dos usos comunes:

  1. implementar llamadas al sistema
  2. notificar un depurador de un evento específico.

Nota del Docente:

El lenguaje Python, suele se mas usado para el desarrollo de aplicaciones en las que el error en el ingreso de datos puede generar una excepción. Esto no sucede en C++, desde nuestro enfoque en la materia, la misma es aprender C++ para facilitar la lectura de datos de sensores, datos registrados en archivos, en el ingreso de datos por parte del usuario para C++ asumimos que normalmente no hay errores de tipo ( se simula que se leen desde un sensor) por eso no vemos como trata C++ las excepciones en profundidad. Pero a modo de información, mostraremos este tema de manera superficial.

Una excepción es la indicación de un problema que ocurre durante la ejecución de un programa.

El manejo de excepciones está diseñado para procesar errores síncronos, que ocurren cuando se ejecuta una instrucción.

Ejemplos comunes de estos errores son :

  • los subíndices de arreglos fuera de rango

  • el desbordamiento aritmético (es decir, un valor fuera del rango de valores representables)

  • la división entre cero

  • los parámetros de función inválidos

  • la asignación fallida de memoria (debido a la falta de memoria).

El manejo de excepciones proporciona un mecanismo estándar para procesar los errores

Si los problemas potenciales ocurren con poca frecuencia, al entremezclar la lógica del programa y la lógica del manejo de errores se puede degradar el rendimiento del programa, ya que éste debe realizar pruebas (tal vez con frecuencia) para determinar si la tarea se ejecutó en forma correcta, y si se puede llevar a cabo la siguiente tarea, podemos decir que se ejecuta el código pero con recaudos que hacen que el rendimiento no sea el mejor.

El nombre “excepción” implica que el problema ocurre con poca frecuencia; si la “regla” es que una instrucción generalmente se ejecuta en forma correcta, entonces la “excepción a la regla” es cuando ocurre un problema.

Los programadores deben escribir programas tolerantes a fallas y robustos, que puedan tratar con los problemas que puedan surgir sin dejar de ejecutarse, o que terminen de una manera no dañina.

Se pueden definir una clase de excepción para representar el tipo del problema que podría ocurrir, en nuestro caso vamos a mostrar ejemplos de excepciones estándares y no estándares.

Gestión de la memoria (Stack y Heap) en C++

Cuando ejecutamos un programa, nuestro sistema operativo le asigna un proceso con algo de memoria, llamada la memoria virtual, donde ejecutarse. Esa memoria virtual se divide en segmentos o áreas:



  • El segmento de texto, conocido como segmento de código, contiene las instrucciones ejecutables y es solo de lectura: https://en.wikipedia.org/wiki/Code_segment.
  • El segmento de datos contiene la inicialización de las variables estáticas, globales y estáticas locales: https://en.wikipedia.org/wiki/Data_segment.
  • El segmento heap (montón) contiene memoria reservada dinámicamente por el programador para almacenar variables temporales en tiempo de ejecución. Es un segmento compartido por todos los subprocesos, bibliotecas compartidas y módulos cargados dinámicamente en un proceso: https://en.wikipedia.org/wiki/Manual_memory_management.
  • El segmento stack (pila), ubicado en la parte superior de la memoria, contiene la pila de llamadas (call stack). Se trata de una estructura LIFO (último en llegar primero en salir) donde se almacenan los argumentos pasados al programa, las cadenas del entorno, argumentos de las funciones, variables locales no inicializadas, además de almacenar el registro de llamada de funciones y el retorno: https://en.wikipedia.org/wiki/Stack-based_memory_allocation.

Diferencias entre stack y heap

Los segmentos más interesantes para nosotros son el montón (heap) y la pila (stack), así que voy a resumir sus diferencias clave en una tabla:



¿Cuándo utilizar el montón y la pila?

  • El montón (heap) debe utilizarse cuando se necesita asignar un gran bloque de memoria. Por ejemplo crear una matriz de gran tamaño o una estructura demasiado grande para mantener una variable durante mucho tiempo.

  • La pila (stack) es mejor utilizarla cuando se trabaja con variables relativamente pequeñas que solo se requieren mientras una función está viva, debido a que provee un acceso más fácil y rápido.

¿Por que mencionamos esto?

La estructura de datos de una aplicación creadas por funciones o programas utilizan distinto tipos de memoria, y ante la terminación no programada u ocasionada por una excepción hay que crear una destrucción de los datos de la  pila de ejecución del programa. 
Cuando una excepción ocurre, TODAS las funciones que se usaron o llamaron para llegar al lugar donde ocurre la excepción DEBEN ser tratar o interrumpir su ejecución. 
Cuando decimos TODOS , nos referimos a los que se encuentran en el Call Stack.
Es por eso que existen dos estados:
  1. Ejecución normal
  2. Estado de Pánico, que es cuando se dispara una excepción o cadenas de excepciones, ya que las funciones suelen estar enganchadas, unas con otras.

Buenas prácticas

  • Utilizar excepciones para errores inesperados: No para errores de lógica o flujo de control.
  • Documentar las excepciones: Especificar el tipo de excepción y su significado.
  • Lanzar excepciones específicas: Usar clases derivadas de std::exception para mayor precisión.
  • Evitar la anidación excesiva de bloques try/catch: Simplifica el código y facilita la depuración.