BASIC의 개발 노트

File System - Read & Write + Directory 본문

OS

File System - Read & Write + Directory

B2SIC 2021. 5. 26. 19:08

Read & Write

read_size -> fd가 가리키는 File에서 request_size 만큼 읽어서 buf에 넣는다.

written_size -> buf에 있는 값을 fd에 쓰는데 request_size 만큼 쓴다.

read_size = read(fd, buf, request_size);
written_size = write(fd, buf, request_size);

echo는 원래 뒤에 오는 문자열을 모니터에 출력해주는데 방향을 바꿔서(>) foo 라는 File에 저장되도록 했다.

그래서 cat foo 를 통해서 File을 읽어서 모니터로 출력하도록 했더니 hello가 출력이 됐다.

 

그럼 cat이 어떻게 동작하는지 살펴보자.

 

cat에서 File 관련 System Call Trace

그러기 위해선 strace를 사용하는데, strace는 어떤 system call 들을 사용했는지 살펴볼 수 있다.

그렇게 추려서 system call만 보면 oepn을 했을 때 return 값인 fd가 3 이 나왔다.

그리고 그 3을 read 했다. 한 Block(4KB)만큼 읽고 보니 6Byte(6개의 문자) 만큼 읽었다.

이후 stdout에 해당 하는 fd 값인 1에 방금 읽은 6개의 문자를 쓰도록 했다.

그랬더니 hello가 출력이 됐고, 그 다음에 또 4KB(4096 Byte)를 read 하도록 했는데 return 값으로 0 이 왔다.

즉 더 이상 읽을 내용이 없다는 뜻이다. 따라서 close가 왔다.

 

fsync

메인 메모리에 있는 내용에 변경된 점이 생겨서 이를 하드 디스크에 있는 File에도 바꿔줄 필요가 있는데

민감한 프로그램의 경우 그 때 그 때 바로 써줄 필요가 있다.

보통 UNIX/LINUX 에서는 일정 간격(약 30초)으로 한꺼번에 쓰기를 하지만

fsync를 사용하면 즉시 쓰기를 하도록 할 수 있다.

(+ 정말 민감한 프로그램이라면 open 할 때 새로 만들어졌을 수 있기 때문에 directory 정보도 fsync를 하는 것이 중요)

 

Renaming

mv를 이용해서 foo를 bar로 변경. (Renaming)

이 때 Renaming은 atomic 하게 진행 해야 한다. (이름이 바뀌었거나 / 아예 바뀌지 않았거나)

두 개가 공존하면서 중간 상태라는 것은 존재할 수 없다.

 

mv 동작

Remove

unlink라는 것을 사용한다. (밑에서 자세하게 설명한다.)

명령어는 rm 인데, strace로 추적을 해보니 unlink 라는 system call을 사용했다.

 

MetaData

File에 대한 메타 정보라는 것이 존재하는데 File을 어떻게 구성했는지, 어떤 Block들로 구성 됐는지 등

이런 정보들이 inode와 superblock에 들어있어야한다.

메타정보를 읽어오기 위한 API로 stat이라는 것이 있다.

stat(file_name, struct stat) -> File name(PATH)을 주면 open 해서 stat에 정보를 읽어다준다.

fstat(fd, struct stat) -> 이미 open 된 File의 File Descriptor를 주면 stat에 정보를 넘겨준다.

stat이 헷갈릴 수 있는데 구조체인 stat이 존재하고, system call인 stat이 있고, 명령어인 stat도 있다.

 

struct인 stat에 들어있는 정보

 

struct stat에 있는 내용들은 File에 존재하는, Head 에 들어있는 메타 정보를 말한다.

stat structure 중 몇 가지만 살펴보면..

nlink: 물리적인 File을 몇 군데에서 참조하는지에 대한 정보

mode: 누가 읽을 수 있고, 누가 쓸 수 있는지 등에 대한 권한 정보

이런 내용들을 꺼내 올 수 있는 것이 stat 이라는 system call 이다.

 

또한 stat을 명령어로 사용할 수도 있다.

 

지금까지 File에 대해서 살펴보았다면 이제 Directory에 관련된 내용도 살펴보자.

