Como compartilhar memória entre aplicativos gravados em C / C ++

9

Estou passando por um programa escrito em C / C ++ para controle em robótica. Basicamente, três programas diferentes são executados ao mesmo tempo e eles se comunicam via memória compartilhada. O Google que encontrei parece com o vxWorks e os cabeçalhos de interprocessamento das bibliotecas de reforço ( documentação do Boost: compartilhamento de memória entre processos ).

Agora, não quero olhar para a implementação, posso ler o link acima. Mas não consigo entender como a biblioteca de impulso faz isso. Quero dizer, um aplicativo aloca memória e outro acessa essa memória, mas como eles se comunicam? não é perigoso fazer isso?

cauchy
fonte
o que parece incerto nos documentos que você indicou? "Ao colocar objetos em uma região mapeada e mapear essa região em endereços diferentes em todos os processos, os ponteiros brutos são um problema, pois são válidos apenas para o processo que os colocou lá. Para resolver isso, o Boost.Interprocess oferece um ponteiro inteligente especial que pode ser usado em vez de um ponteiro bruto. Portanto, as classes de usuário que contêm ponteiros brutos (ou Boost smart ponteiros, que possuem internamente um ponteiro bruto) não podem ser colocados com segurança em uma região mapeada compartilhada do processo.Esses ponteiros devem ser substituídos por ... "
gnat
Não esqueça que o sistema operacional é o processo que realmente cuida da memória. Os processos que alocam e usam a memória no final do dia usam o SO para fazer suas solicitações. O sistema operacional gerencia o multiprocessamento e garante que não haja conflitos.
Rob Sedgwick
@gnat A implementação é clara. Como o Boost faz com que a implementação não esteja nesses documentos ... Não quero replicá-lo (isso seria absurdo), mas entendê-lo.
cauchy

Respostas:

11

Mas não consigo entender como a biblioteca de impulso faz isso.

O mecanismo de interprocesso de impulso tem três componentes necessários para funcionar:

  1. arquivo mapeado na memória: um arquivo mapeado na memória precisa ser criado e passado para um alocador boost.interprocess. Esse alocador pega pedaços do arquivo e os usa como se fossem retornados por um std :: alocador, com o mapeamento aplicado para que a memória seja compatível com a memória específica em processo.

  2. container boost.interprocess; esse tipo de contêiner usará a memória retornada pelo alocador e oferecerá uma interface como std :: container (begin / end / size / push_back, etc).

  3. mecanismo de sincronização; pode ser qualquer mutex entre processos e deve ser usado para impedir condições de corrida de acesso a dados.

Quero dizer, um aplicativo aloca memória e outro acessa essa memória, mas como eles se comunicam? não é perigoso fazer isso?

A memória alocada é na verdade um arquivo mapeado na memória compartilhada. A comunicação é indireta, com os aplicativos configurando ou lendo os dados, conforme necessário. A segurança vem do uso de primitivas de sincronização interprocessos.

utnapistim
fonte
2
Vale a pena notar: Todos esses três mecanismos do IPC exigem suporte ao kernel; portanto, para aqueles que estão curiosos sobre como isso é feito (como o OP indicou), não pode ser feito apenas por aplicativos individuais. Os aplicativos precisam solicitar ao kernel que mapeie os arquivos ou sincronize com outros processos.
precisa saber é o seguinte
5

a memória compartilhada não é a imagem completa do IPC, é um mecanismo de passagem de dados, mas você ainda precisa de alguma maneira de informar o outro processo de que alguns dados foram atualizados e estão disponíveis para leitura. Você decide como você faz isso, normalmente você usaria um objeto de evento ou mutex do SO; cada processo espera que isso seja definido, a gravação do aplicativo o define assim que termina a gravação. Em seguida, os threads nos outros programas são ativados e lidos.

Como alternativa, você pode pesquisar, ler os dados regularmente para obter um valor que muda quando os dados são atualizados (por exemplo, um contador incremental).

gbjbaanb
fonte
4

O Boost usa o mapeamento de memória de um arquivo.

O unix e o Windows suportam a criação de arquivos que não existem no sistema de arquivos normal para esse fim.

Em seguida, você precisará sincronizar o acesso a essa memória como faria se threads diferentes o acessassem. Isso significa que leituras simultâneas podem ocorrer sem sincronização, mas assim que um processo desejar, você precisará impedir que outros acessem.

Operações atômicas na memória compartilhada ainda serão possíveis se você desejar uma sincronização sem bloqueio.

