시스템 소프트웨어

파일 입출력 (File Input Output)

masimelo 2023. 2. 4. 16:36

파일

  • 파일은 컴퓨터 등의 기기에서 의미 있는 정보를 담는 논리적인 단위
  • 파일

일반 파일 (Ordinary File)

  • 일반 파일은 텍스트 파일, 이진 파일 등을 포함함
  • UNIX 계열의 운영체제에서 파일의 확장자는 특별한 의미가 없음
    • 파일의 확장자는 파일명의 일부분으로, 해당 파일의 형식을 짐작하는 용도로 쓰일 수 있도록 붙임.
    • 운영체제 또는 응용프로그램은 파일명 뒤에 확장자를 붙임으로써, 각 파일의 속성을 구분함.
      • 컴파일러 등의 일부 프로그램은 특정 확장자를 구분/요구하기도 함.
  • 파일의 입출력은 커널(kernel) 단계에서 버퍼링(buffering) 작업을 함.
    • 디스크의 여러 부분에 분리•저장되어 있는 데이터를 하나의 파일로 연결된 것처럼 함
  • 파일 내부에 저장되는 데이터의 구조는 운영체제에 의해 제어되는 것이 아닌, 처리하는 프로그램에 따라 제어됨.

디렉토리 (Directory)

  • UNIX 계열의 운영체제에서 디렉토리는 파일의 한 종류
  • 디렉토리는 파일의 목록을 저장하는 파일
  • 디렉토리는 파일을 포함하며, 하위 디렉토리 또한 포함할 수 있음.
  • 디렉토리는 일반 파일과는 다른 개념이지만, 읽을 때는 일반 파일과 동일하게 취급함.
  • 빈 디렉토리도 . 과 .. 두 파일을 포함하고 있음.
    • . 는 디렉토리 자신
    • .. 는 상위 디렉토리

파일 접근 프리미티브

파일 접근 프리미티브

  • 시스템 호출 (system call)
    • 응용 프로그램은 하드웨어에 직접 접근할 수 없음
    • 응용프로그램이 하드웨어에 접근할 수 있도록, 커널이 응용프로그램에 제공하는 함수 또는 인터페이스를 시스템 호출 또는 시스템콜이라고 함
  • UNIX 계열의 파일 접근 프리미티브
    • 입출력 장치에 접근 할 수 있도록 하는 UNIX 계열의 커널에서 제공하는 시스템 호출의 작은 집합
      함수 설명
      create() 빈 파일을 생성함
      open() 파일을 읽거나 쓰기 위해 열거나, 빈 파일을 생성함
      close() 열려 있는 파일을 닫음
      write() 파일에 내용을 씀
      read() 파일의 내용을 읽음
      lseek() 파일 내부에 특정 바이트로 이동함

파일 지시자(File descriptor)

  • UNIX계열의 파일 접근 프리미티브는 파일 지시자의 개념을 이용
  • 파일 지시자는 음이 아닌 정수
    • 단순히 숫자인 이유는 프로세스가 유지하고 있는 FD 테이블의 인덱스이기 때문
  • 프로세스가 실행 중인 파일에 접근하고자 하면, 커널은 해당 프로세스의 파일 디스크립터 숫자 중에 사용하지 않는 가장 작은 값을 할당함
  • 후에 프로세스가 열려있는 파일에 시스템 호출을 이용하여 접근할 때, 이 값을 이용해 해당 파일을 지칭할 수 있음

creat() 함수

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

int creat(const char *pathname, mode_t mode);
  • 설명
    • 빈 파일을 생성함
    • 유효한 파일이 있는 경우 파일을 열고, 파일 지시자를 반환함
  • 인자
    • pathname : 파일의 이름 또는 경로
    • mode : 파일의 접근 권한
      • 파일의 접근 권한을 나타내는 8진수
      • 해당 파일이 이미 존재하는 경우 무시됨
      • sys/stat.h 헤더파일에 정의된 다음의 플래그를 OR 비트 연산자와 함께 사용할 수 있음
  • 반환
    • 파일 지시자
    • 오류시 -1

open()

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

int opnen(const char *pathname,int flags, [mode_t mode]);
  • 설명
    • 파일을 읽거나 쓰기 위해 열거나, 빈 파일을 생성하고 파일 지시자를 반환함
  • 인자
    • pathname : 파일의 이름이나 경로
    • flags : 파일의 열기 모드
      • 파일의 열기 모드를 나타내는 상수
      • fcntl.h 헤더 파일에 정의된 플래그를 OR 비트 연산자와 함께 사용
    • mode : 파일의 접근 권한
      • flats가 O_CREAT인 경우에만 적용
  • 반환
    • 파일 지시자
    • 오류시 -1

close()

#include <unistd.h>

int close(int fd);
  • 설명
    • 열려있는 해당 파일을 닫고, 이를 시스템에 알림
    • 열려있는 모든 파일은 프로세스가 종료될 때, 자동으로 닫힘
  • 인자
    • fd : 파일 지시자
  • 반환
    • 성공 시 0
    • 오류시 -1
  • 수행 중인 한 프로세스가 동시에 개방할 수 있는 파일의 개수에 제한이 있기 때문에, close() 함수를 사용하는 것을 권장함

open()과 close()함수 예시

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644

int main() {
    int fd = 0;
    char *pathname = "./newfile.txt";
    fd = open(pathname, O_CREAT | O_RDWR, PERMS);

    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }
    printf("fd of %s is %d\n", pathname, fd);

    close(fd);

    return 0;
}

write()

#include <unistd.h>

ssize_t write(int fd, void *buffer,size_t nbyte);
  • 설명
    • 열려있는 해당 파일에 데이터를 지정한 크기만큼 씀
  • 인자
    • fd: 파일 지시자
    • buffer : 파일에 쓸 데이터의 메모리 공간에 대한 포인터
    • nbyte : 파일에 쓸 데이터의 크기
  • 반환
    • 성공 시 쓰여진 데이터의 크기
    • 오류 발생시 -1
  • 현재 파일의 위치에 데이터를 쓰고, 읽기-쓰기 포인터(offset)는 마지막으로 쓴 byte의 바로 뒤로 이동
  • 파일 open()시에
    • O_CREAT 또는 O_RDWD flag 사용
    • O_APPEND flag 사용

write() 함수 예시

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644

int main() {
    int fd = 0;
    char *pathname = "./mymsg.txt";
    fd = open(pathname, O_CREAT | O_TRUNC | O_RDWR, PERMS);
    char *msg = "Hello my name is Sohee\n";
    ssize_t wsize = 0;

    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }

    wsize = write(fd, (char *)msg, strlen(msg));
    if (wsize == -1) {
        perror("write() error");
        exit(-2);
    }

    printf("fd : %d, wsize : %ld\n", fd, wsize);

    close(fd);

    return 0;
}

read() 함수

#include <unistd.h>

ssize_t read(int fd, void * buffer, ssize_t nbyte);
  • 설명
    • 열려있는 해당 파일에서 데이터를 지정한 크기만큼 읽고, 이를 메모리 공간에 저장함
  • 인자
    • fd : 파일 지시자
    • buffer : 파일에서 읽은 데이터를 저장할 메모리 공간에 대한 포인터
    • nbyte : 파일에서 읽을 데이터의 크기
  • 반환
    • 성공시 읽은 데이터의 크기
    • 파일의 끝을 읽은 경우 0
    • 오류시 -1
  • 현재 파일의 위치에서 데이터를 읽고, 읽기-쓰기 포인터(offset)는 마지막으로 읽은 byte의 바로 뒤로 이동함.

read() 함수 예시

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644
#define MAX_SIZE 1024

int main() {
    int fd = 0;
    char *pathname = "./mymsg.txt";
    char buf[MAX_SIZE + 1] = {'\0'};
    ssize_t rsize = 0;
    ssize_t tsize = 0;

    fd = open(pathname, O_RDONLY, PERMS);
    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }

    do {
        memset(buf, '\0', MAX_SIZE + 1);
        rsize = read(fd, buf, MAX_SIZE);
        if (rsize == -1) {
            perror("read() error");
            exit(-2);
        }
        printf("%s", buf);
        tsize += rsize;
    } while (rsize > 0);

    printf("fd : %d, total rsize : %ld, tsize= %ld\n", fd, rsize, tsize);

    close(fd);

    return 0;
}

write()와 read() 함수 예시

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644
#define MAX_SIZE 32

struct _User {
    char name[MAX_SIZE + 1];
    int age;
};
typedef struct _User User;

