作者dctzeng (DC)
看板Python
標題[問題] 請教處理ctypes dll 回傳 c_ubyte_p 問題
時間Thu Aug 14 23:05:32 2014
抱歉爬了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://webptt.com/m.aspx?n=bbs/Python/M.1408028735.A.1EB.html
※ 編輯: dctzeng (111.243.146.150), 08/14/2014 23:39:41
1F:推 LiloHuang: 如果要在 Python 內操作資料,資料通常都得做搬動的 08/14 23:35
2F:→ LiloHuang: 我猜你應該是想要做以下的事情...我先把問題簡化 08/14 23:35
3F:→ LiloHuang: 假設使用 addr = ctypes.cdll.msvcrt.malloc(1024) 08/14 23:36
4F:→ LiloHuang: 得到一個記憶體位置為 addr,類似你的GetBufferAddress 08/14 23:36
5F:→ LiloHuang: 如果我先用 memmove(addr, "\xFF\xFF\x00\x00", 4) 08/14 23:36
6F:→ LiloHuang: 此時應該會寫入 4 byte 的資料,考慮 little endian 08/14 23:37
7F:→ LiloHuang: cast(x, POINTER(c_int)).contents.value 會讀出 65535 08/14 23:37
8F:→ LiloHuang: 抱歉上面的 x 是指 addr 的意思,推文實在塞不下 08/14 23:38
9F:→ LiloHuang: 再次更正 x = c_void_p(addr) 08/14 23:39
10F:→ LiloHuang: 使用 c_void_p(addr) 的方式將你的指標塞進去後 08/14 23:40
11F:→ LiloHuang: 就可以用 cast 搭配 contents.value 的方式來做存取了 08/14 23:41
12F:→ dctzeng: 那在python宣告記憶體,把位址傳給dll去處理會省copy嗎? 08/14 23:42
13F:推 LiloHuang: 這樣記憶體就是由 Python runtime 幫你管理 08/14 23:44
14F:→ dctzeng: 例如宣告1MB string 把位址當參數給dll當char* 處理好嗎 08/14 23:44
15F:→ LiloHuang: 至於拷貝看是拷貝什麼,畢竟很多東西都得變 PyObject 08/14 23:44
16F:→ LiloHuang: 可以的呀,正常操作狀況不會一直拷貝那 1MB 的 08/14 23:45
17F:→ dctzeng: 但其實string 不是char*,是硬碟任意資料 fd.Read() 來的 08/14 23:45
18F:→ dctzeng: 就是 fd.Read()讀一坨資料; dll讀一坨資料,兩個相比而已 08/14 23:47
19F:→ LiloHuang: 你可以看看 byref, addressof, cast 這幾個的用法 08/14 23:47
20F:→ LiloHuang: 資料從 Python 跟 C API 做交換,ctypes 會有一些成本 08/14 23:48
21F:→ dctzeng: 只是資料量大而且多thread要長時間跑,很不想額外memcpy 08/14 23:48
22F:→ LiloHuang: 當你東西是使用指標 (也就是我上面的範例) 算快的做法 08/14 23:48
23F:→ LiloHuang: 如果是我寫我會盡量讓 Python runtime 來幫我管記憶體 08/14 23:49
24F:→ LiloHuang: 當然還是得看場合,到底是誰該擺那一塊記憶體... 08/14 23:51
25F:→ LiloHuang: 好比說用 create_string_buffer 來產生 buffer object 08/14 23:52
26F:→ dctzeng: 那1MB會重複一直讀出來是因為那是dll的buffer,會重複更新 08/14 23:54
27F:推 LiloHuang: 不好意思我現在看到你修改後的文章了... 08/14 23:55
28F:→ dctzeng: 喔~ 不會 看到你提一些關鍵字沒用過,google查一查 偷學 08/14 23:56
29F:推 LiloHuang: 那我重新修正,如果你只是想要比較用 c_char_p 就好 08/15 00:08
30F:→ LiloHuang: foo = 'x'*1024*1024*100 # 記憶體耗用 100 MB 左右 08/15 00:09
31F:→ LiloHuang: bar = c_char_p(foo) # 這個動作不會拷貝 100MB 08/15 00:09
32F:→ LiloHuang: ctypes.memmove(bar, "test", 4) # 硬是修改 foo 字串 08/15 00:09
33F:→ LiloHuang: # 雷同於你傳入 DLL 08/15 00:10
34F:→ LiloHuang: print foo[0:10] # 順便再印出 foo 的一部分觀察 08/15 00:10
35F:→ LiloHuang: 以上的 foo 類似於你從 fd.read 讀出的字串結果 08/15 00:11
36F:→ LiloHuang: ctypes.memmove 類似於你帶入 DLL 的部分,試試看囉 08/15 00:12
37F:→ LiloHuang: 可搭配 ProcessExplorer 選 python.exe 觀察記憶體用量 08/15 00:14
我試試 先謝謝
※ 編輯: dctzeng (111.243.146.150), 08/15/2014 00:23:42
38F:推 LiloHuang: 順便回你文章的那個問題,那個寫法應該也不會拷貝資料 08/15 00:41
39F:→ LiloHuang: 至於之所以會出錯的原因是 from_address 是吃 integer 08/15 00:42
40F:→ LiloHuang: 用了 hex 就變成了字串,錯誤訊息是指參數型別不正確 08/15 00:43
41F:→ LiloHuang: 這種做法也是拿一個指標來操作的做法,都相當的快速 08/15 00:44
※ 編輯: dctzeng (111.243.146.150), 08/15/2014 01:06:02