[Linux_System_programming] 02 File I/O

Linux System Programming (Robert Love) chapter 02 File I/O summary

파일 열기

1
2
3
4
5
6
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *name, int flags);
int open(const char *name, int flags, mode_t mode)
  • 파일을 열고 파일 디스크립터에 매핑
  • flags 인자는 O_RDONLY, O_WRONLY, O_RDWR중 하나를 포함해야 함, 그외 O_APPEND, O_NONBLOCK 등이 있음 (책참조)
  • mode에서는 새로운 파일의 권한(ex 0644, S_IRWXU 등이 있음, 책참조)
  • O_WRONLY|O_CREAT|O_TRUNC 조합은 너무 일반적이라 creat()시스템 콜을 제공
  • 에러 발생 시 -1 리턴 후 errno를 적절한 에러값으로 설정

파일 읽기

1
2
3
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t len)
  • fd가 참조하는 파일의 현재 오프셋에서 len byte바이트 만큼 buf에 읽기
  • 현재 읽은 바이트수 반환, 에러시 -1 반환, 0 반환 시 EOF(읽을 데이터 없음)을 나타냄(책에 리턴의 다양한 상황에 대해 나와 있음)
  • 논블럭 읽기 모드에서 읽을 데이터가 없다면 -1반환, errno를 EAGAIN으로 설정(논블럭 모드시 반드시 점검할 것)
  • 최대 읽기 값은 SSIZE_MAX(LONG_MAX, 0x7ffffff)
    • SIZE_MAX는 size_t의 최대값으로 부호가 없는 값, ssize_t는 부호 있는 값을 나타냄

파일 쓰기

1
2
3
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count)
  • count 바이트 만큼 fd가 참조하는 현재 파일 시작지점이 buf인 내용 기록
  • 현재 쓴 바이트수 반환, 에러시 -1 반환 후 errno를 적당한 값으로 변경
  • O_APPEND 모드는 파일 오프셋이 항상 파일 끝에 위치하도록 함(멀티 프로세스 관점에서 보면 파일 오프셋을 원자적으로 갱신)
  • 논블럭 쓰기 모드에서 블럭되면 -1반환, errno를 EAGAIN으로 설정
  • write 명령 시스템콜은 물리적 영역에 바로 쓰는 것이 아닌 버퍼에 복사해 놓음, 이후 이 버퍼를 수집해서 정렬 후 디스크에 씀
  • 동기식 입출력을 지원하기 위해 fsync()와 fdatasync(), sync() 시스템 콜 지원
    • fsync()는 버퍼의 모든 변경점을 디스크에 씀(메타데이터, 즉 파일생성시간 등 포함)
    • fdatasync()는 메터데이터를 제외한 데이터만 기록
    • sync()는 인자/반환값이 없이 버퍼의 모든 내용을 기록하도록 요구, 범용성이 높음
  • open() 호출 시 O_SYNC 플래그 사용하면 모든 파일 입출력 동기화(O_DSYNC, O_RSYNC도 있다, 역할은 찾아볼 것)

직접입출력

  • open() 시 O_DIRECT 플래그를 사용하면, 캐시/버퍼링/입출력관리 값은 복잡한 계층을 우회하여 직접 입출력 관리, 효과가 미미하다.

파일 닫기

1
2
3
#include <unistd.h>

int close(int fd)
  • fd에 연관된 파일과의 매핑해제, 프로세스에서 파일 떼어냄
  • 파일을 닫더라도 버퍼의 내용을 디스크에 강제로 쓰지 않는다.
  • 파일을 닫을 때 커널 내부에서 그 파일을 표현하는 자료구조 해제, 메모리에서 inode 복사본 제거
  • close의 반환값을 검사하는 것이 좋다. EBADF(fd가 유효하지 않음) 및 EIO 가 중요

파일 탐색

1
2
3
4
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t pos, int origin)
  • 파일의 특정위치로 이동하는 함수, origin 인자 : SEEK_CUR, SEEK_END, SEEK_SET
  • 호출 성공 시 새로운 파일 오프셋을 반환하며 에러가 발생하면 -1을 반환, errno를 설정
  • lseek(fd, 0, SEEK_CUR)을 이용하면 현재 오프셋을 알아낼 수 있다.
  • 파일의 끝을 넘어 위치를 지정하는 것도 가능, 이의 경우 데이터를 읽으면 EOF 반환, 쓰기 요청시 새로운 공간 생성 후 0으로 채움
  • 이렇게 0으로 채운 공간을 구멍이라고 하는데 이는 물리적 공간을 차지하지 않으며 이를 다루는 과정에서 물리적 입출력 작업이 필요하지 않다.

