Skip to content

Commit

Permalink
fix: enlaces a ejemplos
Browse files Browse the repository at this point in the history
  • Loading branch information
jesustorresdev committed Oct 20, 2024
1 parent 2bebb10 commit fb0c5de
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 50 deletions.
6 changes: 3 additions & 3 deletions content/C09-procesos.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ En el <<ejemplo-createprocess>> se puede ver cómo se usa {win32_createprocess}
[[ejemplo-createprocess]]
.Crear un proceso usando Windows API
====
El código fuente completo de este ejemplo está disponible en {createprocess_c}.
El código fuente completo de este ejemplo está disponible en {createprocess_cpp}.
[source,c]
----
Expand Down Expand Up @@ -656,7 +656,7 @@ Los nuevos procesos se crean con la llamada {linux_fork}, que se encarga de crea

.Crear un proceso en sistemas POSIX
====
El código fuente completo de este ejemplo está disponible en {fork_c}.
El código fuente completo de este ejemplo está disponible en {fork_cpp}.
[source,c]
----
Expand Down Expand Up @@ -772,7 +772,7 @@ Para eso necesitamos {linux_exec}, una familia de funciones cuyo propósito es c

.Ejecutar otro programa en un proceso nuevo en sistemas POSIX
====
El código fuente completo de este ejemplo está disponible en {forkexec_c}.
El código fuente completo de este ejemplo está disponible en {fork_exec_cpp}.
[source,c]
----
Expand Down
16 changes: 8 additions & 8 deletions content/C10-ipc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -592,11 +592,11 @@ Se puede consultar una lista de las señales del estándar POSIX en https://es.w
Mientras que la lista completa de señales soportadas en Linux se puede consultar en https://man7.org/linux/man-pages/man7/signal.7.html[«signal(7) -- Linux Manual»].
====

El ejemplo en {mqueue_server_cpp} y en otros ejemplos de este capítulo, utiliza señales para manejar `SIGINT`, `SIGTERM` y para mostrar la hora periódicamente.
El código dedicado a eso está en {timeserver_c} y se comparte entre todos los ejemplos.
En todos los casos se evita usar `SA_RESTART` porque interesa que el proceso interrumpa lo que esté haciendo cuando llegue una señal para terminar.
El ejemplo en {mqueue_server_cpp} y en otros ejemplos de este capítulo, utiliza señales para mostrar la hora periódicamente.
El código dedicado a eso está en {timeserver_cpp} y se comparte entre todos los ejemplos.
En todos los casos se usa `SA_RESTART` porque interesa que se maneje la señal de `SIGARLM` para mostrar la hora, sin interrumpir cualquier llamada al sistema en la que puede estar esperando el proceso cuando llegue la señal.

En {signals_c} hay un programa de ejemplo que muestra cómo manejar las señales del sistema y que sirve para ver cómo funcionan.
En {signals_cpp} hay un programa de ejemplo que muestra cómo manejar las señales del sistema y que sirve para ver cómo funcionan.
Solo hay que ejecutarlo y luego enviarle señales con el comando `kill` desde otra terminal.
En este caso si se usa `SA_RESTART`, porque el programa debe estar esperando la pulsación de una tecla con `getc()` y no nos interesa que deje de hacerlo cuando llegue una señal.

Expand Down Expand Up @@ -665,15 +665,15 @@ En los sistemas POSIX las *tuberías con nombre* se denominan _FIFO_ y tienen pr

Con {linux_fork} es muy sencillo lanzar otros procesos para que ejecuten tareas en paralelo.
El proceso hijo tiene acceso a los datos del padre por la forma en la que funciona {linux_fork} y gracias a las tuberías anónimas puede comunicar los resultados al padre.
En {forkpipe_cpp} se puede observar un ejemplo de esto.
En {fork_pipe_cpp} se puede observar un ejemplo de esto.

Además, el hecho de que cada extremo se comporte como un archivo —uno en modo solo lectura y el otro en modo solo escritura— hace posible redirigir la E/S estándar del proceso hijo.
Es decir, conectar la entrada, la salida estándar o la salida de error a una tubería, desde la que leer lo que el proceso intenta imprimir por la pantalla de la terminal o proporcionarle lo que debe leer, como si fuera desde el teclado.
En {forkredir_c} se puede ver un ejemplo de cómo ejecutar el comando `ls` y redirigir su salida al proceso padre para contar el número de líneas en lo que el comando quería mostrar por pantalla.
En {fork_redir_c} se puede ver un ejemplo de cómo ejecutar el comando `ls` y redirigir su salida al proceso padre para contar el número de líneas en lo que el comando quería mostrar por pantalla.