앞에서도 언급했는데 Directory가 개념적으로 중요하지만 물리적인 모양은 File과 똑같다.

Directory

strace mkdir

 

strace로 mkdir을 추적해보니 mkdir이라는 system call을 사용했다.

여기서도 mkdir이라는 command 이름과 system call 이름이 같은 것을 알 수 있다. (보통 이런 방식을 많이 사용)

 

관련해서 directory의 목록을 볼 수 있는 명령 ls가 있다.

(-a: all, -al: all & list)

 

directory를 지우고 싶을 때 사용하는 rmdir도 있다.

 

지난 번에 본 그림을 다시 가져와보면 directory에는

directory를 표현하는 File인 i-node가 있고, directory의 내용을 구성하는 block이 있다.

block에는 그림에서 my나 book 처럼 안에 담긴 내용들이 들어가게 되고,

i-node에는 메타정보가 있고, 어느 block을 사용했는지에 대한 pointer 값이 있다.

 

그러면 directory를 새로 만든다는 것은 block 부분에 새로 추가할 directory를 반영해서 목록에 추가해주면 된다.

또한 새로 만든 directory에 대한 File과 똑같은 구조를 갖는 block과 i-node가 하드디스크에 만들어진다.

i-node는 각자 식별 번호를 가지고 있다. (ex: my는 200번, book은 100번 인 것 처럼)

따라서 물리적인 수준에서는 배열처럼 존재하는 구조가 있고 index가 있기 때문에 200번은 200 index number를

100번은 100 index number, 이런 식으로 구성이 된다.

새로 만드는 directory 역시 block과 i-node 구조를 갖추고 i-node number가 부여된다.

 

directory에 사용되는 API도 따로 존재한다.

 

파일에서는 open이지만 directory에서는 opendir, Type은 DIR pointer를 사용한다.

그냥 open으로 읽으면 바이트 단위로 읽기 때문에 이상하게 읽힌다.

이 대신 directory에서는 directory entry 단위로 한 줄 한 줄 읽는다.

읽을 때도 readdir, 이 때 읽는다라는 것은 주로 우리가 ls 명령을 사용할 때 발생한다.

읽을 때 구조적으로 읽어주게 되는데, 여기서 말하는 구조는 dirent로 아래에서 볼 수 있다.

 

파일은 close 지만 directory는 closedir을 사용한다.

 

단 directory write system call은 없다. 생각해보면 당연한 것이다. (잘못 쓰면 계층구조가 다 깨져버릴 수 있다.)

directory를 추가한다고 하면 directory write가 아니라 mkdir을 사용해서 쓴다.

 

Link

이름을 중복시킬 수 있는 방법, 기존에 있는 File이름을 새로운 이름으로 주겠다는 뜻이다.

link(old_name, new_name) -> 결국 새로운 경로(PATH)를 추가하겠다는 의미. (경로가 이름이기 때문에)

link의 동작

 

link 명령 -> ln

원래 있는게 file인데 file2로 link를 하고 cat file2를 하니까 file과 같은 내용이 나온다.

ls -i 옵션을 주면 숫자가 같이 나오는데 이 숫자가 i number(i-node의 index 값) 를 의미한다.

그래서 ls -i file file2 를 했을 때 나온 i number가 같기 때문에 물리적으로 같은 것을 가리킨다는 뜻이다.

 

그렇다면 file을 지우면 어떻게 될까?

물리적으로 같은 것을 가리키는데 원래 file을 지우면 마치 다 지워질 것 같지만 그렇지 않고 남아있다.

따라서 cat file2로 확인했을 때 hello 가 보인다.

rm 은 앞에서 strace로 살펴보았을 때 unlink system call을 사용했다.

unlink라는 것은 link 목록에서 지워버리는 것이다. 여기서는 67158084에 대한 link가 2개 였는데 하나를 unlink 했기

때문에 link count 값이 2에서 1로 감소하게 된다.

따라서 link 목록에서 지우고 link count 를 감소시키는 것이 file이나 directory에서 지우는 것(rm/rmdir)의 의미가 된다.

