리눅스 시스템 프로그래밍_Ch 2

·2021년 1월 23일
1

리눅스

목록 보기
2/3
post-thumbnail

Ch 2 파일 입출력

2장부터 4장까지는 파일에 대해 다루는데, 2장에서는 파일 입출력의 기본을 알아보고 파일을 다루는 가장 기본적이고 간단한 방법인 시스템 콜(syscall)에 대해 이야기한다.


1. OS

  • OS = Kernel+ Shell
    - 커널은 운영체제의 핵심으로 운영체제가 수행하는 모든 것이 저장됨.
    - 쉘은 리눅스 환경에서 일반적으로 쓰는 명령어들을 읽어들여 해석하고 결과를 출력해주는 역할을 함.

  • 사용자 영역과 커널 영역이 나뉘어져 있고 각 영역엔 버퍼가 있음.
    - syscall을 통해 파일 입출력을 하게 되면 커널을 통해 입출력을 수행함.
    - 예를 들어 open() 이라는 시스템콜을 호출하면, kernel 모드에 들어가서 시스템 콜에 대한 명령어를 수행함.

2. 파일 디스크립터(fd)

  • 본격적으로 시스템 콜을 보기 전에 파일 디스크립터(fd)에 대한 개념을 간단하게 살펴보자면,
    - 프로세스마다 각각 파일 디스크립터 테이블을 가지고 있음. 이 테이블은 사용자 영역과 커널 영역 모두에서 프로세스 내에서 고유한 식별자로 사용됨.
    - 커널은 프로세스 별로 열린 파일 목록인 파일 테이블을 관리함.

    - 파일을 열면 파일 디스크립터가 반환되고 이 파일 디스크립터를 이용하여서 관련 시스템 콜의 다양한 연산을 수행.
    - 프로세스는 최소한 0,1,2 세 가지 파일 디스크립터를 열어둠

3. 파일 열기

파일을 읽고 쓰기 전에 open()이나 creat() 시스템 콜을 사용해서 파일을 열어야 한다.

Open()

#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);
  • 파일을 열고 파일 디스크립터를 얻음.
  • 경로 이름이 name인 파일을 파일 디스크립터에 맵핑하고, 성공하면 이 파일 디스크립터를 반환함.
  • Open() flags
    - O_RDONLY(읽기 전용 모드), O_WRONLY(쓰기 전용 모드), O_RDWR(읽기/쓰기 모드) 중 하나를 포함해야함.
    - flags 매개 변수에 비트 OR연산으로 값을 더 추가해서 열기 동작 변경 가능
    (예시)
    • O_APPEND: 덧붙이기 모드로 파일을 엶.
    • O_CREAT: name에 적은 파일이 없으면 파일을 새로 만듦.
    • O_TRUNC: 파일이 존재하고, 일반 파일이며 flags 인자에 쓰기가 가능하도록 명시되어 있으면 파일 길이를 0으로 자름.
      ...
  • mode
    - 파일의 접근 권한 설정
    (예시)
    • S_IRWXU: 소유자에게 읽기, 쓰기, 실행 권한 있음.
    • S_IRWXG: 그룹에게 읽기, 쓰기, 실행 권한 있음.
    • S_IRWXO: 그 외 모든 사용자에게 읽기, 쓰기, 실행 권한 있음.
      ...

creat()

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int creat (const char *name, mode_t mode);
  • O_WRONLY | O_CREAT | O_TRUNC 조합이 일반적이라 아예 이런 동작 방식을 지원하는 creat() 시스템 콜을 만듦.

4. 읽기

가장 대표적인 읽기 메커니즘은 POSIX.1에 정의된 read() 시스템 콜을 사용하는 것.

read()

#include <unistd.h>

ssize_t read (int fd, void *buf, size_t len);
  • fd가 참조하는 파일의 현재 파일 offset에서 len 바이트만큼 buf로 읽어 들임.
  • 성공시 buf에 쓴 바이트 숫자를 반환. 실패시 -1 반환.
  • 읽을 데이터가 충분하면 한 번에 len 바이트만큼 읽지만, 읽을 데이터가 len 바이트 보다 더 적을 경우 더 적게 읽기도 함.

