개요

Python을 통해 Socket 통신 프로그램을 구현할 때 setsockopt()과 getsockopt()을 사용하는 일이 있었는데

Python 코드 내에서 setsockopt()은 정상적으로 진행할 수 있었으나 getsockopt()을 통해서 값을 읽어올 때 문제가 생겼다.

 

해당 문제는 getsockopt()을 통해 객체를 리턴 받을때 해당 객체가 단순한 포인터로 되어 있으면 Python에서 제공되는 ctype을 통해 잘 읽어올 수 있었다. 그러나 이중 포인터로 된 값을 읽어올 때는 Python을 통해서만 불러오는건 힘든 작업이었다...ㅠ

 

그래서 구글링하여 알아낸 방법이 Python에서 C 함수를 호출하는 방법이였다.

즉, Python에서 얻어내기 힘든 이중 포인터를 통해 저장된 값을 C 함수로 대신 읽어서 다시 Python으로 리턴해주는 방법이다.

 

아래의 예제는 이중 포인터에 대한 예제는 아니지만 간략하게 Python에서 사용하는 C 모듈을 생성하는 방법에 대해서 설명한다.

 

진행하기에 앞서서 먼저,

$ ls /usr/include/python3.x/Python.h

명령어를 통해 Python.h 헤더파일이 존재하는지 확인한다.

 

필자의 테스트 컴퓨터에는 Python 3.8이 올라가있으며,

위 그림과 같이 /usr/include/python3.8/Python.h 경로에 파일이 존재하는 것을 확인할 수 있었다.

각자의 컴퓨터마다 python의 3.6, 3.8 등과 같이 버전이 다를테니 이점 참고하기 바란다.

 

만약, Python.h가 존재하지 않는다면

$ sudo apt install python3-dev

or

$ sudo apt install python3.x-dev

위 명령어 두개 중에 하나를 사용하길 바란다.

아래 python3.x의 x부분은 본인이 원하는 python3 버전을 입력하면 된다.

 

설치를 완료하고 다시 ls 명령어를 통해 Python.h 헤더파일이 존재하는지 확인한다.

 

확인이 되었다면 이제 아래 1. 부터 3. 까지 진행해보도록 하자.

 

 

1. Python에서 사용할 C 모듈 생성

1.1. C 코드 작성

  • c_module.c
/**
 * @file	: c_module.c
 **/

#include <stdio.h>
#include <python3.8/Python.h>

/* 2개의 int 인자를 Python으로 받아와 Add 해주는 함수*/
PyObject* add(PyObject* self, PyObject* args)
{
    int a, b;
    int result;
    PyObject* ret;

    /**
     * @func    : PyArg_ParseTuple()
     * @brief   : Python에서 넘겨진 args 파라미터를 매핑시켜주는 함수 \
     *             "i"는 int를 의미하는 string \
     *             "ii"는 int를 2개 받는다는 의미 \
     *             자세한 string format은 아래 link를 참조.
     *
     * @link    : http://web.mit.edu/people/amliu/vrut/python/ext/parseTuple.html
     **/
    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;

    result = a+b;

    /**
     * @func    : Py_BuildValue()
     * @brief   : Python으로 넘겨줄 return 값 생성 함수
     * @return  : PyObject*
     */
    ret=Py_BuildValue("i", result);

    return ret;
}

/* 2개의 int 인자를 Python으로 받아와 Sub 해주는 함수*/
PyObject* sub(PyObject* self, PyObject* args)
{
    int a, b;
    int result;
    PyObject* ret;

    if (!PyArg_ParseTuple(args, "ii", &a, &b))
        return NULL;

    result = a-b;

    ret=Py_BuildValue("i", result);

    return ret;
}





/**
 * @var         : PyMethodDef Methods
 * @brief       : C 함수를 Python 메소드로 매핑 시켜주는 배열
 * @param       : {
 *                  char*       Python에서 사용할 메소드 이름,
 *                  PyCFunction 실제 C에서 구동할 함수 포인터,
 *                  int         호출 구성 방법을 나타내는 플래그 비트,
 *                  char*       메소드의 간략한 설명
 *                }
 **/
