개발 공부

손승열(Son Seungyeol)

[운영체제][OSTEP] 파일과 디렉터리

운영체제가 영속 장치를 관리하는 방법에 대해 공부해봅니다.

손승열(Son Seungyeol)
[운영체제][OSTEP] 파일과 디렉터리

🚪 들어가며

영속 저장 장치(persistent storage)는 전원 공급이 차단되면 내용이 사라지는 메모리와 다르게 그러한 상황에서도 그대로 데이터를 보존한다.

다음과 같은 영속 저장 장치들이 존재한다.

  • 하드 디스크 드라이브

  • 솔리드스테이트드라이브(solid state drive, SSD)


1️⃣ 파일과 디렉터리

저장 장치의 가상화에 대한 두 가지 주요 개념이 개발됨.

[ 📝 파일 ]

  • 단순히 읽거나 쓸 수 있는 순차적인 바이트의 배열

  • 각 파일은 저수준의 이름(low-level name)을 가짐.

    ➡️ 보통은 숫자로 표현되지만 사용자는 그 이름에 대해서 알지 못함.

    ➡️ 이를 아이노드 번호(inode number)라고 부름.

  • 각 파일은 아이노드 번호와 연결되어 있음.

  • 대부분 시스템에서 운영체제는 파일의 구조를 모름.(그림인지 문서인지 등등)

  • 파일 시스템의 역할은 그러한 데이터를 디스크에 안전히 저장하고, 데이터가 요청되면 처음 저장했던 데이터를 돌려주는 것

[ 📁 디렉터리 ]

  • 디렉터리도 저수준의 이름을 가짐.

    ➡️ 파일과 달리 디렉터리의 내용은 구체적으로 정해져 있음.

  • 디렉터리는 〈사용자가 읽을 수 있는 이름, 저수준의 이름〉 쌍으로 이루어진 목록을 갖고 있음.

  • 디렉터리의 각 항목은 파일 또는 다른 디렉터리를 가리킨다.

  • 디렉터리 내에 다른 디렉터리를 포함함으로써 사용자는 모든 파일들과 디렉터리들이 저장되어 있는 임의의 디렉터리 트리(directory tree, 또는 디렉터리 계층(directory hierarchy))을 구성할 수 있음.

디렉터리 계층

루트 디렉터리(root directory)부터 시작함.
➡️ Unix 기반의 시스템에서 /으로 표현

루트 디렉터리부터 원하는 파일이나 디렉터리의 이름을 표현할 때까지 구분자(sperator)를 사용하여 하위 디렉터리를 명시

예를 들어 다음과 같은 디렉터리 구조를 갖는 경우를 살펴보자.

/
├── foo
│   └── bar.txt       
└── bar
    ├── bar
    └── foo
        └── bar.txt
  • 디렉터리들과 파일들은 파일 시스템 트리 안에서 서로 다른 위치에 있는 경우, 동일한 이름을 가질 수 있음.

  • 두 bar.txt의 절대 경로명(absolute pathname)은 각각 다음과 같음.

    • /foo/bar.txt

    • /bar/foo/bar.txt

  • 파일 이름이 두 부분으로 구성되어 있음.(마침표로 분리)

    • 첫 번째 부분은 임의의 이름임.

    • 두 번재 부분은 대체적으로 파일의 종류를 나타내기 위해 사용됨.

      ➡️ 하지만 대부분 관용적(convention)일 뿐임.

      ➡️ 파일 이름이 main.c 라고 해서 내용이 반드시 C 소스 코드일 필요 X

위의 예시에서처럼 파일 시스템을 통해 파일들을 효율적으로 명명할 수 있음.

💡 어떤 자원을 접근하는 가장 첫 단계는 그 대상의 이름을 아는 것
➡️ 시스템에서 이름짓기 기능은 매우 중요함.

Unix 시스템상에서 파일 시스템은 디스크, USB 메모리, CD-ROM 등 다양한 장치에 존재하는 파일들을 통합된 방법으로 접근할 수 있도록 함.

모든 파일들은 하나의 디렉터리 트리 상에 위치함.


2️⃣ 파일 시스템 인터페이스

파일의 생성과 접근, 삭제 등을 비롯한 파일 시스템 인터페이스를 좀 더 상세하게 논의해보자.


3️⃣ 파일의 생성

✅ open 시스템 콜을 사용하여 파일을 생성할 수 있음.
➡️ open()을 호출하면서 O_CREAT 플래그를 전달하면 프로그램은 새로운 파일을 만들 수 있음.

