[리눅스] open, close, write, read (+ file descriptors)

2020. 10. 5. 22:39W.IT/W.리눅스

 기본적으로 유닉스와 리눅스의 구조는 파일로 되어있다고 생각하시면 됩니다.

 여기서 코딩을 하며 파일이나 장치를 컨트롤하거나 액세스 하기 위한 가장 중요한 함수들은 아래 네 가지입니다.


      open, close, write, read


 이 함수들은 OS에서 직접 제공하며 그 자체로서 운영체제에 대한 인터페이스 역할을 한다고 생각하시면 됩니다.

 여기서 또 중요하게 아셔야 되는 부분은 fd 즉, file descriptors입니다. 한글로 번역한다면 파일 기술자라고 하는 이 값은 파일이나 장치를 열고 액세스 하기 위해 사용할 수 있는 유일한 정수 값입니다. 

 한마디로, 이 fd 값을 통해 열린 파일이나 장치 중 원하는 특정 파일이나 장치로 접근할 수 있습니다.

 

 open함수를 사용하지 않아도 기본적으로 0, 1, 2라는 fd 값을 가질 수 있습니다.

                0 : 표준 입력
                1 : 표준 출력
                2 : 표준 에러

 

open

 파일이나 장치에 액세스 경로를 만드는 과정이 성공적으로 수행되었다면 fd를 반환하고 실패하였다면 -1을 반환하는 함수입니다. 이때, fd 값은 다른 프로세스 혹은 모듈들과 공유를 할 수 없으며 만약 동시에 같은 파일을 연다면 서로 다른 fd 값을 받아 관리합니다.

 

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

int open(const char *path, int flags);
int open(const char *path, int flags, mode_t mode);

 

 open 함수의 파라미터 중 path는 열 파일이나 장치의 이름을 받으며 flags는 해당 파일을 열 때 행하는 행동을 말합니다.

 flags의 종류는 다양합니다. 이 값은 OR 연산을 통해 여러 개를 받을 수 있으며 필수적으로 넣어야 하는 값과 선택적으로 넣을 수 있는 값이 존재합니다.

 

먼저, 필수적으로 하나 이상이 들어가야 하는 값은 아래와 같습니다.

 

O_RDONLY  파일을 읽기 전용으로 열기 (read only)
O_WRONLY  파일을 쓰기 전용으로 열기 (write only)
O_RDWR  파일을 읽기와 쓰기용으로 열기 (read and write)
O_EXEC  파일을 실행 전용으로 열기 (execute)

 

선택적으로 OR 비트 연산을 통해 들어갈 수 있는 값들은 아래와 같습니다.

 

O_APPEND  파일의 끝에서부터 데이터 쓰기(추가)
O_TRUNC  파일이 존재할 경우 파일의 크기를 0으로 세팅(내용 삭제, 파일을 write 할 수 있는 경우에만)
O_CREAT  파일이 없다면 파일 생성 (mode 에서 주어진 권한으로 생성)
O_EXCL  두 프로그램이 동시에 파일을 생성하지 못하게 하나의 함수 호출만 수행 (O_CREATE와 함께 사용, 파일이 이미 존재하면 open 실패 처리)
O_SYNC  열 파일이 write 함수를 사용할 경우 실제 물리적인 입출력이 끝날 때 까지 대기
O_DIRECTORY  열 파일이 디렉토리가 아니면 에러 처리

 

앞서 말한 flags 값들 보다 더 다양한 값들이 존재합니다. 그 값들이 궁금하시면 open man page에 들어가 보시거나 구글링을 통해 검색하시면 됩니다.

 

마지막으로 mode는 flags 값이 O_CREAT 일 경우, 파일을 생성할 때 사용되는 권한 값입니다. 이 값들도 OR 비트 연산을 통해 여러 개의 값을 넣을 수 있습니다.

 

S_IRUSR  소유자에게 읽기 권한을 준다.
S_IWUSR  소유자에게 쓰기 권한을 준다.
S_IXUSR  소유자에게 실행 권한을 준다.
S_IRGRP  소유 그룹에게 읽기 권한을 준다.
S_IWGRP  소유 그룹에게 쓰기 권한을 준다.
S_IXGRP  소유 그룹에게 실행 권한을 준다.
S_IROTH  그 외 사용자에게 읽기 권한을 준다.
S_IWOTH  그 외 사용자에게 쓰기 권한을 준다.
S_IXOTH  그 외 사용자에게 실행 권한을 준다.

 

