MMIO 방법인 ioremap()과 mmap() 비교 정리


3 분 소요

1. 개요

리눅스에서 메모리와 I/O를 다루는 두 인터페이스를 비교해보자.

  • mmap()
    유저 공간 프로세스가 자신의 가상 주소 공간에 파일이나 메모리 영역을 매핑해 달라고 커널에 요청하는 시스템 콜이다.
  • ioremap()
    커널 내부(주로 디바이스 드라이버)에서 디바이스의 물리 I/O 메모리(레지스터 등)를 커널 가상 주소 공간으로 매핑하는 커널 함수이다.

둘 다 “물리적인 어떤 것”을 “가상 주소”에 연결한다는 공통점이 있지만,

  • 누가 호출하는지,
  • 무엇을 매핑하는지,
  • 어떤 속성으로 접근하는지,

가 상당히 다르다.


2. 호출 주체의 차이

2.1 mmap()

  • 호출 주체: 유저 공간 프로세스
  • 형태: 시스템 콜
void *addr = mmap(NULL, size,
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED,
                  fd, 0);
  • 반환값 addr유저 공간 가상 주소이다.
  • 애플리케이션 코드에서는 일반 포인터처럼 *(int *)addr 형태로 접근한다.

2.2 ioremap()

  • 호출 주체: 커널 / 디바이스 드라이버
  • 형태: 커널 내부 함수
void __iomem *base;

base = ioremap(phys_addr, size);
if (!base)
    return -ENOMEM;
  • 반환값 base커널 가상 주소(__iomem 표시)이다.
  • 이 주소에 대해서는 readl(), writel() 같은 MMIO용 헬퍼 함수로 접근해야 한다.

3. 주로 매핑하는 대상의 차이

3.1 mmap()의 대상

mmap()은 주로 다음과 같은 대상을 유저 프로세스 주소 공간에 매핑하는 데 사용한다.

  • 일반 파일 (예: 큰 파일을 메모리처럼 접근)
  • 익명 메모리 (MAP_ANONYMOUS) – 공유 메모리 등
  • tmpfs, /dev/shm 등 메모리 기반 파일시스템
  • 드라이버가 허용할 경우, 디바이스 메모리도 매핑 가능

디바이스 메모리에 대한 mmap()은 보통 다음 구조를 가진다.

  1. 유저 프로세스: mmap()/dev/mydev 같은 디바이스 파일에 대해 호출
  2. 커널 드라이버: file_operations.mmap 콜백 안에서
    remap_pfn_range(), dma_mmap_*() 등을 사용해 물리 I/O 메모리를 유저 공간에 매핑

3.2 ioremap()의 대상

ioremap()은 주로 다음을 매핑한다.

  • SoC 주변장치 레지스터
  • PCI 디바이스 BAR에 할당된 MMIO 레지스터 영역
  • 기타 메모리 매핑 I/O 영역

즉, 하드웨어 데이터시트에 나오는 “레지스터 물리 주소”를 커널이 접근 가능한 가상 주소로 바꾸는 역할을 한다.


4. 메모리 속성과 캐시 정책의 차이

4.1 mmap()의 일반적인 속성

일반적인 파일이나 익명 메모리를 mmap() 할 때는:

  • 캐시 가능한(cacheable) 일반 RAM으로 매핑되는 경우가 많다.
  • CPU 캐시, 페이지 캐시와 연동되어 디스크 I/O와도 밀접하게 연결된다.
  • 애플리케이션은 이 영역을 일반 메모리처럼 사용한다.

단, 디바이스 메모리를 mmap() 하는 경우는 드라이버에서 pgprot_noncached() 같은 것을 사용해 비캐시 또는 특수 속성으로 바꿔줄 수 있다.

4.2 ioremap()의 메모리 속성

디바이스 레지스터는 다음과 같은 이유로 캐시되면 안 된다.

  • 레지스터에 쓰기를 했는데, 실제 디바이스까지 쓰기가 가지 않고 캐시에만 머무르면 오류가 발생할 수 있다.
  • 레지스터에서 읽을 때, 이전 값이 캐시에 남아 있으면 하드웨어 상태를 잘못 읽게 된다.

