GetNextLine 과제 시작

정하나둘·2022년 11월 28일
0

GetNextLine

목록 보기
1/1

0. 선행지식

- 1. 파일 디스크럽터 (fd)

  • 운영체제가 만든 파일 또는 소켓의 지칭을 편히 하기 위해 부여된 숫자이다.
  • 기본적으로 파일 디스크럽터는 정수형으로 넘버링되고 0,1,2는 이미 할당이 되어있어 3부터 부여한다.
    0 : 표준입력 (Standard Input)
    1 : 표준출력 (Standard Output)
    2 : 표준에러 (Standard Error)

- 2. read() 함수

size_t read (int fd, void *buf, size_t bytes)

  • bytes 수 만큼 fd를 읽어 buf에 저장한다.
  • 리턴값 : 읽어 온 바이트 수, 실패 시 -1.
    - 파일을 끝까지 읽었으면, 다음 번에는 더 이상 읽을 바이트가 없기 때문에 0을 반환.

- 3. gcc -d 플래그

  • 외부에서 #define을 정의한다
  • 이 문제에서 컴파일은 다음과 같이 진행된다.

    $ gcc -Wall -Wextra -Werror -D BUFFER_SIZE=32 get_next_line.c get_next_line_utils.c

즉, BUFFER_SIZE를 컴파일 할 때 정하게 된다.

- 4. static 변수

  • 데이터 영역에 저장되어 프로그램 종료시까지 남아있기 때문에, 다음 line을 읽을 시작 주소를 계속 저장할 수 있도록 backup 버퍼를 static 변수로 선언해야 한다.

2. 목표

  • GNL 함수를 loop 안에서 호출하면 fd의 텍스트를 EOF가 올 때 까지 한 번에 한 줄씩 읽을 수 있다.
  • GNL 함수를 처음 호출했을 때 파일을 끝까지 읽었다 하더라도, 두 번째 호출했을 때는 두 번째 line부터 시작해야한다.
  • file로부터, redirection으로부터, stdin으로부터 읽었을 때 함수가 제대로 동작해야 한다.
  • BUFFER_SIZE가 1일 때도, 9999일 때도, 10000000(1000만)일 때도 함수가 제대로 동작해야 한다.

3. 아이디어

  1. 파일을 read 할 임시 버퍼를 만든다.(buf)
  2. read한 버퍼를 백업할 static 버퍼를 만든다.(backup)
  3. return 해줄 line 문자열을 선언한다.
  4. buf에 BUFFER_SIZE+1(널 값)만큼 malloc으로 할당해주고
  5. 함수(make_line)를 만들어 line에 값을 넣어준다. (매개변수 fd, backup, buf)
    • 해당 함수 내부에서 개행 문자를 찾으면 해당 backup데이터가 리턴되고 그 값이 line에 들어간다.
  6. free(buf);
    buf = NULL;로 버퍼 메모리를 해제하고 포인터가 가르키는 값을 NULL로 초기화 해준다.
  7. 지금 line과 backup 값은 개행을 포함하고 그 다음 문자들도 일부분 가지고 있으므로 line은 개행까지만 가지고 있어야 하고 backup은 개행 다음 부분부터 끝까지를 가지고 있어야 하므로 새로운 함수(make_backup(line))을 이용해 backup에 값을 넣고 포인터 매개변수로 받은 line에 값을 변경해준다.
  8. line값을 리턴해주면 개행을 포함한 한 줄이 출력되고 backup은 줄바꿈 이후의 데이터 값을 가지고 있다.

static 변수를 사용해야 하는 이유

버퍼 사이즈가 10이라고 가정하고

abcd
1234

