CSAPP(10) - System-Level I/O
업데이트:
1. Unix I/O
- 리눅스 파일은 바이트의 시퀀스이다.
- 모든 입출력 장치는 파일로 모델링되어 있다.
- 모든 입출력은 파일에 읽거나 쓰는 동작이다.
파일 열기
- 앱은 커널에 파일 열기를 요청
- 커널은 descriptor라 하는 음수아닌 정수를 반환
- 서술자는 파일을 식별한다.
- 리눅스 셸로 시랭된 프로세스는 표준입력(서술자 0), 표준출력(서술자 1), 표준에러(서술자 2) 3가지 파일을 열면서 시작된다.
파일 위치를 변경
- 커널의 파일 위치르 유지한다. 위치는 최초 0이다.
- 파일 위치는 파일 시작점부터의 바이트 오프셋이다.
seek
연산
파일 읽고 쓰기
- 파일에서 메모리로
n
바이트를 복제하는 연산 - 현재 위치
k
에서 시작해서n
만큼 증가시킨다. m
바이트 크기의 파일이 있을 때,k >= m
이면 end-of-life(EOF)이다.- 명시적인 EOF 문자는 없다.
- 쓰기 연산도 유사하게
n
바이트를 메모리에서 파일로 복제한다.
파일 닫기
- 앱이 커널에 닫기 요청을 한다.
- 커널은 파일 열때 생성한 자료구조를 해제하고 서술자를 사용 가능한 서술자 풀에 반납한다.
- 프로세스가 어떤 이유든 종료되면, 커널은 열린 파일을 닫고 메모리 자원을 해제한다.
2. Files
리눅스 파일은 type이 있다.
- regular file은 임의 데이터를 가진다.
- text file은 아스키 또는 유니코드 문제로 이루어진 파일
- 텍스트 라인으로 이루어져 있고, 각 라인은 개행문자로 끝난다. 개행문자
\n
는 ascii line feed, 숫자0x0a
와 같다.
- 텍스트 라인으로 이루어져 있고, 각 라인은 개행문자로 끝난다. 개행문자
- binary file은 나머지
- 커널은 두개를 구분하지 않음
- text file은 아스키 또는 유니코드 문제로 이루어진 파일
- directory 파일은 link의 배열로 이루어진다.
- 링크는 파일(다른 디렉토리)에 파일명을 매핑한다.
- 최소 두개가 있다.
.
,..
- socket은 네트워크를 동해 다른 프로세스와 통신하는데 쓰이는 파일이다.
- 이외에도 named pipes, symbolic links, charater, block devices 등이 있다.
리눅스의 모든 파일은 root 디렉토리로부터 시작한다.
- 절대 경로 : root로부터의 경로
- 상대 경로 : 현재 디렉토리로부터의 경로
3. Opening and Closing Files
open
함수를 통해 파일을 열거나 생성한다.
- flag 인수로 읽기전용, 쓰기전용, 읽기쓰기 모드를 지정한다.
- 쓰기의 경우 create, truncate, append 모드도 함께 지정한다.
mode
인수로 접근 권한 비트를 설정한다.- owner, group, other의 read, write, execute 권한.
4. Reading and Writing Files
read
, write
함수를 통해 입출력을 수행한다.
요청보다 적은 바이트를 전송할 때도 있다.
- 읽기 중 EOF에 도달했을 때
- 터미널에서 텍스트 라인을 읽을 때
- 네트워크 소켓에서 읽거나 쓸때
- 리눅스 파이프를 통해 읽거나 쓸때
6. Reading File Metadata
stat
, fstat
함수로 파일의 메타데이터를 가져올 수 있다.
7. Reading Directory Contents
opendir
함수는 경로이름을 받아서 디렉토리 스트림(디렉토리 리스트의 추상화)를 반환한다.readdir
은 디렉토리 스트림의 다음 디렉토리를 반환한다.closedir
은 스트림을 닫고 리소스를 해제한다.
각 디렉토리 엔트리의 구조
struct dirent {
ino_t d_ino; /* inode number */
char d_name[256]; /* Filename */
}
8. Sharing Files
커널은 열란 파일을 세가지 자료구조로 표현한다.
- Descriptor table
- 각 프로세스는 별도의 서술자 테이블을 가진다.
- 엔트리들은 프로세스의 열린 파일 서술자에 의해 색인되어 있다.
- 각 엔트리는 파일 테이블 엔트리를 가리킨다.
- File table
- 열린 파일의 집합이 저장되는 곳.
- 모든 프로세스가 공유한다.
- 각 엔트리는 현재 파일 위치, 참조하는 서술자 엔트리의 수, v-node 테이블의 엔트리를 가리키는 포인터로 구성되어 있다.
- 서술자를 닫으면 참조수가 감소된다.
- 커널은 참조수가 0이 될 때까지 파일 테이블을 지우지 않는다.
- v-node table
- 모든 프로세스가 공유한다.
- 각 엔트리는
stat
구조의 대부분을 저장한다.(파일 메타데이터)
- 파일 공유 시 각 서술자는 별도의 파일 위치를 가지고 있으므로 파일의 다른 위치를 읽을 수 있다.
fork
시에 자식 프로세스는 부모의 서술자 테이블을 복제하며, 동일한 열린 파일 테이블을 공유한다.
9. I/O Redirection
리눅스 쉘의 입출력 리디렉션 연산자는 표춘입출력을 디스크 파일로 연관시킨다.
리디렉션은 dup2
함수를 통해 수행된다.
- 서술자
oldfd
를newfd
로 복사하고 덮어쓴다.
- 서술자 1(표준출력)은 fileA(터미널)과, 서술자4는 fileB(디스크파일)과 연관되어 있었다. 두 파일의 참조수는 1이다.
- 리디렉트이후 두 서술자는 fileB와 연관된다. fileA는 닫히고 파일 엔트리와 v-node 엔트리는 제거된다. B의 참조수는2가된다.
리디렉트를 해도 표준출력 fd는 남아있으나, 터미널을 가리키던게 파일로 바뀌는 것.
10. Standard I/O
C언어는 고수준 표준 입출력 라이브러리가 있다.
- 유닉스 입출력의 대안 표준 입출력 라이브러리는 열린 파일을 stream으로 모델한다.
- 프로그래머에게 스트림은
FILE
타입의 구조체를 가리키는 포인터. FILE
스트림은 파일 서술자와 스트림 버퍼의 추상화.- 스트림 버퍼의 목적은 버퍼링을 통해 비싼 입출력 시스템 콜을 최소화하는 것.
11. Putting It Together: Which I/O Functions Should I Use?
- 가능할 때마다 표준 입출력 합수를 사용하라.
scanf
로 이진 파일을 읽지 마라.- 네트워크 입출력 시에는 Unix 입출력을 사용하라.
- 표준 함수와 네트워크 파일은 상호 호환되지 않는다.
12. Summary
- 시스템 콜을 직접 쓰는 대신 버퍼링 가능한 표준 라이브러리를 사용하자.
- 커널은 열린 파일을 표현하는데 3개의 자료구조를 사용한다.
- 서술자 테이블 엔트리
- 열린 파일 테이블 엔트리
- v-node 테이블 엔트리
- 파일 공유는 v-node를 공유하며, 별도의 열린 파일을 가진다. (메타데이터 공유, pos 별도 유지)
- 입출력 리디렉션은 각 서술자를 동일한 열린 파일로 연관시킨다.
too many open files
버그가 생기는 이유는 무엇일까?
- 로깅 혹은 소켓?
- 네트워크 트래픽과 연관이 없다면 로깅쪽일 가능성이 높겠다.
댓글남기기