기술 정리/리눅스시스템프로그래밍

리눅스 시스템 프로그래밍 - 3장, 4장

투칼론 2016. 3. 16. 10:11
반응형

* 개인적으로 스터디한 내용을 메모한 내용이다.


CHAPTER 3 버퍼 입출력


블록은 파일시스템의 최소저장단위를 나타내는 추상개념이다. 모든 파일 시스템의 연산은 블록 단위로 일어난다.


3.1 사용자 버퍼 입출력 
dd(disk dump) 명령어로 블록크기를 1byte, 1024byte, 1130byte하면 1024byte가 가장 빠름을 확인할 수 있음. 블록의 배수로 연산을 수행했을때 가장 빠름


* 특정 디바이스의 블록크기를 알아내려면 stat() 시스템 콜이나 명령어를 이용하면 됨

* 프로그램 내부에서 직접 사용자 버퍼링을 구현하는 것도 가능함


3.2 표준 입출력 

* FILE이 대문자인 이유는 매크로임. 매크로에 typedef로 선언되어 있음


3.3 파일 열기 

* fopen()


3.4 파일 디스크립터로 열기 

* 파일디스크립터로 스트림 열기 : fdopen()


3.5 스트림 닫기

* 스트림 닫기 : fcose()

* 모든 스트림 닫기 : fcloseall()


3.6 스트림에서 읽기

* 한번에 한문자씩 읽기 : fgetc()

* 읽은 문자 되돌리기 : ungetc() - 여러문자를 돌리면 LIFO 방식으로

* 한줄씩 읽기 : fgets()

* 바이너리 데이터 읽기 : fread()


3.7 스트림에 쓰기

* 한번에 한문자씩 쓰기 : fputc()

* 문자열 기록하기 : fputs()

* 바이너리 데이터 기록하기 : fwrite()


3.8 사용자 버퍼 입출력 예제 프로그램

* structure 구조체를 읽고 씀

* 아키텍처와 ABI가 동일한 경우에만 바이너리 데이터를 일관적으로 읽고 쓸수 있음


3.9 스트림 탐색하기

* stream에서 파일 위치를 조작 : fseek()

* stream의 위치를 설정 : fsetpos()

* stream을 시작위치로 되돌림 : rewind()

* 현재 위치 알아내기 : ftell()


3.10 스트림 비우기

* fflush()는 단지 사용자 버퍼에 있는 데이터를 커널 버퍼로 쓰기

참고로 fsync()는 디바이스에 기록


3.11 에러와 EOF

* stream에 에러 지시자가 설정되었는지 검사 : ferror()

* stream에 EOF가 설정되었는지 검사 : feof()

* stream에서 에러 지시자와 EOF 지시자를 초기화 : clearerr(0


3.12 파일 디스크립터 얻어오기

* fileno()


3.13 버퍼링 제어하기

* 세가지 유형의 사용자 버퍼링 - setvbuf()

* 모드 - 버퍼 미사용(_IONBF), 행 버퍼(_IOLBF), 블록버퍼(_IOFBF)


3.14 스레드 세이프

* 스레드는 개별 프로세스 내에 존재하는 여러 개의 실행 단위

* 스레드에서 데이타에 접근할때 동기화에 대해 주의를 기울이지 않거나 스레드 로컬로 만들지 않으면 스레드가 공유 데이터를 덮어쓸수도 있음

* 수동으로 파일 락 걸기 : flockfile()

* 수동으로 파일 락 풀기 : funlockfile()

* flockfile()의 논블록 버전 : ftrylockfile()


3.15 표준 입출력 비평

* 이중 복사로 인한 성능 문제

* 커널에서 표준입출력의 버퍼로 복사, 표준입출력 버퍼에서 인자로 복사


3.16 맺음말




CHAPTER 4 고급 파일 입출력


백터입출력, epoll, 매모리맵 입출력, 파일 활용법 조언, 비동기식 입출력 등에 언급


4.1 백터 입출력

* 자연스런 코딩 패턴, 효율, 성능, 원자성이 선형입출력에 비해 장점임

* readv(), writev() 함수


4.2 epoll() 

* FILE이 대문자인 이유는 매크로임. 매크로에 typedef로 선언되어 있음


4.3  메모리맵 입출력
리눅스 커널은 표준 파일 입출력 대안으로 애플리케이션이 파일을 메모리에 매핑할 수 있는 인터페이스를 제공
* mmap() - fd가 가리키는 offset위치에서 특정 바이트 수만큼을 메모리에 매핑하는 함수. 표준입출력보다 불필요한 버퍼복사가 없고, 컨텍스트 스위치도 오버헤드도 발생하지 않아 장점. 
* 페이지크기 - 페이지는 메모리 관리 유닛(MMU)에서 사용하는 최소 단위
* sysconf() - 페이지 크기를 얻을 수 있는 POSIX의 표준 메서드, 리눅스는 getpagesize() 함수를 제공
* munmap() - map()으로 생성한 매핑을 해제하기 위한 시스템 콜
* mremap() - 메모리 영역을 확장하거나 축소하기 위한 시스템 콜
* mprotect() - 기존 메모리 영역에 대한 접근 권한 변경
* msync() - fsync() 시스템 콜의 메모리 맵핑 버전
* madvise() - 프로세스가 맵핑을 어떻게 사용할 것인디 커널에 알려주는 시스템 콜. 주어진 힌트에 따라 커널은 의도한 용도에 따라 맞게 맵핑의 동작 방식을 최적화할수 있게 함.

4.4  파일 활용법 조언
* posix_fadvise() - fd의 범위에 대한 힌트를 커널에 제공함. madvise()의 힌트와 유사함
* readahead() - posix_advise()는 커널 2.6에서 추가됨. 기존에 readahead()를 리눅스 전용으로 제공함. fd가 가리키는 offset 범위 내의 영역을 페이지 캐시를 생성함

4.5 비동기식 입출력
동기화(synchronized), 비동기화(nonsynchronized), 동기식(synchronous), 비동기식(asynchronous) 용어가 혼란스러움
* 비동기식 입출력 - aio_read(), aio_write(), aio_return() 등 지원

4.6 입출력 스케줄러와 성능
디스크와 시스템의 나머지 부분 간의 상대적인 성능 차이가 크다. 디스크 성능 저하는 데이터를 읽고 쓰는 헤드를 이동시키는 과정임.

* 디스크 주소 방식 - 최근 하드디스크는 유일한 블록 번호를 모든 실린더/헤더/섹터에 맵핑해서 성능 개선함
* 입출력 스케줄러으 동작 방식 - 병합과 정렬이라는 두가지 기본 동작을 수행
* 리눅스 2.4 커널의 입출력 스케줄러는 리누스 엘리베이터 같은 단순한 접근 방식이었는데, 커널 2.6에서는 새로운 입출력 스케줄러를 공개함 - 데드라인/예측/CFQ/NooP 입출력 스케줄러
* 입출력 스케줄러 선택과 설정 - /sys/block/[device]/queue/scheduler 값을 변경해서 cfq, deadline, noop 방법을 설정할 수 있음
* 입출력 성능 극대화는 중요
* 입출력 요청 목록을 탐색이 편리한 순서로 정렬하기 위한 옵션 - 파일의 전체 경로, inode 번호, 파일의 물리적인 디스크 블록