를 읽는다고 생각하면 한 번 부르면 abcd를, 한 번 더 부르면 1234를 *line에 넣어줘야 한다.
그런데 버퍼 사이즈가 10이라 한 번에 파일을 끝까지 다 읽어버리기 때문에 처음 함수가 호출 됐을 때 나머지 1234를 따로 저장해둬야 한다. 그래서 따로 저장할 떄 함수가 끝나도 날아가지 않게 하려고 static(정적)변수를 쓰게 된다.

4. 주의해야 할 부분들

- 1. BUFFER_SIZE가 0보다 작거나, 같거나 fd가 0보다 작은 경우

  • return (NULL);로 NULL값을 리턴해주어야 한다.

- 2. read함수가 0이나 -1을 반환하는 경우

파일을 끝까지 다 읽어서 0을 반환 || 빈 파일을 읽어 0을 반환
- line = 0 으로 메모리 해제가 된 상황. 남은 backup을 line에 넣어준다. main에서 다른 파일을 read할 수도 있으니 free(backup) 후 backup = NULL;을 해준다.
-> 해당 설정을 해줘야 main함수에서 getnextline(int fd)함수를 다시 사용할 때 backup이 메모리 오류를 일으키지 않는다.

- 3. txt파일에 개행이 하나도 없거나 개행 다음에 바로 '\0'이 있는 경우

  • make_backup 함수에서
int i = 0;
while (line[i] && line[i] != '\n')
	i++;
if (line[i] == '\0' || line[i + 1] == '\0')
	return (NULL);

해당 부분에서 line[i] == '\0'은 txt파일 내에 개행이 없을 때의 예외처리이고 line[i + 1] == '\0' 은 개행 다음에 '\0'이 있을 경우의 예외처리이다. 해당 조건들은 line과 backup을 편집할 편집할 필요가 없으므로 NULL을 리턴해주면 된다.

- 4. 이중포인터 backup의 세로 축 크기를 몇으로 정적할당 할 것인가?

backup의 세로 축은 fd가 들어간다. *backup이 가리키는 값을 fd에 따라서 따로 관리하고 싶었다.(?) 한 파일이 끝나기 전에 gnl로 다른 파일을 호출할 수도 있으니까.

파일 디스크럽터는 0~OPEN_MAX까지의 값을 가질 수 있으며, OPEN_MAX 값은 플랫폼에 따라 다르다고 한다.

OPEN_MAX 값은 헤더파일에 과제를 제출하기 전 헤더파일에 설정해 주는데, 임의로 값을 넣게되면 재정의 되었다고 오류가 발생한다. OPEN_MAX가 정의되어 있는 디렉토리는 POSIX 디렉토리이며 POSIX는 MAC OS가 따르는 일부 규정이다. 흔히 OPEN_MAX는 OS마다 값이 달라 사용하지 말고 연결 리스트로 구현하라고 하지만 본인은 42서울에서 공부할 때 MAC OS를 통해 개발을 하고 POSIX에 해당 내용이 규정이 되어있어 굳이 다른 OS의 OPEN_MAX값을 고려해야한다고 생각하지 않는다고 디펜스를 했다.(나름 설득력이 있었던 모양이다)

- 5. BUFFER_SIZE의 최대 크기는?

100만까지는 되는데 1000만 부터는 Segmentation fault가 뜬다.
-> 자동변수는 stack 영역에 저장되는데, 보통 스택 사이즈가 윈도우는 1메가, 리눅스는 8메가로 설정되어있다. 만약에 char buf[BUFFER_SIZE + 1]; 라고 선언하고 여기에 스택 사이즈보다 큰 수를 받으면 스택 오버플로우가 생길 수 있다.

스택 오버플로우 피하기

  1. 정적 변수로 선언하여 데이터 영역에 잡는다.
  2. 전역 변수로 선언하여 데이터 영역에 잡는다.
  3. malloc 등을 사용, 동적 할당하여 힙 영역에 잡는다.
  4. 시스템 설정 스택 영역 사이즈를 늘린다.
profile
내가 다시 보려고 만드는 42서울 본과정 블로그

0개의 댓글