मैं एक बाइनरी पायथन लाइब्रेरी का उपयोग कर रहा हूं जो बफर ऑब्जेक्ट देता है। यह ऑब्जेक्ट मूल रूप से C ऑब्जेक्ट का एक आवरण है जिसमें वास्तविक मेमोरी बफर के लिए एक पॉइंटर होता है। मुझे जो चाहिए वह पाइथन से उस पॉइंटर में निहित मेमोरी एड्रेस प्राप्त करना है, समस्या यह है कि बफर ऑब्जेक्ट में इसे प्राप्त करने के लिए पाइथन विधि नहीं है, इसलिए मुझे इसे प्राप्त करने के लिए कुछ हैकी चाल करने की ज़रूरत है।

फिलहाल मुझे सूचक मूल्य प्राप्त करने का एक बदसूरत और असुरक्षित तरीका मिला है:

मैं सी वस्तु की आंतरिक संरचना जानता हूँ:

typedef struct _Buffer {
  PyObject_VAR_HEAD PyObject *parent;

  int type; /* GL_BYTE, GL_SHORT, GL_INT, GL_FLOAT */
  int ndimensions;
  int *dimensions;

  union {
    char *asbyte;
    short *asshort;
    int *asint;
    float *asfloat;
    double *asdouble;

    void *asvoid;
  } buf;
} Buffer;

तो मैंने यह पायथन कोड लिखा:

# + PyObject_VAR_HEAD size
# + 8 bytes PyObject_VAR_HEAD PyObject *parent
# + 4 bytes from int type
# + 4 bytes from int ndimensions
# + 8 bytes from int *dimensions
# = 24
offset = sys.getsizeof(0) + 24

buffer_pointer_addr = id(buffer) + offset
buffer_pointer_data = ctypes.string_at(buffer_pointer_addr, 8)
buffer_pointer_value = struct.unpack('Q', buffer_pointer_data)[0]

यह मेरे लिए लगातार काम कर रहा है। जैसा कि आप देख सकते हैं कि मुझे id(buffer) के साथ पायथन बफर ऑब्जेक्ट का मेमोरी एड्रेस मिल रहा है, लेकिन जैसा कि आप जानते होंगे कि यह बफर का वास्तविक पॉइंटर नहीं है, बल्कि सिर्फ एक पायथन नंबर है जो सीपीथॉन में होता है पायथन ऑब्जेक्ट को पता।

तो फिर मैं सी संरचना में सभी चर के आकार जोड़कर गणना की गई ऑफसेट जोड़ रहा हूं। मैं PyObject_VAR_HEAD को छोड़कर बाइट आकार (जो स्पष्ट रूप से पूरी तरह से असुरक्षित है) को हार्डकोड कर रहा हूं, जो मुझे sys.getsizeof(0) के साथ मिलता है।

ऑफ़सेट जोड़कर मुझे मेमोरी एड्रेस मिलता है जिसमें वास्तविक बफर में पॉइंटर होता है, फिर मैं इसे ctypes.string_at के साथ निकालने के लिए ctypes का उपयोग करता हूं, पॉइंटर के आकार को 8 बाइट्स के रूप में हार्डकोड करता हूं (मैं 64 बिट ओएस पर हूं), फिर मैं इसे वास्तविक पायथन int में बदलने के लिए struct.unpack का उपयोग करता हूं।

तो अब मेरा प्रश्न है: मैं सभी आकारों को हार्डकोड किए बिना एक सुरक्षित समाधान कैसे लागू कर सकता हूं? (यदि यह मौजूद है)। शायद ctypes के साथ कुछ? यह ठीक है अगर यह केवल CPython पर काम करता है।

1
ciclopez 22 अक्टूबर 2020, 19:19

1 उत्तर

सबसे बढ़िया उत्तर

सी स्ट्रक्चर पैडिंग के बारे में जांच करने और निम्नलिखित मान्यताओं के आधार पर मुझे एक सुरक्षित समाधान मिला:

  • कोड का उपयोग केवल CPython पर किया जाएगा।
  • बफर पॉइंटर सी स्ट्रक्चर के अंत में है।
  • बफर पॉइंटर आकार को शून्य * सी-प्रकार से सुरक्षित रूप से निकाला जा सकता है क्योंकि यह सी संरचना में बने union{} में से सबसे बड़ा होने जा रहा है। वैसे भी अधिकांश आधुनिक ओएस पर डेटा पॉइंटर प्रकारों के बीच कोई भिन्न आकार नहीं होगा।
  • सी स्ट्रक्चर सदस्य बिल्कुल वही होंगे जो प्रश्न में दिखाए गए हैं

इन सभी मान्यताओं और यहां पाए गए नियमों के आधार पर: https://stackoverflow.com/a/38144117/8861787, हम सुरक्षित रूप से कह सकते हैं कि संरचना के अंत में कोई पैडिंग नहीं होगी और हम कुछ भी हार्डकोड किए बिना पॉइंटर निकाल सकते हैं:

# Get the size of the Buffer Python object
buffer_obj_size = sys.getsizeof(buffer)

# Get the size of void * C-type
buffer_pointer_size = ctypes.sizeof(ctypes.c_void_p)

# Calculate the address to the pointer assuming that it's at the end of the C Struct
buffer_pointer_addr = id(buffer) + buffer_obj_size - buffer_pointer_size

# Get the actual pointer value as a Python Int
buffer_pointer_value = (ctypes.c_void_p).from_address(buffer_pointer_addr).value
1
ciclopez 23 अक्टूबर 2020, 13:30