Cómo compartir memoria entre aplicaciones escritas en C / C ++

9

Estoy revisando un programa escrito en C / C ++ para control en robótica. Básicamente, tres programas diferentes se ejecutan al mismo tiempo y se comunican a través de la memoria compartida. Google-ling alrededor que encontré piensa como vxWorks y los encabezados de interproceso de bibliotecas de impulso ( documentación de Boost: compartir memoria entre procesos ).

Ahora, no quiero ver la implementación, puedo leer el enlace de arriba. Pero no puedo entender cómo la biblioteca de impulso hace esto. Quiero decir, una aplicación asigna memoria y otra accede a esa memoria, pero ¿cómo se comunican? ¿No es inseguro hacer esto?

cauchy
fuente
¿Qué parece poco claro en los documentos que ha referido? "Al colocar objetos en una región asignada y asignar esa región en una dirección diferente en cada proceso, los punteros sin procesar son un problema ya que solo son válidos para el proceso que los colocó allí. Para resolver esto, Boost.Interprocess ofrece un puntero inteligente especial que se puede usar en lugar de un puntero sin formato. Por lo tanto, las clases de usuario que contienen punteros sin formato (o punteros inteligentes Boost, que poseen internamente un puntero sin formato) no se pueden colocar de forma segura en una región asignada del proceso compartida. Estos punteros se deben reemplazar con ... "
mosquito
No olvide que el sistema operativo es el proceso que realmente cuida la memoria. Los procesos que asignan y usan la memoria al final del día usan el sistema operativo para hacer sus solicitudes. El sistema operativo gestiona el procesamiento múltiple y se asegura de que no haya conflictos.
Rob Sedgwick
@gnat La implementación es clara. ¿Cómo hace Boost que la implementación no esté en esos documentos? No quiero replicarlo (eso sería absurdo) sino entenderlo.
cauchy

Respuestas:

11

Pero no puedo entender cómo la biblioteca de impulso hace esto.

El mecanismo de interproceso de impulso tiene tres componentes necesarios para trabajar:

  1. archivo mapeado en memoria: es necesario crear un archivo mapeado en memoria y pasarlo a un asignador boost.interprocess. Este asignador tomará fragmentos del archivo y los usará como si fueran devueltos por un std :: allocator, con la asignación aplicada para que la memoria sea compatible con la memoria específica en proceso.

  2. boost.interprocess container; Este tipo de contenedor utilizará la memoria devuelta por el asignador y ofrecerá una interfaz similar a std :: container (begin / end / size / push_back, etc.).

  3. mecanismo de sincronización; Esto puede ser cualquier mutex entre procesos y debe usarse para evitar condiciones de carrera de acceso a datos.

Quiero decir, una aplicación asigna memoria y otra accede a esa memoria, pero ¿cómo se comunican? ¿No es inseguro hacer esto?

La memoria asignada es en realidad un archivo mapeado de memoria compartida. La comunicación es indirecta, con ambas aplicaciones configurando o leyendo los datos, según lo necesiten. La seguridad proviene del uso de primitivas de sincronización entre procesos.

utnapistim
fuente
2
Vale la pena señalar: estos tres mecanismos de IPC requieren soporte del kernel, por lo que para aquellos que tienen curiosidad por cómo se hace esto (como lo indica el OP), no se puede hacer solo con aplicaciones individuales. Las aplicaciones tienen que pedirle al núcleo que asigne memoria a los archivos o que se sincronice con otros procesos.
Cort Ammon
5

la memoria compartida no es la imagen completa para IPC, es un mecanismo de transmisión de datos, pero aún necesita alguna forma de informar al otro proceso que algunos datos se han actualizado y están disponibles para ser leídos. La forma de hacerlo depende de usted, por lo general usaría un objeto de evento o mutex del sistema operativo, cada proceso espera a que se establezca, la escritura de la aplicación lo establece una vez que finaliza la escritura. Luego los hilos en los otros programas se despiertan y leen.

Alternativamente, puede sondear, leer los datos regularmente para obtener un valor que cambia cuando se actualizan los datos (por ejemplo, un contador incremental).

gbjbaanb
fuente
4

Boost utiliza la asignación de memoria de un archivo.

Tanto Unix como Windows admiten la creación de archivos que no existen en el sistema de archivos normal solo para este propósito.

Entonces necesitará sincronizar el acceso a esa memoria como lo haría si diferentes hilos tuvieran acceso a ella. Lo que significa que las lecturas concurrentes pueden ocurrir sin sincronización, pero tan pronto como un proceso desee escribir, deberá evitar que los demás accedan a él.

Las operaciones atómicas en la memoria compartida aún son posibles si desea una sincronización sin bloqueo.

