[Python] ctypes 를 이용한 shard library 직접 호출 Develop Tip

홍민희 님의 Wand 라는 것을 살펴보다가,
파있썬의 ctypes 바인딩을 했다는 이야기가 나옵니다.

첨에는 뭔가 했다가, 바로 앗! 하고야 말았습니다.

기존에 C로 만든 함수와 파있썬을 붙이기 위해서는

c 함수에서 

#include <Python.h>
...

static PyObject * foo(PyObject *self, PyObject *args)
{
  ...
  if (!PyArg_ParseTuple(args,"isi", &pa, &pb, &pc)) {
    error();
  }
  ...
}

등과 같이 작업을 하였습니다.

그동안 몇번 생각을 했던 적이 있습니다.

C# 코딩을 하다보면, unmanaged dll을 호출하기 위한
dllimport 라는 함수가 있습니다.
그러면 C#에서 바로 해당 unmanaged dll 함수를 사용하는 것이지요.
파이썬에 이런게 있으면 얼마나 좋을까... 하구요.

바로 ctypes 라는 것이 이런 개념이었습니다.... ㅎㅎㅎ

바로 샘플을 만들어서 돌려보았습니다.

우선 mylib.c 라는 파일을 만들어 아래 내용을 넣어줍니다.

int sum(int i, int j)
{
    return i + j;
}

int mystrlen(char *s)
{
    char *p = s;
    for (;*p;++p);
    return p-s;
}

int hexdump(void *v, int len)
{
    unsigned char *p = v;
    int i = 0;
    for (;i<len;++i) {
        printf("%02x", p[i]);
        if (i > 0 && i % 8 == 0) printf("\n");
    }
    printf("\n");
    return len*2;
}

그 다음 다음과 같이 libmylib.so 라는 이름의 shared library 를 만듦니다.

$ gcc -c -fPIC mylib.c
$ gcc -shared mylib.o -o libmylib.so

다음에 test.py 라는 파일에 아래 내용을 넣습니다.

from ctypes import *
import os

s='Hello world?'

clib = cdll.LoadLibrary('libc.so.6')
print 'clib.time() = %d' % clib.time(None)
clib.printf("clib.printf(s) = <%s>\n", s)

mylib = cdll.LoadLibrary('%s/libmylib.so' % os.getcwd())

print 'mylib.sum(1,2) => %d' % mylib.sum(1,2)

print 'mystrlen("%s") => %d' % (s,mylib.mystrlen(s))

v=[]
v.append('a')
v.append('0')
v.append('1')
v.append(chr(0))
v.append('2')
v.append('3')
s = ''.join(v)
print 'python s = <%s>' % s
r = mylib.hexdump(s,len(s))
print "mylib.hexdump(s,len(s)) = %s" % r

위와 같이 내용을 넣고 실행하면,

$ python test.py 
clib.time() = 1369726019
clib.printf(s) = <Hello world?>
mylib.sum(1,2) => 3
mystrlen("Hello world?") => 12
python s = <a0123>
613031003233
mylib.hexdump(s,len(s)) = 12

와 같이 원하는 결과가 나옵니다.

call by reference 처럼 결과를 받아오는 것도 필요합니다.

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer('\000' * 32)
>>> print i.value, f.value, repr(s.value)
0 0.0 ''
>>> libc.sscanf("1 3.14 Hello", "%d %f %s",
...             byref(i), byref(f), s)
3
>>> print i.value, f.value, repr(s.value)
1 3.1400001049 'Hello'

와 같이 패러미터를 위한 방을 미리 c_int(), c_float(), create_string_buffer() 등으로 만든 다음
결과를 받아오면 됩니다.


참고로 C의 구조체를 다룰 수도 있는데,
다음과 같이 참고하면 됩니다.

class WrappedKey(Structure):
_fields_ = [('version', c_ushort),
('length',  c_ushort),
('id',      c_char * 8 ),
('key',     c_char * 32),
('auth',    c_char * 4)]
def __init__(self):
pass
def make(self, tp): # in tuple
self.version, self.length, self.id, self.key, self.auth = tp
def print_values(self):
print self.version, self.length, self.id, self.key, self.auth

사용 시
with open("MasterKey_V2.dat") as file:
data = file.read()
print type(data)
wk = WrappedKey()
wk.make(struct.unpack('HH8s32s4s', data[:48]))

C API 호출 시 

ret = CryptTest.mylib.UnWrapKey(
AI_AES_ECB,
AI_SHA1_HMAC,
szPBEKey,
len(szPBEKey),
byref(szRealKey),
byref(realKeyLen),
szKeyLabel,
byref(wk)
)

같은 식으로 하면 됩니다.

어느 분께는 도움이 되셨기를...




