사전 장치 파일
개념

장치 파일

gabury1고친 사람 github-actions[bot]

장치 파일은 프로그램이 디스크나 터미널 같은 장치를 보통 파일처럼 열고 읽고 쓰게 해 줍니다. 이 파일로 들어온 읽기와 쓰기는 커널이 그 장치를 맡은 디바이스 드라이버에게 넘깁니다. 디바이스 드라이버는 특정 장치를 다룰 줄 아는 커널 쪽 코드입니다. 유닉스 계열 운영체제는 이런 파일을 /dev 디렉터리 아래에 모아 둡니다.

쉽고 빠른 이해

장치 파일은 장치로 이어지는 파일 이름입니다. 셸에서 명령 > /dev/null 로 출력을 버려 봤다면 이미 장치 파일에 쓴 것입니다.

장치 파일이 없으면 프로그램은 장치마다 따로 만든 호출 방법을 익혀야 합니다. 장치 파일이 있으면 파일을 다루던 방법 하나로 장치까지 다룹니다. 누가 그 장치를 쓸 수 있는지도 파일 권한으로 정합니다.

  1. 장치 파일에는 내용 대신 「어느 드라이버의 몇 번째 장치인가」를 가리키는 번호가 적혀 있습니다
  2. 프로그램이 그 파일을 열면 커널이 번호를 보고 드라이버를 고릅니다
  3. 그 뒤의 읽기와 쓰기는 전부 그 드라이버가 받아 처리합니다

대가도 있습니다. 읽기와 쓰기로 나타낼 수 없는 조작은 장치마다 따로 정한 명령으로 보내야 합니다. 네트워크 카드처럼 장치 파일이 아예 없는 장치도 있습니다.

상세

회사에는 부서마다 메일 주소가 있습니다. 경리부에 보내든 인사부에 보내든 쓰는 법은 똑같이 메일 쓰기입니다. 받은 메일을 어떻게 처리할지는 그 부서가 압니다.

장치 파일은 장치에 붙인 파일 이름입니다. 프로그램은 이 이름을 보통 파일처럼 열고 읽고 씁니다. 그 요청을 받아 장치를 움직이는 것은 그 장치를 맡은 드라이버입니다. 리눅스 같은 일부 시스템은 장치 파일을 장치 노드라고도 부릅니다.

가장 흔히 만나는 장치 파일은 /dev/null 입니다. 여기에 쓴 내용은 전부 버려집니다. 읽으면 곧바로 파일 끝이 나옵니다.

셸에서 명령 > /dev/null 로 출력을 버리는 것이 이 파일에 쓰는 일입니다. 여기서 > 는 셸의 리다이렉션입니다. 리다이렉션은 명령의 출력을 화면 대신 파일로 보내는 셸 기능입니다. 받는 파일이 /dev/null 이면 출력은 그대로 사라집니다.

/dev/null 뒤에는 하드웨어가 없습니다. 커널 안의 코드가 장치 노릇을 합니다. 장치 파일이 가리키는 것은 하드웨어만이 아닙니다. 이처럼 커널이 흉내 내는 장치일 때도 있습니다.

장치를 파일로 다루는 까닭

장치는 종류마다 다루는 법이 다릅니다. 디스크는 정해진 크기의 덩어리 단위로 읽고 씁니다. 터미널은 사람이 친 글자를 한 자씩 넘깁니다. 장치마다 호출 방법을 따로 두면 프로그램도 장치 수만큼 방법을 익혀야 합니다.

유닉스는 이 차이를 파일 인터페이스 뒤로 숨겼습니다. 열 때는 open, 읽고 쓸 때는 read 와 write, 닫을 때는 close 를 부릅니다. 이 넷은 프로그램이 커널에 일을 맡기는 호출인 시스템 콜입니다. 보통 파일을 열든 장치를 열든 부르는 시스템 콜은 같습니다.

파일을 다루던 도구가 장치에도 그대로 통합니다. /dev/urandom 을 읽으면 난수 바이트가 나옵니다. 난수 전용 호출을 몰라도 파일 읽기 하나로 얻습니다.

디스크를 통째 복사해 이미지 파일로 떠 두는 일도 같은 원리로 됩니다. 디스크의 장치 파일을 처음부터 끝까지 읽어 보통 파일에 쓰면 됩니다. 디스크 전용 복사 도구가 없어도 파일 복사가 곧 디스크 복사가 됩니다.

누가 장치를 쓸 수 있는지도 파일처럼 정합니다. 장치 파일에도 소유자와 권한 비트가 붙습니다. 디스크의 장치 파일을 관리자만 읽게 해 두면 일반 사용자는 디스크를 직접 못 읽습니다. 장치용 접근 규칙을 따로 만들 필요가 없습니다.

