파일 기술자는 현재 열려 있는 파일을 구분할 목적으로 시스템에서 붙여놓은 번호
open()
함수로 파일을 열 때 부여됨 -> 열린 파일을 참조하는 데 사용하는 지시자 역할을 한다! 0: 표준 입력(standard input)
1: 표준 출력(standard output)
2: 표준 오류(standard error)
물론, 0,1,2 fd를 닫아놓으면 0,1,2에도 할당 가능
파일을 연다는 것은 파일의 내용을 읽거나 파일에 내용을 쓸 수 있는 상태로 변경하는 것 !
파일 기술자(fd)
를 리턴-1
을 리턴하고 errno에 실패 이유를 설명하는 오류 코드 저장
#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);
//pathname : 열려는 파일이 있는 경로
//flags : 파일 상태 플래그
//mode : 접근 권한 - 0644 같이 지정하거나 상숫값(S_IROTH 등)으로도 지정가능
플래그는 OR 연산자
를 사용해 연결도 가능!
플래그를 지정하는 부분이 없다
// 다음 두 함수는 같은 기능을 한다 !
creat(pathname, mode);
open(pathname, O_CREAT | O_O_WRONLY | O_TRUNC, mode);
입출력 작업을 모두 완료하면 무조건 파일을 닫자 !
한 프로세스가 열 수 있는 파일 개수에 제한이 있어, 파일을 제대로 닫지 않으면 최대 허용 개수를 초과해 더 이상 파일을 열지 못할 수 있다
#include <unistd.h>
int close(int fd);
// 0번 fd를 닫았을 때 어떤 일이 발생할까?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main() {
int fd;
close(0);
fd = open("test.txt", O_RDWR);
if(fd == -1){
perror("open");
exit(1);
}
printf("test.txt : fd = %d\n", fd); // test.txt : fd = 0
close(fd);
}
데이터가 텍스트든 이미지든 무조건 바이트 단위 로 읽음에 주의!
파일 기술자가 가리키는 파일에서 count에 지정한 크기만큼 바이트를 읽어 buf로 저장한 메모리 영역에 저장한다.
실제 읽은 바이트 수
를 리턴하며 오류가 발생하면 -1
을 리턴
리턴값이 0이면 파일의 끝에 도달해 더 이상 읽을 내용이 없다는 것
read()함수 실행할 때마다 읽은 크기만큼 오프셋이 이동
해 다음 읽을 위치를 가리킨다.
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//fd : 파일 기술자
//buf : 파일에 기록할 데이터를 저장한 메모리 영역
//count : buf의 크기(기록할 데이터의 크기)
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
//fd : 파일 기술자 - 쓰기를 수행할 파일
//buf : 파일에 기록할 데이터를 저장한 메모리 영역
//count : buf의 크기(기록할 데이터의 크기)
buf
가 가리키는 메모리 영역에서 count
로 지정한 크기만큼 읽어 파일에 쓰기를 수행한다
바이트 수를 리턴
하며 오류가 발생하면 -1
을 리턴함.오프셋이란 파일의 시작 지점부터 현재 위치까지의 바이트 수
한 파일에서 파일 오프셋은 오직 하나다 !
off_t lseek(int fd, off_t offset, int whence);
//fd: 파일 기술자
//offset: 이동할 오프셋 위치
//whence: 오프셋의 기준 위치
lseek(fd, 5, SEEK_SET);
lseek(fd, 0, SEEK_END);
cur_offset = lseek(fd, 0, SEEK_CUR);
반대방향으로 오프셋을 이동하려면 offset 값을 음수
로 지정
새로운 오프셋을 리턴
, 실패하면 -1을 리턴
dup(int oldfd);
기존 파일 기술자를 인자로 받아 새로운 파일 기술자를 리턴
새로 할당되는 파일 기술자는 현재 할당할 수 있는 파일 기술자 중 가장 작은 값
으로 자동 할당!
dup2(int oldfd, int newfd);
새로운 파일 기술자를 자동으로 할당해 새로운 파일 기술자를 지정할 수 있게 해 줌
파일 기술자 oldfd
를 newfd
로 복사
fcntl(int fd, int cmd, ... /* arg */);
//fd: 파일 기술자
//cmd: 명령
//arg: cmd에 따라 필요시 지정하는 인자들
파일 기술자가 가리키는 파일에 cmd로 지정한 명령을 수행
int remove(const char *pathname);
path 로 지정한 파일이나 디렉터리를 삭제한다
내부적으로 삭제 대상이 파일이면 unlink() 함수를 호출하고,
삭제 대상이 디렉터리면 rmdir()를 호출한다.
디렉터리는 비어있을 때만 삭제된다!
int fsync(int fd);
//fd: 디스크로 저장할 파일의 파일 기술자
메모리에 위치하고 있는 파일의 내용을 디스크로 보내 메모리와 디스크의 내용을 동기화
한다!
메모리의 내용이 디스크로 모두 기록되기 전에는 리턴하지 않음!
리눅스에서 파일을 읽고 쓰는 방법은 저수준(low-level) 파일 입출력과 고수준(high-level) 파일 입출력이 있다!
저수준 파일 입출력은 리눅스 커널의 시스템 호출을 이용해 파일 입출력을 수행함.
열린 파일을 참조할 때 파일 기술자 (file discriptor)을 사용
(장점 👍🏻)
시스템 호출을 이용하므로 파일에 빨리 접근할 수 있고,
바이트 단위로만 입출력을 수행하기 때문에 일반 파일 뿐만 아니라 특수 파일도 다룰 수 있지만,
(단점 👎🏻)
응용 프로그램을 작성할 때 바이트를 적당한 형태의 데이터로 변환 하는 여러 기능을 함수로 추가 구현해야한다는 단점이 있음.
고수준 파일 입출력은 c언어의 표준함수로 제공되며, 데이터를 바이트에 한정하지 않고 버퍼를 이용해 한꺼번에 읽기와 쓰기를 수행함.
열린 파일을 참조할 때 파일 포인터(file pointer)를 사용