핑백

  • 지훈현서 : [Cython] 싸이썬, 파이썬에 속도를 더하다 2013-12-04 11:12:05 #

    ... 의 블로그 내용을 보았습니다. 파이썬을 짜면서 가장 걸리는 부분이 속도 문제인데 이를 해결하기 위하여프로파일링 등을 통한 시간이 오래 걸리는 함수들을 C로 포팅하여 CTypes 등으로 해결하고는 했습니다. 그런데 만약 뭔가 위에처럼 좋은게 있다면 얼마나 좋을까 하는 생각 들어서 살펴보았습니다. Cython 홈페이지를 들어가 ... more

  • 지훈현서 : [Python] PyPy, Cython, CTypes 속도 비교 2015-01-29 16:54:19 #

    ... 생성됩니다.이를 파이썬에서 이용할 수 있습니다.(다음 블로그 참조) ctypes :파이썬의 ctypes 를 이용하여 직접 쉐어드 라이브러리를 호출하는 것입니다.(다음 블로그 참조) 이런 식으로 빠르게 해 볼 수 있는데요, 우선 다음코드를 확인해 봅니다.(테스트는 같은 팀의 김진우 선임의 도움을 받았습니다) 아주 간단한 코드인데 바깥 ... more

덧글

  • 성은 2013/08/26 15:58 # 삭제 답글

    좋은 자료 감사드립니다.
  • 지훈현서아빠 2013/08/27 09:22 #

    별말씀을요, 도움이 되셨다면 저의 보람입니다~ ^^
  • elime 2014/06/23 21:14 # 삭제 답글

    고맙습니다. cython 하다가 짜증나서 그냥 c로 커널부분 쓰고 python에서 적당히 받아서 쓸수 있는거 없다 찾다가 이곳에 다다랐습니다.
    신세계네요! 거듭 감사합니다.
  • 지훈현서아빠 2014/06/23 22:12 #

    도움이 되셨다니 저의 보람입니다~~ ^^
  • asjgi 2016/05/13 16:43 # 삭제 답글

    call by reference 처럼 결과를 받아오는 것도 필요합니다.
    이부분 부터 이해가 안되네요 흐엉 ㅠㅠ
  • 지훈현서아빠 2016/05/13 21:30 #

    call by value, call by reference 등은 모두 C 에서 나온 개념들이라...
    그 부분을 공부하시면 도움이 될 듯 합니다.
  • asjgi 2016/05/16 21:24 # 삭제 답글

    call by refrence 모양으로

    c 파일에서는
    char creat_string_buffer(char *s){

    printf("string s = %s n" , s);
    return s;
    }

    이렇게 보내고 받을때는

    string_world = 'string world'

    s_return = mylib.creat_string_buffer(string_world)

    print 'mylib.string(string world) => %s' % s_return

    이렇게 받도록 바꿔 봤는데 ;
    계속 숫자가 찍히는 상황이 발생하는데요.
    뭐를 잘 못 한걸까요 ㅠ 아니면 제약사항으로 문자열은 보내지지 않나요 ?
  • asjgi 2016/05/16 21:38 # 삭제

    clib.time() = 1463402258
    clib.printf(s) = <Test by python to C lang => just call by cython>
    mylib.sum(1,2) => 3
    mystrlen("Test by python to C lang => just call by cython") => 47
    mylib.c_int(1,2) => 3
    string s = string world
    mylib.string(string world) => 571861772
    python s = <a0123>
    613031003233
    mylib.hexdump(s,len(s)) = 12
  • 지훈현서아빠 2016/05/17 09:22 #

  • asjgi 2016/05/17 10:46 # 삭제 답글

    아뇨 C베이스 코드에서의 수정과 출력은 정상적으로 잘 나와요 하지만 return s 해서 해당 리턴 값을 Python으로 받았을때 문자값으로 떨어지는게 아니라 주소 값?(-724428020) 으로 떨어져서 문제 입니다. ㅠ C베이스 코드처럼 문자의 주소를 프린트 하면 문자가 찍히는게 아니라 Python에서는 그냥 주소를 찍어버리는 듯 해서요......... 궁금합니다 ㅠㅠ 어떻게 해결 할 수 있는지 어제 오늘 파이썬을 처음으로 보고 있어서 모르는 점이 많네요ㅠ 알려주셔요 ~
  • 지훈현서아빠 2016/05/17 14:00 #

    http://stackoverflow.com/questions/32331772/how-to-display-a-string-at-a-certain-address-with-ctypes
    restype 을 지정하지 않으셔서 그랬을 듯 합니다만...
  • 지훈현서아빠 2016/05/17 14:02 #

  • asjgi 2016/05/17 14:59 # 삭제 답글

    restype을 지정해주지 않아서 그랬네요..........

    다시 콜백 문자열 잘 떨어 집니다. ^_______^ 근데요 restype이 해주는 역활이 뭐죠 `ㅡ` ? 아니다 제가 찾아 볼께요 ㅋㅋㅋ
  • 지훈현서아빠 2016/05/17 15:57 #

    C와 Python 은 엄연히 그 데이터 형이 다르지요. 그것을 서로 맞추어 주는 Casting 이라 생각하면 되지 않을까 싶습니다.
    도움이 되셨기를 ...
댓글 입력 영역

구글애드텍스트