본문 바로가기
SW/C,C++

[C/C++] 커널 드라이버에서의 함수포인터

by FastBench 2024. 4. 22.
반응형

커널 드라이버에서의 함수포인터


구조체에 함수 포인터를 사용하는 방법은 커널 드라이버나 다른 시스템 수준의 프로그래밍에서 매우 유용하게 사용된다. 이러한 방식은 다양한 기능을 동적으로 처리할 수 있게 해주며, 특히 장치 드라이버의 인터페이스를 관리할 때 흔히 볼 수 있다.

 

커널 드라이버에서의 사용

커널 드라이버에서는 구조체와 함수포인터를 사용하여 다양한 하드웨어 또는 가상 장치의 동작을 추상화한다.
예를 들어, Linux의 파일 시스템 또는 네트워크 장치 드라이버에서는 각각의 장치 특성에 맞게 동작을 정의할 수 있는 함수 포인터를 포함하는 구조체를 사용한다.

 

이러한 방식은 각 장치 또는 드라이버가 동일한 인터페이스를 공유하면서도, 내부적으로 다른 구현을 가질 수 있게 해주어 코드의 재사용성과 모듈성을 높인다.

 

커널 드라이버와 플러그인 아키텍처

커널 드라이버에서의 함수 포인터 사용은 플러그인 아키텍처의 구현을 가능하게 한다:

  1. 동적 연결: 각 드라이버 모듈은 특정한 장치 또는 기능을 대상으로 하는 함수들을 포함한다. 이 함수들은 드라이버 구조체 내의 함수 포인터를 통해 연결된다. 이렇게 함으로써, 커널은 실행 중에 다양한 드라이버 모듈을 로드하고, 각 모듈이 제공하는 기능을 사용할 수 있다.
  2. 추상화된 인터페이스: 드라이버 구조체는 일반적으로 추상화된 인터페이스를 제공한다. 예를 들어, 모든 네트워크 카드 드라이버는 데이터를 송수신하는 기본 함수를 제공할 수 있으며, 이는 함수 포인터를 통해 구현된다. 각 드라이버는 이 인터페이스에 맞춰 구현되므로, 다양한 네트워크 카드가 동일한 방식으로 제어될 수 있다.
  3. 확장성: 새로운 하드웨어 지원을 추가하기 위해서는 새 드라이버 모듈만 개발하면 된다. 기존 시스템을 수정할 필요 없이 새 모듈을 로딩함으로써, 시스템은 새 하드웨어의 기능을 바로 사용할 수 있게 된다.

예시

먼저 device 구조체가 존재하고, 해당 device 의 동작을 정의하는 device_operation 구조체가 존재한다.

device_operation 에 들어가는 함수들은, 각 device / driver 제조사 별로 구현이 다르기 때문에, 
커널입장에서는 공통된 인터페이스를 제공해야 하므로 함수포인터로 선언한다.

#include <stdio.h>

typedef struct device {
    const char* name;
    int status;
} device;

// Device operation 구조체
typedef struct device_operations {
    int (*open)(device *dev);
    int (*close)(device *dev);
    int (*read)(device *dev, char *buffer, size_t size);
} device_operations;

그리고 아래와 같이 operation 함수들을 정의해준다.

// 각 함수 구현
int device_open(device *dev) {
    printf("Opening device %s\n", dev->name);
    dev->status = 1;
    return 0; // 성공
}

int device_close(device *dev) {
    printf("Closing device %s\n", dev->name);
    dev->status = 0;
    return 0; // 성공
}

int device_read(device *dev, char *buffer, size_t size) {
    printf("Reading from device %s\n", dev->name);
    // 예시 데이터
    snprintf(buffer, size, "Sample data from %s", dev->name);
    return 0; // 성공
}

snprintf 는 buffer 에 문자열을 버퍼오버플로우없이 안전하게 저장하도록 하는 표준 함수이다.

 

main 함수에서 디바이스 구조체를 초기화 해주고, 함수를 함수포인터에 연결해준다.
실제 이 과정은 커널드라이버내의 초기화 함수에서 이뤄질 것이다.

int main() {
    device myDevice = {"Device1", 0};
    device_operations myDeviceOps = {
        .open = device_open,
        .close = device_close,
        .read = device_read,
    };

    // 장치 사용 예제
    myDeviceOps.open(&myDevice);
    char buffer[100];
    myDeviceOps.read(&myDevice, buffer, sizeof(buffer));
    printf("Processed data : %s\n",buffer);
    myDeviceOps.close(&myDevice);

    return 0;
}

결과

 

이렇게 간단한  디바이스 드라이버 예시로부터 구조체 함수포인터가 사용되는 것을 살펴보았는데,

이전 게시글에서는 함수포인터를 '콜백함수'의 용도에 집중해서 설명했다면

 

이번 게시글에서는 함수포인터의 용도 중 하나인 플러그인 아키텍처의 구현을 위주로 설명했다.

 

이러한 구조는 특히 디바이스 드라이버 개발에서 다양한 하드웨어 또는 소프트웨어 컴포넌트의 기능을 동적으로 로딩하고 실행할 수 있도록 한다.

반응형

댓글