GNL 마치며

nhwang·2022년 3월 17일
0

-공부한 내용 :

file descriptor(fd) / offset
read
flag >>>> -D BUFFER_SIZE=xx
글로벌 vs Static

꼬리의 꼬리를 물면 블랙홀 흡수될 시간만큼 되고도 다 이해하지 못할 만큼 딥해서 적절한 중간 선에서 끊어야 했다.

fd : 파일과 대응되는 값. file의 고유 ID와 유사하다.
OS 내부적으로 파일 관리자가 있는데, 그것이 fd를 저장하고 있다. 이것은 스택처럼 날아가는 값이 아님.
offset : 파일의 어디까지 읽었는지, 어디까지 썼는지를 나타내는 지표. 진행한 [index]와 매우 유사.
fd와 마찬가지로 내부적 관리자가 fd와 함께 관리된다.

read(fd, buffer, toread) : 호출될때 마다. fd의 파일에 offset을 기준으로 toread만큼 buffer에 데이터를 담는다.
실패시 -1, 파일의 끝에 도달시 0, 정상적으로 읽은 경우 읽은 데이터의 갯수를 반환.
*널문자 \0을 담지 못함에 주의. 실질적으로 toread보다 반환값이 작으면 EOF라 봐도 무방하다.

-D flag : 매크로 정의와 같아서 뒤에 나오는 값을 그냥 컴파일시 선언.

Global vs Static : 스택과 달리 힙영역 처럼 둘다 살아있지만 Static은 선언된 함수가 호출될 때 마다 살아있다.
*스태틱은 초기화에 대한 공부없이 건드렸다가 시간을 많이 썼었다. (기본적으로NULL, 0등의 값이 선언과 동시에 들어간다.)

요구사항

효율성 - 하나의 라인 뽑을때 마다 전체 파일을 읽는것은 안됌. (이미 읽은 부분으로 되돌아가서 read호출 금지)
안정성 - 하나의 파일디스크립터에서 동작을 모두하기 전까지 다른 fd의 영향을 받아서는 안된다.
(bonus를 위해서는 여러 디스크립터를 받더라도 파일별로 이어서 계속 진행되어야 함)
*표준입출력 / 리다이렉션에도 정상 반응 해야함

어떻게든 편하게 구현하고 싶어서 최대한 잔머리를 썼는데... 이게 오히려 안좋은 결과를 불러와 3트까지 하고
정말 스트레스를 받았었던 과제. 뮬리넷에서는 leak하나도 허용치 않는데, 거기에서 내가 설계한 부분이 많이 갈렸다.

-설계 배경

보너스 내용에 스태틱을 한 개만 써라 라는 것을 보고 의문이 들었다.
"왜 스태틱을 써야하지??"
당연히 이전에 과제를 마친 사람들로부터 스태틱을 써야 한다는 말을 들은 적이 있기는 했지만
한 단계씩 설계/구현하며 내가 직접 필요성을 느끼기 전까지는 스태틱을 집어넣기 않기로 다짐했다.
본래의 반골기질이 발동했다고 볼 수 있다.

실제로 과제의 내용에서 컴파일시 정의한 버퍼사이즈가 몇이든 반응하게 해라라는 조건만 있지,
read의 세번째 인자를 반드시 컴파일때 받은 버퍼사이즈로 하라는 내용은 없었다.

그렇다는 말은 세번째 인자를 내 마음대로 해도 된다는 점에서 착안.
toread를 1로 주고 한칸씩만 따지면서 \n이나 \0 만나면 바로 리턴해버리도록 짰다.

*실제로 이 방식을 사용하면 Static이 1개가 아니라 0개를 가지고도 보너스파트까지 수행이 가능하다.
ㄴ> 여러칸을 한번에 읽고 개행 뒤의 문자열을 보관하는 방식이 필요가 없기 때문이다.
파일관리자가 어차피 알아서 offset관리를 해주니 1칸씩만 읽어버리면 개행뒤는 읽지 않고 바로 리턴.
*접은 이유 : 문자열이 10000개라면 리드가 1만번 호출되는데, 바람직한 방식은 아니라고 생각했다.

-1,2트 설계

리드를 1칸씩 하는 방식을 포기하면 결국 정해준 버퍼사이즈만큼 읽어오는 방식을 써야하는데,
이러면 결국 읽어들인 문자열이 개행뒤의 문자가 더 있는 형식이면 이 값을 잃어버리지 않고
가지고 있어야만 한다. 즉 남겨둘 데이터를 잡고 있을 무언가가 필요했다.
ㄴ>Static이 필요한 이유!

static변수 = fdlist
fdlist > fdnode > strlist > strnode의 형식으로 설계.

strnode에 문자 하나씩 하나씩 연결한 단 방향 리스트의 헤드를 strlist가 잡고,
strlist와 fd에 대한 정보를 fdnode가 잡고
fdnode들의 양방향 리스트의 헤드를 fdlist가 잡도록 설계.

leak을 반드시 잡는다. 이것만 잡으면 진짜 메모리에 대한 부분은 도사수준의 개발자가 될 수 있을것 같다라는 생각에
이 기간동안 정말 피에 gnl이 흐를정도로 자면서도 생각을했는지 꿈도 꾸고 스트레스를 많이 받았었다.

노드의 존재 이유가 없어지면 상단의 노드로 쭉 타고들어가면서
free를 하고 주소 전부 printf로 찍어봤지만 leak은 발견되지 않았다.

뮬리넷에서만 갈리고 valgrind, fsanitize, leaks, lldb 모든 방식을 동원했지만 leak추적이 되지않고
블랙홀은 점점 다가오니 반골 기질을 후회하였음.
**25줄 룰, 함수 10개이하를 충족하는 건 쉬웠지만, leak 잡는데에 시간을 너무 많이 썼는데도 해결되지 않아 3트로 돌아섰다.

-3트 설계
결국 다른 카뎃들의 설계와 비슷한 방식으로 fdnode의 헤드만 static으로 잡고 나머지는 문자열로 기억하게끔 처리.

strjoin,lcat,lcpy등 문자열을 다루는 함수를 많이써서
buf나 읽어왔던 save내부에 항상 필요없는 부분을 \0처리 해주는 부분을 신경을 많이썼고,
구조가 단순해진 만큼 leak은 완벽하게 잡았지만 함수당 25줄이내, 총 함수 10개 이하를 맞추는데에 오히려 챌린징했다.

마치며...
앞으로도 할 프로젝트가 많아서 아직은 잘 모르겠지만 피씬때, libft때만 놓고 봤을 때는 가장 힘들고 스트레스 받았던 과제였다.
다만 확실한건 메모리에 대한 공부가 무지막지하게 되서 이제 heap이니 stack이니 의식하지 않아도 어디로 들어갈지
변수의 수명이 어디까지인지 그냥 자동으로 보이는 수준이 되었다.

profile
42Seoul

0개의 댓글