장치 파일에 적힌 정보

장치 파일은 디스크에 데이터를 담지 않습니다. 담는 것은 이 파일이 어느 장치를 가리키는지 알려 주는 정보 몇 가지뿐입니다.

이 정보는 아이노드에 적힙니다. 아이노드는 파일 시스템이 파일마다 하나씩 두는 정보 묶음입니다. 보통 파일이면 소유자와 권한, 크기 같은 것이 여기 들어갑니다. 장치 파일은 같은 묶음에 장치를 가리키는 정보를 적습니다.

적히는 정보는 셋입니다.

정보 뜻
종류 문자 장치인가 블록 장치인가
주 번호 어느 드라이버가 이 장치를 맡나
부 번호 그 드라이버가 맡은 장치 가운데 어느 것인가

주 번호는 드라이버를 고르는 번호입니다. 부 번호는 드라이버가 자기가 맡은 장치들을 서로 가르는 번호입니다. 디스크 드라이버 하나가 디스크 두 개를 맡으면 두 장치 파일의 주 번호는 같고 부 번호만 다릅니다.

리눅스에서 ls -l 로 장치 파일을 보면 이 정보가 드러납니다. /dev/sda 는 리눅스가 첫째 디스크에 붙이는 이름입니다.

터미널
$ ls -l /dev/null /dev/sda
crw-rw-rw- 1 root root 1, 3 Sep 24 10:00 /dev/null
brw-rw---- 1 root disk 8, 0 Sep 24 10:00 /dev/sda

맨 앞 글자가 종류입니다. c 는 문자 장치이고 b 는 블록 장치입니다. 보통 파일이면 크기가 나올 칸에 1, 3 처럼 번호 두 개가 나옵니다. 앞이 주 번호이고 뒤가 부 번호입니다. /dev/sda 는 disk 그룹만 읽고 쓰게 되어 있습니다. 앞 소절에서 말한 권한이 이것입니다.

문자 장치와 블록 장치

종류는 장치를 두 갈래로 가릅니다. 둘은 데이터를 주고받는 단위와 읽는 순서가 다릅니다.

문자 장치 블록 장치
주고받는 단위 바이트를 흐름으로 정해진 크기의 블록
읽는 순서 대개 들어온 차례대로 아무 위치나 골라서
예 터미널 · /dev/null · /dev/urandom 디스크 · 디스크의 파티션

터미널은 친 글자가 들어온 차례대로만 읽힙니다. 디스크는 처음이든 한가운데든 원하는 블록으로 바로 갑니다.

두 갈래는 커널 안에서 거치는 길도 다릅니다. 블록 장치를 오가는 데이터는 대개 커널의 캐시를 거칩니다. 커널은 읽은 블록을 메모리에 잠시 붙들어 둡니다. 같은 블록을 다시 읽으면 디스크까지 가지 않고 메모리에서 꺼냅니다.

문자 장치의 데이터는 대개 이 캐시를 거치지 않습니다. 터미널에 들어온 글자는 한 번 읽으면 끝이라 붙들어 둘 까닭이 없습니다. 디스크의 블록은 같은 위치를 다시 읽을 일이 많아 붙들어 두는 값어치가 있습니다.

요청이 드라이버에 닿는 길

이 소절은 /dev/sda 를 열고 한 번 읽기까지를 따라갑니다. 드라이버는 커널에 올라올 때 열기·읽기·쓰기 함수와 함께 「이 주 번호는 내가 맡는다」고 등록해 둡니다. 그래서 번호 하나만 알면 커널이 드라이버를 고를 수 있습니다.

열기가 끝나면 커널은 프로그램에게 파일 디스크립터를 돌려줍니다. 파일 디스크립터는 프로세스가 연 파일을 가리키는 작은 정수입니다. 이후의 읽기와 쓰기에서 어느 파일을 다루는지 이 정수로 알립니다.

sequenceDiagram
    participant 프로그램
    participant 커널
    participant 드라이버
    participant 장치
    프로그램->>커널: open · /dev/sda
    Note over 커널: 아이노드에서 종류와 주 번호를 읽는다
    커널->>드라이버: 주 번호로 고른 드라이버의 열기 함수
    커널-->>프로그램: 파일 디스크립터
    프로그램->>커널: read · 파일 디스크립터
    커널->>드라이버: 드라이버의 읽기 함수
    드라이버->>장치: 장치가 알아듣는 절차
    장치-->>드라이버: 데이터
    드라이버-->>커널: 데이터
    커널-->>프로그램: 데이터

