하드웨어 디자이너와 시스템 아키텍트 시스템 복잡성, 리소스 제한, 개발 용이성을 기준으로 I/O 디바이스와의 인터페이스를 선택한다. MMIO는 더 복잡한 I/O 작업과 세밀한 제어가 필요한 시스템에 적합하고 개발자에게 더 친숙한 방식인 반면, PMIO는 리소스게 제한적인 시스템에서 주소 공간을 절약할 수 있는 방식이다.
MMIO는 입출력 디바이스를 메모리 주소 공간에 매핑하는 방식이다. CPU가 메모리에 접근하는 것과 동일한 인스트럭션 및 메커니즘을 사용한다.
PMIO는 I/O를 위한 별도의 주소 공간과 전용 인스트럭션을 사용한다. 예를 들어 CPU에서 메모리로의 IORQ라는 컨트롤 사인을 하나 더 두는 식이다.
디바이스 파일 생성. 응용 프로그램이 드라이버 통해 디바이스 제어.
디바이스 파일이란?
: 디바이스 드라이버 와의 인터페이스로, 파일시스템 상에 있다. '/dev' 디렉토리에 존재하며 시스템콜을 이용해 디바이스에 접근할 수 있도록 하기 위해 만들어졌다. 'mknod' 커맨드로 만들 수 있다.
디바이스 드라이버 사용법
1) OS 커널에 디바이스 드라이버 모듈 삽입 (insmod)
2) 모듈 삽입됐는지 확인 (lsmod 혹은 cat /proc/modules)
3) 디바이스 파일 있는지 확인 (ls -l /dev)
4) 디바이스 파일 없으면 만들기 (mknod /dev/{devicename}{type}{major}{minor}
5) 응용프로그램으로 디바이스 드라이버 제어 테스트
응용 프로그램이 mmap() 시스템콜 통해 디바이스 제어. 입출력 디바이스가 사용자 프로세스의 주소 공간에 direct하게 매핑된다.
장점
함수 원형
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. 디바이스 레지스터의 물리 주소는 하드웨어 문서 등 다양한 방법을 통해 알 수 있다.