그래서 ioremap()은 아키텍처별 구현에서 해당 영역을 비캐시(non-cache) 또는 디바이스 전용 메모리 타입으로 매핑한다.
커널에는 용도에 따라 다음과 같은 변형도 존재한다.

  • ioremap() / ioremap_nocache() / ioremap_wc()

5. 매핑 결과 주소를 사용하는 방식의 차이

5.1 mmap() 결과 (user-space)

int fd = open("file.bin", O_RDWR);
void *p = mmap(NULL, 4096,
               PROT_READ | PROT_WRITE,
               MAP_SHARED,
               fd, 0);

int v = ((int *)p)[0];
((int *)p)[1] = 123;
  • 결과 주소는 일반 포인터처럼 사용한다.
  • 컴파일러는 이를 일반 메모리로 취급한다.
  • 별도의 특수 헬퍼 함수 없이 *ptr, ptr[i] 형태로 접근한다.

5.2 ioremap() 결과 (kernel-space)

#define REG_STATUS   0x00
#define REG_CONTROL  0x04

void __iomem *base = ioremap(phys, 0x1000);

u32 status = readl(base + REG_STATUS);
writel(0x1, base + REG_CONTROL);
  • 결과 주소는 __iomem 타입으로 표시된다.
  • 접근은 readb/readw/readl/readq, write*() 계열 함수로 수행한다.
    • 아키텍처별 MMIO 접근 방식
    • 엔디언 처리
    • 메모리 배리어(순서 보장)
      등을 이 헬퍼들이 담당한다.

단순히 다음과 같이 쓰는 것은 권장되지 않는다.

u32 v = *(volatile u32 *)(base + REG_STATUS);  // 비권장

이는 아키텍처 간 이식성과 메모리 ordering 측면에서 문제가 될 수 있기 때문이다.


6. mmap()ioremap()이 함께 쓰이는 경우

디바이스 레지스터를 유저 공간에서 직접 접근시키고 싶은 경우, 전형적인 구조는 다음과 같다.

  1. 드라이버 초기화
    • ioremap()으로 디바이스 레지스터 영역을 커널 가상 주소에 매핑한다.
  2. file_operations.mmap 구현
    • remap_pfn_range() 등을 사용해 해당 물리 영역을 유저 공간 프로세스에 매핑한다.
  3. 유저 프로세스
    • /dev/mydev에 대해 mmap()을 호출해 얻은 포인터를 통해 레지스터를 직접 읽고 쓴다.

이렇게 보면:

  • ioremap()커널이 하드웨어에 접근하기 위한 매핑
  • mmap()유저 공간이 파일/메모리/디바이스를 자기 주소 공간에서 보도록 하기 위한 매핑

이라고 정리할 수 있다.


7. 요약 비교 표

항목 mmap() ioremap()
호출 주체 유저 공간 프로세스 커널 / 디바이스 드라이버
위치 시스템 콜 커널 내부 함수
반환 주소 공간 유저 가상 주소 커널 가상 주소(__iomem)
주 대상 파일, 익명 메모리, 드라이버가 허용한 디바이스 디바이스 레지스터, MMIO 영역
메모리 속성 기본은 캐시 가능 RAM (상황에 따라 변경 가능) 비캐시 또는 디바이스 타입 메모리로 매핑
접근 방식 일반 포인터 dereference readl()/writel() 등 MMIO 헬퍼로 접근
대표 사용 시나리오 파일 매핑, shared memory, user-level I/O 레지스터 맵 매핑, 드라이버에서 하드웨어 제어

8. 핵심 정리

  • mmap()
    유저 공간에서 "이 파일/메모리를 내 주소 공간에 붙여줘"라고 커널에 요청하는 시스템 콜이다.
    결과는 유저 주소이고, 일반 포인터처럼 접근한다.
  • ioremap()
    커널에서 "이 물리 I/O(레지스터) 영역을 커널 주소 공간에 매핑해줘"라고 하는 함수이다.
    결과는 커널 주소이고, readl()/writel() 같은 함수로 접근한다.

즉, 두 함수는 모두 “매핑”을 하지만,

  • 레이어(유저 vs 커널),
  • 대상(일반 메모리/파일 vs I/O 레지스터),
  • 접근 방식(일반 포인터 vs MMIO 헬퍼)

관련 내용에 대한 질문이나 태클을 환영합니다. 댓글 남겨주세요.



태그: ,

카테고리: ,

작성:

업데이트:

댓글남기기