•
libft에서 인자로 받는 fd를 활용해 함수를 구현할 때 등장하는 File Descriptor 
•
get_next_line에서 파일을 읽기 위해 필요했던 함수 open 과 read 
두 과제를 거치면서 파일 디스크립터 및 파일 입출력 함수에 대해 공부한 내용을 조금이나마 정리해 보았다.
리눅스에서 파일에 접근하는 가장 기본적인 방법은 “시스템 콜”을 활용하는 것이다.
시스템 콜(System Call) 이란?
운영체제는 커널 모드와 사용자 모드로 나뉘어 구동된다.
시스템 콜은 커널 모드의 기능을 사용자 모드가 사용할 수 있도록, 즉 프로세스가 하드웨어에 직접 접근해서 필요한 기능을 사용할 수 있게 해준다.
파일을 다루는 데 사용되는 3개의 시스템 콜을 알아보자 
open() 시스템 콜 
읽기 및 쓰기를 위해 파일을 여는 함수이다. <fcntl.h> 헤더파일에 존재한다.
int open(const char *path, int oflag);
C
복사
•
path : 열고자 하는 파일의 경로 이름으로, 절대경로와 상대경로 모두 가능하다.
•
oflag : 파일을 읽기용으로 열 것인지, 쓰기용으로 열 것인지를 결정한다.
O_RDONLY | 읽기 전용으로 열기 |
O_WRONLY | 쓰기 전용으로 열기 |
O_RDWR | 읽기 및 쓰기 전용으로 열기 |
이 외에도 열기 동작을 설정할 수 있는 옵션들이 존재하며 |(OR) 연산을 통해 사용할 수 있다.
•
mode : 세 번째 매개변수로 파일의 접근 권한을 설정하기도 한다.
open("./test.txt", O_RDONLY);
C
복사
open("./test.txt", O_WRONLY | O_CREAT);
C
복사
시스템은 open() 시스템 콜을 통해 열린 파일의 정보를 어딘가에 저장해야 하고, 이후에 읽거나 쓰기 위해 파일에 접근할 수 있어야 한다. 이를 가능하게 하기 위해 어떤 일들이 일어나는 걸까?
우선 파일 정보를 저장하는 세 가지 테이블에 대해 알아보자!
•
Inode Table
프로세스가 파일을 열면 커널은 Inode Table에 빈 엔트리를 할당하고, 여기에 해당 파일의 속성(파일의 종류, 권한, 크기, 데이터 블록을 가리키는 포인터 등)을 저장한다. Inode Table에서는 하나의 파일에 대해 하나의 엔트리만 존재할 수 있기 때문에, 같은 파일은 하나의 엔트리를 공유한다.
•
(Open) File Table
모든 열린 파일들을 관리하기 위한 테이블이다. 파일이 열릴 때마다 무조건 하나의 엔트리가 생성되므로, 같은 파일에 대해 여러 개의 엔트리가 존재할 수 있다. 엔트리에는 읽기 및 쓰기 전용에 대한 정보와 Inode table을 가리키는 포인터 등이 저장된다.
•
File Descriptor Table
각 프로세스에서 현재 사용 중인 파일을 관리하기 위한 테이블이다.
File Table에 대한 포인터를 저장하는 배열으로, 파일 디스크립터 값이 이 배열의 인덱스이다.
이미지 출처 : File descriptor - Wikiwand
open() 을 통해 파일을 열었을 때, 시스템 콜은 커널 내에서 다음 과정을 거친다.
1.
Inode Table의 빈 엔트리를 채운다. inode 정보가 이미 있으면 해당 엔트리를 사용한다.
2.
File Table에서 새로운 엔트리를 할당하고 정보를 채운다.
3.
File Descriptor Table을 처음부터 탐색해서 사용되지 않는 영역에 File Table 엔트리 포인터를 저장하고, 그 인덱스를 반환한다.
결국 open() 시스템 콜은 연 파일의 파일 디스크립터 값을 반환한다. (오류 발생 시 -1 반환)
이 파일 디스크립터 값을 가지고 read() 및 write() 를 활용해 파일을 읽고 쓸 수 있게 될 것이다!
read() 시스템 콜 
파일의 내용을 읽어들이는 함수이다. <unistd.h> 헤더파일에 존재한다.
ssize_t read(int fd, void *buf, size_t nbyte);
C
복사
•
fd : 읽고자 하는 파일의 파일 디스크립터 값
•
buf : 읽어들인 내용을 저장할 곳
•
nbyte : 읽어들일 바이트 수
read(fd, buffer, 10);
C
복사
#include <tcntl.h>
#include <unistd.h>
#include <stdio.h>
#define BUFFER_SIZE 42
int main(void)
{
char buf[BUFFER_SIZE + 1];
int fd;
ssize_t read_byte;
fd = open("./test.txt", O_RDONLY);
while ((read_size = read(fd, buf, BUFFER_SIZE)) > 0)
{
buf[read_size] = '\0';
printf("%s\n", buffer);
}
close(fd);
return (0);
}
C
복사
read() 시스템 콜은 정상적으로 작동된 경우 읽어온 바이트 수를 리턴한다. (오류 발생 시 -1 반환)
write() 시스템 콜 
파일에 내용을 입력하는 함수이다. <unistd.h> 헤더 파일에 존재한다.
ssize_t write(int fd, const void *buf, size_t nbyte);
C
복사
•
fd : 쓰고자 하는 파일의 파일 디스크립터 값
•
buf : 파일에 입력할 내용을 저장한 곳
•
nbyte : 입력할 바이트 수
write(fd, buffer, 10);
C
복사
#include <tcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
int fd;
const char *buf;
ssize_t write_byte;
fd = open("./test.txt", O_WRONLY);
buf = "Hello, World!"
write_byte = write(fd, buf, strlen(buf));
if (write_byte == -1)
printf("ERROR\n");
else
printf("Success!\n");
return (0);
}
C
복사
write() 시스템 콜은 정상적으로 작동된 경우 쓰기에 성공한 바이트 수를 리턴한다. (오류 발생 시 -1 반환)
참고한 자료
잘못된 부분이 있다면 알려주세요