예를 들어 현재의 디렉터리에 "foo"라는 파일을 만드는 코드는 다음과 같다.

c
int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);

open()은 다수의 플래그를 받음.

이 예시에서는 사용한 플래그들은 다음과 같다.

  • O_CREAT: 파일을 생성

  • O_WRONLY: 파일이 열렸을 때 쓰기만 가능

  • O_TRUNC: 파일이 이미 존재할 때는 파일의 크기를 0 byte로 줄여서 기존 내용을 모두 삭제

open()의 중요한 항목은 리턴값인 파일 디스크립터(file descriptor)임.
➡️ 프로세스마다 존재하는 정수로서 Unix 시스템에서 파일을 접근하는 데 사용됨.
➡️ open된 파일을 읽고 쓰는 데 사용(해당 파일에 대한 권한 필요)

이러한 측면에서 파일 디스크립터는 capability임.
➡️ 특정 동작에 대한 수행 자격을 부여하는 핸들임.

파일 디스크립터를 파일 객체를 가리키는 포인터로 볼 수도 있음.
➡️ 그러한 객체를 생성하면, read() 또는 write()와 같은 다른 "메소드"로 파일에 접근 가능


4️⃣ 파일의 읽기와 쓰기

✅ 이미 존재하고 있는 파일을 읽는 경우 cat 프로그램을 사용하여 파일의 내용을 화면에 덤프

prompt> echo hello > foo
prompt> cat foo
hello
prompt>

🤔 위의 예시에서 cat 프로그램은 어떻게 파일 foo에 접근할까?
➡️ 이를 알아보기 위해 프로그램이 호출하는 시스템 콜을 추적하는 도구를 사용해보자.

Linux의 strace는 프로그램이 실행되는 동안에 호출된 모든 시스템 콜을 추적하고, 그 결과를 화면에 보여줌.

다음을 통해 cat이 어떤 동작을 하는지 살펴보자.(몇몇 호출들은 생략됨.)

prompt> strace cat foo
...
open("foo", O_RDONLY|O_LARGEFILE) = 3
read(3, "hello\n", 4096) = 6
write(1, "hello\n", 6) = 6
hello
read(3, "", 4096) = 0
close(3) = 0
...
prompt>

파일에 쓰는 것도 비슷한 단계를 거침.


5️⃣ 비 순차적 읽기와 쓰기

때로는 파일의 특정 오프셋부터 읽거나 쓰는 것이 유용할 때가 있음.

예를 들어 문서의 인덱스를 만들고 특정 단어를 찾는다고 해 보자.
➡️ 이러한 경우 문서 내의 임의의 오프셋에서 읽기를 수행해야 함.

✅ 이를 위해 lseek()라는 시스템 콜을 사용

함수의 프로토타입은 다음과 같다.

c
off_t lseek(int fildes, off_t offset, int whence);
  • 첫 번째 인자: 파일 디스크립터

  • 두 번째 인자: 파일의 특정 위치(file offset)를 가리킴.

  • 세 번째 인자: whence라고 부르며, 탐색 방식을 결정

man 페이지에는 다음과 같이 나와 있다.

whence가 SEEK_SET이면 오프셋은 offset 바이트로 설정된다.
만약 whence가 SEEK_CUR이면 오프셋은 현재 위치에 offset 바이트를 더한 값으로 설정된다.
만약 whence가 SEEK_END이면 오프셋은 파일의 크기에 offset 바이트를 더한 값으로 설정된다.

즉, 프로세스가 open()한 각 파일에 대해 운영체제는 "현재" 오프셋을 추적하여 다음 읽기 또는 쓰기 위치를 결정함.

오프셋은 두 가지 중 하나의 방법으로 갱신됨.

  • N 바이트를 읽거나 쓸 때 현재 오프셋에 N이 더해짐.

    ➡️ 따라서 각 읽기 또는 쓰기는 암묵적으로 오프셋을 갱신

  • lseek로 명시적으로 오프셋을 변경

lseek()는 디스크 암을 이동시키는 디스크의 탐색(seek) 작업과 아무 관계도 없음.
➡️ lseek() 호출은 커널 내부에 있는 변수의 값을 변경함.
➡️ 즉, lseek()를 호출한다고 디스크 탐색을 하는 것은 아님.


6️⃣ fsync()를 이용한 즉시 기록