int main() {
    int fd = 0;
    char *pathname = "./myname.txt";
    ssize_t wsize = 0;

        char username[MAX_NAME_SIZE + 1] = {'\0',};
        int userage = 0;

    User *user = (User *)malloc(sizeof(User));
    memset(user->name, '\0', MAX_SIZE + 1);
    user->age = 0;

    printf("Name : ");
    scanf("%[^\n]", username);
    strcpy(user->name, username);

    printf("Age : ");
    scanf("%d", &userage);
    user->age = userage;

    fd = open(pathname, O_CREAT | O_TRUNC | O_RDWR, PERMS);
    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }

    wsize = write(fd, (User *)user, sizeof(User));
    if (wsize == -1) {
        perror("write() error");
        exit(-2);
    }
    printf("fd: %d, wSize: %ld \n", fd, wsize);
    close(fd);
    free(user);
    return 0;
}

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644
#define MAX_SIZE 32

struct _User {
    char name[MAX_SIZE + 1];
    int age;
};
typedef struct _User User;

int main() {
    int fd = 0;
    char *pathname = "./myname.txt";
    ssize_t rsize = 0;
    ssize_t tsize = 0;

    User *user = (User *)malloc(sizeof(User));
    memset(user->name, '\0', MAX_SIZE + 1);
    user->age = 0;

    fd = open(pathname, O_RDONLY, PERMS);
    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }

    rsize = read(fd, (User *)user, sizeof(User));
    if (rsize == -1) {
        perror("read() error");
        exit(-2);
    }
    printf("fd: %d, rSize: %ld \n", fd, rsize);
    printf("User Name: %s, User Age: %d \n", user->name, user->age);
    close(fd);
    free(user);
    return 0;
}

lseek() 함수

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int flag);
  • 설명
    • 열려있는 해당 파일에서 읽기-쓰기 포인터(offset)의 위치를 반환
  • 인자
    • fd : 파일 지시자
    • offset : 읽기-쓰기 포인터의 이동 시작 위치에서 이동할 byte의 수
      • 0, 음 또는 양의 정수
        • 음수인 경우에 시작위치에서 이전으로 이동
        • 양수인 경우에 시작 위치에서 이후로 이동
    • flag : 읽기-쓰기 포인터의 이동 시작 위치
      • SEEK_SET : 파일의 시작
      • SEEK_CUR : 파일의 현재 offset 위치
      • SEEK_END : 파일의 끝
  • 반환
    • 성공시 읽기-쓰기 포인터의 변경된 위치
    • 오류시 -1

lseek() 함수 예시

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define PERMS 0644
#define MAX_SIZE 32

int main() {
    int fd = 0;
    char *pathname = "./mymsg.txt";
    ssize_t wsize = 0;
    char msg[MAX_SIZE + 1] = "NICE to meet you\n";

    fd = open(pathname, O_RDWR);
    if (fd == -1) {
        perror("open() error");
        exit(-1);
    }
    lseek(fd, (off_t)0, SEEK_END);

    wsize = write(fd, (char *)msg, strlen(msg));
    if (wsize == -1) {
        perror("write() error");
        exit(-2);
    }
    printf("fd: %d, wSize: %ld \n", fd, wsize);

    close(fd);
    return 0;
}

에러

errno 변수

  • 예외가 발생했을 때, 프로스램이 더 많은 정보를 얻을 수 있도록 UNIX계열은 오류코드를 저장한 정수형 변수를 제공
  • errno 변수는 전역적으로 접근이 가능한 정수형 변수임
  • perror() 서브루틴
    • 자신에게 전달된 문자열 인수와 errno변수의 현재 값에 연관된 추가 메시지 등으로 구성된 표준 오류를 화면에 출력
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int fd = 0;
    fd = open("./notexist", O_RDWR);
    if (fd == -1) {
        fprintf(stderr, "error %d\n", errno);
    }
    return 0;
}

 

open() 함수의 에러

close() 함수의 에러

write() 함수의 에러

read() 함수의 에러

'시스템 소프트웨어' 카테고리의 다른 글

연결 리스트 (Linked list)  (0) 2023.03.08
배열과 문자열 (Array and String)  (0) 2023.03.07
시그널 (Signal)  (0) 2023.03.07
파일과 디렉토리 (File and Directory)  (0) 2023.02.05
프로세스 (Process)  (0) 2023.02.05