[問題] 請教處理ctypes dll 回傳 c_ubyte_p 問題

看板Python作者 (DC)時間11年前 (2014/08/14 23:05), 11年前編輯推噓5(5036)
留言41則, 2人參與, 最新討論串1/1
抱歉爬了google找不太到的樣子,來這找高手大大 import ctypes 外部的dll C code 類似: static char gp_huge_buffer[1024][1024]; unsigned char *GetBufferAddress(void){ //change gp_huge_buffer contain return (unsigned char *)gp_huge_buffer; } Python 類似去呼叫dll: mydll = ctypes.cdll.LoadLibrary(path) mydll.GetBufferAddress.argtypes = [] mydll.GetBufferAddress.restype = ctypes.POINTER(ctypes.c_ubyte) # Google找到的,好像是在python中拷貝一份,而且好像不能用 p_ubyte = mydll.GetBufferAddress() byte_array = c_ubyte * buffer_size bytes = byte_array.from_address(hex(p_ubyte)) # error ! LP_c_ubyte cannot be # interreted as integer 因為存資料的 buffer 是在 dll 中配置的,C 資料放進去想和 python 的 fd.read()比對 為了速度和記憶體考量下不想在python再重新從dll拷貝出來 請問有辦法向C語言直接拿pointer存取GetBufferAddress回傳位址中的任意byte嗎? ps 雖然不想用 bytes = byte_array.from_address(hex(p_ubyte)) 但想請教一下這樣是為甚麼報錯呢? -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 111.235.222.156 ※ 文章網址: http://www.ptt.cc/bbs/Python/M.1408028735.A.1EB.html ※ 編輯: dctzeng (111.243.146.150), 08/14/2014 23:39:41

08/14 23:35, , 1F
如果要在 Python 內操作資料,資料通常都得做搬動的
08/14 23:35, 1F

08/14 23:35, , 2F
我猜你應該是想要做以下的事情...我先把問題簡化
08/14 23:35, 2F

08/14 23:36, , 3F
假設使用 addr = ctypes.cdll.msvcrt.malloc(1024)
08/14 23:36, 3F

08/14 23:36, , 4F
得到一個記憶體位置為 addr,類似你的GetBufferAddress
08/14 23:36, 4F

08/14 23:36, , 5F
如果我先用 memmove(addr, "\xFF\xFF\x00\x00", 4)
08/14 23:36, 5F

08/14 23:37, , 6F
此時應該會寫入 4 byte 的資料,考慮 little endian
08/14 23:37, 6F

08/14 23:37, , 7F
cast(x, POINTER(c_int)).contents.value 會讀出 65535
08/14 23:37, 7F

08/14 23:38, , 8F
抱歉上面的 x 是指 addr 的意思,推文實在塞不下
08/14 23:38, 8F

08/14 23:39, , 9F
再次更正 x = c_void_p(addr)
08/14 23:39, 9F

08/14 23:40, , 10F
使用 c_void_p(addr) 的方式將你的指標塞進去後
08/14 23:40, 10F

08/14 23:41, , 11F
就可以用 cast 搭配 contents.value 的方式來做存取了
08/14 23:41, 11F

08/14 23:42, , 12F
那在python宣告記憶體,把位址傳給dll去處理會省copy嗎?
08/14 23:42, 12F

08/14 23:44, , 13F
這樣記憶體就是由 Python runtime 幫你管理
08/14 23:44, 13F

08/14 23:44, , 14F
例如宣告1MB string 把位址當參數給dll當char* 處理好嗎
08/14 23:44, 14F

08/14 23:44, , 15F
至於拷貝看是拷貝什麼,畢竟很多東西都得變 PyObject
08/14 23:44, 15F

08/14 23:45, , 16F
可以的呀,正常操作狀況不會一直拷貝那 1MB 的
08/14 23:45, 16F

08/14 23:45, , 17F
但其實string 不是char*,是硬碟任意資料 fd.Read() 來的
08/14 23:45, 17F

08/14 23:47, , 18F
就是 fd.Read()讀一坨資料; dll讀一坨資料,兩個相比而已
08/14 23:47, 18F

08/14 23:47, , 19F
你可以看看 byref, addressof, cast 這幾個的用法
08/14 23:47, 19F

08/14 23:48, , 20F
資料從 Python 跟 C API 做交換,ctypes 會有一些成本
08/14 23:48, 20F

08/14 23:48, , 21F
只是資料量大而且多thread要長時間跑,很不想額外memcpy
08/14 23:48, 21F

08/14 23:48, , 22F
當你東西是使用指標 (也就是我上面的範例) 算快的做法
08/14 23:48, 22F

08/14 23:49, , 23F
如果是我寫我會盡量讓 Python runtime 來幫我管記憶體
08/14 23:49, 23F

08/14 23:51, , 24F
當然還是得看場合,到底是誰該擺那一塊記憶體...
08/14 23:51, 24F

08/14 23:52, , 25F
好比說用 create_string_buffer 來產生 buffer object
08/14 23:52, 25F

08/14 23:54, , 26F
那1MB會重複一直讀出來是因為那是dll的buffer,會重複更新
08/14 23:54, 26F

08/14 23:55, , 27F
不好意思我現在看到你修改後的文章了...
08/14 23:55, 27F

08/14 23:56, , 28F
喔~ 不會 看到你提一些關鍵字沒用過,google查一查 偷學
08/14 23:56, 28F

08/15 00:08, , 29F
那我重新修正,如果你只是想要比較用 c_char_p 就好
08/15 00:08, 29F

08/15 00:09, , 30F
foo = 'x'*1024*1024*100 # 記憶體耗用 100 MB 左右
08/15 00:09, 30F

08/15 00:09, , 31F
bar = c_char_p(foo) # 這個動作不會拷貝 100MB
08/15 00:09, 31F

08/15 00:09, , 32F
ctypes.memmove(bar, "test", 4) # 硬是修改 foo 字串
08/15 00:09, 32F

08/15 00:10, , 33F
# 雷同於你傳入 DLL
08/15 00:10, 33F

08/15 00:10, , 34F
print foo[0:10] # 順便再印出 foo 的一部分觀察
08/15 00:10, 34F

08/15 00:11, , 35F
以上的 foo 類似於你從 fd.read 讀出的字串結果
08/15 00:11, 35F

08/15 00:12, , 36F
ctypes.memmove 類似於你帶入 DLL 的部分,試試看囉
08/15 00:12, 36F

08/15 00:14, , 37F
可搭配 ProcessExplorer 選 python.exe 觀察記憶體用量
08/15 00:14, 37F
我試試 先謝謝 ※ 編輯: dctzeng (111.243.146.150), 08/15/2014 00:23:42

08/15 00:41, , 38F
順便回你文章的那個問題,那個寫法應該也不會拷貝資料
08/15 00:41, 38F

08/15 00:42, , 39F
至於之所以會出錯的原因是 from_address 是吃 integer
08/15 00:42, 39F

08/15 00:43, , 40F
用了 hex 就變成了字串,錯誤訊息是指參數型別不正確
08/15 00:43, 40F

08/15 00:44, , 41F
這種做法也是拿一個指標來操作的做法,都相當的快速
08/15 00:44, 41F
※ 編輯: dctzeng (111.243.146.150), 08/15/2014 01:06:02
文章代碼(AID): #1JxD0_7h (Python)
文章代碼(AID): #1JxD0_7h (Python)