open
헤더 : fcntl.h
open은 두 가지 형태의 시스템 콜을 가지고 있다. 형태는 아래와 같다. 참고로 rush 및 bsq에서는 첫 번째 시스템 콜을 이용할 것이다.
int open(const char *pathname, int flags);
int open(const char *pathname, int flag, mode_t mode);
pathname
파일의 경로와 이름으로, 절대경로의 파일명과 상대경로의 파일명 모두 허용된다. rush와 bsq에서는 간단하게 파일명으로 지정하고 읽고자 하는 파일을 Makefile과 같은 경로에 두면 된다.
flags
파일을 어떻게 열지를 결정하는 플래그로서, 읽기 전용으로 열 때는 O_RDONLY, 쓰기 전용으로 열 때는 O_WRONLY, 읽기 쓰기로 열고 싶을 때는 O_RDWR을 사용한다. rush와 bsq에서는 원본을 훼손해서는 안되므로 플래그를 O_RDONLY로 설정한다.
반환값
성공적으로 파일을 열게 되면 파일 디스크립터를 반환한다. 그렇지 않으면 음수를 반환한다. open이 제대로 되었는지 확인하기 위해서는 int형 변수를 하나 선언해준 후, open의 결과값을 할당하여 해당 결과값이 0 미만인지 확인하면 된다.
* 파일 디스크립터 : 파일 디스크립터(파일 서술자) fd는 특정 파일에 접근하기 위한 추상적인 키로, 프로그램에서 파일이나 장치에 접근하는 데 사용하는 정수값이다. 모든 프로세스가 갖추어야 하고, 갖추고 있는 파일 서술자는 다음과 같이 세 개가 있다.
정수값 0 | stdin | /dev/stdin |
정수값 1 | stdout | /dev/stdout |
정수값 2 | stderr |
사용법
#include <fcntl.h>
#define SUCCESS 1
#define FAILURE -1
int main(void)
{
int fd; // 리턴값을 저장
fd = open("hello.txt", O_RDONLY);
/*
제대로 open이 되었다면, fd에는 파일디스크립터 중 표준 입력에 해당하는 0값이 저장.
제대로 open되지 않았다면, fd에는 음수가 담김.
*/
if (fd < 0)
return (FAILURE);
else
return (SUCCESS);
}
read
헤더 : unistd.h
ssize_t read(int fd, void *buf, size_t nbytes);
fd (파일 디스크립터)
open에서 설명했던 fd와 동일하다. 정상적으로 파일이 열렸다면 음수가 아닌 fd가 반환될 것이므로 해당 fd를 넣어주면 된다. 대개 open을 통해 정상적으로 파일을 열었을 경우 fd는 3이 온다. 아까 설명했던 0과 1, 2가 아닌 3이 오는 것은 3번 레퍼런스를 통해 이해하자. 아니다. 3번 레퍼런스를 보면 상당히 복잡한 CS 얘기로 넘어가는데, 저어엉말 간단하게 표현하면 다음과 같다.
정상적으로 open한 파일의 위치를 가리키는 번호가 저장된다. 0과 1, 2는 시스템적으로 건들면 안 되는 플래그이므로 3부터 번호를 부여한다. 만약 하나의 C 파일에서 2개의 파일을 open했고, 두 파일 모두 정상적으로 open되었다면 처음 open한 파일의 fd는 3, 두 번째 open한 파일의 fd는 4다. 이렇게 순서대로 번호가 부여된다.
추가적으로, 동일한 파일을 두 번 open해서 서로 다른 fd1과 fd2에 할당한다면 그것 또한 3과 4로 별개의 번호가 부여된다.
buf
파일에서 읽어 들인 입력값을 저장할 버퍼를 의미한다. 어떤 자료형으로 읽어올지 몰라서 기본적으로는 void*로 매개변수로 결정되어 있다. 우리가 int형 자료를 입력받고자 한다면 buf를 int형 배열로 선언해준 후 매개변수로 넣어주면 되고, char형 자료를 입력받고자 한다면 buf를 char형 배열로 선언해준 후 매개변수로 넣어주면 된다.
nbytes
얼마만큼 읽어올지를 결정한다. size_t는 쉽게 생각해서 unsigned int라고 생각하면 되는데, 길이는 음수일 수 없으므로 크게 고려하지 말고 읽어 들일 글자 수만큼 매개변수로 주자.
반환값
정상적으로 파일에 대한 내용을 읽어온다면 읽은 바이트 수, 즉 nbytes의 값을 반환한다. 그렇지 않다면 0을 반환한다.
*ssize_t : ssize_t는 signed size type의 약자로, 보통의 32bit machine에서는 간단히 말해 int다. IO 함수의 반환값으로 많이 사용되는데, 그 이유는 해당 IO 함수의 실패를 알려주기 위해서이다. 레퍼런스 2번을 참고하면 해당 IO 함수의 실패를 알려주기 위해 size_t가 아닌 ssize_t이 필요한 이유를 설명해준다. 본 포스팅은 최대한 짧은 시간 내에 open과 read, close에 대한 개념을 익히는 데에 목적을 둔 것이므로 자세한 내용은 생략한다.
사용법
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define SUCCESS 1
#define FAILURE -1
int main(void)
{
int fd1;
int fd2;
int n;
char buf[100];
// 정상적으로 open되었는지 확인
fd1 = open("hello.txt", O_RDONLY);
if (fd1 < 0)
return (FAILURE);
fd2 = open("Hi.txt", ORDONLY);
if (fd2 < 0)
return (FAILURE);
printf("fd1 = %d, fd2 = %d\n", fd1, fd2);
// read
n = read(fd1, buf, 5);
printf("n = %d\n", n);
printf("buf = %s\n", buf);
// close
close(fd1);
close(fd2);
return (0);
}
/******** 결과값 **********
fd1 = 3, fd2 = 4
n = 5
buf = hello
*************************/
close
헤더 : unistd.h
int close(intfd)
fd (파일 디스크립터)
계속 나오는 그 fd다. 위 read에서 설명한 fd를 이해했다면 쉽다. open된 특정 파일을 나타내는 번호 fd를 의미하는 것이며, open하고 있는 그 file을 close하겠다는 것이다.
반환값
정상적으로 close 했다면 0을, 실패했다면 -1을 반환한다. 사실 정상적으로 close되지 않는 경우가 어떤 것인지 아직 파악하지 못했다. 구글링을 열심히 해봐도 close에 대한 얘기가 너무나도 없어서 나도 그냥 close(fd) 만 알고 있어야겠다고 생각한다.
레퍼런스
1. https://reakwon.tistory.com/39
2. https://lacti.github.io/2011/01/08/different-between-size-t-ssize-t/
'Dev > 42Seoul' 카테고리의 다른 글
구조체 정리 (0) | 2020.07.16 |
---|---|
헤더 파일 관련 정리 (0) | 2020.07.16 |