2017-07-19 3 views
1

Il existe de nombreux exemples montrant comment écrire des variables individuelles ou même des membres individuels d'une structure dans la mémoire partagée, mais existe-t-il un moyen de placer la structure entière dans la mémoire partagée?Comment écrire une structure complète dans la mémoire partagée en python?

Ceci est un exemple de ce que je fais jusqu'à présent (dans mon programme actuel - il y a plus de 50 champs dans la structure - probablement 100+ au moment où j'ai fini). Il met à jour la mémoire partagée avec les coordonnées x, y, z toutes les 0,05 secondes. Bien que cela fonctionne comme il se trouve, il est en train d'emballer une nouvelle structure à chaque étape et d'écrire le tout dans la mémoire partagée - ce qui me semble inefficace.

import mmap 
import struct 
import ctypes 
import time 
import random 

class GenericData(ctypes.Structure): 
    _pack_ = 4 
    _fields_ = [ 
     ('PosX', ctypes.c_float), 
     ('PosY', ctypes.c_float), 
     ('PosZ', ctypes.c_float), 
    ] 

# fake getters: 
def getX(): 
    return random.random()*10 
getZ = getY = getX 

def main(): 
    buff = mmap.mmap(0, ctypes.sizeof(GenericData), "$MyTag$") 
    data = GenericData() 
    fmt = ''.join([f[1]._type_ for f in data._fields_]) 

    while (1): 
     data.PosX = getX() 
     data.PosY = getY() 
     data.PosZ = getZ() 

     print "Setting %f, %f, %f " % (data.PosX, data.PosY, data.PosZ) 
     struct.pack_into(fmt, buff, 0, *[getattr(data,field) for field,typ in data._fields_]) 
     time.sleep(0.05) 

if __name__ == "__main__": 
    main() 

Je suis conscient que je pouvais créer une cartographie des variables à des emplacements dans le fichier de mémoire partagée, mais avec tant de domaines, c'est un peu difficile à manier.

Je voudrais penser que la structure pourrait être le tampon (ou mappé à la mémoire tampon) et en définissant simplement des données.PosX, la mémoire partagée est mise à jour. Est-ce possible? Y a-t-il un moyen de rendre cela plus efficace? La ligne struct.pack_into est celle qui me concerne.

Je voudrais penser que quelque chose comme cela pourrait se faire:

buff = mmap.mmap(0, ctypes.sizeof(GenericData), "$MyTag$") 
data = from_buffer(buff, GenericData) 
while (1): 
    data.posX = getX() 
    data.posY = getY() 
    data.posZ = getZ() 
    time.sleep(0.05) 

... qui serait alors mettre à jour la mémoire partagée. Possible?

+1

Vous êtes très, très proche. Avez-vous recherché le tutoriel ctypes et la référence pour "from_buffer"? C'est vraiment simple: 'data = GenericData.from_buffer (buff)'. – eryksun

+0

'fileno' devrait être -1 pour mapper la mémoire anonyme. Sur Windows, mmap autorise 0 pour cela, même si 0 est un descripteur de fichier valide. Toutefois, dans une future version, il peut être mis à jour pour déprécier en utilisant 0 sur Windows. – eryksun

+0

@eryksun: Merci! Je savais que j'étais proche - je ne savais pas que j'étais si proche! Aussi, merci pour la clarté sur 0 vs -1 pour le descripteur de fichier. Vouliez-vous fournir une réponse officielle pour que je puisse l'accepter? (sinon, je vais me répondre et vous citer) –

Répondre

1

Comme @eryksun a souligné dans le premier commentaire à la question, vous pouvez utiliser ctypes.from_buffer pour partager la structure de ctypes GenericData avec le tampon de mmap buff. @eryksun a également souligné que, bien que Windows autorise 0 comme un descripteur de fichier pour mapper la mémoire anonyme, -1 est la valeur correcte - comme cela est mentionné dans les docs.

Avec cela, voici un exemple de travail, ajusté pour inclure @ réponse de eryksun:

import ctypes 
import mmap 
import time 
import math 

class GenericData(ctypes.Structure): 
    _pack_ = 4 
    _fields_ = [ 
     ('PosX', ctypes.c_float), 
     ('PosY', ctypes.c_float), 
     ('PosZ', ctypes.c_float), 
    ] 

# fake getters: 
def getX(): 
    return random.random()*10 
getZ = getY = getX 

def main(): 
    buff = mmap.mmap(-1, ctypes.sizeof(GenericData), "$MyTag$") 

    data = GenericData.from_buffer(buff) 

    for fname, ftype in data._fields_: 
     setattr(data, fname, 0) 

    count = 0 

    while (1): 
     data.PosX = getX() 
     data.PosY = getY() 
     data.PosZ = getZ() 

     print ("Setting %f, %f, %f " % (data.PosX, data.PosY, data.PosZ)) 
     count += 1 
     time.sleep(0.05) 


if __name__ == "__main__": 
    main()