개발 공부

손승열(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를 사용하면 mvrename(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 🅒🅞🅝🅣🅔🅝🅣🅢

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