write() 호출의 목적은 대부분 해당 데이터를 가까운 미래에 영속 저장 장치에 기록해 달라고 파일 시스템에 요청하는 것

성능상 이유로 파일 시스템은 쓰기들을 일정 시간(5초 또는 30초 등)동안 메모리에 모음.
➡️ 버퍼링

일정 간격으로 쓰기요청(들)이 저장 장치에 전달되지만, 응용 프로그램의 입장에서는 write() 호출 즉시 쓰기가 완료된 것처럼 보임.

⚠️ write() 호출 후 디스크에 쓰기 직전 기계가 크래시하는 경우 등 드물게 데이터가 유실되는 경우가 발생

DBMS의 복원 모듈과 같은 일부 프로그램은 때때로 강제적으로 즉시 디스크에 기록할 수 있는 기능이 필요
➡️ ✅ 이를 위해 Unix에서 제공되는 인터페이스가 fsync(int fd)임.

프로세스가 특정 파일 디스크립터에 대해 fsync()를 호출하면 파일 시스템은 지정된 파일의 모든 더티(dirty = 갱신된) 데이터를 디스크로 강제로 내려보냄.
➡️ 모든 쓰기들이 처리되면 리턴

다음의 사용 예시를 살펴보자.

foo라는 파일을 열어서 데이터를 쓰고 fsync()를 호출하여 해당 블럭을 즉시 디스크에 강제적으로 기록하는 경우이다.

c
int fd = open("foo", O_CREAT | O_WRONLY | O_TRUNC);
assert(fd > −1);
int rc = write(fd, buffer, size);
assert(rc == size);
rc = fsync(fd);
assert(rc == 0);

어떤 경우 파일 foo가 존재하는 디렉터리도 fsync() 해주어야 함.
➡️ 디렉터리를 함께 fsync()함으로써, 파일 자체와 이 파일이 존재하는 디렉터리 모두 안전하게 디스크에 저장하는 것이 보장됨.

💡 파일이 새로이 생성된 경우, 디렉터리를 반드시 fsync() 해주어야 함.


7️⃣ 파일 이름 변경

✅ mv 명령으로 파일명을 변경할 수 있음.

예를 들어 foo를 bar라는 새로운 이름으로 바꾸는 명령어는 다음과 같음.

prompt> mv foo bar

💡 strace를 사용하면 mv가 rename(char *old, char *new)이라는 두 개의 인자를 갖는 시스템 콜을 호출하는 것을 볼 수 있음.
➡️ 각 인자는 원래의 파일 이름(old)과 새로운 이름(new)를 나타냄.

rename()은 한 가지 흥미로운 특성을 보장
➡️ (대체적으로) 시스템 크래시에 대해 원자적으로 구현됨.
➡️ 이름 변경 중 크래시가 발생하면 파일 이름은 원래의 이름이나 새로운 이름, 둘 중의 하나를 갖게 됨.


8️⃣ 파일 정보 추출

파일 시스템은 각 파일에 대한 정보를 보관함.
➡️ 메타데이터(metadata)

✅ 어떤 파일의 메타데이터를 보려면 stat()이나 fstat() 시스템 콜을 사용
➡️ 이 호출들은 파일에 대한 경로명(또는 파일 디스크립터)을 입력으로 받음.

stat의 구조는 다음과 같음.

c
struct stat {
  dev_t st_dev;         /* ID of device containing file */
  ino_t st_ino;         /* inode number */
  mode_t st_mode;       /* protection */
  nlink_t st_nlink;     /* number of hard links */
  uid_t st_uid;         /* user ID of owner */
  gid_t st_gid;         /* group ID of owner */
  dev_t st_rdev;        /* device ID (if special file) */
  off_t st_size;        /* total size, in bytes */
  blksize_t st_blksize; /* blocksize for filesystem I/O */
  blkcnt_t st_blocks;   /* number of blocks allocated */
  time_t st_atime;      /* time of last access */
  time_t st_mtime;      /* time of last modification */
  time_t st_ctime;      /* time of last status change */
};

💡 파일의 크기(바이트 단위), 저수준 이름(아이노드 번호), 소유권, 파일이 접근되고 변경된 시간 등의 많은 정보가 있음.

일반적으로 파일 시스템은 아이노드에 이 정보를 보관
➡️ 어떤 파일 시스템들은 이 자료 구조의 이름을 dnode라고 함.(기본 개념은 유사)


9️⃣ 파일 삭제

✅ Unix에서는 rm 프로그램을 실행하여 파일을 삭제