catraca arrepiante
fonte
Você pode explicar como "As operações atômicas na memória compartilhada ainda são possíveis se você desejar uma sincronização sem bloqueio", por favor? Você quer dizer que, se cada programa for C ++, você precisará usar a std::atomicclasse de modelo C ++ 11 ( cplusplus.com/reference/atomic ) em cada um dos dois programas para que eles possam gravar no espaço compartilhado sem sincronização aplicada por meio de bloqueios?
Gabriel Staples
@GabrielStaples sim ou os intrínsecos atómicas equivalentes
roquete aberração
Desculpa por te incomodar novamente; Não estou familiarizado com "intrínsecas atômicas". Você pode me indicar uma referência para estudá-los mais? Uma pesquisa rápida no Google não é clara.
Gabriel Staples
3

A memória compartilhada ainda é apenas memória. Você pode colocar um mutex, spinlock ou qualquer outra primitiva de sincronização lá e usá-los para sincronizar o acesso de seus processos à memória compartilhada, exatamente como os threads usam essas primitivas para sincronizar o acesso à memória visível para eles.

As únicas diferenças reais são:

  1. os threads compartilham toda a memória e o mesmo espaço de endereço; portanto, ponteiros brutos trabalham para eles. A memória compartilhada entre processos funciona exatamente da mesma forma, mas pode ser mapeada em endereços diferentes em cada processo, portanto, você não pode simplesmente passar ponteiros brutos entre eles

    • NB. isso tem um efeito indireto em alguns detalhes de implementação de métodos virtuais, informações sobre o tipo de tempo de execução e alguns outros mecanismos C ++. Atenha-se a tipos trivialmente inicializáveis ​​(dados antigos simples) sem métodos virtuais ou conversões dinâmicas na memória compartilhada, não use typeid neles e você deve ficar bem.
  2. algumas primitivas de sincronização podem precisar de sinalizadores ou atributos especiais para funcionar corretamente entre processos (consulte o PTHREAD_PROCESS_SHAREDatributo para mutexes de encadeamento POSIX, por exemplo). Isso não tem nada a ver com a memória e a sincronização em si, mas devido à interação do kernel / agendador necessária para ativar os garçons adormecidos.


Assim:

mas como eles se comunicam?

Da mesma maneira que diferentes threads se comunicam, permitindo as advertências acima

não é perigoso fazer isso?

Sim, é exatamente tão inseguro para os processos se comunicarem via memória compartilhada quanto para os threads se comunicarem via memória compartilhada, e eles precisam de sincronização equivalente (ou idêntica) para torná- la segura.

Sem utilidade
fonte
3

Observe que C e C ++ são linguagens diferentes.

A memória compartilhada é impossível no padrão C11 ou C ++ 11 (já que o padrão não define isso), ou mesmo no C ++ 14 (cujo rascunho n3690 , e presumivelmente padrão oficial, não menciona memória compartilhada fora do multi-threading ) Então você precisa de bibliotecas extras para obter memória compartilhada. Mas alguns sistemas operacionais têm suporte para memória compartilhada. Portanto, existem várias bibliotecas que fornecem memória compartilhada, construídas acima dos serviços existentes do sistema operacional. Talvez você possa considerar o uso da biblioteca de estruturas POCO (que abstrai detalhes específicos do SO)

Para Linux (e talvez POSIX), consulte shm_overview (7) . Você precisará sincronizar, veja também sem_overview (7)

O VXWorks (que eu não conheço, mas pesquisei por ele) possui o VxMP

Você precisa entender cuidadosamente o que realmente está acontecendo. Você provavelmente deseja compartilhar apenas dados antigos simples struct(não classes C ++!) E deve ter muito cuidado com os endereços (cada processo pode obter um endereço diferente para o segmento de memória compartilhada comum) e com a sincronização.

Como alternativa, use threads. Observe que o padrão C ++ 11 define uma biblioteca de threads .

Basile Starynkevitch
fonte
1
É claro que a memória compartilhada é possível no C ++ 11 e no C11! o fato de não fazer parte do padrão não faz sentido, a GUI também não faz parte do padrão. Existem várias bibliotecas de algumas plataformas que permitem usar a memória compartilhada e vários métodos de sincronização ...
AK_
1
Meu ponto foi que a memória compartilhada não é definida pelo C 11 ++ padrão (mas algumas implementações tê-los através de serviços específicos OS)
Basile Starynkevitch
Eu acho que você deve dedicar um tempo para entender o que realmente significa o termo "padrão de linguagem", o que é uma implementação compatível com o padrão e o que é uma biblioteca.
AK_
Por favor, aponte-me para a seção no padrão C ++ 11 (ou no rascunho n3690 do C ++ 14) falando sobre memória compartilhada.
precisa saber é o seguinte
3
Acho que ambos concordamos, mas podemos discordar sobre o que significa C ++ 11 padrão. Para mim, é exatamente (não mais que) o padrão ISO C ++ 11 (ou o rascunho gratuito equivalente a ele). Bibliotecas externas não contam como padrão, mesmo que sejam comuns e multiplataforma.
Basile Starynkevitch