Files
etsii-vault/TERCERO/SS/SS 24-25.md
2025-10-09 19:25:27 +02:00

373 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# <mark style="background: #FFF3A3A6;">TEMA 1: Introducción</mark>
Aunque la sintaxis de Java derive de C, NO son tan similares. Hay que entender el ámbito del lenguaje: por ejemplo C (Software de Sistemas, sustitución de assembly, etc) o Java (aplicaciones, etc). Existen palabras reservadas con más de un uso (`static` o `const`).
> **C++ amplia el ámbito de C**
## <mark style="background: #ADCCFFA6;">1. Introducción y características</mark>
### <mark style="background: #FFB86CA6;">Estandarización</mark>
Los estándares más usados para C y C++ son el 99 y 98 respectivamente.
```C
#include "archivo.h"
// "pega" archivo.h y sigue compilando
void main() {
}
```
Dependiendo del compilador se pueden especificar directivas como `#pragma once` para solo incluir el archivo una vez.
### ⚠ Incongruencia
C/C++ es portable para **aplicaciones** pero para Software de Sistemas **NO** $\rightarrow$ siempre habrá problemas de portabilidad. **SÍ** que es portable desde el punto de vista de los 80:
- Hay compilador C para cualquier arquitectura.
- Si sé C, puedo programar en cualquier arquitectura.
- El código fuente es compatible.
### <mark style="background: #FFB86CA6;">Nivel de abstracción</mark>
Varía de un lenguaje a otro (C tiene menor nivel de abstracción que los lenguajes modernos). Las estructuras del lenguaje y funcionamiento general son lo más parecido al código máquina. C++ tiene mayor nivel de abstracción pero aumenta la sobrecarga.
### <mark style="background: #FFB86CA6;">Runtime, librerías, frameworks</mark>
**Runtime** es el código que añade el compilador, que no está asociado al código de usuario sino al lenguaje. En C es mínimo, por eso se prefiere para sistemas empotrados.
### <mark style="background: #FFB86CA6;">Relación C/C++</mark>
Compatible C -> C++. Las extensiones mas importantes de C++ son POO, tipado, librerías...
La mayor parte de compiladores son duales. La extensión del archivo determina si hay que compilar C (.c, .h) o C++ (.cpp, .hpp ó .hh).
### <mark style="background: #FFB86CA6;">Java VS C/C++</mark>
| **Java** | **C** | **C++** |
| ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| El programa se compone de clases (propiedades y métodos). | El programa es un conjunto de datos y funciones, **no existen clases**. | |
| Para ejecutar un programa, se empieza por el punto de entrada (main) y se van instanciando, modificando, destruyendo objetos. | Para ejecutar un programa, se empieza por el punto de entrada (main), se inicializan las variables globales y se van creando, modificando variables y ejecutando funciones. | |
| Base de programación: Objetos | No hay base de programación | |
| La unidad de compilación es el fichero (o clase pública). | La unidad de compilación es el fichero. | |
| <span style="color:red">Básicamente, típico lenguaje orientado a objetos</span> | <span style="color:red">Básicamente, típico lenguaje imperativo (procedural)</span> | |
## <mark style="background: #ADCCFFA6;">2. Programar en C/C++</mark>
A diferencia de Java, que declarar y definir se realiza a la vez:
```java
public class Persona {
private String name;
...
public String getName() {
return this.name;
}
}
```
En C/C++ se debe declarar primero la función para que al compilar, la primera pasada la "registre", y luego a la hora de ejecutar, se pueda ejecutar dicha función.
```C
void funcion(); // declaración
int main()
{
funcion();
return 0;
}
void funcion()
{
// definición
}
```
En C/C++ se pueden usar sentencias `#include` para "importar" archivos que contienen declaraciones (de variables, funciones, etc)
### <mark style="background: #FFB86CA6;">Tipos de datos</mark>
**Variables:**
- `char` = 8 bits (-128, 127)
- `unsigned char` = 8 bits (0, 255)
- `short int` = 16 bits
- `int` = 32 bits
- `long int` = 32 bits
- `long long int` = 64 bits
**En cuanto a punteros:**
- `<tipo>*` 32 bits (x86) 64 bits (x64)
# <mark style="background: #FFF3A3A6;">TEMA 2: Depuración</mark>
## <mark style="background: #ADCCFFA6;">1. Introducción</mark>
El depurador es una herramienta para diseccionar el código y poder analizarlo para arreglar los errores. En ingeniería se sigue la regla "Para matar moscas mejor usar un matamoscas que un cañón. "
## <mark style="background: #ADCCFFA6;">2. Tipos de error</mark>
Los errores son comportamientos no deseados del código causados por la inexperiencia (al principio) o falta de atención del programador.
### <mark style="background: #FFB86CA6;">Errores sintácticos</mark>
Son los errores más fáciles de detectar y corregir. El compilador suele detectar todos y decirte dónde están y qué los causa (pero no siempre). Suelen provocar más errores en cadena.
### <mark style="background: #FFB86CA6;">Errores lógicos/semánticos</mark>
Más difíciles de detectar (ninguna herramienta los detecta del todo bien) pero relativamente fáciles de corregir.
### <mark style="background: #FFB86CA6;">Errores de diseño</mark>
Todos los componentes del sistema funcionan pero el sistema no hace lo esperado. Problemas asociados a la Ing. del Software. Muy difíciles de detectar y costosos de arreglar.
## <mark style="background: #ADCCFFA6;">3. Fases del tratamiento de errores</mark>
Hay una serie de pasos comunes (pero no infalibles):
- Detectar el problema
- Recolectar datos
- Diagnóstico
- Reparación
- Verificación
### <mark style="background: #FFB86CA6;">Detección del problema</mark>
Hay varias herramientas:
- **Warnings:** muchas veces son la posible causa de errores futuros. Se deberían arreglar siempre que se pueda, pero OJO: algunas veces son <span style="color:rgb(255, 0, 0)">falsos positivos</span>.
### <mark style="background: #FFB86CA6;">Controlar la ejecución</mark>
Los computadores actuales no son capaces de determinar la información importante para dar con un error. Para eso se usa el **depurador**, que aunque no te da la causa del error como tal, si que se puede descubrir "pidiéndole" la información adecuada.
### <mark style="background: #FFB86CA6;">Inspección de estado</mark>
**Contexto:** datos accesibles en medio de la ejecución (variables locales o globales, parámetros...).
### <mark style="background: #FFB86CA6;">Pila de llamadas y marco de pila</mark>
La pila de llamadas es un bloque de memoria que el S.O. asigna al programa para llamadas y retornos de funciones. Si este se llena, se produce un "stack overflow". El marco de pila es básicamente la región de la pila marcada por los punteros ESP (límite superior de la pila) y EBP (inicio de marco de pila), que pertenece a una función.
![[Pasted image 20241003113528.png|500]]
# <mark style="background: #FFF3A3A6;">TEMA 3: Generación de programas</mark>
## <mark style="background: #ADCCFFA6;">1. Introducción</mark>
Es conveniente conocer como se genera el código:
- qué es un programa ejecutable
- cómo se trata desde el punto de vista del SO y del IDE
y qué elementos intervienen en la generación del ejecutable:
- compiladores y linkers
- archivos objeto, runtime, bibliotecas estáticas y dinámicas
## <mark style="background: #ADCCFFA6;">2. Archivos ejecutables</mark>
![[Pasted image 20241010091834.png|600]]
El ejecutable se genera a partir de:
- el código fuente
- el proceso de compilación
La gran parte del ejecutable la añade el IDE y **no** es generada del código fuente.
## <mark style="background: #ADCCFFA6;">3. Transformación de formatos</mark>
![[Pasted image 20241010092102.png|600]]
Hay al menos tres formatos:
- Código fuente de alto nivel: creado por el programador y compilado por el compilador del lenguaje X.
- Código **objeto**: creado por el compilador, procesado por el linker.
- Ejecutable: creado por el linker, procesado por el cargador del SO.
## <mark style="background: #ADCCFFA6;">4. Tipos de archivo ejecutable</mark>
Hay tantos tipos de ejecutable como mecanismos de carga distintos. Los detalles del mecanismo de carga dependen del SO.
### <mark style="background: #FFB86CA6;">Imágenes binarias</mark>
- El ejecutable es un **mapa de memoria**.
- Es el formato más simple, sin información de carga/reubicación.
- En el archivo están las zonas de memoria que usa el programa (el SO lo carga y se ejecuta).
- Se usan en computadores donde el SO o es muy simple o no existe (embedded).
### <mark style="background: #FFB86CA6;">Ejecutables con información de carga/reubicación</mark>
- El ejecutable también es un mapa de memoria, pero más complejo
- Hay varios formatos muy utilizados:
- **COFF (Common Object File Format):** UNIX y Windows (sólo para archivos .obj y .lib).
- **PE (Portable Executable):** variante del COFF para Windows con código ejecutable (.exe y .dll).
- **ELF (Executable and Linkable Format):** variante del COFF usado en UNIX.
¿Qué contiene un ejecutable?
- Descripción de los **recursos iniciales** del programa
- código
- datos inicializados
- atributos estáticos
- constantes
- tamaño de datos no inicializados y pila
- **Instrucción inicial** donde comienza el código
- **Tablas de reubicación** de datos y código
- Información de debug (opcional)
Las **secciones mínimas** son:
![[Pasted image 20241010115957.png]]
## <mark style="background: #ADCCFFA6;">5. Generación del ejecutable: enlace (link)</mark>
Es el proceso final de la construcción del ejecutable. Hay dos tipos:
- **Estático:** posible en todos los IDE.
- **Dinámico:** posible en algunos SO/IDE. En el dinámico, el SO completa parte del enlace en la carga del programa y/o durante su ejecución.
![[Pasted image 20241010120358.png]]
Al linker se le da ese nombre ya que el .exe es el resultado de unir secciones de cada uno de los .obj/lib.
![[Pasted image 20241017112904.png|600]]
Una vez construidas las secciones, el linker resuelve las referencias pendientes (relocalización de código). Para este proceso, el linker necesita saber qué instrucciones quedan por rellenar, que lo encuentra en la **tabla de reubicación** del archivo objeto.
![[Pasted image 20241017113303.png|600]]
## <mark style="background: #ADCCFFA6;">6. Bibliotecas</mark>
Una biblioteca es básicamente un contenedor de archivos objeto.
![[Pasted image 20241017113556.png]]
### <mark style="background: #FFB86CA6;">Enlace dinámico</mark>
A diferencia del **enlace estático**, donde el ejecutable se construye totalmente durante el linkado, en el **enlace dinámico** algunas bibliotecas se enlazan cuando el ejecutable se carga en memoria (por lo que se encarga el SO).
- El principal objetivo de esto es ahorrar espacio en disco y memoria.
- En **linkado estático**, si una función (por ejemplo `printf`) está en una biblioteca estática, `printf`está en el ejecutable y en **todos** los ejecutables del disco que usen la función. Si varios de los ejecutables están en ejecución, todos tendrán una copia exactamente igual de `printf`.
- El **linkado dinámico** trata de minimizar esta situación almacenando `printf` (al igual que todas las funciones de la biblioteca estándar de C/C++) en un archivo DLL, y luego sigue estos pasos:
- El SO carga el programa que usa funciones en DLL, para esto localiza todos los .dll que necesita.
- Si falta alguno, para la carga
- Si están todos, sigue la carga
- Si continúa la carga, para cada función en DLL del programa comprueba si hay algún programa usando la función (usando como ejemplo `printf`):
- Si no lo hay, carga en memoria el módulo donde está `printf` en el DLL y la enlaza.
- Si lo hay, localiza la función en memoria y la enlaza. Además crea una nueva sección de datos para `printf` en el espacio de memoria virtual del nuevo programa.
- Al cerrarse todos los programas que usan ese código, se descarga de memoria.
**El objetivo es que todo programa en ejecución comparta el código de printf.obj pero que cada uno tenga una copia privada de los datos definidos en este.**
# <mark style="background: #FFF3A3A6;">TEMA 4: Modelo de programación de un SO</mark>
### <mark style="background: #FFB86CA6;">Arquitectura de un SO</mark>
![[Pasted image 20241024090239.png|600]]
## <mark style="background: #ADCCFFA6;">1. Para qué programar directamente con el SO</mark>
![[Pasted image 20241024113018.png|600]]
### <mark style="background: #FFB86CA6;">Como se usa indirectamente</mark>
Vía bibliotecas/runtime/frameworks del lenguaje. Por ejemplo:
```C
if( (stream = fopen("data", "w+" )) == NULL )
```
Flujo de parámetros: `fopen` $\rightarrow$ `CreateFile` $\rightarrow$ `NTCreateFile` $\rightarrow$ `Kernel`
Flujo de errores es el inverso
### <mark style="background: #FFB86CA6;">Como se usa directamente</mark>
En C/C++ parece fácil: usándolo. La mayoría de SO están escritos en C así que la API es accesible con C/C++.
### <mark style="background: #FFB86CA6;">Por qué usarlo directamente</mark>
- Se ahorra sobrecarga de la biblioteca/framework.
- Acceso a todas las capacidades del SO
- Ejemplos: `system`, `CreateProcess`
- Más control de errores
## <mark style="background: #ADCCFFA6;">2. Versiones de un SO</mark>
- Qué cambia entre versiones de un SO:
- Si se incluyen o no algunos módulos
- Evolución del kernel
- Soporte del procesador
- En Windows (NT): x86, x64... Poca variedad comparado con otros SO.
- ¿Qué implica?
- Portabilidad: binaria (raramente), source (hay que recompilar).
- API: nuevas funciones con cada versión y funciones deprecated.
## <mark style="background: #ADCCFFA6;">3. Programación en Windows desde C/C++</mark>
### <mark style="background: #FFB86CA6;">Tratamiento de cadenas y caracteres</mark>
- **ANSI 8 bits/ASCII con codificación MCBS:**
- Tipo `char`, `char*`, `char ...[]` (ANSI C)
- Tipos `CHAR` y variantes en Windows.h
- Las funciones estándar en C/C++ trabajan con ASCII: `printf`, `putc`, `getc`...
- **Unicode:**
- Uso interno en Windows (WCHAR y variantes)
- Estándar en otros lenguajes (Java, C#, Python,...)
- Funciones terminadas en W (`MessageBoxW`)
### <mark style="background: #FFB86CA6;">Tratamiento de errores</mark>
Las funciones devuelven un entero, y si este es negativo es que ha terminado con error. Para más información sobre el error se usa `GetLastError()` y para mensajes al usuario `FormatMessage()`.
# <mark style="background: #FFF3A3A6;">TEMA 5: Control de ejecución: procesos e threads</mark>
En Windows todos los procesos tienen al menos el "main thread". El programa termina cuando termina el thread principal. Dos modelos básicos:
- **Multithread:** cada proceso tiene múltiples threads. Típico de Windows aunque se usa cada vez más en UNIX/Linux.
- **Multiproceso:** múltiples procesos. Típico de UNIX/Linux, aunque también se puede en Windows. En Linux es **muy fácil** usando `fork()`.
## <mark style="background: #ADCCFFA6;">1. Operaciones básicas con threads</mark>
Organizar la ejecución en varios threads **¿para qué?**
EJEMPLO: Programa de proceso de audio
- **thread 1:** lee datos de la entrada de audio, los procesa y envía el resultado a la salida.
- Periódico
- Intensivo en datos
- Requiere velocidad de proceso
- **WORKER THREADS**
- **thread 2:** controla la UI (subir, bajar volumen, play, pause...) Complicado si alguna tarea es bloqueante.
- No periódico
- No intensivo
- No requiere velocidad de proceso
- **USER INTERFACE THREAD**
### <mark style="background: #FFB86CA6;">Crear un thread</mark>
En Windows se usa `CreateThread()` que devuelve un `HANDLE`. Los elementos que integran el thread son:
- Función de entrada
- Parámetro(s)
- Pila
- Almacenamiento local al thread
- El HANDLE y el ID
Los threads son **asíncronos**.
#### <mark style="background: #D2B3FFA6;">Ejemplo</mark>
```C
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "Spanish");
  
DWORD Ciclo = 1000; // Ciclo entre pitidos (en ms)
DWORD Frecuencia = 500;
DWORD Duracion = 100;
while (TRUE){
// bip a frec. y duración que se le pasa
Beep(Frecuencia, Duracion);
Sleep(Ciclo);
}
printf("\nPulse tecla RETORNO para terminar\n");
getchar();  // bloqueo
return 0;
}
```
Para evitar el bloqueo, en C++ existen alternativas (en C **NO**), como `std::thread` o `std::async`.
### <mark style="background: #FFB86CA6;">Suspender/Reactivar un thread</mark>
El thread se puede suspender o reactivar usando respectivamente `SuspendThread` y `ResumeThread`. El thread está suspendido si el contador != 0.
### <mark style="background: #FFB86CA6;">Terminar un thread</mark>
Un thread finaliza cuando termina su función de entrada, cuando se termina a sí mismo con `ExitThread` o cuando lo "terminan" con `TerminateThread`.
## <mark style="background: #ADCCFFA6;">2. Organización de los datos en un thread</mark>
Los hilos comparten variables globales y código. También pueden pasarse punteros (posibilidad de errores).
### <mark style="background: #FFB86CA6;">Código reentrante</mark>
Código que puede ser usado desde más de un hilo. Se puede aplicar a:
- Código completo del hilo
- Algunas funciones
- Bibliotecas
Cada instancia de un thread tiene sus propios recursos, los cuales hay que proteger con mecanismos de control de la concurrencia.
## <mark style="background: #ADCCFFA6;">3. Creando procesos</mark>
`CreateProcess` crea un proceso concurrente. Algunos puntos a destacar en la descripción de `CreateProcess`:
- **`IpApplicationName`**: es opcional. Si es `NULL`, el nombre del programa se incluye en el siguiente parámetro.
- **`IpCommandLine`**: si el path del ejecutable no está, se sigue la secuencia de búsqueda que viene al final de este parámetro.
- **`IpProcessAttributes, IpThreadAttributes, bInheritHandles, IpEnvironment, IpCurrentDirectory`**: <strong style="color:red;">no los usaremos</strong>, poner a NULL/FALSE.
- **`IpStartupInfo`**: la estructura `STARTUPINFO` <strong style="color:red;">no la usaremos</strong> pero tiene que estar.
- **`IpProcessInformation`**: rellena la estructura `PROCESS_INFORMATION` con información sobre el proceso y thread creado (handles, process/threads ids).
## <mark style="background: #ADCCFFA6;">4. Punto de vista de la programación y procesos POSIX</mark>
- Aunque el proceso creado se le llama "hijo" **no hay relación lógica** entre ambos, al menos en Windows. En UNIX/Linux/POSIX **sí hay**.
- La creación del proceso es asíncrona, **NO** es una "llamada".
- En la biblioteca de C: `exec` (UNIX) / `_exec` (Win) funcionan distinto en Windows y UNIX/Linux. En Windows se basa en `CreateProcess` y en UNIX/Linux el proceso actual carga un nuevo programa:
- `posix spawn` es lo equivalente a `CreateProcess` que básicamente hace: `fork` + `exec`
# <mark style="background: #FFF3A3A6;">TEMA 6: Control de concurrencia y sincronización</mark>
En el tema anterior se trataba como lanzar varios hilos que cooperan para una tarea. También vimos el mecanismo más básico de sincronización: la espera activa. Sin embargo, hay técnicas más complejas.
## <mark style="background: #ADCCFFA6;">1. Control de concurrencia y sincronización</mark>
Cuando varios hilos cooperan para realizar un trabajo es necesario coordinarlos. Los mecanismos para coordinarlos se pueden dividir en dos grupos:
### <mark style="background: #FFB86CA6;">Control de concurrencia</mark>
Hay al menos un recurso compartido por varios hilos (variables globales, buffers, la consola, etc.). Esto puede llevar a conflictos, por ejemplo en el uso de la consola:
- Un `printf` no habría problema.
- Si son varios la secuencia de ejecución no se asegura.
- Es complicado usar `getchar`, `scanf` y demás funciones I/O desde varios hilos en ejecución.
### <mark style="background: #FFB86CA6;">Sincronización</mark>
Uno o varios hilos deben esperar a que otro/s terminen alguna tarea. Ya hemos visto uno de ellos:
- `WaitForSingleObject(HandleThread, ...)` espera a que un hilo termine.
- `std::thread.join` versión muy simplificada, equivalente a `WaitForSingleObject(pth -> native_handle(), INFINITE);`
## <mark style="background: #ADCCFFA6;">2. Sincronización entre hilos</mark>
### <mark style="background: #FFB86CA6;">Secciones críticas</mark>
Es el mecanismo de control de concurrencia mas fácil. Se trata básicamente de:
- Definir una variable tipo `CRITICAL_SECTION` (normalmente global).
- Identificar las zonas de código que usan un recurso compartido por distintos hilos.
- "Marcarlas" como secciones críticas:
- `EnterCriticalSection()` al inicio
- `LeaveCriticalSection()` al final
- Antes de usar las funciones anteriores debemos asegurarnos de inicializar la sección crítica con `InitializeCriticalSection()` y cuando ya no se vaya a usar más de borrarla con `DeleteCriticalSection()`.
<div class="nota"><h4>NOTA</h4><p><code>EnterCriticalSection()</code> bloquea sin timeout. Una vez bloquea, el hilo permanece bloqueado hasta que la sección crítica se libere. Esto hace que los errores sean difíciles de tratar. Se puede subsanar con <code>TryEnterCriticalSection()</code></p></div>
### <mark style="background: #FFB86CA6;">Mútex</mark>
El funcionamiento es similar a las secciones críticas. Algunas diferencias son:
- Tienen nombre y se pueden usar para coordinar hilos en **distintos procesos**.
- Permiten timeout.
Se usan de la siguiente manera:
- El mútex debe estar creado antes de usarlo.
- `WaitForSingleObject(HandleMutex, ...)` antes de usar el recurso compartido. La función bloquea el hilo si otro ya "posee" el mútex. Cuando se desbloquea puede devolver `WAIT_OBJECT_0` indicando éxito (y el hilo captura el mútex) o un error.
- `ReleaseMutex()` después de usar el recurso compartido.
### <mark style="background: #FFB86CA6;">Funciones <code>WaitFor</code></mark>
Hemos visto que `WaitForSingleObject()` se bloquea hasta que un hilo termine o el hilo capture el mútex. En general, se llama objetos con bloqueo (waitable objects) a aquellos elementos del SO sobre los que se puede usar una función `WaitFor`.
<div class="nota"><h4>NOTA</h4><p>Un waitable object puede estar en el estado <code>signaled</code> (señalado) o <code>nonsignaled</code> (no señalado)</p></div>
La función se bloquea si el objeto está `nonsignaled`.
### <mark style="background: #FFB86CA6;">Eventos</mark>
Los eventos son "notificaciones" de que ha pasado algo en el SO. Se usan de la siguiente manera:
- El evento debe estar creado antes de usarlo.
- `CreateEvent()`
- `WaitForSingleObject(HandleEvento, ...)` se bloquea si el evento está `nonsignaled`. Cuando se desbloquea puede devolver éxito (`WAIT_OBJECT_0`) si está `signaled` o error (`WAIT_TIMEOUT`,`WAIT_ABANDONED`,`WAIT_FAILED`).
- Manipular el evento:
- `SetEvent()` marca el evento como `signaled`
- `ResetEvent()` marca el evento como `nonsignaled`
- `PulseEvent()` desbloquea hilo/s esperando en el momento de ejecutarse y el evento pasa a `nonsignaled`.
![[Pasted image 20241125141145.png]]
### <mark style="background: #FFB86CA6;">Semáforos</mark>
Mucho menos usados en Windows que en UNIX/Linux. Un semáforo tiene un contador interno.
- El semáforo está abierto (`signaled`) si el contador es mayor que 0.
- El semáforo está cerrado (`nonsignaled`) si el contador es 0.
Se usan de la siguiente manera:
- El semáforo se debe crear antes de usarlo con `CreateSemaphore()`.
- `WaitForSingleObject(HandleSemaforo, ...)` se bloquea si el semáforo está cerrado (`nonsignaled`). Cuando se desbloquea puede devolver:
- `WAIT_OBJECT_0` el semáforo está abierto. Decrementa el contador en 1 y si llega a 0 el semáforo se cierra.
- Otro valor indicando error.
- `ReleaseSemaphore()` incrementa el contador en 1
### <mark style="background: #FFB86CA6;">Otros mecanismos</mark>
- **Interlock:** Se usan en variables compartidas. Permiten incrementar/decrementar una variable sin peligro. Son las más rápidas (frecuentes en drivers).
- **Mensajes:** Implementación rara en Windows pero muy comunes en otros SO. Muy útiles para intercambiar y sincronizar datos. En Windows se usan para el sistema de ventanas.
## <mark style="background: #ADCCFFA6;">3. Sincronización entre procesos</mark>
Es posible usar mútex/eventos/semáforos entre procesos al igual que hacemos con los hilos.
### <mark style="background: #FFB86CA6;">Sincronización + datos (named pipes)</mark>
Una named pipe permite enviar datos de forma sincronizada entre dos o más procesos. Las principales características son:
- Tubería de mensajes: los mensajes enviados por un lado se reciben por el otro en el mismo orden.
- Bidireccional: permite a un proceso enviar/recibir con la misma tubería.
- Utiliza el paradigma C-S.
<div class="nota">
<h4>Ejemplo típico: named pipe</h4>
<ul>
<li>El servidor crea un objeto de sincronización para indicar que se ha creado la named pipe</li>
<li>Cada cliente crea su propio objeto de sincronización con el mismo parámetro <code>Name</code> para ver si la named pipe ha sido creada, y luego se bloquea esperando a que la tubería esté creada.</li>
<li>Tras crear la tubería, el servidor indica que la tubería existe con el evento de sincronización</li>
</ul>
</div>