지정한 위치 읽고 쓰기

1
2
3
4
#include <unistd.h>

ssize_t pread(int fd, void *buf, size_t count, off_t pos);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t pos);
  • 읽고 쓸 파일 오프셋을 지정하여 read(), write() 수행함, 그러하 하기 차이점 존재
    • 호출 완료 후 파일포인터를 갱신하지 않음
    • lseek사용 시 발생할 수 있는 경쟁 상태를 회피 가능(멀티 스레드에서 각 스레드가 동시에 offset을 업데이트 하려는 경우)

파일 잘라내기

1
2
3
4
5
#include <sys/types.h>
#include <unistd.h>

int ftruncate(int fd, off_t len);
int truncate(const char *path, off_t len)
  • 파일을 len 크기로 잘라내는 시스템 콜
  • 호출 성공 0 반환, 에러가 발생하면 -1을 반환, errno를 설정
  • 파일의 크기보다 큰 값으로 잘라내기 가능, 확장된 바이트는 모두 0으로 채워 짐

다중입출력

  • 여러 개의 파일 디스크립터를 동시에 블록하고 그중 하나라도 블록되지 않고 읽고 쓸 준비가 되면 알려주는 기능
    • 논블록 입출력을 사용하면 프로세스가 계속 기다리면서 임의의 순서대로 입출력을 요청해야 함(비효율적임)
  • 3가지 다중입출력 방식 제공 : select, poll, epoll

select()

1
2
3
4
5
6
7
8
#include <sys/select.h>

int select(int n, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

FD_CLR(int fd, fd_set* set);
FD_ISSET(int fd, fd_set* set);
FD_SET(int fd, fd_set* set);
FD_ZERO(fd_set* set);
  • 동기화된 다중 입출력 메커니즘 제공, 파일 디스크립터가 입출력을 수행할 준비가 되거나 옵션으로 정해진 시간이 경과할 때 까지만 블럭
  • argument
    • n : 파일디스크립터 집합에서 가장 큰 파일 디스크립터 숫자
    • readfds : 데이터 읽기가 가능한지 파악하기 위한 파일 디스크립터 집합
    • writefds : 데이터 쓰기가 가능한지 파악하기 위한 파일 디스크립터 집합
    • exceptfds : 예외 발생 또는 소켓에서 대역을 넘어서는 데이터가 존재하는지 파악하기 위한 파일 디스크립터 집합
    • timeout : NULL이 아니면 timeout 시간 설정, 함수 반환시 남은 시간을 넣어서 반환 됨
  • timeout을 제외한 나머지 fds집합을 NULL로 넘겨서 다양한 시스템에서 동작하는 쉬운 잠들기 구현 가능
  • pselect() : 파일 디스크립터와 시그널을 기다리는 사이에 발생할 수 있는 경쟁상태 해결을 위해 sigmask인자 추가

poll()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <sys/poll.h>

struct pollfd
{
  int fd;
  short events;
  short revents;
};

int poll(struct pollfd* fds, nfds_t nfds, int timeout);
  • systemv에서 제공하는 다중입출력, poll의 단점을 보완(그러나 이식성의 이유로 select가 많이 사용)
  • argument
    • fds : 감시하고자 하는 단일 파일 디스크립터를 명시, events 필드는 감시할 이벤트 비트마스크, revents는 반환 이벤트
    • nfds : 감시할 파일 디스크립터 갯수
    • timeout : timeout 밀리 초단위, 음수 사용시 무한 대기

select VS poll

  • poll이 파일 디스크립터 숫자가 큰경우 점 더 효율정
  • select를 사용하면 파일 디스크립터 집합을 반환하는 시점에 재구성되므로 잇단 호출과정에서 매번 초기화 필요
  • select가 상대적으로 이식성이 높다

커널 부분

가상파일 시스템

  • 추상화된 파일 시스템에 대한 설명(책 참조)

페이지 캐시

  • 디스크 파일 시스템에서는 캐시를 가지고 있음

페이지 쓰기 저장

  • 커널은 버퍼를 통해 쓰기 작업을 지연(플러셔 스레드가 별도로 있음)
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy