1 - Procesos en C: Fundamentos

Este primer tema se centra en los procesos del sistema operativo Windows. Revisamos cómo se representan, cómo se crean con CreateProcess y cómo el proceso padre espera su finalización con WaitForSingleObject para obtener el código de salida. También veremos ventajas, desventajas y un mini lanzador de comandos que puedes probar en CLion o desde la terminal de Windows.

1.1 ¿Qué es un proceso?

Un proceso es un programa en ejecución con su propio contador de programa, registros y espacio de memoria. El sistema operativo le asigna recursos (CPU, memoria, descriptores de archivo) y lo aísla de otros procesos para que no interfieran directamente.

1.2 Espacio de memoria y aislamiento

Cada proceso ve un espacio de direcciones virtuales privado. Este aislamiento impide que un proceso corrompa la memoria de otro y permite al sistema operativo mover páginas o aplicar protecciones. La comunicación controlada se logra con tuberías, sockets o memoria compartida, siempre mediada por el kernel.

1.3 Creación de procesos con CreateProcess

Windows no implementa fork(), como en Linux; la forma habitual de crear un proceso es CreateProcess. Esta función lanza un nuevo proceso con su propia imagen de ejecución y devuelve identificadores (PROCESS_INFORMATION) para interactuar con él. Requiere preparar una estructura STARTUPINFO y una cadena de comando (cmd.exe /C ... si queremos ejecutar una orden de shell).

1.4 Reemplazo de imagen

Windows no tiene un análogo directo a exec() (reemplazar el proceso actual sin cambiar de PID). La estrategia es crear un proceso nuevo con la imagen deseada y, si ya no se necesita el actual, finalizarlo. Por eso se usa CreateProcess para lanzar el binario que se quiera ejecutar.

1.5 Espera y código de salida

Para sincronizar el fin de un proceso hijo se utiliza WaitForSingleObject sobre el handle del proceso. Luego GetExitCodeProcess devuelve el código de salida. Siempre hay que cerrar los handles (CloseHandle) para no filtrar recursos.

1.6 Ventajas y desventajas del multiproceso

  • Ventajas: aislamiento de memoria que mejora la robustez; uso de varios núcleos sin preocuparse por bloqueos internos del lenguaje; posibilidad de reiniciar hijos defectuosos sin tumbar al padre.
  • Desventajas: creación y cambio de contexto más costosos que los hilos; comunicación entre procesos más compleja; necesita manejar la recolección para no dejar zombis.

1.7 Aplicación en C (CLion, Windows): Lanzador de comandos con procesos hijos

El siguiente programa pide un comando, lo ejecuta con cmd.exe /C mediante CreateProcessA y espera a que termine con WaitForSingleObject. Funciona en Windows (consola) y puedes pegarlo en main.c de un proyecto de CLion.

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  char comando[128];

  printf("Ingresa un comando (por ejemplo: dir, date /T, ver): ");
  if (!fgets(comando, sizeof comando, stdin)) {
    fputs("No se pudo leer la entrada.\n", stderr);
    return EXIT_FAILURE;
  }

  size_t len = strlen(comando);
  if (len > 0 && comando[len - 1] == '\n') {
    comando[len - 1] = '\0'; /* quitar salto de linea */
  }

  if (strlen(comando) == 0) {
    fputs("No se ingreso comando.\n", stderr);
    return EXIT_FAILURE;
  }

  char linea[256];
  if (snprintf(linea, sizeof linea, "cmd.exe /C %s", comando) >= (int)sizeof(linea)) {
    fputs("El comando es demasiado largo.\n", stderr);
    return EXIT_FAILURE;
  }

  STARTUPINFOA si;
  PROCESS_INFORMATION pi;
  ZeroMemory(&si, sizeof(si));
  ZeroMemory(&pi, sizeof(pi));
  si.cb = sizeof(si);

  BOOL ok = CreateProcessA(
    NULL,          /* lpApplicationName */
    linea,         /* lpCommandLine (mutable) */
    NULL, NULL,    /* seguridad del proceso e hilos */
    FALSE,         /* heredar handles */
    0,             /* flags */
    NULL, NULL,    /* entorno y directorio actual */
    &si, &pi
  );

  if (!ok) {
    DWORD err = GetLastError();
    fprintf(stderr, "CreateProcess fallo (%lu)\n", err);
    return EXIT_FAILURE;
  }

  WaitForSingleObject(pi.hProcess, INFINITE);

  DWORD exit_code = 0;
  if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
    fprintf(stderr, "No se pudo obtener el codigo de salida (%lu)\n", GetLastError());
  } else {
    printf("El hijo finalizo con codigo %lu\n", exit_code);
  }

  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
  return EXIT_SUCCESS;
}

Al ejecutar: el padre lanza cmd.exe /C con el comando ingresado, espera a que el proceso termine y muestra su código de salida. Si CreateProcess falla, imprime el error code de Windows.

Salida del lanzador de comandos en Windows mostrando el código de salida del proceso hijo