scarf@127.0.0.1


파일 식별자(file descriptor)란?


3줄 요약

  • 어떠한 파일을 가리키는 바로가기
  • 파일은 파일 뿐만 아니라 디렉토리, 파이프 등 다양함
  • 0은 표준 입력, 1은 표준 출력, 2는 표준 오류

파일 식별자를 알아보자

  • 파일 디스크립터, 파일 식별자라고 하니까 설명을 아무리 읽어도 이해가 가지 않는다.
  • 하지만 하는 일은 쉽게 생각해서 바로가기다.
  • 물론 실제 바로가기와 완전히 같지는 않다. 차이는 나중에 알아보자.

수학귀신에서 어렵게 팩토리얼이라고 하지 않는다. 이라고 부른다. 마찬가지로 이제부터는 파일 식별자가 아니다. 바로가기다.

프로그램이 실행되면 기본적으로 바로가기 3개를 가지게 된다.

  • 0 -> 표준 입력
  • 1 -> 표준 출력
  • 2 -> 표준 오류

보통은 표준 입력에서 읽어와서 표준 출력이나 오류로 내보내게 된다.

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags)
int open(const char *pathname, int flags, mode_t mode)
"파일을 (만들고) 열기"


  • 파일을 열고(아니면 만들고) 그 파일을 가리키는 바로가기를 반환한다.

read

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count)
"바로가기가 가리키는 파일을 바이트로 읽기"

fd: 바로가기
buf: 읽은 바이트를 저장할 버퍼 포인터
count: 읽을 양
반환값: 읽은 바이트 수
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
  int 바로가기;
  char 읽은내용[8];

  // 읽기 전용 & 추가 모드로 파일 열기
  바로가기 = open("a.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
  if (바로가기 < 0) {
    perror("파일 열던 중 오류 발생\n");
    exit(0);
  }
  for (int i = 0; i < 3; i++) {
    memset(읽은내용, 0, sizeof(읽은내용));
    read(바로가기, 읽은내용, sizeof(읽은내용));
    printf("[인덱스 %d] %s\n", i, 읽은내용);
  }
  close(바로가기);
}

예제 코드에서 변수 바로가기는 "a.txt"에 대한 바로가기를 담고 있다.

dup

int dup(int oldfd)

oldfd: 복사할 바로가기
반환값: 새로 만들어진 바로가기

바로가기를 복사해서 반환한다. 가령 마인크래프트.exe 게임의 바로가기 마인크래프트 - 바로가기가 있다고 해보자. 이 바로가기를 복사하면 어떻게 될까? 마인크래프트.exe 에는 변화가 없고 마인크래프트 - 바로가기(2)가 생길 것이다.

마찬가지로 dup()을 실행하면 oldfd 바로가기를 복사해 새로운 바로가기를 반환한다. 이때 새 바로가기는 남는 번호중 가장 낮은 번호가 나온다. 어떤 이야기냐면 바로가기를 계속 만들면

  • 마인크래프트 - 바로가기(3)
  • 마인크래프트 - 바로가기(4)
  • ... 가 생기듯 4, 5, 6... 같은 식으로 다음에 남는 번호의 바로가기를 만들어 준다는 것이다.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {
  int foo바로가기 = open("foo.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
  int foo바로가기2 = dup(foo바로가기);

  printf("foo바로가기: %d, foo바로가기2: %d\n", foo바로가기, foo바로가기2);
  write(foo바로가기, "안녕 세상! ~ foo.txt - 바로가기\n", 41);
  write(foo바로가기2, "난 안녕 못한데 ~ foo.txt - 바로가기(2)\n", 50);
  close(foo바로가기);
  close(foo바로가기2);
  return 0;
}
❯ cat foo.txt
안녕 세상! ~ foo.txt - 바로가기
난 안녕 못한데 ~ foo.txt - 바로가기(2)

foo바로가기, foo바로가기2 모두 같은 foo.txt 파일을 가리키고 있다.

dup2

int dup2(int oldfd, int newfd)
"newfd가 가리키는 파일을 oldfd가 가리키는 파일로 바꾼다"
만약 newfd 바로가기가 이미 열려 있었으면 닫은 후 새로 열게 된다
반환값: newfd

예제 1

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

int main() {
  int bar바로가기 = open("bar.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
  int 반환값 = dup2(bar바로가기, STDOUT_FILENO);

	printf("반환값: %d, bar바로가기: %d\n", 반환값, bar바로가기);
  printf("HAYO!\n");
  return 0;
}
❯ ./"dup"
❯ cat bar.txt
반환값: 1, bar바로가기: 3
HAYO!

printf를 했는데 터미널에 출력이 뜨지 않고 bar.txt에 그 내용이 기록되었다. 어째서일까? 우선 STDOUT_FILENO표준 출력 바로가기와 같다. 지금까지

  • bar바로가기 -> "bar.txt"
  • 표준 출력 바로가기` -> 표준 출력

였다면 dup2로 표준 출력 바로가기가 가리키는 파일을 bar바로가기와 똑같이 바꿔줘서

  • bar바로가기 -> "bar.txt"
  • (좀 전까지)표준 출력 바로가기` -> "bar.txt"

가 된 것이다. printf는 표준 출력 바로가기가 가리키는 파일에 내용을 출력하는데 이건 이제 "bar.txt"를 가리키는 바로가기가 되었으니 "bar.txt"에 출력 내용이 써지게 된 것이다.

예제 2

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

int main() {
  int bar바로가기 = open("bar.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
  int spam바로가기 = open("spam.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
  int 반환값 = dup2(bar바로가기, spam바로가기);

  printf("반환값: %d, bar바로가기: %d, spam바로가기: %d\n", 반환값, bar바로가기,
         spam바로가기);
	write(bar바로가기, "애옹\n", 8);
  write(spam바로가기, "꿈틀꿈틀\n", 14);
  return 0;
}
❯ ./"dup"
반환값: 4, bar바로가기: 3, spam바로가기: 4
❯ cat bar.txt
애옹
꿈틀꿈틀
  • bar바로가기 -> "bar.txt"
  • spam바로가기 -> "spam.txt"

dup2로 spam바로가기가 bar바로가기와 같은 파일을 가리키게 바꾸면

  • bar바로가기 -> "bar.txt"
  • spam바로가기 -> "bar.txt"

참고자료

inode inode #2 파일 테이블/파일 식별자 테이블 파일 디스크립터 디렉토리도 파일인데 cat이 안 되는 이유 ext4 파일시스템 구조