file descriptor, open(), read() - C

Kimmyki·2022년 8월 28일
0

C언어 비벼보기

목록 보기
1/1

요새 공부를 하고 있건만...
어째 제대로 집중도 못 하고, 기껏 공부를 열심히 해도 머릿속에는 남는 게 없었어요.
과제 끝내기만 하면 머리가 깡통이 되어서요...
정보를 전하기 위해 글을 쓰다 보면 저한테 남는 게 많을 것 같아서 글을 쓰기 시작했어요.

처음 게시글을 적게 되었는데 찬찬히 적어볼게요.
이런 글 쓰는 건 첨이라 틀린 부분이나 모호한 부분이 존재할 거에요.

그런 부분은 가감 없이 지적해주셔요.
진짜루요.
꼭이요.

저 뿐 아니라 다른 사람들도 잘못 알게 되는 거니까 사람 여럿 살리시는 겁니다. ㅎㅎ;;


텍스트 파일을 읽어오게 하고 싶다고 가정해봐요
컴퓨터한테 파일을 '읽어오게 시키기' 위해서는 뭐가 필요할까요?

잘 모르겠으면 이렇게 생각해봐요.
시각 장애인에게 "내 휴대폰 좀 건네 줄래?"라고 말한다면 뭐라고 대답할까요?

분명,
"니 휴대폰이 어딨는데?"
하고 대답할 거에요

컴퓨터도 '어디서' 무엇을 읽어야 하는지 알고 싶을 거에요
'어디서' 무엇을 읽어야 하는지 알려 줘야해요

컴퓨터에게 위치를 알려주기 위해서 저희는 open() 함수를 사용할 거에요.
read()함수로 text파일을 읽어서 버퍼에 저장할 거구요.


open 함수의 원형은 아래와 같아요:

int open (const char *FILENAME, int FLAGS[, mode_t MODE])

-매개변수

[FILENAME]:
파일명이에요. 근데 경로를 알려줘야 해요. "./text.txt"처럼 상대 경로로 알려줘도 좋아요. (절대 경로로도 가능한지 아직 테스트 안 해봐서 모르겠네요.)

[FLAGS]:
파일에 대한 열기 옵션이에요.
아래와 같은 플래그가 있어요.
-O_RDONLY : 읽기 전용
-O_WRONLY : 쓰기 전용
-O_RDWR : 읽고 쓰기
더 많은 플래그가 존재하지만, 넘어갈게요.

-반환 값

이 친구는 int값을 반환해요.
이 값의 정체가 '어디서'에 해당해요.

read함수의 원형을 보면 ssize_t read (int fd, void *buf, size_t len);
fd가 있죠?
이 반환 값이 fd에 들어가요.

이제 이 fd가 대체 무엇이길래? 하고 생각하실 거에요.
함수 설명은 여기서 마치고 fd를 설명할게요.


File Descriptor

파일 디스크립터는 '프로세스' 내에서 열린 파일을 구별하기 위한 0에서부터 시작되는 정수예요.
(프로세스는 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램을 의미해요. 프로그램 자체와 프로그램의 상태가 메모리 상에서 실행되는 작업 단위를 지칭해요.)