strace를 사용하여 rm이 어떤 시스템 콜을 사용하는지 살펴보자.

다음은 foo라는 파일을 삭제하는 경우이다.

prompt> strace rm foo
...
unlink("foo") = 0
...

💡 위에서 확인할 수 있듯, unlink()는 지워야 하는 파일 이름을 인자로 받은 후에 성공하면 0을 리턴함.

🤔 왜 시스템 콜의 이름이 "remove"나 "delete"가 아니라 "unlink"일까?
➡️ 이를 이해하기 위해서는 파일뿐만 아니라 디렉터리에 대해서도 이해해야 함.


🔟 디렉터리 생성

디렉터리 관련 시스템 콜들은 디렉터리를 생성하고, 읽고, 삭제함.
➡️ 단, 디렉터리에는 절대로 직접 쓸 수 없음.

💡 디렉터리는 파일 시스템의 메타데이터로 분류되며, 항상 간접적으로만 변경됨.
➡️ 예를 들면 파일이나 디렉터리 또는 다른 종류의 객체들을 생성함으로써 디렉터리를 변경할 수 있음.

✅ 디렉터리 생성을 위한 시스템 콜로 mkdir()이 있음.

같은 이름의 mkdir 프로그램을 사용하여 동작을 살펴보자.

foo라는 디렉터리를 생성하는 경우이다.

prompt> strace mkdir foo
...
mkdir("foo", 0777) = 0
...
prompt>

처음 디렉터리가 생성되면 빈 상태이지만, 사실은 아주 기본적인 내용이 들어 있기는 함.

  • "."(dot) 디렉터리: 디렉터리 자신을 나타내기 위한 것

  • ".."(dot-dot): 자신의 부모 디렉터리를 가리키기 위한 것


1️⃣1️⃣ 디렉터리 읽기

✅ Unix에서는 ls 프로그램을 통해 디렉터리를 읽을 수 있음.

ls와 유사한 도구를 직접 만들어 어떻게 동작하는지 알아보자.

디렉터리의 open은 파일을 open하는 것과는 다른 새로운 시스템 콜을 사용함.

다음 예시 프로그램은 opendir(), readdir(), closedir()를 사용하여 디렉터리의 내용을 출력한다.

c
int main(int argc, char *argv[])
{
  DIR *dp = opendir(".");
  assert(dp != NULL);
  struct dirent *d;
  while ((d = readdir(dp)) != NULL) {
    printf("%d %s\n", (int) d−>d_ino, d−>d_name);
  }
  closedir(dp);
  return 0;
}

다음의 struct dirent 자료 구조를 통해 각 디렉터리 항목에 저장된 정보를 확인할 수 있다.

c
struct dirent {
  char d_name[256];        /* filename */
  ino_t d_ino;             /* inode number */
  off_t d_off;             /* offset to the next dirent */
  unsigned short d_reclen; /* length of this record */
  unsigned char d_type;    /* type of file */
};

디렉터리에는 많은 정보가 있지 않기 때문에 프로그램은 각 파일에 stat()을 호출하여 파일 크기와 같은 구체적인 정보를 얻음.
➡️ ls가 -l 플래그를 전달받았을 때, 추가 정보를 얻기 위해 stat()을 호출


1️⃣2️⃣ 디렉터리 삭제하기

✅ rmdir() 시스템 콜을 사용하여 디렉터리를 삭제할 수 있음.
➡️ rmdir이라는 같은 이름의 프로그램이 이 시스템 콜을 사용

파일 삭제와 다른 점은 디렉터리 삭제는 하나의 명령으로 아주 많은 양의 데이터를 지울 수 있기 때문에 좀 더 위험하다는 것
➡️ 그렇기 때문에 rmdir()은 디렉터리를 지우기 전에 디렉터리가 비어 있어야 한다는 조건이 붙음.(즉, "."와 ".."외에는 없어야 함.)

💡 비어있지 않은 디렉터리에 대해서 rmdir()을 호출하면 실패


1️⃣3️⃣ 하드 링크

파일 삭제 시 unlink()를 사용하는 이유를 이해하기 위해 파일 시스템 트리에 항목을 추가하는 새로운 시스템 콜 link()를 알아보자.

link() 시스템 콜은 두 개의 인자를 받음.

  • 원래의 경로명

  • 새로운 경로명

💡 원래 파일 이름에 새로운 이름을 "link"하면 동일한 파일을 접근할 수 있는 새로운 방법을 만들게 됨.
➡️ ✅ Unix에서 ln이 그 일을 함.