Por otro lado, las tuberías con nombre permiten que un proceso se comunique con cualquier otro, solo con conocer la ruta de la tubería.
En {fifo_server_c} tenemos un ejemplo de un programa que muestra la hora del sistema de forma periódica, mientras espera órdenes de una tubería que sirve de canal de control remoto.
Los programas en {fifo_client_c} y {fifo_client_cpp} pueden conectarse a esa tubería y mandar el comando que hace terminar {fifo_server_c}.
En {fifo_cpp} tenemos un ejemplo de un programa que muestra la hora del sistema de forma periódica, mientras espera órdenes de una tubería que sirve de canal de control remoto.
El programa en {fifo_control_cpp} puede conectarse a esa tubería y mandar el comando que hace terminar {fifo_cpp}.

=== Sockets

Expand Down
8 changes: 3 additions & 5 deletions content/C11-memoria_compartida.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void* p = mmap(
Es decir, que al crear un hijo con {linux_fork} este tendrá una copia de toda la memoria del proceso padre, excepto esta región en particular, que será la misma que la del padre.
Por lo tanto, escribiendo y leyendo en esa región, ambos procesos pueden comunicarse.

En {anom_shared_memory_cpp} se puede ver un ejemplo muy simple, similar a {forkpipe_cpp} pero utilizando memoria compartida para comunicar ambos procesos.
En {anom_shared_memory_cpp} se puede ver un ejemplo muy simple, similar a {fork_pipe_cpp} pero utilizando memoria compartida para comunicar ambos procesos.
Como se puede apreciar, la versión que usa memoria compartida es bastante más sencilla que la que utiliza tuberías.

En Microsoft Windows se puede hacer algo similar con {win32_createfilemapping}:
Expand Down Expand Up @@ -137,10 +137,8 @@ Para esto último se indica el tamaño de la región y el desplazamiento dentro
Un objeto de memoria compartida recién creado tiene tamaño 0.
Para redimensionarlo se utiliza {linux_ftruncate}, que lo que necesita es el descriptor del objeto y el nuevo tamaño.

En {shared_memory_server_c} y {shared_memory_client_cpp} se puede ver el ejemplo de un programa que muestra periódicamente la hora del sistema.
En este caso controlado por otro mediante memoria compartida.
Ambos programas usan la clase definida en {shared_memory_hpp} para gestionar el objeto de memoria compartida.
Sus métodos muestran de forma práctica cómo utilizar las llamadas al sistema comentadas.
En {shared_memory_cpp} se puede ver el ejemplo de un programa que muestra periódicamente la hora del sistema.
En este caso controlado por {shared_memory_control_cpp} mediante memoria compartida.

En Microsoft Windows también se utiliza {win32_createfilemapping} para crear el objeto de memoria compartida con nombre.
Simplemente hay que indicar el nombre en el último argumento de la función.
Expand Down
22 changes: 11 additions & 11 deletions content/C13-sincronización.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,9 @@ Mientras que en sistemas POSIX es necesario crear el *semáforo* en una región

En el ejemplo {anom_shared_memory_cpp} de comunicación mediante memoria compartida, se usa un *semáforo* para que el proceso hijo indique al proceso padre que ha terminado de calcular el factorial y el resultado ya está en la memoria.

En {shared_memory_server_c} está el ejemplo completo de un programa que muestra periódicamente la hora del sistema y que puede ser controlado remotamente, mediante memoria compartida, con un cliente como el de {shared_memory_client_cpp}.
En {shared_memory_cpp} está el ejemplo completo de un programa que muestra periódicamente la hora del sistema y que puede ser controlado remotamente, mediante memoria compartida, con un programa como el de {shared_memory_control_cpp}.

Para enviar los mensajes entre el cliente y el servidor, en la memoria compartida se reserva hueco para un búfer en el que el cliente copia el comando que quiere enviar y para dos *semáforos*:
Para enviar los mensajes entre ambos programas, en la memoria compartida se reserva hueco para un búfer, en el que el programa de control copia el comando que quiere enviar, y para dos *semáforos*:

[source,c]
----
Expand All @@ -399,24 +399,24 @@ struct memory_content
};
----
<1> Indica cuándo `command_buffer` está vacío, así que se inicializa a 1.
El cliente usa {linux_sem_wait} en este *semáforo* antes de escribir un nuevo comando en `command_buffer`:
El programa de control usa {linux_sem_wait} en este *semáforo* antes de escribir un nuevo comando en `command_buffer`:

* Si el *semáforo* está a 0, el cliente pasa y escribe el comando.
* Si el *semáforo* está a 0, el programa de control pasa y escribe el comando.
Después llama a {linux_sem_post} en `ready`.

* Si el *semáforo* está a 1, el cliente queda bloqueado y tiene que esperar a que el servidor use {linux_sem_post} sobre el mismo *semáforo*.
El servidor lo hace después de leer el comando para interpretarlo.
* Si el *semáforo* está a 1, el programa de control queda bloqueado y tiene que esperar a que el servidor use {linux_sem_post} sobre el mismo *semáforo*.
El programa controlado lo hace después de leer el comando para interpretarlo.

