[리눅스 시스템 프로그래밍] 3.5 파일 테이블과 파일 디스크립터 테이블

리눅스 시스템에서는 프로세스마다 파일 디스크립터 테이블을 갖고 있습니다. 그리고 파일 디스크립터 테이블에는 open에서 사용한 flags와 파일 테이블 요소의 위치 정보를 포함합니다.

그리고 커널은 모든 열려진 파일들을 관리하는 파일 테이블을 가지고 있습니다. 파일 테이블의 항목 엔트리에는 파일의 상태 flags와 현재 파일의 작업 offset과 파일의 vnode 테이블의 위치 정보를 포함합니다. 그리고 vnode 테이블의 각 항목은 inode 정보와 현재 파일의 크기를 포함합니다.

먼저 하나의 프로세스에서 같은 파일을 두 번 열었을 때를 살펴봅시다. 같은 파일을 두 번 열면 서로 다른 파일 디스크립터를 부여하고 서로 다른 파일 오프셋을 유지합니다. 따라서 프로세스에서 파일 입출력은 open 함수로 연 작업을 구분하는 것이며 실제 물리적인 파일이 같은지는 구분하지 않습니다.

이 때 프로세스의 파일 디스크립터 테이블에는 각각의 파일 디스크립터를 부여하고 커널에서도 각각의 파일의 상태와 현재 작업 위치를 별도로 갖습니다. 다만 이 둘은 같은 vnode를 참조합니다.

[그림 3.6] 하나의 프로세스에 같은 파일을 두 번 열었을 때의 열려진 파일의 데이터 구조
[그림 3.6] 하나의 프로세스에 같은 파일을 두 번 열었을 때의 열려진 파일의 데이터 구조

다음은 같은 파일을 두 번 open 하였을 때 파일 오프셋이 독립적임을 보여주는 코드입니다.

// ex_twice_open.c
#include "eh.h"
int main(int argc,char **argv)
{
    int fd1 = 0, fd2=0;
    char buf[10+1]="";
    if(argc != 2)
    {
        fprintf(stderr,"usage: %s [file name]\n",argv[0]);
        return 1;
    }
    fd1= open(argv[1],O_RDONLY);
    if(fd1 == -1)
    {
      perror("failed open ");
      return 1;
    }
    fd2= open(argv[1],O_RDONLY);
    if(fd2 == -1)
    {
      perror("failed open 2");
      return 1;
    }
    read(fd1,buf,10);
    printf("fd1:%s\n",buf);
    read(fd2,buf,10);
    printf("fd2:%s\n",buf);
    close(fd1);
    close(fd2);
    return 0;
}

만약 두 개의 프로세스가 같은 파일을 열었을 때는 어떠한 모습일까요? 이 때도 각 프로세스의 파일 디스크립터 테이블에는 파일을 연 파일 디스크립터에는 플래그를 유지하고 커널의 파일 테이블 엔트리를 참조합니다. 그리고 커널의 파일 테이블 엔트리에는 파일 상태 플래그와 현재 파일 작업 offset과 vnode 엔트리를 참조합니다. 물론 같은 파일을 참조하였을 때를 예로 들었으므로 같은 vnode 엔트리를 참조합니다.

[그림 3.7] ex_twice_open 실행 화면
[그림 3.7] ex_twice_open 실행 화면
[그림 3.8] 두 개의 프로세스가 같은 파일을 열었을 때의 열려진 파일의 데이터 구조
[그림 3.8] 두 개의 프로세스가 같은 파일을 열었을 때의 열려진 파일의 데이터 구조