Ch2-2. Device Control Method

뿌먹코의 탐구생활·2024년 4월 4일

목차

1. Interface with I/O devices

하드웨어 디자이너와 시스템 아키텍트 시스템 복잡성, 리소스 제한, 개발 용이성을 기준으로 I/O 디바이스와의 인터페이스를 선택한다. MMIO는 더 복잡한 I/O 작업과 세밀한 제어가 필요한 시스템에 적합하고 개발자에게 더 친숙한 방식인 반면, PMIO는 리소스게 제한적인 시스템에서 주소 공간을 절약할 수 있는 방식이다.

[MMIO] Memory mapped I/O

MMIO는 입출력 디바이스를 메모리 주소 공간에 매핑하는 방식이다. CPU가 메모리에 접근하는 것과 동일한 인스트럭션 및 메커니즘을 사용한다.

  • 장점 : 별도의 I/O 명령어 없이 일반적인 메모리에 접근(read(), write())하는 것처럼 입출력 디바이스와 통신할 수 있어 프로그래밍이 간편하다. 또한 디바이스에 대한 더 세밀한 제어가 가능하여 복잡한 입출력 작업을 할 때 유리하다.
  • 단점 : 메모리 주소와 입출력 디바이스 주소를 같은 주소 공간에 할당하기 때문에 메모리 주소 영역이 줄어든다.

[PMIO] Port mapped (Isolated) I/O

PMIO는 I/O를 위한 별도의 주소 공간과 전용 인스트럭션을 사용한다. 예를 들어 CPU에서 메모리로의 IORQ라는 컨트롤 사인을 하나 더 두는 식이다.

  • 장점 : 메모리 접근과 입출력 접근이 명확하게 구분되어 있어 설계와 디버깅이 용이하다. 입출력 디바이스의 주소 공간이 제한되어, 메모리 주소 공간을 절약할 수 있다.
  • 단점 : 프로그래밍이 복잡해질 수 있다.

2. Device Control Methods (User Space)


Device Driver

디바이스 파일 생성. 응용 프로그램이 드라이버 통해 디바이스 제어.

디바이스 파일이란?
: 디바이스 드라이버 와의 인터페이스로, 파일시스템 상에 있다. '/dev' 디렉토리에 존재하며 시스템콜을 이용해 디바이스에 접근할 수 있도록 하기 위해 만들어졌다. 'mknod' 커맨드로 만들 수 있다.

디바이스 드라이버 사용법
1) OS 커널에 디바이스 드라이버 모듈 삽입 (insmod)
2) 모듈 삽입됐는지 확인 (lsmod 혹은 cat /proc/modules)
3) 디바이스 파일 있는지 확인 (ls -l /dev)
4) 디바이스 파일 없으면 만들기 (mknod /dev/{devicename}{type}{major}{minor}
5) 응용프로그램으로 디바이스 드라이버 제어 테스트

mmap()

응용 프로그램이 mmap() 시스템콜 통해 디바이스 제어. 입출력 디바이스가 사용자 프로세스의 주소 공간에 direct하게 매핑된다.

장점

  • 성능이 좋다. (디바이스 드라이버와 달리 추가적인 메모리 카피가 없다)
  • 사용하기 쉽다. (MMIO)
  • 데이터 공유가 쉽다. (fork시 파일 디스크립터를 공유하기 때문)

함수 원형

void *mmap (void *start, size_t length, int prot, int flags, int fd, off_t offset)

mmap() 함수는 fd 에 의해 지정된 파일에서 offset 으로부터 length 바이트만큼 메모리(프로세스 주소 공간)에 매핑하다록 요청한다. 이때 length 는 페이지 크기의 배수여야 한다. 여기서는 start 라는 주소에서의 매핑 시작을 시스템에 제시할 수 있지만 반드시 지켜져야하는 것은 아니다. 대부분의 경우 이 값을 0으로 주며, 시스템은 알아서 시작 주소를 지정한다. mmap()의 반환값은 프로세스 주소 공간에서 매핑된 객체의 시작 주소이다.

참고1. mmap()은 프로세스 주소 공간에서 fragmentation을 일으킬 수 있다.
참고2. mmap()을 사용하려면 '/dev/mem'을 open() 함수로 열어야 한다.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define DEVICE_MEM_ADDR 0x12345678 // 예시 주소, 실제 하드웨어에 따라 달라짐
#define MAP_SIZE 4096

int main() {
    int mem_fd;
    void *mapped_region;
    volatile unsigned int *device_register;

    // /dev/mem 파일 열기
    mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (mem_fd == -1) {
        perror("open /dev/mem failed");
        return -1;
    }

    // 물리 주소 매핑
    mapped_region = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, DEVICE_MEM_ADDR & ~(MAP_SIZE - 1));
    if (mapped_region == MAP_FAILED) {
        perror("mmap failed");
        close(mem_fd);
        return -1;
    }

    // 매핑된 주소로부터 디바이스 레지스터 접근
    device_register = (volatile unsigned int *)((char *)mapped_region + (DEVICE_MEM_ADDR & (MAP_SIZE - 1)));
    
    // 디바이스 레지스터 읽기/쓰기 (예시)
    unsigned int value = *device_register; // 레지스터 읽기
    printf("Register value: 0x%x\n", value);
    
    *device_register = value + 1; // 레지스터 쓰기 (단순 예시)

    // 정리
    munmap(mapped_region, MAP_SIZE);
    close(mem_fd);

    return 0;
}

참고3. 디바이스 레지스터의 물리 주소는 하드웨어 문서 등 다양한 방법을 통해 알 수 있다.


profile
Computer Science & Engineering / Daily Life

0개의 댓글