<2> Indica cuándo `command_buffer` tiene un comando, así que se inicializa a 0.
El servidor usa {linux_sem_wait} en este *semaforo* antes de leer el comando en `command_buffer` para interpretarlo:
El programa controlado usa {linux_sem_wait} en este *semaforo* antes de leer el comando en `command_buffer` para interpretarlo:

* Si el *semáforo* está a 0, el cliente pasa y lee el comando.
* Si el *semáforo* está a 0, el programa de control pasa y lee el comando.
Después llama a {linux_sem_post} en `empty`.

* Si el *semáforo* está a 1, el servidor queda bloqueado y tiene que esperar a que el cliente use {linux_sem_post} sobre el mismo *semáforo*.
El cliente lo hace después de escribir un nuevo comando en `command_buffer`.
* Si el *semáforo* está a 1, el programa controlado queda bloqueado y tiene que esperar a que el programa de control use {linux_sem_post} sobre el mismo *semáforo*.
El programa de control lo hace después de escribir un nuevo comando en `command_buffer`.

El detalle de cómo cliente y servidor usan ambos semáforos, se puede ver en el código de {shared_memory_client_cpp} y {shared_memory_server_c}, respectivamente.
El detalle de cómo ambos programas usan los semáforos, se puede ver en el código de {shared_memory_control_cpp} y {shared_memory_cpp}, respectivamente.

Finalmente, para resolver el *problema del productor-consumidor* tenemos que considerar que:

Expand Down
18 changes: 9 additions & 9 deletions content/C19-sistema_de_archivos.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -896,18 +896,18 @@ Aparte de estos mecanismos, cada sistema operativo puede implementar algunas fun
Por ejemplo, la llamada {linux_fcntl} de Linux permite un tipo de bloqueo con las ventajas de los bloqueos originales de {linux_fcntl} pero asociados a descriptores de archivo.
Esto permite usarlos para sincronizar hilos de un mismo proceso y para que un proceso pueda transferir la adquisición del bloqueo a sus hijos, como ocurre con los bloqueos BSD de {linux_flock}.
En {filelock_server_c} y {filelock_client_cpp} se puede ver un ejemplo similar al de capítulos anteriores, pero usando en esta ocasión bloqueo de archivos.
El servidor {filelock_server_c} es un programa que muestra periódicamente la hora del sistema.
Mientras que el cliente {filelock_client_cpp}, simplemente envía una señal `SIGTERM` al servidor cuando queremos que termine.
Para que el cliente conozca el PID del servidor —de entre todos los procesos en ejecución en el sistema— el servidor escribe su PID en un archivo en una ubicación conocida por ambos.
En {filelock_server_c} y {filelock_stop_cpp} se puede ver un ejemplo similar al de capítulos anteriores, pero usando en esta ocasión bloqueo de archivos.
El programa {filelock_server_c} hace de servidor proporciona periódicamente la hora del sistema.
Mientras que el programa {filelock_stop_cpp}, simplemente envía una señal `SIGTERM` a {filelock_server_c} cuando queremos que termine.
Para que {filelock_stop_cpp} conozca el PID de {filelock_server_c} —de entre todos los procesos en ejecución en el sistema— este último escribe su PID en un archivo en una ubicación conocida por ambos.
Como el cliente lee el archivo con una única operación {linux_read} y el servidor lo escribe con una única operación {linux_write}, no hace falta el uso de bloqueo de archivos para sincronizarlos.
Gracias a la semántica de coherencia POSIX, el cliente no puede leer el archivo en medio de la escritura.
Es decir, o ve el PID completo escrito por el servidor o no ve ninguno.
Como {filelock_stop_cpp} lee el archivo con una única operación {linux_read} y {filelock_server_c} lo escribe con una única operación {linux_write}, no hace falta el uso de bloqueo de archivos para sincronizarlos.
Gracias a la semántica de coherencia POSIX, el {filelock_stop_cpp} no puede leer el archivo en medio de la escritura.
Es decir, o ve el PID completo escrito por {filelock_server_c} o no ve ninguno.
Pero si puede darse el caso de que se ejecuten varios servidores al mismo tiempo.
Pero si puede darse el caso de que se ejecuten varios servidores {filelock_server_c} al mismo tiempo.
Cada uno debe comprobar si archivo existe y, si es así, leer el PID que contiene y comprobar si hay un proceso con ese mismo PID.
Si el archivo no existe o no encuentra un proceso con ese PID, debe entender que es el nuevo servidor y escribir su PID en el archivo, para que lo encuentre el cliente.
Si el archivo no existe o existe no encuentra un proceso con el PID indicado, debe entender que es el nuevo servidor y escribir su PID en el archivo, para que lo encuentre el programa de control.
En caso contrario, debe terminar.
Para evitar que varios servidores den todos esos pasos al mismo tiempo, acaben creyendo que son los únicos y sobrescriban el archivo varias veces, el acceso al archivo debe hacerse en *exclusión mutua*.
Expand Down
Loading

0 comments on commit fb0c5de

Please sign in to comment.