포스트

임베디드 스터디 - 디바이스 드라이버 기본

임베디드 스터디 - 디바이스 드라이버 기본

커널 공간 / 사용자 공간

  • 운영체제는 사용자 프로그램이 하드웨어에 직접 접근하는 걸 허용하지 않음
  • 이유는 두 가지임
    1. 안전성 : 레이스 컨디션, 데이터 오염, 시스템 마비 방지
    2. 보안성 : 악의적 프로세스의 하드웨어 탈취 방지
  • CPU의 Privilege Mode는 사용자 공간에서 MMIO에 직접 접근하려 할 때 즉시 Exception을 발생시킴

Everything is a File

  • Linux 시스템의 기본 철학 : 모든 자원을 파일 인터페이스로 통일함
  • 일반 파일뿐 아니라 하드웨어 장치, 프로세스 정보, 네트워크 소켓까지 전부 open()read() / write()close() 로 접근
1
2
3
4
5
/dev/ttyS0      → UART 장치
/dev/sda        → 스토리지 장치
/proc/1234/mem  → PID 1234 프로세스의 메모리
/sys/class/gpio → GPIO 핀
/dev/null       → 데이터 버리는 블랙홀
  • Linux는 해당 설계 철학을 바탕으로 다음 이점을 가짐
    1. 애플리케이션이 자원 종류를 몰라도 사용 가능함. UART든 파일이든 소켓이든 동일한 API로 다루므로 코드 복잡도가 감소함
    2. 셸 파이프라인으로 자원 조합이 가능함. cat /dev/ttyS0 | grep ERROR 처럼 장치 출력을 파이프로 바로 처리하는 것이 가능

디바이스 파일

  • Linux에서 /dev/ttyS0와 같이 존재하는 파일이 디바이스 파일이다.
  • /dev 폴더에서 ls -l 명령을 실행하면 다음과 같은 형태로 출력이 나오는 것을 확인할 수 있다.
1
2
3
4
5
6
7
8
9
crw-rw----  1      root   dialout   4, 64   May 5 09:00   ttyS0
│           │      │      │         │       │             │
│           │      │      │         │       │             └ 파일 경로
│           │      │      │         │       └ 최종 수정 시각
│           │      │      │         └ Major, Minor number
│           │      │      └ 그룹 소유자
│           │      └ 사용자 소유자
│           └ 하드링크 수
└ 파일 타입 + 권한
  • ls -l 출력의 첫 번째 문자는 파일 타입을 나타내며, 디바이스 파일은 c 또는 b로 구분된다.
기호의미
-일반 파일
d디렉터리
l심볼릭 링크
c문자 디바이스 파일
b블록 디바이스 파일
  • 하드링크 수 (1) — 이 파일을 가리키는 하드링크가 몇 개인지. 디바이스 파일은 보통 1이다.
  • 사용자 소유자 (root) — 이 파일의 소유자. /dev 아래 디바이스 파일은 대부분 root 소유이다.
  • 그룹 소유자 (dialout) — 이 파일에 접근 가능한 그룹. UART 시리얼 포트는 관례적으로 dialout 그룹에 속하며, 일반 사용자가 /dev/ttyS0에 접근하려면 이 그룹에 속해 있어야 한다.

Major/Minor number

  • Major number : 해당 디바이스 파일의 드라이버 매핑 번호
    • 4번 : TTY 드라이버
  • Minor number : 해당 디바이스의 장치 인스턴스 번호
    • 같은 드라이버를 사용하는 디바이스 파일에 대해 각각의 식별 번호를 부여함

디바이스 드라이버 실행 흐름

  1. cat이 open(“/dev/ttyS0”)을 호출한다.
  2. 이 순간 시스템 콜이 발생하여 CPU가 사용자 공간 → 커널 공간으로 전환된다.
  3. 커널은 /dev/ttyS0의 Major number 4 를 읽어 드라이버 테이블을 조회한다.
  4. 드라이버 테이블에서 Major 4 → TTY 드라이버의 open 함수 포인터를 찾아 호출한다.
  5. cat이 이제 read(“/dev/ttyS0”)를 호출한다. 다시 시스템 콜 발생 → 커널 공간 전환.
  6. 커널이 TTY 드라이버의 read 함수를 호출하면서 Minor number 64 를 넘긴다.
  7. TTY 드라이버는 Minor 64를 보고 “0번 UART 장치”임을 파악, 해당 UART 레지스터 주소에 접근하여 데이터를 읽는다.
  8. 읽은 데이터를 커널 버퍼 → 사용자 공간 버퍼로 복사하여 cat에게 반환한다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.