환경구축
노트북에 설치된 우분투 14.10 기준.
sudo apt-get install python-2.7.8 -y
책 내용:
# cd /usr/local
# wget http://python.org/ftp/python/2.5.1/Python-2.5.1.tgz
# tar -xvfz Python-2.5.1.tgz
# mv Python-2.5.1.tgz Python25
# cd Python25
"그러면 파이썬 소스코드가 다운로드 돼 /usr/local/Python25에 압축 해제 된다. 다음에는 파이썬 소스코드를 컴파일하고 파이썬 인터프리터가 제대로 동작하는지 확인한다.
# ./configure --prefix=/usr/local/Python25
# make && make install
# pwd
/usr/local/Python25
# python
ctypes
ctypes 는 파이썬 개발자가 이용할 수 있는 가장 강력한 라이브러리 중 하나.
ctypes 라이브러리는 동적 링크 라이브러리 함수의 호출을 가능하게 함.
복잡한 C 데이터 타입을 사용 가능하게 함.
메모리를 관리하는 로우레벨(low level) 함수들을 제공
--> ctypes 에 대한 사용방법, 관련 지식이 필요함.
동적 라이브러리 활용
ctypes 라이브러리를 이용하는 첫 번째 단계는 그것이 동적 링크 라이브러리의 함수를 어떻게 해석해 접근하는지 그 방법을 이해하는 것.
동적으로 링크되는 라이브러리는 컴파일된 바이너리로서 프로세스가 실행될 때 해당 프로세스에 동적으로 링크됨.
윈도우 플랫폼에서는 이를 동적 링크 라이브러리(DLL, Dynamic Linked Library)라고 부름.
리눅스 에서는 공유 객체 (SO, Shared Object) 라고 함.
이 두 개(DLL,SO)는 외부에 EXPORT 주소를 제공함. 그리고 이 주소를 이용해 해당 함수의 실제 메모리상 주소를 구함.
-->
동적 링크 라이브러리는 컴파일된 바이너리.
프로세스 실행시 해당 프로세스에 동적으로 링크됨.
하지만 이 라이브러리는 본래 실행시에 동적 라이브러리가 제공하는 함수의 메모리 상 주소를 구해야 하지만,
ctypes 라이브러리를 이용하면 이런 작업들을 생략할 수 있다.
이름 -> 메모리 주소
이 ctypes 라이브러리 에서는 세 가지 방법으로 동적 라이브러리를 로드할 수 있는데,
cdll(), windll(), oledll()
이 세가지 방법의 차이점은 export 함수를 호출하는 방법과 리턴 값을 반환하는 방법에 있음.
1. cdll()
- 표준 cdecl 호출 규약을 이용하는 함수를 export 하는 라이브러리를 로드하는데 사용.
2. windll()
- 마이크로소프트 Win32 API가 사용하는 stdcall 호출 규약을 이용하는 함수를 export 하는 라이브러리를 로드하는데 사용
3. oledll()
- windll() 방법과 동일하게 동작하지만, export 함수가 반환하는 값이 HRESULT 라 가정한다.
HRESULT -> 마이크로소프트 컴포넌트 객체 모델 (COM,Component Object Model)에서 여러 메시지를 반환하기 위해서 특별히 사용되는 것
#호출 규약에 따른 설명
출처: http://egloos.zum.com/qufl1999/v/2338204
__cdecl (C delcaration) 호출 규약이란?
- 함수를 호출 받은 쪽(함수의 내부) 에서 파라미터를 제거하도록 되어 있음.
따라서 파라미터의 갯수가 명확한 곳에서 사용하게 되어있음.
--> 윈도우 API 같은 경우 함수에 따른 인자들이 명확히 정의되어 있으므로 cdecl 규약을 (주로)사용함. // 이 부분 질문
Q1. 책에서는 Win32 API가 stdcall을 사용한다고 말했는데, 어떤 곳에서는 윗 부분처럼 함수의 파라미터가 명확하게 정의되어 있어 함수 내부에서 파라미터를 pop하는 cdecl 방식을 주로 이용한다고 함. 진실은?
__stdcall (standard call)
- 함수를 호출한 쪽에서 return 받은 후 stack에서 제거함. 따라서 호출한 함수가 자신의 파라미터를 알고 있기 때문에 printf 와 같이 파라미터가 가변적인 함수에서 사용가능 함.
--> 아마 스택에서 제거하는 방식이 가변형 파라미터에 대해 유연하게 대처할 수 있으므로 그런듯. // 질문
Q2. 스택에서 꺼낼 때, 함수내에서 pop 하는 것과 호출자에서 pop 하는 것중 무엇이 다르며, 왜 함수 내에서 가변적인 파라미터를 스택에서 pop 할 수 없는가.
(아래에 답)
CDeclFunction(g_szCdeclCall, 1, 'a');
// 아래는 실제 어셈블리 코드
push 61h // 'a' 를 스택에 넣습니다.
push 1 // 1 를 스택에 넣습니다.
mov eax, [g_szCdeclCall (00403014)]
push eax // 문자열을 넣습니다.
call CDeclFunction (0040105c)
add esp,0Ch // parameter 를 제거합니다.
특징:
파라미터를 오른쪽 부터 왼쪽 순으로 넣음.
add ~ --> 파라미터를 스택에서 제거
StdCallFunction ( g_szStdCall, 2, 'b');
// 아래는 실제 어셈블리 코드
push 62h // 'b'를 스택에 넣습니다.
push 2 // 2를 스택에 넣습니다.
mov ecx, dword ptr [g_szStdCall (00403010)]
push ecx // 문자열을 넣습니다.
call StdCallFunction (00401069) // 함수를 호출합니다.
특징:
파라미터를 오른쪽부터 왼쪽 순으로 넣음.
함수내에서 파라미터를 제거하는 과정이 없음.
대신, 함수 내에서 파라미터를 제거.
(함수 내에,
ret 0Ch 라는 어셈블리 구문이 있는데, 이 구문은 그냥 리턴하지 말고 0Ch 만큼 이동함.
------------- 정리 ----------------
stdcall cdecl 공통점
- 함수의 인자를 스택에 넣을 때 오른쪽에서 왼쪽으로 쌓음. (가장 첫 번째 인자가 스택의 맨 위)
stdcall cdecl 차이점
stdcall:
- 호출된 함수, stdcall로 정의된 함수 내부에서 스택이 정리됨. (pop됨)
cdecl:
- 호출한 함수, cdecl로 정의된 함수 외부에서 스택이 정리됨.
--> 이러한 특징 덕에 cdecl로 정의된 함수는 가변 인자를 지원함.
A2.
호출한 함수에서는 가변 인자의 스택의 크기를 알지만 호출된 함수에서는 가변 인자의 스택의 크기를 알 수 없기 때문.
Total
- stdcall 방식은 cdecl 방식보다 빠르지만 가변 인자를 지원하지 못함.
- OS가 호출하는 함수, DLL에 들어가는 함수는 가능하면 stdcall을 사용. (stdcall이 표준)
예제:
# ctypes를 이용, 변수 seitz에 "loves the python" 문자열의 포인터를 할당하는 방법
from ctypes import *
>>> c_int()
c_long(0)
>>> c_char_p("Hello world!")
c_char_p('Hello world!')
>>> c_ushort(65531)
>>>
>>> seitz = c_char_p("loves the python")
>>> print seitz
c_char_p('loves the python')
>>> print seitz.value
loves the python
>>> exit()
C 언어 에서는 포인터의 값에 대해 *로 접근했지만, python 에서는 .value 로 접근하는 듯 하다.
구조체 정의
C 언어
struct beer_recipe
{
int amt_barley;
int amt_water;
};
Python
class beer_receipe(Structure):
_fields_ = [
("amt_barley",c_int),
("amt_water",c_int),
]
ctypes에서의 구조체 정의는 C언어와 매우 흡사.
union 은 struct 와 매우 비슷하지만, union 에서는 "모든 멤버 변수가 동일한 메모리 공간을 공유" 한다는 점이 다르다.
동일한 메모리 공간을 공유하기에 union 에서는 서로 다른 데이터 타입에 동일한 값을 저장할 수 있다.
union 정의
C 언어
union {
long barley_long;
int barley_int;
char barley_char[8];
}barley_amount;
Python
class barley_amount(Union):
_fields_ = [
("barley_long",c_long),
("barley_int",c_int),
("barley_char",c_char * 8),
]
barley_amount union의 barley_int 멤버 변수에 66 할당 시, 다른 멤버 변수 barley_char를 이용해 그 값에 해당하는 문자를 출력할 수 있다.
(
barley_int = 66 일 때,
printf("%c",barley_char);
-----result-----
B
예제
chapter1-unions.py
from ctypes import *
class barley_amount(Union):
_fields_ = [
("barley_long",c_long),
("barley_int",c_int),
("barley_char",c_char * 8),
]
value = raw_input("Enter the amount of barley to put into the beer vat: ")
my_barley = barley_amount(int(value))
print "Barley amount as a long: %ld" % my_barley.barley_long
print "Barley amount as a int: %d" % my_barley.barley_int
print "Barley amount as a char: %s" %my_barley.barley_char
---------- result -----------
Enter the amount of barley to put into beer vat: 66
Barley amount as a long: 66
Barley amount as a int: 66
Barley amount as a char: B # Ascii 기준 66 은 'B'
--> ctypes 에서는 배열을 배열 요소 갯수를 (인덱스 갯수를) 해당 요소 타입에 곱하는 형태로 정의.
(c_int * 8) == int asdf[8];
'스터디 > 파이썬 해킹 프로그래밍' 카테고리의 다른 글
스터디 질문 답변 (0) | 2015.01.14 |
---|---|
2015-01-15 스터디 (0) | 2015.01.12 |
2015-01-12 질문 정리 (0) | 2015.01.12 |
레지스터 / 브레이크 포인트 (0) | 2015.01.11 |
스터디 OT (feat. Skype) (0) | 2015.01.11 |