파일 형태로 표현된 커널 내 객체, 즉 OS의 자원이다. 유닉스 시스템은 OS 자원을 파일 형태로 표현한다.
OS 자원이라고 함은 디스크, CPU, 네트워크, RAM 등을 말한다. 리눅스에서 이런 자원에 데이터 전송, 장치 접근 시 사용하는 파일을 특수 파일이라고 한다
device files, pipes, socket, ...
특수 파일은 장치와 데이터를 주고 받는 통로이다 데이터 블록이 없으며(디스크에 저장 X) 장치 번호를 inode에 저장한다. 모니터나 프린터 역시 특수 파일을 통해 데이터를 주고 받는다. 디바이스는 반드시 HW를 말하는것이 아니고 SW적인 디바이스도 있다.
- : 일반 파일
d : 디렉토리
b : 블록 장치 특수 파일
c : 문자 장치 특수 파일
l : 심볼릭 링크
- major device number : 장치의 종류(monitor, printer, ...)
- minor device number : 장치의 수
$ ls -l /dev/
total 0
crw------- 1 root root 5, 1 <-- major, minor ...
crw-r--r-- 1 root root 10, 235 Mar 11 10:09 autofs
drwxr-xr-x 2 root root 40 Mar 11 10:09 block
drwxr-xr-x 2 root root 100 Mar 11 10:09 bsg
crw------- 1 root root 10, 234 Mar 11 10:09 btrfs-control
drwxr-xr-x 3 root root 60 Mar 11 10:09 bus
/dev
디렉토리에 있는 시리얼 포트의 목록을 보면 아래와 같은 내용이 출력된다. 출력 결과를 분석해보자.
(1) (2) (3) (4) (5) (6) (7)(8) (9) (10)
crwxrwxrwx 1 root tty 4 64 Jan 1 2006 ttyS00
crwxrwxrwx 1 root tty 4 65 Jan 1 2006 ttyS01
crwxrwxrwx 1 root tty 4 66 Jan 1 2006 ttyS02
crwxrwxrwx 1 root tty 4 67 Jan 1 2006 ttyS03
-
(1) 접근 권한을 보면
crwxrwxrwx
로 c로 시작하는 것은 장치가 "문자 장치"임을 알려준다. c 가 아닌 b로 시작한다면 예로 하드디스크와 같이 블럭 단위로 읽거나 쓰기를 하는 블록 장치라는 의미이다. -
(5)의 4는 메이저 장치 번호, (6)의 64, 65, 66 등은 마이너 장치 번호이다. 우리가 작성하는 프로그램은 하드웨어 장치를 직접 제어하는 것이 아니라 커널을 통해 제어하게 된다. 하드웨어를 파일 개념으로 처리할 수 있는 것도 중간에 커널이 가상 파일을 만들어서 제공하기 때문에 가능한 것이다.
-
프로그램에서 하드웨어 장치에 대해 어떤 작업을 커널에게 요청하면, 커널은 메이저 번호를 가지고 어떤 디바이스 드라이버 사용할 지를 결정하게 된다. 디바이스 드라이버는 커널로부터 받은 정보 중 마이너 장치 번호를 가지고 자기에게 할당된 장치 중 어떤 장치를 제어할 지를 결정한다. 위의 장치 목록을 보면 메이저 번호가 모두 4로 똑같고 마이너 번호만 다르다. 커널은 메이저 번호로 따라 디바이스 드라이버를 선택하고 다음 처리를 넘기면 디바이스 드라이버는 마이너 번호를 가지고 어느 장치를 사용할 지를 결정한다는 뜻이다.
디바이스 드라이버를 만들어 커널을 올릴 때, 다른 디바이스 드라이버와 구별할 수 있도록 앞에 번호가 붙여진다. 이 번호가 주 장치 번호, 주 번호이다.
insmod user_device_name
이렇게 insmod
명령을 실행하면 user_device_name
디바이스 드라이버 안에 주 번호를 몇 번으로 등록할 지 커널에 요청하는 코드가 들어 있다. 즉, user_device_name
디바이스 드라이버를 만들 때부터 프로그래머에 의해 주 장치 번호가 결정된다. 커널은 디바이스 드라이버에서 요청하는 장치 번호로 user_device_name
디바이스 드라이버를 커널 영역으로 로드한다.
user_device_name
가 주 번호를 250을 사용한다고 가정해보자.
insmod
명령을 실행했으니 이제 커널에서는 디바이스 드라이버를 사용할 준비가 되었다. 그러나 아직 커널만 알고 밖에서는 아무도 모른다. 커널 밖에서도 이 디바이스 드라이버를 사용할 수 있도록 공개해 주어야 하는데, 이때 응용 프로그램이 접근하기 쉽도록, 또한 같은 장치이면서 내부에 처리하는 번호가 다르다면 하나의 디바이스 드라이버에서 모두 처리할 수 있도록, 결국 응용프로그램에서 쉽게 사용할 수 있도록 가상 파일 시스템으로 제공한다.
/dev
안에 있는 아이템들, 다시 말해 node
들이 가상 인터페이스의 역할을 하는 것이다.
]# mknod /dev/user_device_S0 c 250 0
]# mknod /dev/user_device_S1 c 250 1
]# mknod /dev/user_device_S2 c 250 2
]# mknod /dev/user_device_S3 c 250 3
]# mknod /dev/user_device_S4 c 250 4
만약 아래와 같은 통신 포트를 open 하는, 즉 통신 포트를 사용하기 위한 프로그램 코드를 실행한다고 해보자.
fd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK );
통신 포트라는 장치를 사용하기 위해서 /dev/ttyS0
를 open했다. 하지만 그것은 실제 디바이스 드라이브 파일명이 아니다. 이 프로그램 코드는 당연히 커널로 전송되어진다. 커널은 /dev/ttyS0
에서 주 장치 번호를 확인한다. 또한 주 장치를 보고 커널에 등록된 디바이스 드라이버중에 장치 번호에 해당하는 디바이스 드라이버를 찾아서 응용 프로그램의 open()
에 대한 명령을 전송해 준다. 이때, 함께 전송되는 것이 /dev/ttyS0
에 해당하는 부 장치 번호이다.
디바이스 드라이버는 커널로부터 받은 부 장치 번호를 가지고 자신이 처리하는 여러 장치 중 어느 장치를 처리할 지를 파악하게 된다.
즉, /dev/ttyS0
장치를 사용한다면,
- 커널은
/dev/ttyS0
의 주 장치 번호 4번에 해당하는 디바이스 드라이버를 찾고 - 디바이스 드라이버에 부 장치 번호 64를 전송해 주면,
- 디바이스 드라이버는 자기가 처리하는 여러 통신 포트 중에 부 장치 번호인 64로 처리할 포트를 알게 된다.
참고