(A file descriptor is a number that uniquely identifies an open file in a computer's operating system. It describes a data resource, and how that resource may be accessed. - 파일 디스크립터는 컴퓨터의 OS에서 열린 파일을 고유하게 식별하는 숫자입니다. fd는 데이터 자원을 묘사하며, 어떻게 그 자원에 접근할 수 있는지 묘사합니다. -Computer Hope)

프로그램이 파일-또는 네트워크 소켓같은 데이터 자원-을 열라고 요청하면 커널은 다음을 시행해요:

  1. 접근 권한을 부여한다.
  2. global file table에 입구를 생성한다.
  3. 그 입구의 위치가 담긴 소프트웨어를 제공한다.

( - file table에 관한 설명 - The File Table contains a complete list of source files with their various attributes, ordered by a unique, non-localized, identifier. Files can be stored on the source media as individual files or compressed within a cabinet file. For more information, see Using Cabinets and Compressed Sources. - 파일 테이블은 완성된 소스 파일의 리스트를 담고 있습니다. 다양한 속성이 고유하고 현지화 되지 않은 식별자로요. 이하 생략...기술 문서는 번역이 참 어렵네요. 보면서도 무슨 뜻인지 몰라서 긴가 민가 합니다. 정확한 뜻 아시는 분은 꼭! 지적해주세요!)

fd는 음수가 아닌 정수로 구별되어요.(0에서 부터 OPEN_MAX까지)
이 중 0, 1, 2는 정해져 있어요.
표준입력(Standard Input), 표준 출력(Standard Output), 표준에러(Standard Error)이며 이들에게 각각 0, 1, 2라는 정수가 할당되어요.
write()함수의 fd 부분은 이를 위한 거죠.

모든 열린 파일에는 하나 이상의 파일 디스크립터가 존재해요.

아, OPEN_MAX값이 어느 정도인지 보시고 싶으신 분들을 위해 알아보는 코드를 적을게요.

#include <unistd.h>
#include <stdio.h>

int	main(void){
	int	max;

	max = sysconf(_SC_OPEN_MAX);
	printf("%d\n", max);
	return(0);
}

실행 결과로 알 수 있어요.
제 맥에서는 2560으로 나오네요!

(위 짤 참고)
프로세스가 성공적으로 파일을 열어달라는 요청을 하면, 커널은 커널의 글로벌 파일 테이블의 입구를 가리키는 파일 디스크립터를 건네줘요.
파일 테이블의 입구는 inode, byte offset, 접근 권한 등의 정보를 포함하고 있어요.

접근 권한은 위에서 설명했어요.
저희가 알아야 할 부분은 byte offset이에요.
이제 이 과제를 할 때 필수적인 read() 함수를 설명할 때 같이 설명할게요.


아래는 read함수 원형이에요.

ssize_t read (int fd, void *buf, size_t len)

[int fd]:
읽어올 파일의 파일 디스크립터에요.
추가 설명할 건 없겠네요.

[void *buf]:
파일을 읽어오고 그 값을 저장할 버퍼에요.

[size_t len]:
몇 바이트 만큼 읽어올지 정하는 정수에요.

[반환값]:
얼만큼의 바이트를 읽어왔는지 나타내요.
-1을 반환하면 읽어오는 데에 실패했다는 의미에요.
0을 반환하면 오프셋이 끝에 도달해서 더 읽어올 거리가 없단 것을 나타내요.

read 함수는 fd가 가리키는 파일의 오프셋에서 데이터를 len 만큼 읽어와서 buf에 저장해요.

아, 오프셋이 무엇이냐고요?
이 사진 기억하세요?

여기서 0, 0, 12, 8 이라고 써 있잖아요?
배열이나 자료 구조 오브젝트 내의 오프셋(offset)은 일반적으로 동일 오브젝트 안에서 오브젝트 처음부터 주어진 요소나 지점까지의 변위차를 나타내는 정수형이라고 한대요.

무슨 뜻인지 이해하기 어렵죠?

간단한 예를 들면 문자 A의 배열이 abcdef를 포함한다면 'c' 문자는 A 시작점에서 2의 오프셋을 지녀요.
배열 같은 곳에서 위치를 나타내는 정수라고 보면 되겠네요.

여기서 궁금한 점이 있을 수 있어요.
아니, 그럼 내가 다 읽어오고 오프셋을 변경하고 싶으면 어떡해?
그런 기능은 없는 것 같은데?

그건 걱정하지 않아도 되어요.
읽어온 바이트 만큼 오프셋이 이동하거든요.

이제 위 함수들이 어떻게 사용되는지 보여드릴게요.

#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    char    *buff;
    int     fd; 
    int     check;
   
    buff = (char *)malloc(sizeof(char) * 10);
    // read에 사용될 버퍼 생성.
    fd = open("./text.txt", O_RDONLY);
    // text.txt의 file descriptor 생성.
    check = read(fd, buff, 10);
    // read의 리턴값 check로 함수가 제대로 구동했는지 체크함
    // read로 버퍼에 데이터를 복사해옴
    if (check == 0)
        printf("nothing to read");
    else if (check == -1) 
        printf("fail to read");
    write(1, buff, 10);
    return (0);
}

이제 대충 감 잡으셨을 거라 생각해요.
긴 글 읽어주시느라 고생 많으셨습니다!

profile
숟가락 살인마처럼 무지성 꼴박을 지향 중입니다.

0개의 댓글