open 함수 사용을 예를 든다면 아래처럼 들 수 있습니다.

 

open("won.tistory", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

 

해당 코드는 won.tistory 파일을 읽기와 쓰기용으로 열고 만약 파일이 없다면 생성하되 소유자에겐 읽기와 쓰기 권한을 소유 그룹과 그 외 사용자에겐 읽기 권한을 주겠다는 것입니다.

 

close

 fd 값과 열린 파일이나 장치 사이의 연결을 종료하기 위해 이 함수를 호출합니다.

 

#include <unistd.h>

int close(int fd);

 

 성공적으로 수행되면 0을 실패하였으면 -1을 반환합니다. 

 

 

write

 fd값을 통해 파일로 접근하여 데이터를 쓰기 위해 이 함수를 호출합니다.

 

#include <unistd.h>

size_t write(int fd, const void *buff, size_t size);

 

 파일로 접근하여 (두 번째 파라미터로 넣은) buff의 데이터를 (세 번째 파라미터인) size 만큼 읽어 파일에 씁니다.

 

 성공적으로 수행하였다면 파일의 쓰인 바이트 수(size)를 반환해주며 실패를 하였다면 -1을 반환합니다.

 이때, 반환 값이 0이면 파일의 끝(EOF)에 도달했다는 뜻입니다.

 

write 함수 사용을 간단한 예로 들면 아래와 같습니다.

 

int rt = 0;

rt = write(10, "nice to meet you", 4);

 

 해당 코드는 10인 fd 값을 통해 해당 파일로 접근하여 "nice to meet you"라는 문장 중 4바이트 "nice" 만을 쓰라는 의미입니다.

 해당 write의 반환 값 rt가 4이면 성공적으로 write 함수를 수행했다는 의미이고 -1 이면 write 도중 에러가 났다는 의미이며 0이라면 파일의 끝(EOF)에 도달했다는 의미입니다.

 

read

 이 함수는 write와는 반대로 fd와 연결된 파일로부터 데이터를 읽어오기 위해 호출합니다.

 

#include <unistd.h>

size_t read(int fd, void *buff, size_t size);

 

 write 함수와 반대로 생각하시면 됩니다. 파일로 접근하여 (두 번째 파라미터로 넣은) buff의 데이터에 (세 번째 파라미터인) size 만큼 파일에서 읽어 저장시킵니다.

 

 성공적으로 수행했다면 읽어온 바이트 수(size)를 반환해주며 실패하였다면 -1을, 0을 반환한다면 읽을 것이 없는 파일의 끝(EOF)에 도달했다는 뜻입니다.

 

read 함수 사용을 간단한 예로 들면 아래와 같습니다.

 

char buff[4 + 1] = {0,};
int  rt = 0;

rt = read(10, buff, 4);

 

 해당 코드는 10인 fd 값을 통해 해당 파일로 접근하여 파일을 4바이트만큼 읽어와 buff에 저장하겠다는 의미입니다.

 해당 read의 반환 값 rt가 4이면 성공적으로 read 함수를 수행했다는 의미이고 -1 이면 read 도중 에러가 났다는 의미이며 0이라면 파일의 끝(EOF)에 도달했다는 의미입니다.

 


끝으로 제가 설명드린 함수들이 에러가 난다면 errno를 통해 확인이 가능합니다.

예를 든다면 아래와 같이 errno를 사용하시면 됩니다.

 

 

int rt = 0;

rt = open("won.tistory", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(rt == -1)
{
    printf("errno(%d) \n",errno);
    return 1;
}

 

errno에 대한 정보는 구글링을 통해 검색하시면 몇 번이 무슨 에러인지 설명되어 있습니다.


※ 제 글에서 부족하거나 잘못된 부분이 있다면 댓글로 남겨주시길 부탁드립니다.

※ 제 글은 제가 코딩하면서 지속해서 확인하기 위한 히스토리 성 정보들이자 회사 생활 중 실제 프로그램을 코딩하며 중요하거나 필요했던 정보들을 공유하기 위해 적은 글입니다.