monstruo de trinquete
fuente
¿Puede exponer "Las operaciones atómicas en la memoria compartida aún es posible si desea una sincronización sin bloqueo", por favor? Quiere decir que si cada programa es C ++, debería usar la std::atomicclase de plantilla C ++ 11 ( cplusplus.com/reference/atomic ) en cada uno de los dos programas para que puedan escribir en el espacio compartido sin sincronización forzada a través de bloqueos?
Gabriel Staples
@GabrielStaples sí o los intrínsecos atómicas equivalentes
de trinquete monstruo
Perdón por molestarte de nuevo; No estoy familiarizado con la "intrínseca atómica". ¿Me puede indicar una referencia para estudiarlos más? Una búsqueda rápida en Google no está clara.
Gabriel Staples
3

La memoria compartida sigue siendo solo memoria. Puede poner un mutex, spinlock o cualquier otra primitiva de sincronización allí, y usarlos para sincronizar el acceso de sus procesos a la memoria compartida, exactamente como los hilos usan esas primitivas para sincronizar el acceso a la memoria visible para ellos.

Las únicas diferencias reales son:

  1. los subprocesos comparten toda la memoria y el mismo espacio de direcciones, por lo que los punteros sin procesar funcionan para ellos. La memoria compartida entre procesos funciona exactamente igual, pero puede asignarse a diferentes direcciones en cada proceso, por lo que no puede simplemente pasar punteros sin procesar entre ellos

    • NÓTESE BIEN. esto tiene un efecto secundario en algunos detalles de implementación de métodos virtuales, información de tipo de tiempo de ejecución y algunos otros mecanismos de C ++. Apéguese a los tipos trivialmente inicializables (datos antiguos simples) sin métodos virtuales o conversiones dinámicas en su memoria compartida, no use typeid en ellos, y debería estar bien.
  2. Algunas primitivas de sincronización pueden necesitar indicadores o atributos especiales para funcionar correctamente entre procesos (ver el PTHREAD_PROCESS_SHAREDatributo para mutexes de hilos POSIX, por ejemplo). En realidad, esto no tiene que ver con la memoria y la sincronización en sí mismas, sino debido a la interacción kernel / Scheduler necesaria para despertar a los camareros dormidos.


Entonces:

¿Pero cómo se comunican?

De la misma forma en que diferentes hilos se comunican, permitiendo las advertencias anteriores

¿No es inseguro hacer esto?

Sí, es exactamente tan inseguro que los procesos se comuniquen a través de la memoria compartida como lo es para que los hilos se comuniquen a través de la memoria compartida, y necesitan una sincronización equivalente (o idéntica) para que sea ​​seguro.

Inútil
fuente
3

Tenga en cuenta que C y C ++ son lenguajes diferentes.

La memoria compartida es imposible en C11 puramente estándar , o C ++ 11 (ya que el estándar no define eso), o incluso C ++ 14 (cuyo borrador n3690 , y presumiblemente estándar oficial, no menciona la memoria compartida fuera del multihilo ) Por lo tanto, necesita bibliotecas adicionales para obtener memoria compartida. Pero algunos sistemas operativos tienen soporte para memoria compartida. Por lo tanto, existen varias bibliotecas que proporcionan memoria compartida, creadas sobre los servicios existentes del sistema operativo. Quizás podría considerar usar la biblioteca de marco POCO (que resume los detalles específicos del sistema operativo)

Para Linux (y tal vez POSIX), busque en shm_overview (7) . Tendrá que sincronizar, así que vea también sem_overview (7)

VXWorks (que no sé, pero busqué en Google) tiene VxMP

Necesita comprender cuidadosamente lo que realmente está sucediendo. Probablemente desee compartir solo datos antiguos simples struct(¡no clases de C ++!) Y debe tener mucho cuidado con las direcciones (cada proceso puede obtener direcciones diferentes para el segmento común de memoria compartida) y la sincronización.

Alternativamente, use hilos. Observe que el estándar C ++ 11 define una biblioteca de hilos .

Basile Starynkevitch
fuente
1
¡Por supuesto que la memoria compartida es posible tanto en C ++ 11 como en C11! el hecho de que no sea parte del estándar no tiene sentido, la GUI tampoco es parte del estándar. Hay varias bibliotecas de algunas plataformas que permiten que se debe usar la memoria compartida y varios métodos de sincronización ...
AK_
1
Mi punto era que la memoria compartida no está definida por el estándar C ++ 11 (pero algunas implementaciones las tienen a través de servicios específicos del sistema operativo)
Basile Starynkevitch
Creo que debería tomarse el tiempo para comprender qué significa realmente el término "estándar de lenguaje", qué es una implementación que cumple con los estándares y qué es una biblioteca.
AK_
Indíqueme la sección del estándar C ++ 11 (o en el borrador n3690 de C ++ 14) que habla sobre la memoria compartida.
Basile Starynkevitch
3
Creo que ambos estamos de acuerdo, pero podríamos estar en desacuerdo sobre lo que significa C ++ 11 estándar. Para mí, es exactamente (no más que) el estándar ISO C ++ 11 (o el borrador libre equivalente a él). Las bibliotecas externas no cuentan como estándar, incluso si son comunes y multiplataforma.
Basile Starynkevitch