만약 여기서 unlink를 한 번 더 하게 되면 file2를 지운다는 뜻이며 link count가 1에서 0이 되고 내용이 지워지게 된다.

link count가 0이 되기 전까지는 link counter를 감소시키기만 하면 된다. 왜냐하면 directory 목록에서만 벌어지기 때문.

 

지금 까지 살펴본 link를 Hard link 라고 한다.

그런데, Hard link에는 한계가 존재한다. 디스크는 여러 개의 파티션으로 나눌 수 있는데

Hard link로 쓰기 위해서는 같은 파티션 안에 있어야 한다. 왜 같은 파티션에 있어야 할까?

파티션 별로 File System을 따로 따로 구성하는데, 이 때 i-node가 배열처럼 쭉 있고 여기서 index number를 가지고

식별을 하는데 이 i-node 배열이 파티션 마다 하나씩 따로 갖게 된다.

그 때문에 i number가 파티션 안에서는 unique하게 존재해서 식별이 가능해도

다른 파티션에도 똑같은 숫자가 있을 수 있기 때문에 식별이 안되는 문제가 있다.

 

Symbolic Link

윈도우에서 바로가기에 해당하는 것.

Symbolic link를 사용할 경우 ln -s 옵션을 줘서 file2라는 새로운 경로가 생성이 된다.

이 후 cat file2 했을 때 file과 같은 결과가 나온다.

 

Hard Link와 무슨 차이점일까?

Hard Link는 물리적으로 같은 곳을 가리켜서 link count가 2로 늘어났는데

Symbolic Link는 file2라는 것이 아예 새로 생성이 되고 file2가 file을 가리키는 형태가 된다.

일종의 포인터 역할을 하며 i number가 서로 다르다. 즉 새로운 파일이 존재하는 것이며

그 내용에 포인터가 존재해서 file을 가리키도록 한다. Type은 조금 특별한 link를 뜻하는 Symbolic link file이다.

 

이렇게 Symbolic Link로 구성을 할 경우 파티션을 넘어서 다른 파티션에서도 사용할 수 있다.

 

하지만 주의해야할 점이 있다.

포인터 역할을 한다고 했기 때문에 file을 삭제할 경우 참조할 대상이 사라진 것이나 마찬가지라서 문제가 생긴다.

따라서 이 경우 에러가 발생한다.

 

이처럼 Hard Link와 Symbolic Link는 구별할 수 있어야 한다.

 

Mount

File System을 파티션 별로 구성을 하는데, 이를 mount라는 방법을 사용해서 단일 계층 구조로 통합을 할 수 있다.

윈도우에서는 C드라이브, D드라이브 처럼 드라이브 별 이름을 부여해서 접근을 하도록 하는데

UNIX/LINUX의 경우 이런 것이 없다.

참고로 mkfs 라는 명령을 이용하면 기본적인 File System 구조를 만들어준다.

이를 이용해서 a와 b를 담는 간단한 File System을 구성했다고 볼 때

/dev/sda1에 존재하는 계층 내용을 다른 쪽으로 mounting 할 수 있다.

명령어를 보면 Type은 ext3 type, /dev/sda1을 /home/users 밑에 붙여서 통합하겠다는 의미이다.

 

그림으로 보면 아래와 같다.

따라서 붙이고 나면 /dev/sda1의 루트가 users와 내부적으로 동일한 포인터가 된다.

그래서 a와 b가 users밑에 매달리게 된다.

또한 a와 b의 경로가 / 밑에 a 또는 b였는데 mounting 이후에는 / 밑에 home 밑에 users 밑에 a 또는 b가 된다.

 

이처럼 하드디스크가 여러 개 있으면 거기서 mkfs을 해서 mounting을 하면 통합된 구조를 가질 수 있다.

※ mounting 하는 file system이 모두 동일할 필요는 없다.

 

'OS' 카테고리의 다른 글

Concurrency - Race Condition  (0) 2021.06.04
Concurrency  (0) 2021.06.04
File System  (0) 2021.05.25
RW Lock - Bad Case & Good Case  (0) 2021.05.24
Semaphore Bounded Buffer Problem  (0) 2021.05.23
Comments