PyMethodDef Methods[]={
    {"py_add", add, METH_VARARGS, "int,int add"},
    {"py_sub", sub, METH_VARARGS, "int,int sub"},
    { NULL, NULL, 0, NULL} // 마지막 배열은 NULL 초기화를 해야한다고 함
};

/**
 * @var         : PyModuleDef c_module
 * @brief       : C 코드를 Python 모듈로 초기화 시켜주는 구조체
 * @param       : {
 *                  해당 구조체의 첫번째 멤버는 항상 PyModuleDef_HEAD_INIT로초기화,
 *                  char*       Python에서 사용할 모듈 이름
 *                  char*       모듈의 간략한 설명
 *                  ssize_t     해당 모듈이 사용할 메모리 크기 (대부분 -1을 사용)
 *                  PyMethodDef* 모듈의 메소드가 정리된 배열 포인터
 *                }
 *
 **/
struct PyModuleDef c_module = {
    PyModuleDef_HEAD_INIT,
    "c_module",
    "simple c module",
    -1,
    Methods
};

/**
 * @func        : PyMODINIT_FUNC PyInit_c_module()
 * @brief       : PyModule_Create() 함수를 통해 c_module을\
 *                생성시켜주는 함수.
 **/
PyMODINIT_FUNC PyInit_c_module(void){
    return PyModule_Create(&c_module);
}

위 C 코드는 Python으로부터 2개의 정수(int) 인자를 받아와 Add나 Sub 해주는 함수를 작성한 코드이다.

해당 코드는 PyMethodDef, PyModuleDef, PyModule_Create() 등의 배열, 구조체, 함수 등을 통해서 Python 모듈로 변환된다.

 

해당 코드에 대한 설명은 주석으로 달아놓았으니 참고하길 바란다.

코드를 다 작성하였다면 Python 모듈로 생성하기 위해 setup 파일을 작성해주어야 한다.

 

 

2. 작성된 C 코드를 Python 모듈로 빌드

2.1. setup 파일 생성

  • setup_c_module.py
from distutils.core import setup, Extension

setup(
        name="c_module",
        ext_modules=[
            Extension("c_module", ["c_module.c"], include_dirs=['.'],)
        ]
)

생성할 모듈의 이름과 모듈의 코드(.c) 경로를 setup 파일에 작성해준다.

 

여기까지 진행되었으면 c_module.c와 setup_c_module.py 파일이 두개 존재한다.

두 파일이 존재한다면 이제 c_module을 python 모듈로 빌드시키는 과정을 진행한다.

 

2.2. 빌드 진행

먼저, build를 진행한다.

$ python3 setup_c_module.py build

 

그러면 아래와 같은 출력이 발생할 것이다.

 

출력이 발생한 뒤에는 앞서 생성한 두 파일(c_module.c, setup_c_module.py)뿐만아닌 build/ 라는 디렉터리가 생성되었을 것이다.

 

그후에 install을 진행한다.

$ sudo python3 setup_c_module.py install

 

그러면 아래와 같은 출력이 발생할 것이다.

 

해당 출력이 발생하였다면 정상적으로 C 코드가 Python 모듈로 등록된 것이다.

이제 C코드로 된 모듈을 Python에서 사용해보도록 하자.

 

 

3. 생성된 C 모듈을 Python에서 사용해보기

3.1. Python 코드 작성

  • use_c_module.py
import c_module

result = c_module.py_add(2, 1)
print(result)

result = c_module.py_sub(2, 1)
print(result)

1. 단락의 c_module.c에서 작성한 add와 sub 함수를 사용하기 위해 앞선 과정에서 생성시킨 c_module을 import 한 뒤 해당 모듈의 메소드인 py_add와 py_sub를 실행시키면 된다.

 

그러면, python에서 내부적으로 c_module.c의 add()와 sub()를 실행시켜 해당 함수의 리턴값을 반환해줄 것이다.

 

3.2. Python 코드 실행

3.1.에서 생성한 use_c_module.py를 아래 명령어로 실행시켜보자.

$ python3 use_c_module.py

 

정상적으로 실행된다면 아래와 같이 3과 1이 터미널에 출력될 것이다.

 

정리 끝!

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기