다음의 사용 예시를 살펴보자.

"hello"라는 단어가 저장된 file이라는 파일을 생성하고 file2라는 하드 링크를 생성하는 경우이다.

prompt> echo hello > file
prompt> cat file
hello
prompt> ln file file2
prompt> cat file2
hello

💡 link는 새로이 링크하려는 이름 항목을 디렉터리에 생성하고, 원래 파일과 같은 아이노드 번호를 가리키도록 함.
➡️ 파일은 복사되지 않음.
➡️ 대신 같은 파일을 가리키는 두 개의 이름이 생성됨.

파일을 생성할 때 사실은 두 가지 작업을 하게 됨.

  • 파일 관련 거의 모든 정보를 관리하는 자료 구조(아이노드)를 만듦.

    ➡️ 파일 크기와 디스크 블럭의 위치 등이 포함

  • 해당 파일에 사람이 읽을 수 있는 이름을 연결하고 그 연결 정보를 디렉터리에 생성

파일 시스템에 파일의 하드 링크를 생성한 후에는 원래의 파일 이름과 새로 생성된 파일 이름 간에는 차이가 없음.
➡️ 그 두 개의 파일 이름은 동일한 아이노드 번호에서 찾을 수 있는 파일의 메타데이터에 대한 연결일 뿐임.

💡 위의 경우 unlink()를 호출하여 파일 이름 file을 제거해도 여전히 해당 파일을 어려움 없이 접근할 수 있음.
➡️ file2를 통해서 접근 가능

💡 파일을 unlink하면 아이노드 번호의 참조 횟수(reference count)를 검사
➡️ 이 참조 횟수(때로는 연결 횟수(link count)라고도 불림)가 특정 아이노드에 대해 다른 이름이 몇 개나 연결되어 있는지 관리

unlink()가 호출되면 이름과 해당 아이노드 번호 간의 "연결"을 끊고 참조 횟수를 하나 줄임.

✅ 참조 횟수가 0에 도달하면 파일 시스템은 비로소 아이노드와 관련된 데이터 블럭을 해제하여 파일을 진정으로 "삭제"함.
➡️ 파일의 참조 횟수는 stat()을 사용하여 확인할 수 있음.


1️⃣4️⃣ 심볼릭 링크

심볼릭 링크(symbolic link) 또는 소프트 링크(soft link)라고 부르는 또 다른 아주 유용한 종류의 링크가 존재

하드 링크는 제한이 많은 편임.
➡️ 디렉터리에 대해서는 하드 링크를 만들 수 없음.(디렉터리 트리에 순환 구조를 만들 우려 때문)
➡️ 다른 디스크 파티션에 있는 파일에 대해서도 하드 링크를 걸 수 없음.(아이노드 번호는 하나의 파일 시스템 내에서만 유일)

✅ 심볼릭 링크를 만들기 위해서 동일한 ln 프로그램을 -s 플래그와 함께 사용할 수 있음.

다음의 사용 예시를 살펴보자.

prompt> echo hello > file
prompt> ln −s file file2
prompt> cat file2
hello

표면적으로는 하드 링크와 유사하지만 소프트 링크는 사실 매우 다름.

  • 심볼릭 링크는 다른 형식의 독립된 파일임.

    ➡️ 파일 시스템에 존재하는 세 번째 종류의 유형임.(파일, 디렉터리, 심볼릭 링크)

    ➡️ stat() 또는 ls를 통해 이를 알 수 있음.

  • 연결하는 파일의 경로명을 저장하는 것에 따른 크기(용량)를 지님.

심볼릭 링크가 만들어진 방식 때문에 dangling reference라는 문제가 발생할 수도 있음.

다음의 예시를 통해 이를 알아보자.

prompt> echo hello > file
prompt> ln −s file file2
prompt> cat file2
hello
prompt> rm file
prompt> cat file2
cat: file2: No such file or directory

위에서 알 수 있듯, 하드 링크와 다르게 원래의 파일을 삭제하면 심볼릭 링크가 가리키는 실제 파일은 더 이상 존재하지 않게 됨.


1️⃣5️⃣ 파일 시스템 생성과 마운트

🤔 다수의 파일 시스템들이 존재할 때 이들을 묶어서 어떻게 하나의 큰 디렉터리 트리를 구성할까?

✅ 대부분의 파일 시스템에서 파일 시스템을 생성하는 mkfs라는 도구를 제공

