그림으로 배우는 Linux 구조 - 7 / 파일 시스템
By Bys on October 23, 2024
파일 시스템
6장에서 각종 장치는 디바이스 파일
로 접근 가능하다고 설명했습니다. 하지만 대부분의 저장 장치는 이 장에서 설명하는 파일 시스템
으로 접근합니다.
파일 시스템이 존재하지 않으면 데이터를 디스크 어떤 위치에 저장할지 직접 정해야 합니다. 게다가 다른 데이터를 훼손하지 않도록 비어 있는 영역도 관리가 필요합니다. 그리고 쓰기가 끝나서 나중에 다시 읽어 오려면 어느 위치에, 파일 크기가 얼마이고, 어떤 데이터를 배치했는지 기억해야 합니다.
파일 시스템은 이런 정보를 대신해서 관리해 줍니다. 파일 시스템은 사용자에게 의미 있는 데이터 뭉치를 파일 단위로 관리합니다. 각각의 데이터가 어디에 있는지 사용자가 직접 관리하지 않아도 저장 장치의 관리 영역에 기록됩니다.
위 그림에서 파일 형식으로 데이터를 관리하는 저장 장치의 영역(관리 영역 포함)과 해당 저장 영역을 다루는 처리(피일 시스템 코드) 양쪽을 모두 합쳐서 파일 시스템
이라고 부릅니다.
리눅스 파일 시스템은 각 파일을 디렉토리라고 하는 특수한 파일을 사용해서 분류할 수 있습니다. 디렉토리가 다르면 동일한 파일명을 사용할 수 있습니다. 또한 디렉토리 안에 또 다시 디렉토리를 만들어서 트리 구조를 만들 수 있습니다.
파일 시스템에는 데이터와 메타 데이터 두 종류의 데이터가 있습니다. 데이터는 사용자가 작성한 문서나 영상, 동영상, 프로그램 등에 해당합니다. 이에 반해 메타 데이터는 파일을 관리할 목적으로 파일 시스템에 존재하는 부가적인 정보입니다.
파일접근 방법
파일 시스템에는 POSIX(유닉스 계통 OS의 C 언어 인터페이스 규격) 에서 정한 함수로 접근할 수 있습니다.
- 파일 조작
- 작성, 삭제: create(), unlink() 등
- 열고 닫기: open(), close() 등
- 읽고 쓰기: read(), write(), mmap() 등
- 디렉토리 조작
- 작성, 삭제: mkdir(), rmdir()
- 현재 디렉토리 변경: chdir()
- 열고 닫기: opendir(), closedir()
- 읽기: readdir() 등
이런 함수 덕분에 사용자는 파일 시스템에 접근할 때 파일 시스템 종류의 차이를 의식할 필요없이 파일 시스템이 ext4 혹은 XFS 이라도 관계없이 파일을 만들고 싶다면 create() 함수를 사용합니다.
bash 같은 shell로 다양한 프로그램에서 파일 시스템에 접근할 때 내부적으로는 이러한 함수를 호출합니다.
파일 시스템 조작용 함수를 호출하면 다음과 같은 순서로 처리가 진행됩니다.
- 파일 시스템 조작용 함수가 내부적으로 파일 시스템을 조작하는 시스템 콜을 호출합니다.
- 커널 내부 가상 파일 시스템(Virtual File System, VFS) 처리가 동작하고 각각의 파일 시스템 처리를 호출합니다.
- 파일 시스템 처리가 디바이스 드라이버를 호출 합니다.
- 디바이스 드라이버가 장치를 조작합니다.
메모리 맵 파일
리눅스에는 파일 영역을 가상 주소 공간에 매핑하는 메모리 맵 파일 기능이 있습니다. mmap() 함수를 특정한 방법으로 호출하면 파일 내용을 메모리로 읽어서 그 영역을 가상 주소 공간에 매핑할 수 있습니다.
메모리 맵에 저장된 파일은 메모리와 같은 방법으로 접근할 수 있습니다. 데이터를 변경하면 나중에 저장 장치에 있는 파일에도 정해진 타이밍에 반영합니다. 이런 타이밍은 8장에서 설명합니다.
- hello 문자열이 들어 있는 testfile 작성.
- filemap 작성
package main
import (
"fmt"
"log"
"os"
"os/exec"
"strconv"
"syscall"
)
func main() {
pid := os.Getpid()
fmt.Println("*** testfile 메모리 맵 이전의 프로세스 가상 주소 공간 ***")
command := exec.Command("cat", "/proc/"+strconv.Itoa(pid)+"/maps")
command.Stdout = os.Stdout
err := command.Run()
if err != nil {
log.Fatal("cat 실행에 실패했습니다")
}
file, err := os.OpenFile("testfile", os.O_RDWR, 0)
if err != nil {
log.Fatal("testfile을 열지 못했습니다")
}
defer file.Close()
// mmap() 시스템 콜을 호출해서 5바이트 메모리 영역을 확보
data, err := syscall.Mmap(int(file.Fd()), 0, 5, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
if err != nil {
log.Fatal("mmap() 실행에 실패했습니다")
}
fmt.Println("")
fmt.Printf("testfile을 매핑한 주소: %p\n", &data[0])
fmt.Println("")
fmt.Println("*** testfile 메모리 맵 이후의 프로세스 가상 주소 공간 ***")
command = exec.Command("cat", "/proc/"+strconv.Itoa(pid)+"/maps")
command.Stdout = os.Stdout
err = command.Run()
if err != nil {
log.Fatal("cat 실행에 실패했습니다")
}
// 매핑한 파일 내용을 변경
replaceBytes := []byte("HELLO")
for i, _ := range data {
data[i] = replaceBytes[i]
}
}
AdminDevAccountRole:~/environment/temp $ ./filemap
*** testfile 메모리 맵 이전의 프로세스 가상 주소 공간 ***
00400000-004b6000 r-xp 00000000 103:02 1549630 /home/ubuntu/environment/temp/filemap
004b6000-00591000 r--p 000b6000 103:02 1549630 /home/ubuntu/environment/temp/filemap
00591000-0059e000 rw-p 00191000 103:02 1549630 /home/ubuntu/environment/temp/filemap
0059e000-005c2000 rw-p 00000000 00:00 0
c000000000-c000400000 rw-p 00000000 00:00 0
c000400000-c004000000 ---p 00000000 00:00 0
7cdb55280000-7cdb55300000 rw-p 00000000 00:00 0
7cdb55300000-7cdb57400000 rw-p 00000000 00:00 0
7cdb57400000-7cdb67580000 ---p 00000000 00:00 0
7cdb67580000-7cdb67581000 rw-p 00000000 00:00 0
7cdb67581000-7cdb87580000 ---p 00000000 00:00 0
7cdb87580000-7cdb87581000 rw-p 00000000 00:00 0
7cdb87581000-7cdb99430000 ---p 00000000 00:00 0
7cdb99430000-7cdb99431000 rw-p 00000000 00:00 0
7cdb99431000-7cdb9b806000 ---p 00000000 00:00 0
7cdb9b806000-7cdb9b807000 rw-p 00000000 00:00 0
7cdb9b807000-7cdb9bc00000 ---p 00000000 00:00 0
7cdb9bc31000-7cdb9bca2000 rw-p 00000000 00:00 0
7cdb9bca2000-7cdb9bd22000 ---p 00000000 00:00 0
7cdb9bd22000-7cdb9bd23000 rw-p 00000000 00:00 0
7cdb9bd23000-7cdb9bda2000 ---p 00000000 00:00 0
7cdb9bda2000-7cdb9be02000 rw-p 00000000 00:00 0
7ffdf186f000-7ffdf1890000 rw-p 00000000 00:00 0 [stack]
7ffdf18bf000-7ffdf18c3000 r--p 00000000 00:00 0 [vvar]
7ffdf18c3000-7ffdf18c5000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
testfile을 매핑한 주소: 0x7cdb9bc30000
*** testfile 메모리 맵 이후의 프로세스 가상 주소 공간 ***
00400000-004b6000 r-xp 00000000 103:02 1549630 /home/ubuntu/environment/temp/filemap
004b6000-00591000 r--p 000b6000 103:02 1549630 /home/ubuntu/environment/temp/filemap
00591000-0059e000 rw-p 00191000 103:02 1549630 /home/ubuntu/environment/temp/filemap
0059e000-005c2000 rw-p 00000000 00:00 0
c000000000-c000400000 rw-p 00000000 00:00 0
c000400000-c004000000 ---p 00000000 00:00 0
7cdb55280000-7cdb55300000 rw-p 00000000 00:00 0
7cdb55300000-7cdb57400000 rw-p 00000000 00:00 0
7cdb57400000-7cdb67580000 ---p 00000000 00:00 0
7cdb67580000-7cdb67581000 rw-p 00000000 00:00 0
7cdb67581000-7cdb87580000 ---p 00000000 00:00 0
7cdb87580000-7cdb87581000 rw-p 00000000 00:00 0
7cdb87581000-7cdb99430000 ---p 00000000 00:00 0
7cdb99430000-7cdb99431000 rw-p 00000000 00:00 0
7cdb99431000-7cdb9b806000 ---p 00000000 00:00 0
7cdb9b806000-7cdb9b807000 rw-p 00000000 00:00 0
7cdb9b807000-7cdb9bc00000 ---p 00000000 00:00 0
7cdb9bc30000-7cdb9bc31000 rw-s 00000000 103:02 258904 /home/ubuntu/environment/temp/testfile
7cdb9bc31000-7cdb9bca2000 rw-p 00000000 00:00 0
7cdb9bca2000-7cdb9bd22000 ---p 00000000 00:00 0
7cdb9bd22000-7cdb9bd23000 rw-p 00000000 00:00 0
7cdb9bd23000-7cdb9bda2000 ---p 00000000 00:00 0
7cdb9bda2000-7cdb9be02000 rw-p 00000000 00:00 0
7ffdf186f000-7ffdf1890000 rw-p 00000000 00:00 0 [stack]
7ffdf18bf000-7ffdf18c3000 r--p 00000000 00:00 0 [vvar]
7ffdf18c3000-7ffdf18c5000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
$ AdminDevAccountRole:~/environment/temp $ cat testfile
HELLO
- mmap() 함수 실행에 성공해서 testfile 파일 데이터 시작 주소가 0x7cdb9bc30000 가 되었습니다.
- 이 주소로 시작하는 영역이 실제로 메모리 매핑된걸 알 수 있습니다.
일반적인 파일 시스템
리눅스는 ext4, XFS, Btrfs 같은 파일 시스템을 주로 사용합니다. 파일 시스템은 아래와 같은 특징이 있습니다.
파일 시스템 | 특징 |
---|---|
ext4 | 예전부터 리눅스에서 사용하던 ext2, ext3 에서 전환하기 편함 |
XFS | 뛰어난 확장성 |
Btrfs | 풍푸한 기능 |
각 파일 시스템은 저장 장치에서 만드는 데이터 구조와 관리하는 처리 방식이 다릅니다. 각 파일 시스템은 다음과 같은 차이점이 존재합니다.
- 파일 시스템 최대 크기
- 파일 최대 크기
- 최대 파일 개수
- 파일명 최대 길이
- 동작별 처리 속도
- 표준 기능 이외의 추가 기능 유무
쿼터(용량 제한)
다양한 용도로 시스템을 사용하다 보면 파일 시스템 용량을 무제한으로 사용하다가, 다른 기능실행에 필요한 용량이 부족해지는 경우가 있습니다. 특히나 시스템 관리용 처리를 하는 데 필요한 용량이 부족하면 시스템 전체가 불안정해집니다. 이런 문제를 방지하려면 용도별로 사용 가능한 파일 시스템 용량을 제한하는 기능이 필요합니다. 이런 기능을 쿼터라고 부릅니다.
용도 A에 쿼터 제한을 두는 예시.
쿼터에는 다음과 같은 종류가 있습니다.
- 사용자 쿼터: 파일 소유자인 사용자마다 용량을 제한합니다. 예를 들어 알반 사용자 때문에 /home/ 디렉토리가 가득 차는 사태를 방지합니다.
- 디렉토리 쿼터: 특정 디렉토리 마다 용량을 제한합니다. 예를 들어 어떤 프로젝트 멤버가 공유하는 디렉토리 용량 제한을 둡니다.
- 서브 볼륨 쿼터: 파일 시스템 내부의 서브 볼륨 단위마다 용량을 제한합니다.
파일 시스템 정합성 유지
시스템을 운영하다 보면, 파일 시스템 내용에 오류가 생기기도 합니다. 전형적인 예를 들면 파일 시스템 데이터를 저장 장치에서 읽거나 쓰는 도중에 시스템 전원이 강제적으로 끊기는 경우 입니다.
root 아래에 foo, bar 라는 2개의 디렉토리가 있고, 이 상태에서 bar를 foo 아래로 이동시키면 파일 시스템은 위와 같이 처리합니다.
이런 처리 흐름은 프로세스에서 보자면 중간에 끼어들 수 없는 아토믹 처리입니다. 첫 번째 쓰기(foo 파일 데이터 갱신)가 끝나고 두 번째 쓰기(root 데이터 갱신)가 끝나기 전에 전원이 꺼졌다면 파일 시스템이 어중간한 오류 상태가 될지 모릅니다.
이후에 파일 시스템이 오류를 감지하는데 마운트 작업 중이라면 파일 시스템이 마운트 불가능하거나, 읽기 전용 모드로 다시 마운트 합니다. 또는 시스템 패닉이 일어납니다. 파일 시스템의 오류 방지 기술에는 여러 종류가 있지만 널리 사용되는 건 저널링과 카피 온 라이트 두 가지 방식입니다. ext4와 XFS는 저널링, Btrfs는 카피 온 라이트로 각각 파일 시스템 오류를 방지합니다.
저널링을 사용한 오류 방지
저널링은 파일 시스템 내부에 저널 영역이라고 하는 특수한 메타 데이터 영역을 준비합니다. 이 때 파일 시스템 갱신 방법은 다음과 같습니다.
(파일 시스템에서 저널(journal)은 데이터 무결성을 보장하기 위해 사용하는 기술입니다. 저널링 파일 시스템은 데이터가 실제로 저장되기 전에 변경 사항을 저널에 기록합니다. 이렇게 함으로써 시스템 충돌이나 전원 장애와 같은 예기치 않은 상황에서도 데이터의 일관성을 유지할 수 있습니다.)
- 갱신에 필요한 아토믹한 처리 목록을 일단 저널 영역에 기록합니다. 이 목록을 저널 로그라고 부릅니다.
- 저널 영역에 기록된 내용에 따라 실제로 파일 시스템 내용을 갱신합니다.
저널 로그를 갱신하던 도중에 강제로 전원이 끊겼다면 단순히 저널 로그를 버리기만 하면 실제 데이터는 처리 전 상태와 변함이 없습니다.
실제 데이터를 갱신하던 도중에 강제로 전원이 끊기면 저널 로그를 다시 시작해서 처리를 완료 상태로 만듭니다.
카피 온 라이트로 오류 방지
ext4나 XFS 등은 일단 저장 장치에 파일 데이터를 썼다면 이후 파일을 갱신할 때는 저장 장치의 동일한 위치에 데이터를 써넣습니다.
Btrfs 같은 카피 온 라이트 형식의 파일 시스템은 일단 파일에 데이터를 쓴 이후로는 갱신 할 때마다 다른 장소에 데이터를 작성합니다.
파일 이동 같은 작업은 갱신한 데이터를 다른 장소에 먼저 작성하고 나서 링크를 재작성하는 순서로 이루어집니다.
처리 중에 전원이 끊기는 일이 발생해도 재시작 후에 그전에 만들고 있던 중간 데이터를 삭제하면 오류는 발생하지 않습니다.
오류 방지 기능 덕분에 파일 시스템 오류 발생 빈도는 줄어들지만 완전히 없어지는건 아닙니다. 파일 시스템에 버그가 있거나 장애가 생기면 당연히 문제가 발생하기 때문입니다. 파일 시스템을 정기적으로 백업해서 파일 시스템 오류가 발생하면 마지막으로 백업한 시점으로 복원하는 방법이 일반적입니다.
기타 파일 시스템
ext4, XFS, Btrfs 파일 시스템 외에도 리눅스는 다양한 파일 시스템을 지원합니다. 여기서는 그중에서 몇 가지를 소개합니다.
메모리 기반의 파일 시스템
저장 장치 대신에 메모리를 사용해서 작성하는 tmpfs 파일 시스템이 있습니다. tmpfs 파일 시스템에 저장된 데이터는 컴퓨터 전원이 꺼지면 사라지지만 저장 장치에 접근할 필요가 없어서 접근 속도가 빠릅니다.
tmpfs 는 재시작할 때 기존 파일이 남아 있지 않아도 되는 /tmp 나 /var/run 에서 주로 사용합니다.
[root@ip-10-20-136-105 ~]# mount | grep tmpfs
devtmpfs on /dev type devtmpfs (rw,nosuid,size=4001868k,nr_inodes=1000467,mode=755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
tmpfs on /run type tmpfs (rw,nosuid,nodev,mode=755)
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
......
tmpfs on /var/lib/kubelet/pods/16431a91-0450-4853-b0c3-eeb0fcf90943/volumes/kubernetes.io~empty-dir/istio-envoy type tmpfs (rw,relatime,size=7005596k)
......
free 명령어 출력 결과에 있는 shared 필드값이 tmpfs 등에서 실제로 사용된 메모리 용량을 뜻합니다.
[root@ip-10-20-2-177 ~]# free
total used free shared buff/cache available
Mem: 7912668 448116 6534444 476 930108 7219180
Swap: 0 0 0
다음은 1GiB 의 tmpfs 를 만들어서 /mnt 밑에 마운트하는 예제입니다.
sudo mount -t tmpfs tmpfs-test /mnt -osize=1G
tmpfs 가 사용하는 메모리는 파일 시스템 작성과 동시에 전부 확보하는게 아니라, 데이터에 처음으로 접근했을 때 페이지 단위로 메모리를 확보하는 구조입니다.
[root@ip-10-20-2-177 ~]# free
total used free shared buff/cache available
Mem: 7912668 448116 6534444 476 930108 7219180
Swap: 0 0 0
[root@ip-10-20-2-177 ~]# sudo mount -t tmpfs tmpfs-test /mnt -osize=1G
[root@ip-10-20-2-177 ~]# free
total used free shared buff/cache available
Mem: 7912668 448088 6534444 476 930136 7219208
Swap: 0 0 0
출력 결과처럼 shared 값은 늘어나지 않았습니다. /mnt 아래에 데이터를 기록하고 다시 free 명령어를 실행해 봅니다.
sudo dd if=/dev/zero of=/mnt/testfile bs=100M count=1
[root@ip-10-20-2-177 mnt]# free
total used free shared buff/cache available
Mem: 7912668 444300 6435360 102872 1033008 7120464
Swap: 0 0 0
shared 사용량이 100MiB 늘어난 걸 알 수 있습니다. /mnt 하위 파일을 삭제하면 shared 사용량도 다시 줄어듭니다.
네트워크 파일 시스템
Network File System(NFS) 나 Common Internet File System(CIFS) 은 원격 호스트에 있는 파일 시스템을 로컬에 있는 파일 시스템처럼 조작할 수 있습니다.
여러 기기 저장 장치를 하나로 묶어서 커다란 파일 시스템으로 만드는 CephFS 같은 파일 시스템도 있습니다.
procfs
시스템에 있는 프로세스 관련 정보를 얻기 위해서 procfs 라고 하는 파일 시스템이 존재합니다.
보통 procfs 는 /proc 아래에 마운트됩니다. /rpco/pid 아래에 있는 파일에 접근하려면 pid 에 대응하는 프로세스 정보를 얻을 수 있습니다.
# $$ 는 현재 Shell 의 PID
[root@ip-10-20-2-177 ~]# ls /proc/$$
arch_status auxv cmdline cpuset exe gid_map limits maps mounts ns oom_score patch_state root sessionid smaps_rollup statm task timerslack_ns
attr cgroup comm cwd fd io loginuid mem mountstats numa_maps oom_score_adj personality sched setgroups stack status timens_offsets uid_map
autogroup clear_refs coredump_filter environ fdinfo latency map_files mountinfo net oom_adj pagemap projid_map schedstat smaps stat syscall timers wchan
파일명 | 의미 |
---|---|
/proc/pid/maps |
프로세스 메모리 맵 |
/proc/pid/cmdline |
프로세스 명령줄 인수 |
/proc/pid/stat |
프로세스 상태, 사용한 CPU 시간, 우선도, 사용 메모리 용량 등 |
파일명 | 의미 |
---|---|
/proc/cpuinfo |
시스템에 설치된 CPU 관련 정보 |
/proc/diskstat |
시스템에 설치된 저장 장치 관련 정보 |
/proc/meminfo |
시스템 메모리 관련 정보 |
지금까지 사용한 ps, sar, free 같은 명령어처럼 OS가 제공하는 각종 정보를 표시하는 명령어는 procfs 에서 정보를 수집합니다. starce를 사용해서 실행해보면 /proc/ 아래에 있는 파일에서 데이터를 읽어 오는 걸 알 수 있습니다.
$ strace free
execve("/usr/bin/free", ["free"], 0x7ffeb0e03710 /* 21 vars */) = 0
brk(NULL) = 0x557bcd09b000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc71ab5bc0) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f66de8c5000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=14255, ...}) = 0
mmap(NULL, 14255, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f66de8c1000
close(3) = 0
openat(AT_FDCWD, "/lib64/libprocps.so.8", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\233\276\"j\354S\255\357''\321&c\27>z"..., 188, 744) = 188
......
sysfs
리눅스에 procfs가 도입된 후 얼마 지나지 않아 프로세스 관련 정보 이외에도 커널이 관리하는 잡다한 정보를 아무런 제한 없이 procfs에 저장하는 문제가 생겼습니다. 따라서, procfs를 더이상 남용하지 않도록 이러한 정보를 모아둘 장소를 만든 것이 sysfs 입니다. sysfs는 보통 /sys/ 디렉토리 아래에 마운트 됩니다.
$ ll /sys/block
total 0
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop0 -> ../devices/virtual/block/loop0
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop1 -> ../devices/virtual/block/loop1
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop2 -> ../devices/virtual/block/loop2
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop3 -> ../devices/virtual/block/loop3
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop4 -> ../devices/virtual/block/loop4
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop5 -> ../devices/virtual/block/loop5
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop6 -> ../devices/virtual/block/loop6
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 loop7 -> ../devices/virtual/block/loop7
lrwxrwxrwx. 1 root root 0 Dec 6 10:01 nvme0n1 -> ../devices/pci0000:00/0000:00:04.0/nvme/nvme0/nvme0n1
sysfs 에서 얻을 수 있는 정보의 예로 /sys/block 이 있습니다. 이 데릭토리 아래에는 시스템에 존재하는 블록 장치마다 디렉토리가 존재합니다.
$ cat /sys/block/nvme0n1/dev
259:0
$ ls -l /dev/nvme0n1
brw-rw----. 1 root disk 259, 0 Dec 6 10:01 /dev/nvme0n1
이 중에서 nvme0n1 디렉토리는 NVMe SSD 장치를 가리키고 /dev/nvme0n1 에 대응합니다. 이런 디렉토리 아래에 있는 dev 파일 내용에는 장치의 메이저 번호와 마이너 번호가 들어 있습니다.
Reference
- 그림으로 배우는 리눅스 구조 (다케우치 사토루)
- E-Book
book
linux
filesystem
]