La seguridad de subprocesos es un concepto de programación informática aplicable al código de subprocesos múltiples . El código seguro para subprocesos solo manipula las estructuras de datos compartidas de una manera que garantiza que todos los subprocesos se comporten correctamente y cumplan con sus especificaciones de diseño sin interacción involuntaria. Existen varias estrategias para crear estructuras de datos seguras para subprocesos. [1] [2]
Un programa puede ejecutar código en varios subprocesos simultáneamente en un espacio de direcciones compartido donde cada uno de esos subprocesos tiene acceso a prácticamente toda la memoria de todos los demás subprocesos. La seguridad de subprocesos es una propiedad que permite que el código se ejecute en entornos multiproceso restableciendo algunas de las correspondencias entre el flujo de control real y el texto del programa, mediante sincronización .
Niveles de seguridad para subprocesos
Las bibliotecas de software pueden proporcionar ciertas garantías de seguridad para subprocesos. Por ejemplo, se puede garantizar que las lecturas simultáneas sean seguras para subprocesos, pero es posible que las escrituras simultáneas no lo sean. Si un programa que usa una biblioteca de este tipo es seguro para subprocesos depende de si usa la biblioteca de una manera consistente con esas garantías.
Los diferentes proveedores utilizan una terminología ligeramente diferente para la seguridad de los subprocesos: [3] [4] [5] [6]
- Seguro para subprocesos : se garantiza que la implementación está libre de condiciones de carrera cuando se accede a varios subprocesos simultáneamente.
- Condicionalmente seguro : diferentes subprocesos pueden acceder a diferentes objetos simultáneamente, y el acceso a los datos compartidos está protegido de las condiciones de carrera.
- No es seguro para subprocesos : diferentes subprocesos no deben acceder simultáneamente a las estructuras de datos.
Las garantías de seguridad de subprocesos generalmente también incluyen pasos de diseño para prevenir o limitar el riesgo de diferentes formas de interbloqueos , así como optimizaciones para maximizar el rendimiento simultáneo. Sin embargo, no siempre se pueden ofrecer garantías libres de interbloqueo, ya que los interbloqueos pueden ser causados por devoluciones de llamada y violación de capas arquitectónicas independientes de la propia biblioteca.
Enfoques de implementación
A continuación, analizamos dos clases de enfoques para evitar las condiciones de carrera para lograr la seguridad de los subprocesos.
La primera clase de enfoques se centra en evitar el estado compartido e incluye:
- Reentrada
- Escribir código de tal manera que pueda ser parcialmente ejecutado por un subproceso, ejecutado por el mismo subproceso o simultáneamente ejecutado por otro subproceso y aún así completar correctamente la ejecución original. Esto requiere guardar la información de estado en variables locales para cada ejecución, generalmente en una pila, en lugar de en variables estáticas o globales u otro estado no local. Se debe acceder a todos los estados no locales a través de operaciones atómicas y las estructuras de datos también deben ser reentrantes.
- Almacenamiento local de subprocesos
- Las variables se localizan para que cada hilo tenga su propia copia privada. Estas variables conservan sus valores a través de subrutinas y otros límites de código y son seguras para subprocesos, ya que son locales para cada subproceso, aunque el código que accede a ellas puede ser ejecutado simultáneamente por otro subproceso.
- Objetos inmutables
- El estado de un objeto no se puede cambiar después de la construcción. Esto implica tanto que solo se comparten datos de solo lectura como que se logra la seguridad de subprocesos inherente. Las operaciones mutables (no constantes) se pueden implementar de tal manera que creen nuevos objetos en lugar de modificar los existentes. Este enfoque es característico de la programación funcional y también lo utilizan las implementaciones de cadenas en Java, C # y Python. (Consulte Objeto inmutable ).
La segunda clase de enfoques están relacionados con la sincronización y se utilizan en situaciones en las que no se puede evitar el estado compartido:
- Exclusión mutua
- El acceso a los datos compartidos se serializa mediante mecanismos que garantizan que solo un hilo lea o escriba en los datos compartidos en cualquier momento. La incorporación de la exclusión mutua debe estar bien pensada, ya que el uso inadecuado puede provocar efectos secundarios como puntos muertos , bloqueos en vivo y falta de recursos .
- Operaciones atómicas
- Se accede a los datos compartidos mediante operaciones atómicas que no pueden ser interrumpidas por otros subprocesos. Esto generalmente requiere el uso de instrucciones especiales en lenguaje de máquina , que pueden estar disponibles en una biblioteca de tiempo de ejecución . Dado que las operaciones son atómicas, los datos compartidos siempre se mantienen en un estado válido, sin importar cómo accedan a ellos otros subprocesos. Las operaciones atómicas forman la base de muchos mecanismos de bloqueo de subprocesos y se utilizan para implementar primitivas de exclusión mutua.
Ejemplos de
En el siguiente fragmento de código Java , la palabra clave Java sincronizada hace que el método sea seguro para subprocesos:
Contador de clases { privado int i = 0 ; público sincronizado void inc () { i ++ ; } }
En el lenguaje de programación C , cada hilo tiene su propia pila. Sin embargo, una variable estática no se mantiene en la pila; todos los hilos comparten acceso simultáneo a él. Si varios subprocesos se superponen mientras se ejecuta la misma función, es posible que un subproceso cambie una variable estática mientras que otro está a la mitad de su verificación. Este error lógico difícil de diagnosticar , que puede compilarse y ejecutarse correctamente la mayor parte del tiempo, se denomina condición de carrera . Una forma común para evitar esto es utilizar otra variable compartida como un "bloqueo" o "mutex" (desde mut UAL ex conclusión).
En el siguiente fragmento de código C, la función es segura para subprocesos, pero no reentrante:
# incluye int increment_counter () { estático int contador = 0 ; estático pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ; // solo permite que se incremente un hilo a la vez pthread_mutex_lock ( & mutex ); ++ contador ; // almacena el valor antes de que cualquier otro hilo lo incremente más int result = counter ; pthread_mutex_unlock ( & mutex ); devolver resultado ; }
En lo anterior, increment_counter
puede ser llamado por diferentes subprocesos sin ningún problema ya que se usa un mutex para sincronizar todos los accesos a la counter
variable compartida . Pero si la función se usa en un manejador de interrupciones reentrantes y surge una segunda interrupción mientras el mutex está bloqueado, la segunda rutina se bloqueará para siempre. Como el servicio de interrupciones puede deshabilitar otras interrupciones, todo el sistema podría sufrir.
La misma función se puede implementar para que sea segura para subprocesos y reentrada utilizando los átomos sin bloqueo en C ++ 11 :
# include ómico>int increment_counter () { static std :: atomic < int > contador ( 0 ); // se garantiza que el incremento se realizará de forma atómica int result = ++ counter ; devolver resultado ; }
Ver también
Referencias
- ^ Kerrisk, Michael (2010). La interfaz de programación de Linux . Sin prensa de almidón . pag. 655.
- ^ "Guía de programación multiproceso" . Oracle Corporation . Noviembre de 2010.
Un procedimiento es seguro para subprocesos cuando el procedimiento es lógicamente correcto cuando se ejecuta simultáneamente por varios subprocesos.
- ^ "Reentrada y seguridad del hilo | Qt 5.6" . Proyecto Qt . Consultado el 20 de abril de 2016 .
- ^ "ip :: tcp - 1.51.0" . Boost.org . Consultado el 16 de octubre de 2013 .
- ^ "Clasificaciones de seguridad de subprocesos API" . Publib.boulder.ibm.com. 1998-06-09 . Consultado el 16 de octubre de 2013 .[ enlace muerto ]
- ^ "Niveles de seguridad de la interfaz MT - Guía de programación multiproceso" . Docs.oracle.com. 2010-11-01 . Consultado el 16 de octubre de 2013 .
enlaces externos
- Expertos en preguntas y respuestas de Java (20 de abril de 1999). "Diseño seguro para subprocesos (20/4/99)" . JavaWorld.com . Consultado el 22 de enero de 2012 .
- TutorialsDesk (30 de septiembre de 2014). "Tutorial de sincronización y seguridad de subprocesos con ejemplos en Java" . TutorialsDesk.com . Consultado el 22 de enero de 2012 .
- Venners, Bill (1 de agosto de 1998). "Diseño para la seguridad de los hilos" . JavaWorld.com . Consultado el 22 de enero de 2012 .
- Suess, Michael (15 de octubre de 2006). "Una breve guía para dominar la seguridad de los hilos" . Pensando en paralelo . Consultado el 22 de enero de 2012 .