파일 디스크립터에 대해 알아보자!

윤효준·2024년 10월 9일
0

콤퓨타 공부

목록 보기
12/17

파일 디스크립터란???

파일 디스크립터(File Descriptor, FD)는 운영 체제에서 파일이나 입출력 장치에 접근할 때 사용하는 추상적인 개념이다. 프로세스가 파일이나 소켓, 파이프 등과 같은 자원에 접근할 때, 이 자원을 관리하는 커널에서 숫자 형태로 해당 자원에 대한 핸들을 반환하는데 이 숫자를 파일 디스크립터라고 부른다. 파일 디스크립터는 주로 리눅스나 유닉스 기반 운영 체제에서 사용되며 숫자로 된 값은 자원을 식별하고 커널이 자원에 대한 입출력 작업을 관리하는데 사용된다.

파일 디스크립터의 역할

  • 파일이나 자원에 대한 핸들: 프로세스가 파일을 열면 커널은 해당 파일을 관리하는 파일 디스크립터를 반환한다. 이 파일 디스크립터를 사용해 파일에 읽기, 쓰기 등의 작업을 수행할 수 있다.

  • 고유 식별자: 파일 디스크립터는 프로세스 내에서 유일하게 해당 파일을 식별하는 숫자이다. 프로세스 내에서만 유일하면 되므로 프로세스가 다르면 동일한 fd를 가질 수 있다.(부모와 자식 프로세스는 동일한 파일 디스크립터 번호를 공유한다)

  • 입출력 제어: 프로세스는 파일 디스크립터를 통해 파일, 소켓(작성 예정), 파이프()작성 예정, 표준 입출력 등을 제어할 수 있다.

파일 디스크립터의 종류

  • 표준 입력(0): 일반적으로 키보드 입력을 처리하는 파일 디스크립터
  • 표준 출력(1): 화면에 출력을 처리하는 파일 디스크립터
  • 표준 오류(2): 오류 메세지를 출력하는 파일 디스크립터

표준 출력과 표준 오류는 둘 다 메시지를 출력한다는 공통점이 있어 굳이 따로 나눌 필요가 있는가에 대한 의문이 들 수 있다. 하지만 이 둘을 분리함으로써 오류와 정상 결과를 별도로 처리하고 오류 메세지에 대해 즉각적인 피드백을 받을 수 있으며, 리다이렉션을 통해 로그 관리를 쉽게 할 수 있게 해 준다.

예시

$ ./my_program > output.txt 2> error.log

위 명령어는 my_program정상 출력output.txt로 보내고, 오류 출력error.log로 보낸다.

FD 값의 범위

파일 디스크립터는 양의 정수로 표현되며 0부터 시작한다. 앞서 말한 대로 0, 1, 2는 이미 할당이 되어 있다. 그 이후에 생성되는 파일 디스크립터 값은 3부터 시작하여 프로세스가 열거나 사용하는 파일, 소켓, 파이프 등 자원마다 1씩 증가한다.

파일 디스크립터의 상한

각 운영 체제는 한 프로세스가 동시에 열 수 있는 파일 디스크립터의 최대 개수를 설정한다.
이 제한은 보통 유저 레벨 한계커널 레벨 한계로 나뉜다.

  • 유저 레벨 한계: 현재 프로세스가 사용할 수 있는 파일 디스크립터의 기본적인 제한 값이다.

아래 명령어를 통해 확인 가능

 ulimit -n

아래 명령어를 통해 상한 설정 가능

ulimit -n 4096
  • 커널 레벨 한계: 유저가 설정할 수 있는 파일 디스크립터의 최대값으로 이 값보다 더 큰 값으로 설정할 수는 없다.

아래 명령어를 통해 확인 가능

cat /proc/sys/fs/file-max

시스템 호출과 FD

아래 예시는 파일을 열고 닫는 간단한 예시로 fd에 대한 이해를 돕기 위해 작성했다.

#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd;

    // 파일 열기
    //여기서 fd값이 나온다.
    fd = open("example.txt", O_RDONLY);
    //오류가 발생했을 경우에는 open은 -1을 반환하므로 오류 처리를 해준다.
    if (fd == -1) {
        perror("open");
        return 1; //1을 반환하여 운영체제에 프로그램이 오류가 발생했음을 알린다.
    }
    // 파일 닫기
    if (close(fd) == -1) {
        perror("close");
        return 1;
    }

    return 0;
}

파일 디스크립터 매핑 과정

파일 디스크립터는 사용자 공간에서 커널 공간에 있는 파일 객체와 연결되는 여러 데이터 구조를 통해 간접적으로 파일에 접근한다.

  1. 파일 디스크립터 테이블: 각 프로세스는 파일 디스크립터 테이블을 가진다. 이 테이블은 해당 프로세스가 열어둔 파일 디스크립터를 저장하는 배열이다. 각 파일 디스크립터는 인덱스를 통해 커널 내부의 파일 객체에 대한 포인터를 참조한다.

    • 프로세스에서 파일 디스크립터 테이블은 open(), socket() 등의 시스템 호출에 의해 새로운 파일을 열 때마다 갱신된다.

    • 테이블의 각 항목은 프로세스가 열고 있는 파일 디스크립터를 나타내며 이를 통해 커널의 파일 테이블을 참조한다.

  2. 파일 테이블: 파일 디스크립터 테이블의 각 항목은 커널파일 테이블에 있는 파일 객체에 대한 포인터를 참조한다. 이 파일 객체는 파일 디스크립터가 가리키는 실제 파일 또는 자원을 추상화한 구조체이다. 파일 테이블의 항목은 다음과 같은 정보를 포함한다:

    • 파일 위치 (파일을 읽거나 쓸 때의 현재 위치)

    • 파일의 읽기/쓰기 모드

    • 파일의 상태 플래그

  3. inode 테이블: 파일 테이블의 파일 객체는 다시 inode 테이블에 있는 파일의 inode(파일의 메타데이터를 포함하는 구조체)를 참조한다. inode는 파일 시스템의 구체적인 파일 정보를 저장하는 핵심 데이터 구조로 파일의 실제 데이터 블록 위치파일 시스템 내의 모든 메타데이터를 포함한다.

예시

  1. 프로세스가 open("hello.tx", O_RDONLY)를 호출하면 커널은 해당 파일을 열고 파일 디스크립터 테이블에 새로운 파일 디스크립터 값을 추가한다. 이 파일 디스크립터는 프로세스의 파일 디스크립터 테이블에서 파일 테이블 항목을 참조하게 된다.

  2. 파일 디스크립터 테이블의 항목은 파일 테이블(커널이 관리)에서 example.txt에 대한 파일 객체를 가리킨다. 이 파일 객체에는 현재 파일 포인터 위치, 접근 모드 등이 포함된다.

  3. 파일 객체는 다시 hello.txt의 inode에 대한 포인터를 가지며 inode는 파일 시스템에서 실제 파일 데이터의 위치(물리적 블록과 매핑)를 관리한다.

profile
작은 문제를 하나하나 해결하며, 누군가의 하루에 선물이 되는 코드를 작성해 갑니다.

0개의 댓글