Low-Level File I/O(System call)
- System call을 이용해서 파일 입출력 수행
- File descriptor 사용
- Byte 단위로 디스크에 입출력
- 특수 파일에 대한 입출력 가능
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char* pathname, int flags [, mode_t mode]);
- pathname (file path) : 열려는 파일의 경로
- flags (file state flags) : 여는 방법(access mode) 설정
- mode(file access permission) : 새로 생성(O_CREATE) 할 때만 유효
- return : file descriptor
File descriptor
- 열려 있는 파일을 구분하는 정수값
- Process별로 kernel이 관리
- 파일을 열 때 3번부터 순차적으로 할당됨(Process 당 최대 1024개)
- Default fds(수정 가능)
- 0 : stdin
- 1 : stdout
- 2 : stderr
File table
- 열린 파일을 관리하는 표
- kernel이 process 별로 유지
- 열린 파일에 대한 각종 정보 관리(access mode, file offset, pointer to files)
close
#include <unistd.h>
int close(int fd);
- fd : 닫으려는 file descriptor
- return
Error handling for system call
- System call은 실패 시 -1을 반환
- Error code는 errno에 저장 됨
- error.h에 선언되어 있음
- extern으로 직접 접근 가능
- perror(3) : errno에 저장된 내용을 출력
#include <stdio.h>
void perror(const char* str);
Example
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int fd;
mode_t mode;
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; //664
fd = open("hello.txt", O_CREAT | O_EXCL, mode);
if (fd == -1) {
perror("Error"); exit(1);
}
close(fd);
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int openFile(void) {
int fd = open("hello.txt", O_RDWR);
if (fd == -1) {
perror("File Open");
exit(1);
}
return fd;
}
int main(void) {
int fd = 0;
fd = openFile(); printf("fd = %d\n", fd); // fd = 3
close(fd);
close(0); // stdin의 fd가 삭제되어 다음 fd는 0이 됨
fd = openFile(); printf("fd = %d\n", fd); // fd = 0
close(fd);
return 0;
}
read
#include <unistd.h>
ssize_t read(int fd, void* buf, size_t count);
- fd : 읽으려는 파일의 file descriptor
- buf : 읽은 내용을 저장할 buffer의 시작 주소
- count : 읽을 byte의 수
- return : 실제로 읽은 byte의 수
write
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t count);
- fd : 기록하려는 파일의 file descriptor
- buf : 기록할 내용이 저장된 buffer의 시작 주소
- count : 기록할 byte의 수
- return : 실제로 기록한 byte의 수
File offset
- file operation을 적용할 위치
- 파일의 시작점부터 현재 위치까지의 byte 수
Example
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int rfd, wfd, n;
char buf[10] = { 0 };
rfd = open("hello.txt", O_RDONLY);
if (rfd == -1) {
perror("Open hello.txt");
exit(1);
}
wfd = open("hello.bak", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (wfd == -1) {
perror("Open hello.bak");
exit(1);
}
while ((n = read(rfd, (void*)buf, 6)) > 0)
if (write(wfd, (void*)buf, n) != n) perror("Write");
if (n == -1) perror("Read");
close(rfd);
close(wfd);
return 0;
}
File access methods
- Sequential access(순차 접근) : File을 record(or bytes) 단위로 순서대로 접근 ex. fgetc()
- Directed access(직접 접근) : 원하는 Block을 직접 접근 ex. lseek(), seek()
- Indexed access : Index를 참조하여, 원하는 block을 찾은 후 데이터에 접근
lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- fd : 대상 file descriptor
- offset : 이동시킬 byte의 수(양수 or 음수)
- whence : 기준 위치
- return : 이동 후 file offset
Example
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int fd, n;
off_t start, cur;
char buf[256];
fd = open("linux.txt", O_RDONLY);
if (fd == -1) {
perror("Open linux.txt");
exit(1);
}
start = lseek(fd, 0, SEEK_CUR);
n = read(fd, buf, 255);
buf[n] = '\0';
printf("Offset start=%d, Read Str=%s, n=%d\n", (int)start, buf, n);
cur = lseek(fd, 0, SEEK_CUR);
printf("Offset cur=%d\n", (int)cur);
start = lseek(fd, 6, SEEK_SET);
n = read(fd, buf, 255);
buf[n] = '\0';
printf("Offset start=%d, Read Str=%s", (int)start, buf);
close(fd);
return 0;
}
Page cache & write-back
- Page cache : 데이터를 일정 크기만큼 저장해서 전달, Disk 접근(비용이 큼) 시간 절약을 위해 kernel 내부적 기법
- Page write-back : Page cache에 변경 된 내용을 disk에 반영하는 것, 반영 시기는 kernel이 결정
fsync
#include <unistd.h>
int fsync(int fd);
- Page write-back을 강제로 수행
- fd : 대상 file descriptor
- return
dup / dup2
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
- oldfd : 복사하려는 file descriptor
- newfd : 새로운 fd 지정, dup()의 경우 할당 가능한 fd 중 가작 작은 값 할당
- return : oldfd를 복사한 새로운 fd
Example
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int fd, fd1;
fd = open("tmp.aaa", O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("Create tmp.aaa");
exit(1);
}
close(1);
fd1 = dup(fd);
printf("DUP FD=%d\n", fd1);
printf("Standard Output Redirection\n");
close(fd);
return 0;
}
fcntl
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, /* arg */);
- fd : 대상 file descriptor
- cmd : 수행할 명령
- arg : cmd에 필요한 인자들
- return : cmd에 따라 다름
Example
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
int fd, flags;
fd = open("linux.txt", O_RDWR);
if (fd == -1) {
perror("open");
exit(1);
}
if ((flags = fcntl(fd, F_GETFL)) == -1) {
perror("fcntl");
exit(1);
}
flags |= O_APPEND;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("fcntl");
exit(1);
}
if (write(fd, "hello", 5) != 5) perror("write");
close(fd);
return 0;
}