Fascination
article thumbnail

Chapter 05. 파일 입출력

리눅스 프로그래밍 원리와 실제 - 창병모 교수님


5.1 시스템 호출

1) 컴퓨터 시스템 구조

- 하드웨어는 CPU, 메모리, 저장장치, 주변장치 등으로 구성

- 운영체제는 하드웨어를 운영 관리하는 시스템 소프트웨어이며 유닉스/리눅스의 경우 커널(kernel)이 하드웨어를 운영 관리

- 운영체제 서비스

  • 프로세스 관리
  • 파일 관리
  • 메모리 관리
  • 통신 관리
  • 주변 장치 관리

 

2) 시스템 호출

- 시스템 호출(system call): 운영 체제가 제공하는 서비스에 대한 프로그래밍 인터페이스

- 시스템 호출은 커널에 서비스를 요청하기 위한 프로그래밍 인터페이스로 응용 프로그램은 시스템 호출을 통해서 커널에 서비스를 요청 가능

 

3) 시스템 호출 구현

 

주요 자원 시스템 호출
파일 open(), close(), read(), write(), dup(), lseek() 등
프로세스 fork(), exec(), exit(), wait(), getpid(), getppid() 등
메모리* malloc(), calloc(), free() 등
시그널 signal(), alarm(), kill(), sleep() 등
프로세스 간 통신 pipe(), socket() 등

 

 

5.2 파일

1) 파일 열기와 파일 디스크립터

- 파일

  • 연속된 바이트의 나열
  • 특별한 다른 포맷을 정하지 않음
  • 디스크 파일뿐만 아니라 외부 장치에 대한 인터페이스

- 파일 디스크립터: 열린 파일을 나타내는 번호

- open() 시스템 호출

int open(const char *path, int oflag, [mode_t mode]);
  • path가 나타내는 파일을 열음
  • 성공하면 파일 디스크립터 반환, 실패하면 -1 반환

- oflag

  • O_RDONLY: 읽기 모드, read() 호출은 사용 가능
  • O_WRONLY: 쓰기 모드, write() 호출은 사용 가능
  • O_RDWR: 읽기/쓰기 모드, read(), write() 호출 사용 가능
  • O_APPEND: 데이터를 쓰면 파일 끝에 첨부됨
  • O_CREAT: 해당 파일이 없는 경우에 생성하며 mode는 생성할 파일의 사용권한을 나타냄
  • O_TRUNC: 파일이 이미 있는 경우 내용을 지움
  • O_EXCL: O_CREAT와 함께 사용되며 해당 파일이 이미 있으면 오류
  • O_NONBLOCK: 넌 블로킹 모드로 입출력하도록 함
  • O_SYNC: write() 시스템 호출을 하면 디스크에 물리적으로 쓴 후 반환

 

2) 파일 생성과  파일 닫기

- creat() 시스템 호출

int creat(const char *path, mode_t mode);
  • path가 나타내는 파일을 생성하고 쓰기 전용으로 열음
  • 생성된 파일의 사용권한은 modefh wjdgka
  • 기존 파일이 있는 경우에는 그 내용을 삭제하고 열음
  • 다음 시스템 호출과 동일 open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);

- close() 시스템 호출

int close(int fd);
  • fd가 나타내는 파일을 닫음
  • 성공하면 0, 실패하면 -1을 반환

 

3) 파일 읽기 read()

ssize_t read(int fd, void *buf, size_t nbytes);

- fd가 나타내는 파일에서 nbytes 만큼의 데이터를 읽고 읽은 데이터는 buf에 저장

- 성공하면 읽은 바이트 수, 파일의 끝을 만나면 0, 실패하면 -1을 반환

 

4) 파일 쓰기 write()

ssize_t write(int fd, void *buf, size_t nbytes);

- buf에 있는 nbytes 만큼의 데이터를 fd가 나타내는 파일에 씀

- 성공하면 실제 쓰여진 데이터의 바이트 수를 반환, 실패하면 -1 반환

 

5) 파일에 블록 쓰기

- write() 시스템에서 유의할 점은 이 시스템 호출을 이용해 어떤 데이터든지 파일에 저장할 수 있다는 것

- 두 번째 매개변수 buf는 어떤 타입의 데이터이든지 상관없이 데이터를 저장하고 있는 곳의 시작 주소(포인터)를 받을 수 있음

- 세 번째 매개변수 nbytes는 그 데이터의 크기를 받음

- 정수형 변수에 저장된 값을 그대로 파일에 저장할 수 있고, 구조체 변수 저장도 가능

int i;
write(fd, &i, sizeof(int));

struct student record;
write(fd, &record, sizeof(record));

Q. 파일에 연속된 바이트 형태로 저장된 변수의 값을 어떻게 그대로 복원 가능?

A. read() 시스템 호출을 이용해 파일에 저장된 데이터를 연속된 바이트 형태로 읽어서 원래 자료형 변수에 순서대로 저장하면 원래 데이터 그대로 복원 가능

 

6) 파일 디스크립터 복제 dup()

int dup(int oldfd);

- oldfd에 대한 복제본인 새로운 파일 디스크립터를 생성하여 반환

- 실패하면 -1 반환

int dup2(int oldfd, int newfd);

- oldfd를 newfd에 복제하고 복제된 새로운 파일 디스크립터 반환

- 실패하면 -1 반환

 

 

5.3 임의 접근

1) 임의의 접근과 파일 위치 포인터

- 파일 위치 포인터: 파일 내에 읽거나 쓸 위치인 현재 파일 위치를 가리킴

- 임의 접근 파일: 파일 내의 원하는 지점으로 바로 이동하여 그곳에서 데이터를 읽거나 쓸 수 있음

 

2) 임의 접근 lseek()

- lseek()

off_t lseek(int fd, off_t offset, int whence);
  • 임의의 위치로 파일 위치 포인터를 이동시킬 수 있음
  • 이동에 성공하면 현재 위치를 반환하고 실패하면 -1을 반환

- 파일 위치 이동

lseek(fd, 0L, SEEK_SET);	// 파일 시작으로 이동
lseek(fd, 100L, SEEK_SET);	// 파일 시작에서 100바이트 위치로 이동
lseek(fd, 0L, SEEK_END);	// 파일 끝에서 이동

- 레코드 단위로 이동

lseek(fd, n*sizeof(record), SEEK_SET);	// n+1 번째 레코드 시작 위치로 이동
lseek(fd, sizeof(record), SEEK_CUR);	// 다음 레코드 시작 위치로 이동
lseek(fd, -sizeof(record), SEEK_CUR);	// 전 레코드 시작 위치로 이동

- 파일 끝 이후로 이동

lseek(fd, sizeof(record), SEEK_END);	// 파일 끝에서 한 레코드 다음 위치로 이동

 

3) 임의 접근을 이용한 레코드 수정

(1) 파일로부터 해당 레코드 읽기

(2) 레코드 수정

(3) 수정된 레코드 다시 파일 내의 원래 위치에 쓰기

- 레코드를 읽으면 현재 파일 위치가 자동으로 그 만큼 이동한다는 점에 주의

 

 

profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!