diff --git a/src/cap09/README.md b/src/cap09/README.md new file mode 100644 index 0000000..a02250b --- /dev/null +++ b/src/cap09/README.md @@ -0,0 +1,135 @@ +# Operaciones sobre los procesos + +## Uso de fork() + +La función `fork()` se utiliza para crear un nuevo proceso. +El proceso creado es una copia exacta del proceso que llamó a `fork()`, pero tiene su propio espacio de direcciones. + +```c +#include + +pid_t pid = fork(); + +if (pid < 0) { + // Error al crear el proceso. +} else if (pid == 0) { + // Este bloque se ejecutará en el proceso hijo. +} else { + // Este bloque se ejecutará en el proceso padre. +} +``` + +El archivo [fork.c](fork.c) contiene un ejemplo del uso de `fork()`. + +## Uso de exec() + +La función `exec()` se utiliza para reemplazar la imagen del proceso actual con un nuevo programa. +La función `exec()` solo retorna de su invocación si hay un error. + +```c +#include + +if (pid < 0) { + // Error al crear el proceso. +} else if (pid == 0) { + // En el proceso hijo, ejecutamos otro programa. + execl("/path/to/program", "program", (char *)NULL); + + // Si llegamos aquí, hubo un error al ejecutar exec. +} else { + // Este bloque se ejecutará en el proceso padre. +} +``` + +El archivo [fork-exec.c](fork-exec.c) contiene un ejemplo del uso de `fork()` y `exec()` para ejecutar otro proceso con otro programa. + +## Uso de wait() y waitpid() + +Las funciones `wait()` y `waitpid()` se utilizan para hacer que el proceso padre espere a que termine uno de sus procesos hijos. + +```c +#include +#include + +int status; + +if (pid < 0) { + // Error al crear el proceso. +} else if (pid == 0) { + // En el proceso hijo, ejecutamos otro programa. + execl("/path/to/program", "program", (char *)NULL); + + // Si llegamos aquí, hubo un error al ejecutar exec. +} else { + // En el proceso padre, esperamos a que termine el hijo. + wait(&status); +} +``` + +La función `wait()` retorna cuando termina alguno de los procesos hijos del procesos que la llamó. +Para esperar a un proceso hijo específico, podemos usar `waitpid()`. + +```c +#include + +pid_t pid = fork(); + +if (pid == 0) { + // En el proceso hijo +} else { + int status; + // En el proceso padre, esperamos a que termine el hijo específico. + waitpid(pid, &status, 0); +} +``` + +## Terminación del programa + +Para terminar un programa, simplemente usamos la función `return` desde la función `main()`. +También podemos usar la función `exit()` desde cualquier parte del programa para terminarlo inmediatamente. + +```c +#include + +if (alguna_condicion_de_error) { + exit(1); // Termina el programa con un código de error. +} +``` + +El valor indicado en `exit()` o `return` lo puede leer el padre al volver de `wait()` o `waitpid()` usando la macro `WEXITSTATUS()` sobre el valor de `status` + +```c +#include + +pid_t pid = fork(); + +if (pid == 0) { + // En el proceso hijo +} else { + int status; + // En el proceso padre, esperamos a que termine el hijo específico. + waitpid(pid, &status, 0); + int child_exit_status = WEXITSTATUS(status); +} +``` + +## Errores en las llamadas al sistema + +Cualquiera de las anteriores llamadas al sistema puede fallar. +Para manejar errores en las llamadas al sistema, normalmente comprobamos el valor de retorno de la llamada al sistema. +Si es negativo, es que ocurrió un error y la variable `errno` contiene el código de error. + +```c +#include +#include + +pid_t pid = fork(); + +if (pid < 0) { + fprintf(stderr, "Fallo en fork: %s\n", strerror(errno)); + return 1; // Termina el programa con un código de error. +} +``` + +La función `strerror()` permite obtener un mensaje descriptivo del código de error indicado. \ No newline at end of file diff --git a/src/cap17/README.md b/src/cap17/README.md new file mode 100644 index 0000000..cb97aff --- /dev/null +++ b/src/cap17/README.md @@ -0,0 +1,72 @@ +# Archivos mapeados en memoria + +El mapeo de archivos en memoria es una técnica eficiente que permite a los programas acceder a los datos en un archivo como si fueran una matriz en memoria. +Esto se realiza con las llamadas al sistema `mmap()` y `munmap()`. + +## Uso de mmap() + +La función `mmap()` mapea un archivo en el espacio de direcciones de un proceso. +Aquí se muestra un ejemplo de cómo mapear un archivo en memoria: + +```c +#include +#include +#include +#include +#include +#include +#include +#include + +// Primero se abre el archivo, con los permisos adecuados. +int fd = open("archivo.txt", O_RDONLY); +if (fd < 0) { + fprintf(stderr, "Fallo al abrir el archivo: %s\n", strerror(errno)); + return 1; +} + +// Se usa fstat() para obtener el tamaño del archivo porque a mmap() hay que indicarle +// el tamaño de la porción del archivo que queremos mapear. +struct stat st; +if (fstat(fd, &st) < 0) { + fprintf(stderr, "Fallo en fstat: %s\n", strerror(errno)); + return 1; +} + +void *map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +if (map == MAP_FAILED) { + fprintf(stderr, "Fallo en mmap: %s\n", strerror(errno)); + return 1; +} + +// Ahora se puede acceder a los datos del archivo como si estuvieran en la memoria: +// Por ejemplo, imprimir los primeros 10 caracteres por la consola. +for (int i = 0; i < 10 && i < st.st_size; i++) { + printf("%c", ((char*)map)[i]); +} +``` + +## Uso de munmap() + +Una vez que hayas terminado de trabajar con el mapeo en memoria, se debe liberar con `munmap()`: + +```c +if (munmap(map, st.st_size) < 0) { + fprintf(stderr, "Fallo en munmap: %s\n", strerror(errno)); + return 1; +} +``` + +Y tampoco se debe olvidar cerrar el descriptor del archivo cuando se haya terminado: + +```c +if (close(fd) < 0) { + fprintf(stderr, "Fallo al cerrar el archivo: %s\n", strerror(errno)); + return 1; +} +``` + +## Ejemplos + +El archivo [mmapped-files.c](mmapped-files.c) contiene un ejemplo del uso de `mmap()` y `munmap()` para hacer la copia de un archivo. +El archivo [mmapped-files.cpp](mmapped-files.cpp) contiene un ejemplo similar pero en C++, usando la clase `memory_map` definida en [memory_map.hpp](memory_map.hpp) \ No newline at end of file