RCU

From FdIwiki ELP
Jump to: navigation, search

RCU

RCU (Read Copy Update) es un mecanismo de sincronización de procesos que se introdujo en el kernel de Linux en octubre del año 2002. El objetivo era mejorar la escalabilidad del kernel y eliminar los costes que supone la espera y la toma de un mutex (spin lock) cada vez que se quiere leer o modificar ciertos datos protegidos. Es una técnica de exclusión mutua que bajo ciertas condiciones actúa sin apenas bloqueos.

Ventajas

Comparando RCU con otros mecanismos de sincronización existentes en el kernel de Linux, podemos fácilmente encontrar dos claras ventajas:

· Se eliminan por completo los costes de la toma de mutex, lo cual habitualmente conlleva una serie de acciones muy costosas para la mayor parte de arquitecturas.

· Permite un número "ilimitado" de procesos lectores accediendo a las estructuras protegidas, y además permite que haya un proceso escritor modificando las mismas de manera concurrente a los procesos lectores.

Requisitos

Para poder obtener la mejora de rendimiento esperada al usar RCU, se han de tener en cuenta 4 aspectos generales:

· Solo se debe acceder a los datos protegidos a través de punteros declarados de manera dinámica.

· Las lecturas han de ser muy frecuentes.

· Las modificaciones han de ser poco usuales.

· Se ha de tener en cuenta que las referencias a los datos protegidos se perderán en caso de que el proceso se duerma y sea expropiado de la CPU.

A la hora de utilizar RCU ha de tenerse en cuenta que este solo garantiza la integridad de los datos protegidos cuando concurrentemente a los procesos lectores hay un único proceso escritor. En el caso de que exista la posibilidad de un número mayor de procesos escritores intentando acceder a la vez a los datos ha de utilizarse otro mecanismo de sincronización como los spin lock para garantizar la exclusividad.

API

El API central de RCU y sobre el que están basadas el resto de funciones y macros consta de 5 entradas:

· rcu_read_lock() / rcu_read_unlock() : Estas dos funciones serán llamadas desde los procesos que consulten y utilicen sin modificar los datos protegidos (comúnmente denominados lectores). Básicamente su función es señalar la sección crítica del proceso. Al hacer lock estaremos deshabilitando la expropiación de la CPU, de tal manera que hasta que realicemos unlock no se podrá bloquear el proceso ni realizar un cambio de contexto. Es por este motivo que entre estas dos llamadas no se puede hacer uso de funciones bloqueantes (por ejemplo de entrada/salida).

· rcu_assign_pointer() : Es una macro utilizada por un proceso para modificar los datos protegidos a través de un puntero. Al modificar los datos mantiene dos versiones de los mismos, una actualizada con las nuevas modificaciones y otra "anticuada" con el estado anterior a la modificación. Esto se hace porque como hemos dicho anteriormente la escritura se realiza de manera concurrente con la lectura, de manera que puede que a la vez que modificamos una estructura un proceso lector aún mantenga una referencia a la versión anterior de la misma. Así aseguramos que no se van a corromper los datos que maneja ningún proceso lector.

· rcu_dereference() : Es una macro utilizada en este caso por los procesos lectores para obtener una referencia (puntero) a alguno de los datos protegidos. La llamada a esta macro se ha realizar en todo momento entre las funciones de lock y unlock.

· synchronize_rcu() : Marca el final del código del proceso escritor, bloqueándolo hasta que todas las CPU´s en las que hubiera un proceso lector ejecutándose realicen un cambio de contexto. Esto es debido de nuevo a que puede que haya algún lector con una referencia a algún dato que ha sido modificado. Por ejemplo, en el caso de una lista doblemente enlazada, puede que el proceso escritor elimine o modifique cierto nodo P, pero a pesar de ello aún haya un proceso lector con una referencia a este mismo nodo. Esta función garantiza que todos los lectores hayan finalizado la ejecución de su sección crítica, tras lo cual retornará y permitirá al proceso escritor realizar las tareas de limpieza sin preocuparse de corromper los datos de otro proceso(véase liberar la memoria asociada al nodo P).

RCU cuenta en su API con numerosas funciones ya implementadas para trabajar con cierto tipo de estructuras muy comunes en el kernel (como por ejemplo las listas doblemente enlazadas anteriormente mencionadas), pero hay que tener en cuenta que todas las demás funciones se sustentan sobre las 5 aquí comentadas e internamente hacen llamadas a las mismas.

Referencias

Toda esta información y más sobre RCU se puede obtener, entre otras, de las siguientes fuentes:

· lwn.net

· kernel.org