표준 입출력

Jongwon·2021년 12월 8일
0

Linux Programming

목록 보기
12/25

표준 I/O 라이브러리 Buffering

응용프로그램이 시스템 호출로 운영체제에 파일을 요청 시 계속 검사를 해야하는 과정이 있기 때문에, 자주 하는건 시스템 성능을 저하시킴. 따라서 라이브러리를 만들고, 라이브러리의 함수를 호출하여 Library Buffer에서 데이터를 받아오는 것이 좋다.
따라서 C언어에서 Standard I/O Library를 정의하여 자체적으로 library buffering을 하여 System Call 횟수를 줄인다.
파일 입출력에서는 파일을 File Descriptor로 접근하였지만, 표준 입출력에서는 스트림 형태로 파일에 접근한다. 하나의 스트림을 다루기 위하여 FILE이라는 구조체가 정의되어 있는데, 스트림을 열면, 파일 구조체 포인터가 배정된다.
스트림 내부의 버퍼에서 Read하고 Write 시스템호출 동작을 큰 단위로 하기 때문에 시스템 호출 횟수(시스템 내부 접근 횟수)가 줄어든다.
커널에 의해 STDIN, STDOUT, STDERR 스트림은 프로세스가 자동으로 얻는다.

struct FILE {
	char* _IO_read_ptr;
 	char* _IO_read_end;
	char* _IO_read_base;
	char* _IO_write_ptr;
 	char* _IO_write_end;
	char* _IO_write_base;
	int flags;   // r/w 플래그나, 에러 정보
	int _fileno;  //스트림과 관련된 File Descriptor 번호
 }

I/O 버퍼링 종류

  • line buffered : /n에 의해 입출력이 발생

  • fully buffered : 버퍼가 가득 찬 경우에 입출력이 발생(stdin, stdout등)

  • unbuffered : 응용프로그램과 파일 사이에 버퍼를 사용하지 않음(stderr 등)



    <stdio.h> 헤더

  • FILE *fopen(const char *pathname, const char *type)
    FILE 포인터 반환

    *type 종류
    r, rb: O_RDONLY
    w, wb: O_WRONLY | O_CREAT | O_TRUNC
    a, ab: O_WRONLY | O_APPEND | O_CREAT  => offset을 무시하고 맨 뒤에 추가
    r+, r+b, rb+: O_RDWR
    w+, w+b, wb+: O_RDWR | O_CREAT | O_TRUNC
    a+, a+b, ab+: O_RDWR | O_APPEND | O_CREAT
  • FILE *freopen(const char *pathname, const char type, FILE fp)
    fp를 pahtname 가리키도록 변경, open 후 dup2하는것과 동일한 동작

  • FILE *fdopen(int fd, const char *type)
    이미 open된 fd를 스트림 형태로 다시 열어달라, 단 type은 open과 동일해야함, 운영체제에 open 시스템 호출을 하지는 않음

  • int fclose(FILE *fp)
    0이면 정상종료, EOF면 에러

close시 내부 동작
출력 버퍼에 있는 자료는 저장, 입력 버퍼에 있는 자료는 버려짐
  • int fgetc(FILE *fp) 한 글자 받아오거나 에러시 EOF
  • int getc(FILE *fp) 매크로 형태로 구현한 fgetc, 속도는 빠르지만 함수를 인자로 줄 순 없다
  • int getchar(void) stdin에서 한 글자 받아옴

  • int ungetc(int c, FILE *stream)
    c(EOF제외)를 다시 버퍼에 반납함(파일에 기록되는건 아님). \n같은 문자 읽었을 때 뒤로가기 위해 존재

  • int fuptc(int c, FILE *fp) fp에 c를 출력함, 에러시 EOF
  • int putc(int c, FILE *fp) fputc의 매크로 형태
  • int putchar(int c) stdout에 출력

  • char *fgets(char *buf, int n, FILE *fp) 최대 n개 문자를 fp에서 읽어 buf에 저장
\n이나 EOF만날 때 까지 or n개 문자 읽을 때 까지 읽고, 읽은 문자열의 마지막에 NULL을 채움
  • char *gets(char *buf) stdin에서 fgets, 하지만 크기 지정을 할 수 없어 버퍼 크기 넘기면 해킹가능
\n을 만날 때 까지 읽고, \n을 NULL로 대체하여 읽음

buf 출력, 실패나 에러는 NULL 출력


  • int fputs(const char str, FILE fp) str을 fp에 write함, 양수면 성공, EOF 에러
  • int puts(const char *str) stdout에 write함
끝이 str의 끝이 NULL이어야 한다.
fputs는 \n을 추가해주지 않고, puts는 쓸 때마다 \n을 추가해준다.

<Binary I/O 함수> : \n, NULL 등의 text는 중요하지 않고, R/W할 데이터의 size만 중요
==> 이진배열이나 구조체 입출력 가능

  • size_t fread(void ptr, size_t size, size_t nobj, FILE fp)
  • size_t fwrite(const void ptr, size_t size, size_t nobj, FILE fp)
    Read/Write한 nobj수 반환
size는 객체 1개 크기
nobj는 객체 수

<Offset 위치 관련> : 64bit 시스템에서 offset은 8byte이므로 long타입 offset가짐

  • long ftell(FILE *fp) 현재의 offset 반환
  • int fseek(FILE *fp, long offset, int whence) whence 기준으로 offset만큼 이동, 0이면 성공, 나머지 에러
  • void rewind(FILE *fp) 0으로 offset 되돌리기
  • int fsetpos(FILE stream, fpos_t pos)
  • int fgetpos(FILE stream, fpos_t pos)
    32bit 컴퓨터 사용 시 offset을 long으로 표현할 수 없어, fpos_t라는 구조체를 통해 offset 표현

<에러 관련>
int ferror(FILE fp) 입출력 오류 발생 체크, _IOERR flag 저장
int feof(FILE
fp) 파일의 끝(EOF) 도달 여부 체크, _IOEOF flag 저장
void clearerr(FILE *fp) _flags의 _IOERR, _IOEOF 삭제
0이 아니면 에러, 에러 정보는 FILE 구조체의 _flags에 저장


<버퍼 관련>

  • int fflush(FILE *fp)
    Buffering 되어있는 데이터를 운영체제에 시스템호출로 write 요청, 0이면 성공, EOF는 에러
    => line buffering mode여도 \n없이 바로 출력하게 해줌
  • int setvbuf(FILE fp, char buf, int mode, size_t size)
    0이면 성공, 나머진 실패, buf가 NULL이면 unbuffered, 기본은 fully buffered
  • void setbuf(FILE fp, char buf)
    setvbuf(fp, buf, buf ? _IOFBF : _IONBF, BUFSIZ)와 같다. bufsiz는 기본 1byte
buf: 사용할 버퍼의 포인터
mode : Fully Buffered(_IOFBF), Line Buffered(_OILBF), Unbuffered(_IONBF)
size : 사용될 버퍼의 크기

<멀티쓰레드에서 파일 잠금>

  • void flockfile(FILE *stream) 이미 lock되어있는 stream에 대해서 풀릴때까지 계속대기
  • funlockfile(FILE *stream)
  • int ftrylockfile(FILE *stream) lock 획득 여부만 반환하고 기다리지는 않음
    0이면 lock 성공, 0이 아니면 실패
profile
Backend Engineer

0개의 댓글