좀 열심히 쓴 글

시스템 수준 입출력(I/O)

ch4rli3kop 2019. 5. 17. 14:03
반응형

시스템 수준 입출력(I/O)



입출력(I/O)이라는 동작은 결국 주 메모리와 외부 디바이스 간에 데이터를 주고 받는 동작이다.

입력(INPUT)의 경우 외부 디바이스에서 주 메모리로 데이터를 전송하는 과정이고, 출력(OUTPUT)의 경우 주 메모리에서 외부 디바이스로 데이터를 전송하는 과정이다.

이런 동작에 대해 알아보기 전에, 우선 알아두어야 할 점은 리눅스는 네트워크, 디스크, 터미널 같은 모든 I/O 디바이스들을 파일로 모델링한 뒤, 모든 입력과 출력을 해당 파일을 읽고 쓰는 방식으로 관리한다는 점이다.

즉, 우리의 입출력 역시 일반 파일을 읽고 쓰는 것처럼 관리할 수 있다.

크기가 m인 파일을 읽기위해 현재 파일 위치 k를 증가시킬 때, k>m 인 경우 발생하는 "EOF(end of file)"가 일반 파일에서 뿐만아니라 소켓 통신 내에서 발생하는 이유 역시 입출력 장치가 파일로 관리되기 때문이라고 할 수 있다.


파일 자료구조

프로세스에서 오픈한 파일에 대해서, 커널은 Descriptor table(one table per process), file table(shared by all processes), v-node table(shared by all processes)의 세 가지의 자료구조를 사용하여 정보를 관리한다.

다음의 세 가지 경우를 예시로 들어보면 전체적인 개념이 잡힐 것이다.

다른 파일 2개



각 각의 파일별로 file table, v-node table이 생성된다.

같은 파일

같은 파일을 가리킬 경우, file table은 개별적으로 생성되나, v-node table은 같은 것을 가리키게 된다. file table에 존재하는 File pos 항목을 이용하여 해당 파일을 읽거나 쓸 때 위치를 특정할 수 있는데, file table이 개별적으로 존재함에 따라 같은 파일을 여러 위치에서 동시에 읽고 쓰는 것이 가능하다.

fork시 자식과 부모

자식 프로세스는 부모 프로세스의 descriptor table을 그대로 물려받는다. 증가한 descriptor에 따라서 file table의 참조 횟수도 증가한다. 자식 프로세스와 부모 프로세스 모두가 종료되어야 해당 파일이 커널 내의 테이블에서 제거될 수 있다는 점을 시사한다.


I/O 재지정

리눅스 쉘에서는 파이프('|')나 꺽쇠('>')를 이용하여 입출력을 바꿀 수 있는 연산자가 존재하는데, 이는 해당 프로세스들의 I/O 재지정을 통해 구현된다.

dup2 함수를 이용하여 I/O 재지정 동작을 살펴보도록 하자.

#include <unistd.h>

int dup2(int oldfd, int newfd);

dup2 함수는 oldfd에 해당하는 descriptor table entry를 덮어씌워서 newfd가 가리키는 file table을 가리키도록 한다. 예를 들어 dup2(4, 1);을 한다면 다음과 같을 것이다.

기존 fd 1의 descriptor table entry를 fd 4의 entry 값으로 덮어씌운다.

fd 1이 덮어씌워졌기 때문에, File A의 참조 횟수가 0이 되어 커널은 A를 테이블에서 제거할 것이고, File B의 참조 횟수는 증가하여 2가 된다.

이제 fd 1(일반적으로 표준출력)로 전송되는 데이터는 fd 4로 재지정될 것이다.


표준 I/O

기본적인 입출력 동작은 우선 Unix I/O 모델로서 운영체제 커널에서 구현되며, open, read, write, lseek, stat, close와 같은 함수들이 그에 해당된다. 리눅스 I/O 함수 read의 경우 file의 데이터를 memory로, write의 경우 memory의 데이터를 file로 전송하는 역할을 수행한다.

Unix I/O 함수들을 활용하여 효율적인 함수로 구현한 것이 표준 I/O 함수들인데, fopen, fread, fwrite, fscanf, fgets 등이 이에 해당한다.

시스템 콜이 최대한 적게 불리도록 효율적으로 설계된 함수들이기 때문에 일반적으로 표준 I/O 함수를 사용하는 것이 좋다.

다만, 네트워크 소캣 통신을 구현할 경우에는 표준 I/O 함수는 몇 몇 오류를 발생시킬 수 있기 때문에, 소캣 통신을 위해 따로 구현한 함수 등을 사용하는 것이 좋은 듯 하다.


참고 : https://scs.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=f107c2ce-79d5-4529-baeb-2bb495d8c11a

반응형