OS - 파일 시스템
File system
OS의 파일 관리 소프트웨어.
파일 저장방식
, 파일관리
, 파일보호
등을 관리하는 소프트웨어이다.
파일 자체 내용 뿐 아니라 파일 metadata도 같이 관리한다.
파일 저장을 보통 1차원 적으로 저장하지 않고 관리와 편의성을 위해 디렉토리를 두고 계층적으로 저장한다.
이러한 파일 시스템은 결국 디스크에 저장되는데 디스크 또한 메모리처럼 논리디스크와 물리디스크로 나뉜다
운영체제가 보는것은 논리디스크로 파티션으로 불림.
논리디스크는 하나의 물리디스크를 여러 개 논리디스크로 구성할 수도 있고
여러 개의 물리디스크를 하나의 논리디스크로 구성할 수 도 있다.
디스크의 용도는 크게 2가지 나눔
file system
swap area
File과 관련된 연산들
File
은 이름을 통해 접근하는 단위이다.
비휘발성 보조기억장치에 저장하게 된다.
리눅스 등의 운영체제에서 File
은 꼭 데이터 저장 목적이 아닌 여러 장치를 논리적 단위로 관리하기 위한 목적으로 사용하기도 한다(Device special file).
아래와 같은 연산자를 제공한다.
- File create
- File delete
- File read & write
- File repositioning(lseek)
- File open & close
File repositioning(lseek)
file은 크기가 크기 때문에 어느 위치를 읽거나 쓰는지 가리키는 포인터가 필요한데 이런 접근위치를 수정해주는 연산이다.
file open & close
read & write
를 하기 전 후 의무적으로 수행하는 연산
따로 정의돼 있는 이유는 disk에서 메모리로 내용을 옮기는 것이 아닌 file의 meta data를 메모리로 옮기는 작업이다.
Directory 또한 하나의 파일로써 file의 특성들을 갖고 있다.
단 파일내용이 다른 파일들의 meta data 중 일부(혹은 전부)를 내용으로 저장하고 있는 특별한 파일이다.
디렉토리를 위한 연산도 file에서 사용하는 연산처럼 여러가지다.
- Search file
- Create file
- Delete file
- List a directory
- Rename file
- Traverse the file system(파일 시스템 전체 탐색)
File Attribute(metadata)
File Attribute
는 파일을 관리하기 위한 목적의 정보.
관리를 목적으로 만들어진 정보로 아래와 같은 내용을 포함한다.
- 파일 이름
- 파일 접근권한
- 형식
- 시간
- 유형
- 디스크상 저장 위치
- 크기
File
File open 과정
File open
은 meta data
를 메모리에 올려 놓는 System Call
이다.
meta data
중엔 파일내용의 저장 위치 등 여러 정보가 있기 때문에 meta data
부터 올리고 File content
를 올린다.
per-process file descriptor table(pfdt)
프로세스가 사용할 파일 meta data
주소가 저장된 테이블, PCB 안에 있음
system-wide open file table(soft)
모든 프로세스에서 open된 파일들을 관리하기 위한 테이블
disk
에는 각종 파일의 meta data
와 내용물이 있다.
프로세스가 /a/b
File open
System Call
발생하면
/a/b
File
의 meta data
를 메모리로 올려야 할 것이다.
이때 /a/b
의 meta data
가 어디 있는지 모르니 디스크에서 찾아야 하는데 이 과정이 아래 그림과 같다.
- 이미 알려져 있는
root directory(/)
의meta data
를 읽어 실제 content위치로 이동한다. - 루트 디렉토리 안에는
/a
폴더의meta data
가 있을 것이고 이를 반복해서/a
와/a/b
를File open
한다. - 각 프로세스 마다
open
한 파일에 대한meta data
주소를 갖고 있는 테이블pfdt
이PCB
에 정의되어 있다. - OS가
/a/b
의meta data
를 메모리에 올리고 주소 값을pfdt
에 넣는다. pfdt index
를File open
반환 값으로 사용자 프로세스한테 전달한다.
meta data
는 메모리에 올라가 있고 pfdt
에 meta data
주소도 있기 때문에
fd
를 사용해서 파일 내용에 접근하면 된다.
(다시 접근할 때 루트 디렉토리부터 가지 않아도 됨).
File open
후 프로세스는 read(fd)
함수를 사용해서 System Call
을 하였고
OS는 프로세스가 요청한 위치의 요청한 용량만큼 내용을 읽고 메모리로 읽어 프로세스에게 전달하면 된다.
읽은 메모리는 바로 사용자 프로세스에게 주는 것이 아니라 OS의 메모리 공간 일부에 copy
하고 넘겨준다.
Buffer Cashing
만약 해당 프로세스 혹은 다른 프로세스가 동일한 파일의 동일한 위치의 read
System Call
을 요청하면
바로 읽어 놓은 내용을 전달하면 되는데 이를 Buffer Cashing
이라 한다.
Buffer Cashing
시스템에서 관련 연산은 모두 System Call
이라 OS가 먼저 CPU를 받기 때문에
전에 배웠던 LFU
, LRU
알고리즘을 버퍼 캐시에 입력된 데이터에 써먹을 수 있다
실제 OS에선 추가적으로 필요한 meta data
가 있다.
현재 프로세스가 파일의 어느 위치를 접근하고 있는지를 표시하는 offset
을 관리할 meta data
이다.
이는 프로세스마다 별도로 가지고 있기 때문에 위의 soft
을 나누어서 전체 프로세스의 meta data table(soft)과 각 프로세스가 가리키고 있는 offset을 관리하는 table 2개를 같이 두는 것이 일반적이다.
File Protection
파일의 경우엔 한 파일을 여러 프로세스(사용자)가 같이 사용할 수 있기 때문에
각 파일에 대해 누구에게 어떤 유형의 접근을 허락할 것인지 제어해야 한다.
접근권한을 제어하는 방법은 크게 3가지로 나뉜다.
1. Access control matrix
1. Access control matrix
1. Access control matrix
행렬을 사용해 사용자들과 파일에 어떤 권한이 있는지 모두 검사하는 것이다.
파일의 수가 사용자수에 비해 압도적으로 많기 때문에 희소행렬(0이 많은)이 될 것이라 비효율적.
그래서 Access control matrix
에 linked list
자료구조를 사용해 효율적으로 구현할 수 있다.
그럼에도 오버헤드가 크기 때문에 일반적으로 Grouping
을 사용해서 접근권한을 다룬다.
2. Grouping
우리가 흔히 알고 있는 owner
, group
, public
세 그룹으로 나누고 rwx-rwx-rwx
9bit를 통해 파일 접근권한을 관리하는 방식이다.
3. Password
파일마다 password
를 두는 방법. 읽기 쓰기 실행 별로 비밀번호를 따로 두어야 하고 관리적으로 사용하기 힘든 방법으로 거의 사용되지 않는다.
File Mounting
하나의 물리디스크는 partitioning
을 통해 여러 개의 논리디스크로 나눌 수 있다
그리고 각각의 논리디스크에는 파일 시스템을 설치해 사용할 수 있다
서로 다른 파티션의 File system
을 같이 사용하기 위해선 그림처럼 기존 루트 파일 시스템의 특정 디렉토리 이름에다 해당 File System
을 마운트를 해주면 사용 가능하다.
File Access
1.순차접근(Sequential access)
카세트나 비디오 같은 테이프 매체, 중간부터 보려 해도 처음부터 돌린 다음에 접근하는 방식을 순차접근이라 한다.
2.직접접근(Direct Access, Random Access: 임의접근)
레코드판, CD, HardDisk 같은 매체, 특정 위치를 접근한 다음에 바로 다음 위치로 접근하는 방식은 직접접근이라 한다.
직접접근이 가능한 매체라 하더라도 데이터 관리를 어떻게 하는지에 따라 순차접근으로 접근해야 할 수도 있다.
File system implement (파일 시스템 구현)
Disk에서 파일 저장 방법은 3가지로 나뉜다.
Contiguous Allocation (연속 할당방법)
디렉토리는 파일의 내용이다, 아래 파일들의 meta data
를 가지고 있다.
간략하게 3가지만 표시
- 파일 이름
- 파일 시작위치
- 크기
디렉토리 안 meta data
들 내용대로라면 disk의 저장 공간은 아래 그림처럼 된다.
파일시스템에서 동일한 크기의 저장 단위를 논리적블록(섹터) 이라 한다.
파일은 섹터 단위로 나누어 저장되며 연속 저장 이기 때문에 근접한 블록끼리 연속으로 구성되어 파일 저장이 된다.
Contiguous Allocation 단점
- External fragmentation(외부조각)
파일 크기가 동일하지 않기 때문에 외부조각이 생김으로 비어 있는 공간이 있음에도 활용하지 못하는 비효율성이 생긴다. - File grow
수정을 통해 파일크기가 커질 수 있는데 연속할당 규칙에 따라 크기증가 제한 이 생긴다.
커질 것을 대비해 빈 공간을 배치해 놓는 방법이 있지만 이 경우 내부조각이 발생하고 파일 증가가 확실히 해결되는 것도 아니다.
Contiguous Allocation 장점
-
Fast I/O
속도가 빠르다. Disk에서 I/O소요시간은 대부분 head가 이동하는 시간인데 한번만 이동하면 연속된 데이터를 통째로 읽고 쓸 수 있기 때문에 빠르다.
공간효율보단 속도효율이 중요한swapping
용도, 데드라인이 있는Realtime file
용으로 효율적이다. -
Direct access (Random access)
직접접근 가능하다. 연속할당시스템에서 mail이라는 파일의 4번째 블록부터 보고싶다 할 경우 시작위치에서 4번째 뒤의 블록으로 가면 된다.
Linked Allocation
연결 리스트를 사용해 비어 있는 블록에 파일의 데이터를 저장하는 방법이다. 블록에 content와 다음 블록 위치 포인터를 같이 저장한다.
Linked Allocation 단점
- Direct access 불가능
파일 임의의 블록을 가고 싶어도 첫번째 블록부터 돌아야 가능하다. - I/O 속도 느림
파일 저장 블록이 떨어져 있기 때문에 디스크 헤드가 많이 움직여야 함으로 속도도 떨어진다. - Reliability(신뢰성) 문제
블록 베드섹터가 발생하면 연결고리가 끊기기 때문에 다음 블록 접근이 불가능하다. - Pointer로 인한 저장공간 낭비
컴퓨터와 디스크 인터페이스는 512 BYTE 배수로 데이터를 주고받고 섹터크기도 512 BYTE이다.
때문에 512 BYTE 이하크기의 파일도 512 BYTE 크기로 저장된다.
그런데 포인터가 4byte를 차지하기 때문에 섹터의 크기는 508byte크기가 되고 512 BYTE 크기로 전달된 파일을 한 섹터에 저장하지 못하고 두 섹터를 차지하게 되는 공간낭비가 발생한다.
Linked Allocation 장점
- 외부조각이 발생하지 않는다.
단점이 많기 때문에 이대로 사용하지 않고 Linked Allocation
을 파일 시스템에 그대로 적용하지 않고 약간 변형해서 사용하는데
그것이 마이크로 소프트의 FAT(File-allocation table)
파일 시스템이다.
포인터를 별도의 위치에 보관해 Reliability
와 공간효율성 문제를 해결한 파일 시스템이다.
Indexed Allocation
직접접근을 가능하게 하기 위한 index 블록
을 사용하는 방법
index 블록
(19번블록)을 따로 두고 meta data
에 index블록 저장 위치를 저장하고 있다.
index 블록
에는 파일이 저장된 블록 순번대로 블록 위치를 기록해 놓는다.
Jeep의 4번째 블록을 보고 싶다면 19번 블록에 접근한 후 네 번째인 10번 블록에 접근하면 된다.
Indexed Allocation 단점
- Index로 인한 저장공간 낭비
아무리 작은 파일이라도 2개의 블록이 필요하다(공간낭비).
반대로 아주 큰 파일의 경우 하나의index블록
으로 모든 블록위치를 정장하기 부족하다.
해결방안으로 linked scheme
이나 multilevel index
를 사용하면 된다.
Linked scheme
은 index 블록
에 마지막 위치에 또다른 index 블록
위치를 기록, index 블록
을 여러 개로 구성하는 것이다.
multilevel index
는 two tage
로 운영하는 방법, index 블록
들을 가리키는 index 블록
을 사용하는 것이다
해결 방안들 역시 index 블록
으로 인해 공간 낭비가 생긴다.
Indexed Allocation 장점
- 외부조각이 발생하지 않고 직접접근이 가능하다
UNIX 파일 시스템 구조
지금 보는 파일 시스템은 가장 기본적인 파일 시스템이고 이 파일 시스템이 발전해서
fast file system
, ext2
, ext3
, ext4
등의 파일시스템으로 발전했다.
하나의 논리디스크(원통)에 파일시스템을 설치하였다.
유닉스의 파일시스템 저장구조는 크게 4가지로 나뉜다.
Book block
: 운영체제 설치 위치(커널)Super block
: 파일 시스템에 관한 총체적인 정보Inode list
: 파일meta data
보관소data 블록
UNIX
건 FAT
이건 모든 파일시스템에는 Boot block
이 가장 맨 앞에 위치한다
bootstrap loader
라는 부팅에 필요한 기본 정보가 저장돼 있다.
운영체제 설치 위치(커널)을 찾아서 정상적으로 부팅이 된다.
Super block
은 파일 시스템에 관한 총체적인 정보를 담고 있다.
Inode list
와 data 블록
의 경계선이 어디인지,
data 블록
에서 어디가 빈 블록이고 어디가 실제 사용중인 블록인지 관리한다.
Inode list
는 모든 파일 meta data
보관소이다.
실제 파일 시스템에선 디렉토리에 파일의 meta data
가 저장되지 않는다.
UNIX
경우 일부 meta data
만 디렉토리가 보관하고 대부분의 meta data
는 Inode list
라는 별도의 위치에 보관하고 있는다.
단 파일의 이름은 디렉토리가 보관한다.
그림처럼 디렉토리는 이름만 갖고 Inode 번호
와 매핑한 후 실제 디렉토리의 meta data
는 Inode list
에 저장되어 있는 형식이다.
파일 하나당 Inode
가 하나씩 생성되는데
Inode
가 해당파일의 meta data
를 의미한다고 보면 된다.
meta data
엔 여러 정보가 있지만 disk 어느 부분에 위치하는지 위치정보가 제일 중요하다.
disk 에 분산저장되는 파일의 위치정보는 Indexed Allocation
의 기법을 사용하여 저장하고 관리한다.
모든 파일의 Inode
크기는 같지만 파일 크기는 그렇지 않다.
때문에 파일의 크기가 매우 큰 경우 디스크에 분산 저장되어 있는 블록들의 위치정보를 모두 기록하려면 Indexed Allocation
에서 사용한 방법이 효율적이다.
Inode 에 위치 정보를 기록하는 index 블록
이 저장되는 종류는 아래와 같다.
- direct blocks
- single indirect
- double indirect
- tiriple indirect
파일 크기가 작은 경우 direct 블록
에 있는 포인터 몇 개로만 파일 위치를 가리키고.
파일 크기가 큰 경우 이중 삼중으로 되어있는 double, triple indirect 블록
을 사용해서 해결한다.
FAT 파일 시스템 구조
마이크로 소프트가 DOS 를 만들었을 때 만들어진 파일 시스템.
FAT
과 Root directory
가 생겼다.
FAT
공간에 각 파일들의 meta data
의 위치정보 만 저장되고 실제 meta data
는 각 디렉토리가 가지고 있다.
뿐만 아니라 해당 파일 첫번째 block
(파일 시작 위치정보)이 data 블록
어느 위치에 저장되어 있는지도 디렉토리가 보관한다.
FAT
은 Linked Allocation
을 사용한다.
Linked Allocation
단점이 몇 가지 있었는데 이를 해결하기 위해 FAT라는 시스템을 사용한다.
FAT
안에는 data 블록
안의 블록개수만큼의 연결 리스트 배열이 생성되고(이 배열이 FAT의 크기를 결정)
각 배열요소에 다음 블록의 번호를 저장한다
FAT
의 정보는 중요하기 때문에 복사본을 여러 개 저장해 놓는다(적어도 2개 이상).
FAT
만 확인하면 Linked Allocation
의 여러 단점이 다 극복된다.
베드섹터 발생해도 FAT
안에 위치정보는 있으니까 건너뛰고 접근 가능하기 때문에 신뢰성 회복되고, 직접접근 되고, data 블록
에 포인터도 없어도 되니 512 BYTE 낭비도 없다.
Free space management
파일시스템이 어떻게 파일의 block
들을 관리하는지 보았으니
빈공간은 어떻게 관리하는지 비어 있는 블록들을 관리하는 방법을 알아본다.
Bit map(Bit vector)
data 블록
의 수만큼 bit를 저장할 수 있는 배열을 구성해서 0과 1을 사용해 비어 있는지 파일이 저장되어 있는지 판단하는 방법이다.
물론 비어 있는 공간을 찾기 위해선 디스크 헤드가 한바퀴 돌아줘야 한다.
파일이 생기거나 사라질 때 마다 Bit map
도 수정해야한다.
Bit map
의 단점은 배열을 저장할 부가적인 공간이 필요하고(그렇게 크진 않음)
장점은 연속적인 빈 블록을 찾는데 효과적이다.
파일을 저장할 때 가능하면 연속적으로 저장하는 것이 효율적인데 그런 부분에선 bit map이 효과적이다.
Linked list
디스크 헤드가 돌면서(lseek) 비어 있는 블록을 찾고(선수작업) 블록안에 포인터를 두고 연결시켜 놓은 것이다.
공간 낭비는 없어지지만 연속적인 가용공간 찾기도 힘들고 비효율적이다.
Grouping
앞에서 배웠던 Indexed Allocation
방법을 적용한 예이다.
첫번째 빈 블록이 인덱스 용으로 다른 빈 블록들 위치들을 가리키고
그중 마지막 블록이 또 다른 빈 블록 위치를 가리키는 방법.
연속적인 빈 블록을 찾기는 마찬가지로 힘들다.
Counting
연속적인 빌 블록을 찾기 쉬운 방법.
first free block
, N of contiguous free block
빈 블록 위치와
연속적으로 비어 있는 블록 수를 쌍으로 저장해서 유지 관리하는 방법이다.
Directory Implementation (디렉토리 구현)
File System
에서 디렉토리는 디렉토리를 포함한 하위 파일들의 meta data
를 관리하는 특별한 파일이다
하위 파일들의 meta data
를 디렉토리가 어떻게 관리하는지 알아보자.
Linear list
파일의 이름과 파일의 meta data
들을 순차적으로 저장하는 방법이다.
고정크기의 배열에 파일 이름과 meta data
내용을 저장한다.
구현은 간다 하지만 파일 찾는 과정도 Linear하게 검사해야 하기 때문에 연산에서 많은 시간이 걸린다.
Hash Table
Linear list
보다 좀더 효율적인 디렉토리 연산을 위한 방법이다.
파일의 이름을 해사함수를 적용해서 Hash Table
에 저장, 해시결과와 meta data
를 매핑한다.
해시 함수의 collision 제거 알고리즘을 별도로 구현해야 한다.
Long file name 지원
Long file name
지원하기위해 아래 방법을 사용한다.
만약 파일 이름이 3글자까지라고 할 때 길어서 공간을 벗어날 경우 포인터를 두어 디렉토리 파일 저장공간 뒤쪽부터 나머지 이름을 저장한다.
VFS, NFS
파일 시스템은 FAT
, NTFS
, ext2, 3, 4
등 굉장히 다양하다
사용자 프로세스가 파일시스템에 접근할 때는 System Call
을 통해 접근하는데
파일 시스템 마다 서로 다른 System Call
을 사용해야 한다면 사용자 입장에선 혼란스러울 것이다.
어떤 파일 시스템이 사용되어도 Virtual file system interface
라는 OS layer
를 거치면 동일한 System Call
인터페이스를 통해 접근 가능하다.
분산 시스템에선 네트워크를 통해 파일이 공유될 수 있다.
로컬의 파일시스템 말고 원격(서버의) 파일시스템에 접근해야 한다. VFS interface를 통해 들어갔는데 로컬에 없을 경우 Network File System 모듈
과 RPC/XDR
원격 접근 프로토콜을 통해 원격 파일 시스템에 접근한다.
사용자 프로세스는 파일시스템이 FAT
인지 NTFS
인지 네트워크상에 있는 파일시스템인지 전혀 알 필요가 없다.
파일 접근 System Call
파일에서 아래와 같은 작업을 수행할 때 read write System Call
이 발생하는데
파일 System Call
이 발생할 때 수행하는 cache
전략을 먼저 알아본다.
Page cache & Buffer Cache
Page cache
page cache
는 프로세스의 빠른 동작을 위해 자주 사용하는 내용을 page
단위로 저장해 놓은 공간이다.
프로세스 VM 에서 관리하는 물리메모리 공간이다.
swap area
에서 page
를 불러오지 않고 물리메모리에 저장되엉 있는지 확인 후 불러오기 때문에 cache
라는 개념을 붙여 사용한다.
Buffer cache
파일시스템 데이터 캐싱 전용으로 할당되고 특수 목적 루틴에 의해 관리되는 메모리 풀
읽어온 내용을 OS 가 준비해둔 메모리의 특정 영역에 저장해 놓고 다시 해당 내용을 다시 요청할 때 OS에서 읽어오는 것
(512 BYTE논리 블록 단위로 진행한다).
파일의 데이터를 사용자가 요청했을 때 디스크에서 읽어 사용자에게 전달해주고 끝내지 않고 해당영역에 저장해둔다.
물리메모리 영역을 사용하기 때문에 cache
라는 개념을 붙였다.
그리고 buffer cache
의 경우 파일의 내용을 집어넣고 프로세스가 요청한 block
을 지원해주는 작업들을 모두 OS가 관리하기 때문에
어느 block
이 많이, 빈번히 사용되는지 알기 때문에 공간이 부족해지면 LFU, LRU
알고리즘을 통해 쓸모 없는 block
을 buffer cache
에서 내릴 수 있다.
Memory mapped I/O
Memory mapped I/O
는 파일을 프로세스의 메모리 공간에 매핑 시키고 파일 접근시 메모리 공간에 접근하는 방법이다.
CPU 는 메모리에 접근한다 인식하기 때문에 파일에 접근할 때 rw System Call
를 사용하지 않고 바로 메모리에 접근해서 읽고 수정한다고 인식한다.
따라서 한번 매핑시켜 놓으면 빠른속도로 메모리에서 파일 content 를 읽고 수정 할 수 있다.
매핑이라는건 파일 content
를 물리메모리에 올리는 것이 아닌 단순 연결이다.
매핑만 해놓고 만약 파일 content
가 아직 물리메모리에 올라오지 않았다면 page fault
가 발생하고 해당 내용을 data 블록
에서 물리메모리로 올려야 한다.
rw System Call
을 통해 파일에 접근할 경우 OS가 파일을 읽어 512 BYTE 단위로 buffer cache
에 저장해서 프로세스에 지원해주기 때문에 답답한 면이 있다.
반면에 Memory mapped I/O
는 블럭을 buffer cache
가 아닌 page cache
에 올리기 때문에 4KB단위로 관리한다.
실제 디스크 파일을 rw 하지 않다보니 Memory mapped I/O
프로세스들이 동일한 file을 접근해 읽고 수정할 때 동기화 문제가 생긴다.
Memory mapped I/O
는 OS의 개입이 부족하기 때문에 발생하는 문제임
그러다 보니 범용적 현대 OS 에선 사용하지 않고 임베디드 장치 등에서 사용함
Unified Buffer cache (통합 버퍼 캐시)
Page cache
, Buffer cache
둘다 비슷한 역할이고 따로 사용되다 보니 이중저장같은 문제도 있었다.
2.4 커널 이후로 기존 Buffer cache
역할은 Page cache
내부로 통합되었고 Unified Buffer cache
로 불린다.
그래서 최근엔 buffer cache
를 그냥 page cache
라 하기도 한다.
Unified buffer cache
에서 Buffer cache
사용용도는 파일 접근 입출력용이 아닌
Inode
같은 메타 데이터 관련 블록을 저장하고 관리한다.
File I/O
방법은 이 Unified Buffer cache
를 사용여부에 따라 방법이 나뉜다
Unified buffer cache
를 사용하지 않는 전통적인 시스템에서
Memory mapped I/O
를 하더라도 우선 OS가 buffer cache
로 읽고 이걸 다시 사용자 프로세스의 page cache
로 전달한다.
아무래도 민감한 데이터이다 보니 OS에서 적극적으로 관리하는 것 같다.
Unified Buffer cache
사용하는 시스템에선 rw System Call
, Memory mapped I/O
구분없이 Unified Buffer cache
에 접근한다.
이중저장 문제도 없고 Device Driver
를 호출해 바로 메모리에 읽고 쓴다.
참고자료
https://www.usenix.org/legacy/publications/library/proceedings/usenix2000/freenix/full_papers/silvers/silvers_html/index.html
https://brunch.co.kr/@alden/25