그림의 read 는 open 이 돌려준 파일 디스크립터를 들고 옵니다. 커널은 정수에 묶인 드라이버의 함수를 부릅니다. 그 드라이버가 장치를 움직입니다. 프로그램 쪽에서 보면 보통 파일을 읽을 때와 부른 시스템 콜이 같습니다. 달라진 것은 커널이 그 뒤에 부른 함수뿐입니다.

파일 인터페이스로 모자란 조작

읽기와 쓰기로 나타낼 수 없는 조작이 있습니다. 터미널 창의 크기를 묻는 일이나 드라이브에서 매체를 꺼내는 일이 그렇습니다. 이런 조작은 ioctl 이라는 시스템 콜로 보냅니다. ioctl 은 입출력 제어(input/output control)의 줄임입니다.

ioctl 은 장치 파일의 파일 디스크립터와 명령 번호를 함께 받습니다. 명령 번호의 뜻은 드라이버마다 따로 정합니다. 그래서 ioctl 을 부르는 코드는 상대 장치의 종류를 알아야 합니다. 파일 인터페이스가 숨기려던 장치 사이의 차이가 이 호출에서 다시 드러납니다.

장치 파일이 없는 장치

모든 장치가 장치 파일을 갖지는 않습니다. 대표적인 예가 네트워크 인터페이스입니다. 유닉스 계열에서 네트워크 카드는 /dev 아래에 파일로 나타나지 않습니다. 프로그램은 소켓을 열어 네트워크와 주고받습니다.

네트워크로 보내려면 상대 주소를 정하고 연결을 맺는 일이 먼저 필요합니다. 파일 이름 하나를 여는 것으로는 이 일을 담기 어렵습니다. 네트워크는 소켓이라는 다른 인터페이스로 다룹니다. 소켓도 열고 나면 파일 디스크립터를 받아 read 와 write 로 주고받습니다.

장치 파일을 만드는 주체

장치 파일은 보통 파일과 다른 방법으로 만듭니다. 예전 유닉스에서는 관리자가 mknod 명령에 종류와 두 번호를 주어 손으로 만들었습니다. 장치를 새로 달면 장치 파일도 새로 만들어야 했습니다.

지금의 운영체제는 커널이 장치를 찾는 대로 장치 파일을 만듭니다. 장치를 떼면 그 장치 파일도 사라집니다. 리눅스에서는 커널과 udev 라는 프로그램이 이 일을 나눠 맡습니다.

FreeBSD 에서는 devfs 라는 파일 시스템이 이 일을 맡습니다. devfs 는 디스크에 아무것도 저장하지 않습니다. 커널이 지금 아는 장치를 /dev 아래 파일로 보여 줄 뿐입니다.

장치 파일을 만드는 일은 관리자만 할 수 있습니다. 누구나 만들 수 있다면 권한이 무의미해지기 때문입니다. 디스크를 가리키는 장치 파일을 새로 만들어 권한을 느슨하게 붙이면 그 파일로 디스크 전체를 읽을 수 있습니다.

마운트는 외장 디스크 같은 다른 저장 매체의 파일 시스템을 디렉터리 트리의 한 디렉터리에 붙이는 일입니다. 그렇게 붙인 파일 시스템 안의 장치 파일은 이 기계의 관리자가 만든 것이 아닐 수 있습니다. 누군가 자기 컴퓨터에서 관리자 권한으로 만들어 넣어 왔을 수 있습니다. 이 틈을 막으려고, 마운트할 때 그 안의 장치 파일을 무시하게 하는 설정이 있습니다.

관련 항목

장치 파일 요청이 거치는 커널 부품

커널 · 디바이스 드라이버 · 커널 모듈 · 가상 파일 시스템 · 파일 디스크립터

장치 파일을 다루는 시스템 콜

시스템 콜 · open · read · write · close · ioctl · mmap

장치 파일에 적히는 정보

주 번호 · 부 번호 · 아이노드 · 권한

장치 파일의 하위 종류

문자 장치 · 블록 장치 · 터미널 · 의사 터미널

장치 파일을 만들고 관리하는 수단

mknod · udev · devfs · devtmpfs · 마운트

장치 파일에 입출력을 잇는 셸 기능

셸 · 리다이렉션 · 표준 입력 · 표준 출력 · 표준 오류

장치 파일이 속하는 상위 분류

파일 · 파일 시스템 · 유닉스 · 모든 것은 파일이다

장치 파일 대신 장치를 드러내는 수단

소켓 · 네트워크 인터페이스 · sysfs · procfs

장치 파일을 쓰는 운영체제

Linux · FreeBSD · macOS · Solaris

다른 이름: device file · device special file · 장치 특수 파일 · 디바이스 파일 · device node · 장치 노드