5. 쓰기

파일에 데이터를 기록하기 위해 사용하는 가장 기본적이며 일반적인 시스템 콜은 write()임.

write()

#include <unistd.h>

ssize_t write (int fd, const void *buf, size_t count);
  • count 바이트 만큼 파일 디스크립터 fd가 참조하는 파일의 현재 파일 위치에 시작지점이 buf인 내용을 기록.
  • read()와 비슷하게 동작.

6. 파일 닫기

파일 디스크립터로 읽고 쓰는 작업을 마치고 나면 close() 시스템 콜을 이용해서 파일 맵핑을 끊어야 함.

close()

#include <unistd.h>

int close (int fd);
  • 열려있는 파일 디스크립터 fd에 연관된 파일과의 맵핑을 해제하며 프로세스에서 파일을 떼어냄.
  • 해제된 파일 디스크립터는 더이상 유효하지 않음.
코드를 입력하세요

7. 탐색하기

파일 디스크립터에 연결된 파일의 오프셋을 특정 값으로 지정함.

lseek()

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t pos, int origin);
  • origin 인자
    - SEEK_CUR: fd의 파일 오프셋을 현재 오프셋에서 pos를 더한 값으로 설정.
    - SEEK_END: fd의 파일 오프셋을 현재 파일 크기에서 pos를 더한 값으로 설정.
    - SEEK_SET: fd의 파일 오프셋을 pos 값으로 설정.

8. 동기식 입출력

애플리케이션에서 직접 데이터가 디스크에 기록되는 시점을 제어하고 싶을 때가 있음. 이런 때를 위해 리눅스 커널에서는 성능을 희생하는 대신 입출력을 동기화하는 옵션 제공.

fsync()

#include <unistd.h>

int fsync (int fd);
  • 파일 디스크립터 fd에 맵핑된 파일의 모든 변경점을 디스크에 기록.
  • 이때 파일 디스크립터 fd는 반드시 쓰기 모드로 열려야 함.

9. 다중 입출력

애플리케이션이 여러 개의 파일 디스크립터를 동시에 블록하고 그중 하나라도 블록되지 않고 읽고 쓸 준비가 되면 알려주는 기능. 어떤 파일 디스크립터에 이벤트가 발생했는 지 주기적으로 확인.

select()

#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);
  • 파일 디스크립터가 입출력을 수행할 준비가 되거나 옵션으로 정해진 시간이 경과할 때까지만 블록됨.
  • 검사 대상 파일 디스크립터는 세 가지 집합으로 나뉘어 각각 다른 이벤트를 기다림.
    - readfds: 데이터 읽기가 가능한지(블록되지 않고 read() 작업이 가능한지) 파악
    - writefds: 블록되지 않고 write() 작업이 가능한지 감시
    - exceptfds: 예외가 발생했거나 대역을 넘어서는 데이터가 존재하는지 감시
  • select()에서 사용하는 파일 디스크립터 집합은 직접 조작하지 않고 매크로를 사용해서 관리
    - FD_ZERO: 지정한 집합 내의 모든 파일 디스크립터 제거 (항상 select() 호출 전에 사용)
    - FD_SET: 주어진 집합에 파일 디스크립터를 추가
    - FD_CLR: 주어진 집합에서 파일 디스크립터 하나 제거

poll()

#include <poll.h>

int poll (struct pollfd *fds, nfds_t nfds, int timeout);
  • select()의 몇 가지 결점을 보완하지만, 여전히 습관이나 이식성의 이유로 select()를 더 많이 사용함.
  • fds가 가리키는 단일 pollfd 구조체 배열을 nfds 개수만큼 사용.
  • pollfd 구조체
#include <poll.h>

struct pollfd{
int fd; 	//파일 디스크립터
short events;   //감시할 이벤트
short revents;  //발생한 이벤트
profile
문과생 컴공초보👩🏻‍💻

0개의 댓글