장치명(디스크 파티션, ex) /dev/sda1)과 파일 시스템(ex) EXT3)을 전달하면, 해당 파티션에 전달된 파일 시스템 형식으로 구성된 빈 파일 시스템을 생성함.
➡️ 생성된 파일 시스템은 자체적인 디렉터리 구조로 구성되어 있으며 비어있음.(루트 디렉터리만 존재)

새로이 생성된 파일 시스템은 현재 디스크에 존재함.
➡️ 이 파일 시스템을 루트 디렉터리에서 시작하는 기존의 디렉터리 구성을 통해 접근할 수 있도록 해주어야 함.(마운트)
➡️ ✅ 이 작업은 mount 프로그램을 사용함.(내부적으로 mount() 시스템 콜을 사용하여 실제 작업을 처리)

기존의 디렉터리 중 하나를 마운트 지점(mount point)으로 지정 후 마운트 지점에 생성된 파일 시스템을 "붙여 넣음".

예를 들어 파티션 /dev/sda1에 EXT3 파일 시스템이 다음과 같이 존재한다고 가정하자.

/
├── a
│   └── foo
└── b
    └── foo

이 파일 시스템을 /home/users라는 위치에 마운트하는 경우 다음과 같이 작성하면 됨.

prompt> mount −t ext3 /dev/sda1 /home/users

마운트 작업이 성공하면, 새로운 파일 시스템을 기존 디렉터리 경로를 통해 접근할 수 있게 됨.

/
├── home
.   ├── users
.   .   ├── a
.   .   │   └── foo
    .   └── b
            └── foo

이처럼 여러 개의 개별적인 파일 시스템을 갖는 대신에 마운트는 모든 파일 시스템들을 하나의 트리 아래에 통합시킴.

💡 Unix와 Windows 운영체제의 가장 큰 차이점 중의 하나가 마운트임.
➡️ Unix에서 파일 시스템 파티션은 Windows에서의 드라이브와 같음.
➡️ Unix는 부팅 시 마운트 작업을 통해 여러 개의 파일 시스템 파티션을 단일 디렉터리 구조로 묶음.
➡️ Windows는 묶지 않기 때문에 모든 파티션이 C:, D: 드라이브라는 이름으로 따로 존재함.

✅ 시스템에 어떤 파티션들이 어디에 마운트되었는지 알려면 mount 프로그램을 실행

실행 시 다음과 같은 결과가 출력될 수 있음.

/dev/sda1 on / type ext3 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
/dev/sda5 on /tmp type ext3 (rw)
/dev/sda7 on /var/vice/cache type ext3 (rw)
tmpfs on /dev/shm type tmpfs (rw)
AFS on /afs type afs (rw)

위의 출력 결과를 통해 다음과 같은 사실들을 파악할 수 있음.

  • 총 7개의 파티션이 있음.

  • 각각 다음과 같은 파일 시스템을 사용함.

    • ext3 파일 시스템

    • proc 파일 시스템: 현재 프로세스에 대한 정보를 접근하기 위한 파일 시스템

    • tmpfs: 임시 파일들만을 위한 파일 시스템

    • AFS: 분산 파일 시스템

  • /dev/sda1 파티션은 / 디렉터리에 연결됨.

  • /dev/sda7 파티션은 EXT3 파일 시스템으로 구성되어 있으며 /var/vice/cache에 마운트됨.


📚 참고 문헌

Operating Systems: Three Easy Pieces ― 39: Interlude: Files and Directories

운영체제 아주 쉬운 세 가지 이야기 ― 42: 막간 : 파일과 디렉터리

관련있는 게시물

[운영체제][OSTEP] RAID
개발 공부

[운영체제][OSTEP] RAID

대용량이면서 고속의 신뢰할 수 있는 저장 시스템을 만드는 핵심 기술들에 대해 공부해봅니다.

손승열(Son Seungyeol)
손승열(Son Seungyeol)
[운영체제][OSTEP] 파일 시스템 구현
개발 공부

[운영체제][OSTEP] 파일 시스템 구현

간단한 파일 시스템을 만드는 방법에 대해 공부합니다.

손승열(Son Seungyeol)
손승열(Son Seungyeol)

Made with React, Gatsby and DatoCMS by @smastrom

Contribute or star on GitHub

© 2022-2023 손승열 for 🅒🅞🅝🅣🅔🅝🅣🅢

모두 좋은 하루 보내세요! 😊