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
    • 0 : success
    • -1 : error

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의 수
    • 0 : EOF
    • -1 : 에러

write

#include <unistd.h>

ssize_t write(int fd, const void* buf, size_t count);
  • fd : 기록하려는 파일의 file descriptor
  • buf : 기록할 내용이 저장된 buffer의 시작 주소
  • count : 기록할 byte의 수
  • return : 실제로 기록한 byte의 수
    • -1 : 에러

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
    • -1 : 에러

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
    • 0 : success
    • -1 : error

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
    • -1 : error

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;
}

'💻 Computer Science > System' 카테고리의 다른 글

[System] Files in Unix/Linux  (1) 2024.01.24
[System] High-Level File I/O  (1) 2024.01.24
[System] Thread  (0) 2024.01.04
[System] Event  (0) 2024.01.03
[System] WaitForSingleObject  (0) 2024.01.03