파일 디스크립터(File Descriptor)
Linux/Unix에서 파일은 데이터를 읽을 수 있거나 데이터를 쓸 수 있는 모든 객체를 말한다. 일반적인 정규 파일부터 디렉토리, 소켓, 파이프, 터미널 등등 모든 디바이스도 파일로 취급한다. Linux/Unix에서 프로세스가 이 파일들을 바이트 단위의 입출력으로 다룰 수 있게 하고, 커널 내부의 자료 구조들과의 연결 통로 역할을 하게 하는 것이 파일 디스크립터이다.
프로세스가 실행 중에 파일을 open하면 커널은 해당 프로세스의 파일 디스크립터 숫자 중 사용하지 않는 가장 작은 값을 할당해준다. 그다음 프로세스가 열려있는 파일에 시스템 콜을 이용해서 접근할 때, 파일 디스크립터 값을 이용해서 파일을 지칭할 수 있는 것이다.
기본적으로 할당되는 파일디스크립터는 0(표준 입력), 1(표준 출력), 2(표준 에러)가 있다. 즉, 사용자가 open 한 파일에 대한 파일 디스크립터는 3부터 할당된다.
파일 입출력 관련 기본적인 시스템 콜 함수
Open(2)
파일을 오픈하거나 생성할 때 사용하는 시스템 콜 함수
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int oflag);
int open(const char *pathname, int oflag, mode_t mode);
// 리턴 값: 성공 시 파일 디스크립터, 에러시 -1
const char *pathname : 열고자 하는 파일의 경로(상대 경로 / 절대 경로 모두 가능)
int oflag : 파일이 어떤 용도로 사용될 것인지를 결정하는 옵션
- 반드시 사용해야 하는 플래그(셋 중 하나)
- O_RDONLY - 파일을 읽기 전용으로만 오픈
- O_WRONLY - 파일을 쓰기 전용으로만 오픈
- O_RDWR - 파일을 읽기와 쓰기가 모두 가능하도록 오픈
- 선택적으로 사용할 수 있는 플래그
- O_APPEND - 파일의 원래 데이터를 유지하면서 새로운 데이터를 그 파일의 끝에 추가하고자 할 때 사용
- O_CREAT - 파일이 존재하지 않을 경우 같은 이름의 파일을 생성. 3번째 인자인 파일의 mode와 함께 지정해야 한다.
- O_EXCL - O_CREAT과 함께 쓰이며 만약 파일이 이미 존재한다면 open()이 실패하고 에러를 리턴
- O_TRUNC - O_RDWR 또는 O_WRONLY 모드인 경우에 해당 파일이 이미 존재할 경우 그 파일은 길이가 0이 되면서 이전 데이터를 모두 잃게 된다.
mode_t mode : O_CREAT 옵션을 쓸 때 필수적으로 사용해야하는 옵션으로, 8진수 형태로 나타내며 파일을 읽기, 쓰기, 실행할 사용자의 권한을 지정한다.
S_IRWXU 파일 소유주에게 읽기, 쓰기, 실행 권한을 설정.
S_IRUSR 파일 소유주에게 읽기 권한을 설정.
S_IWUSR 파일 소유주에게 쓰기 권한을 설정.
S_IXUSR 파일 소유주에게 실행 권을을 설정.
S_IRWXG 파일 그룹에게 읽기, 쓰기, 실행 권한을 설정.
S_IRGRP 파일 그룹에게 읽기 권한을 설정.
S_IWGRP 파일 그룹에게 쓰기 권한을 설정.
S_IXGRP 파일 그룹에게 실행 권한을 설정.
S_IRWXO 다른 사용자에게 읽기, 쓰기, 실행 권한을 설정.
S_ROTH 다른 사용자에게 읽기 권한을 설정.
S_WOTH 다른 사용자에게 쓰기 권한을 설정.
S_XOTH 다른 사용자에게 실행 권한을 설정.
또는 세자리 수 8진수 형태의 권한 고유값을 넣어줄 수 있다. 예를 들어 0751은 111 101 001 이므로 파일 주인은 모든 권한을 가지고, 파일 주인이 속한 그룹은 읽기, 실행 권한을 가지고, 그 외 유저는 실행 권한만 가지는 것을 의미한다.
※ 리눅스 파일 권한에 대한 포스팅 : 리눅스의 파일 권한 (Permission)
Close(2)
오픈한 파일을 닫을 때 사용하는 시스템 콜 함수
#include <unistd.h>
int close(int fd);
// 리턴 값: 성공 시 0, 에러 시 -1
int fd : 닫을 파일의 파일 디스크립터
Creat(2)
파일을 생성할 때 사용하는 시스템 콜 함수
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
int creat(const char *pathname, mode_t mode);
// 리턴 값: 성공 시 쓰기전용으로 열린 파일 디스크립터, 에러시 -1
const char *pathname : 생성할 파일의 경로
mode_t mode : 8진수 형태로 나타내며 파일을 읽기, 쓰기, 실행할 사용자의 권한을 지정한다.
open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode); 와 동일한 역할을 한다. 즉, creat()으로 생성한 파일은 접근 모드가 쓰기 전용으로 생성되어 그 파일을 다시 읽기 위해서는 일단 파일을 close() 한 후 읽기쓰기 혼용 권한으로 다시 오픈해야 한다.
lseek(2)
오픈된 파일의 오프셋 위치를 명시적으로 변경해주는 시스템 콜 함수
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
// 리턴 값: 성공 시 새 파일 오프셋, 에러 시 -1
int fd : 조정할 파일의 파일 디스크립터
off_t offset : 기준점으로부터 이동할 바이트 수
int whence : 기준점
- SEEK_SET(0) - 파일의 맨 처음
- SEEK_CUR(1) - 파일의 현재 오프셋 위치
- SEEK_END(2) - 파일의 맨 마지막
※ lseek(fd, 0, SEEK_END); 를 이용하여 파일의 크기를 알 수 있다.
read(2)
오픈된 파일에서 데이터를 읽을 때 사용하는 시스템 콜 함수
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
// 리턴 값: 성공시 읽은 바이트 수, 에러 시 -1
int fd : 읽을 파일의 파일 디스크립터
void *buf : 읽어 들일 데이터를 저장할 배열
size_t nbytes : 읽어들일 데이터의 최대 길이(buf의 길이보다 커서는 안됨)
read() 호출이 완료되면 파일의 현재 오프셋 위치는 이전 오프셋 위치에서 실제 읽은 바이트 수만큼 이 더해진 값으로 갱신된다. 이때 리턴 값은 실제로 읽은 바이트 수이며 읽기를 요청한 바이트 수보다 적을 수 있다. (읽는 도중에 파일의 끝을 만난 경우)파일의 끝을 만난 이후에도 read()를 호출한다면 0을 리턴한다.
write(2)
오픈된 파일에 데이터를 쓸 때 사용하는 시스템 콜 함수
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t nbytes);
// 리턴 값: 성공시 기록된 바이트 수, 에러 시 -1
int fd : 데이터를 쓸 파일의 파일 디스크립터
const void *buf : 쓰고자 하는 데이터가 담긴 버퍼
size_t nbytes : 쓰고자 하는 데이터의 길이(buf의 길이보다 커서는 안됨)
write() 호출이 완료되면 파일의 현재 오프셋 위치는 이전 오프셋 위치에서 실제 쓴 바이트 수만큼 이 더해진 값으로 갱신된다. 그리고 리턴 값도 실제로 쓴 바이트 수가 된다. 그러나 read()와 달리 쓰기를 요청한 바이트보다 실제로 쓴 데이터의 바이트가 더 적은 값이 될 수 없다. 두 값이 다르다면 어디선가 에러가 발생한 것이다.
'C, C++ & Linux' 카테고리의 다른 글
[Linux] 리눅스의 파일 권한 (Permission) (0) | 2022.04.07 |
---|---|
[Linux] 리눅스 기본 명령어 (0) | 2022.03.08 |