Libft 과제를 끝내고 세상에나 3달 만에 다음 과제를 끝내다니…

과제 개요

서브젝트만 보면 간단하다. 파일이나 표준 입력으로부터 읽어들인 것을 개행 단위로 반환해주면 된다.

어려웠던 점

내가 이 과제를 하면서 열 받았던 포인트가 한 가지 있는데, 바로 메모리 관리였다. 두 가지의 에러 메시지가 많이 나왔는데, 하나는 메모리 누수고 다른 하나는 이미 할당 해제된 메모리를 왜 또 해제하려 하냐는 것이었다.

메모리 누수

메모리 누수의 경우, ft_strjoin(strjoin 구현 함수)를 사용할 때 발생했다. 예를 들어

char  *backup;
char  *buffer;

backup = ft_strjoin(backup, buffer);

와 같은 상황이 있다고 했을 때, 원래 있던 backup의 주소값이 길을 잃어버리는(?) 상황이 발생하며 메모리 누수가 생긴다. 이를

char  *backup;
char  *temp_backup;
char  *buffer;

temp_backup = ft_strjoin(backup, buffer);
free(backup);
backup = temp_backup;

위와 같은 방식으로 기존에 있던 backup의 주소값을 할당 해제하고, 그 다음에 ft_strjoin으로 인해 새로 생성된 주소값을 backuptemp_backup을 이용하여 다시 할당하는 방식을 취했다.
근데 재귀의 방식을 사용하면 저렇게 번거로운 mallocfree를 쓰지 않아도 된다고 첫 번째 평가자 분께서 피드백을 주셨다.

이미 할당 해제된 메모리에 free를 할 수 없습니다.

에러 메시지가 정확히 무슨 문구였는지는 기억이 안 나는데, 대충 저런 뉘앙스였던 걸로 기억한다.
말 그대로 할당된 적이 없는 메모리에 free를 하려 할 때 발생하는 에러다. 이거 같은 경우, 반복문 내에서 발생했다.
반복문 내에서 free하는 로직이었는데, 이 경우 로직을 손 봐서 일관되게 반복문 내에서 free할 수 있도록 수정했었다.
특정 조건에서만 ft_strdup(strdup 구현)하던 것을 일관성 있게 duplicate하게 한다든가 하는 방식을 취했다.
코드를 올릴 수 없어서 참 애매하다…

과제를 진행하며 배운 것

lldb

솔직히 그동안은 vscode로 디버깅을 많이 했었다. 그런데 이번 부터는 lldb를 왜 쓰게 됐냐면, 귀찮은 게 컸다.
vscode에서 디버깅을 실행하자니, gcc 링킹 부분을 또 손 대줘야 하는 것 같았다. 그래서 vscode 없이 디버깅을 할 수 있는, low level 코드에 어울리는(?), 말로만 듣던 lldb를 사용해보기로 했다. lldb 사용법을 공부하며 익혔던 것은 아래와 같다.

  1. gcc로 컴파일 할 때, -g 옵션을 붙여 컴파일 함으로써 결과 파일을 디버깅 가능하게 한다.
  2. lldb 결과_파일 명령어로 해당 결과 파일을 lldb의 타겟으로 정한다. 참고로 그냥 lldb만 입력하고 바로 실행하면 타겟을 생성하라고 메시지가 뜬다.
  3. b 명령어로 중단점(break point)을 설정한다.
  4. r 명령어로 실행한다!

이런 방식으로 lldb 디버깅을 시작한다. 그리고 아래에 기타 다른 명령어들을 적도록 하겠다.

  • b [줄 번호/함수 이름]: 중단점을 줄 번호 또는 함수 이름에 설정한다.
  • n: 한 행씩 실행한다.
  • step: 함수 내부로 들어간다.
  • p [변수 명]: 해당 변수의 값을 출력한다. 주소값도 같이 출력된다.
  • display [변수 명]: 해당 변수의 값을 계속 보여준다. 디버깅의 매 동작마다 p 명령어를 실행하는 것과 같다.
  • undisplay [hook 번호]: hook 번호에 해당하는 변수를 더이상 보여주지 않는다. 즉, display를 해제한다.
  • conti: 다음 중단점으로 건너뛴다.
  • finish: 현재 함수를 빠져나온다.

참조 링크: https://hyeyoo.com/64

leaks

이게 제일 악질이었다. 여지껏 알아서 메모리를 처리해주는 언어를 쓰던 나에게는 상당히 까다로웠다. 심지어는 system(leaks a.out);로도 잡히지 않는 누수도 있어서 테스터기에 의존할 수 밖에 없었다.
일단 메모리 누수를 잡는 방법은 두 가지 정도로 익혔다.

  1. system(leaks a.out); 사용
  2. while true; do leaks [프로세스 id]; sleep 0.5; done + lldb

이렇게 있는데, 1번으로 못 잡은 누수를 테스터기가 있다고 했을 때 2번의 방법을 사용했다.
2번의 방법은 위의 lldb 참조 링크에서 해당 작성자분이 올려놓으신 방법이다.
lldb를 r 하면 프로세스 id가 출력되며 디버깅이 시작된다. 이때 나온 프로세스 id를 이용하여 위의 while 명령어를 돌린다. 그러면 0.5초마다 leaks가 몇 바이트 났는지(정상이면 0 byte 0 node인가 이런 식으로 출력)를 알려준다. 디버깅을 한 줄씩 실행하다보면 특정 함수를 실행하고 나서 누수가 있음을 알게 되고, 그 함수 내부에서도 어떤 연산을 한 이후에 누수가 발생했는지를 알 수 있다.
그나저나 저거 sleep은 스레드에서 많이 봤었는데 뭔가 정겹다.

OPEN_MAX

이 부분은 고민을 많이 하고 사용했었다. OPEN_MAX는 한 프로세스가 열 수 있는 file_descriptor의 최대 값을 정해놓은 상수이다. 이는 운영체제마다 다르고, 사용자가 임의로 설정할 수도 있다.

그런데 나는 사용자가 임의로 설정할 수 있으니, 일관성 있는 운영체제에서 지정한 제한을 쓰게 해야겠다고 생각해 limits.h에 정의된 OPEN_MAX를 갖다썼다. 하지만 이렇게 되면 뻔히 보이는 문제가 사용자가 임의로 지정한 OPEN_MAX의 값이 운영체제의 값보다 클 때 정상적으로 동작하느냐였다. 이 극단적인 경우는 뮬리넷에서 평가하지는 않는 항목이었지만, 첫 번째 평가자 분의 피드백이 일리가 있었다.

그 평가자 분께서 제시한 방법으로는 차라리 OPEN_MAX 대신에 그냥 특정 값(4096 이라든가)을 넣거나, 사용자가 지정한 값을 직접 가져오는 것이었다.
그런데 두 번째 방법을 구현하는 데 필요한 함수가 사용 제한이 걸려있어서… 안타깝게도 이 프로젝트 내에서는 해당 방법으로는 해결할 수 없었다.
단, 나중에 minitalk이었나 minishell이었나에서 gnl을 쓸 일이 있으니 그때 이 점을 고려해야 할 것이라고 피드백 주셨다. 그리고 평가지에 OPEN_MAX에 대한 내용은 없으니 fail은 아니라고 하셨다.

참고 링크: 파일 디스크립터(File Descriptor)란?

배열 vs 연결 리스트

이 부분에 대해서는 내가 gnl을 평가하러 갔을 때 들었던 내용이다.
이 과제를 구현하는 데, 크게 배열이나 연결 리스트 두 방법이 있었다. 이에 대해 피평가자 분께서 설명해 주셨는데, 내용은 다음과 같다. 배열은 연속적이고, 연결 리스트는 주소를 이용하여 노드끼리 연결되어 있어 삽입 삭제에 유용하다.

참고 링크: 배열 vs 연결리스트

여담

포기할 줄도 알아야지

사실 이걸 3달 동안 붙잡고 있던 이유가 두 가지가 있다. 하나는 내가 파일 입출력을 다루는 데 약해서이고 다른 하나는 포기할 줄 몰라서이다.
두 번째 이유의 비중이 더 컸는데, 이미 6기 2차 라피신때 한 번 데여보고도 쉽게 고쳐지지 않나보다…
나는 성격 상, 문제를 풀 때 어떻게든 내 힘으로 끝까지 풀어보려고 하는 타입이다. 그런데 이건 42서울의 모토와 맞지도 않고, 그렇게 하면 블랙홀에만 가까워질 뿐이다. 그러다 블랙홀이 1달 남짓 남았을 때, 슬슬 급해져서 염치 불구하고 최근에 평가를 했던 카뎃 분께 가서 내 코드에 어떤 점이 부족했는지를 배워와서 해결했다. 최소한 블랙홀은 여유분 1달 씩은 남겨놓고 싶다.

1주일만 내가 해봐야지, 2주까지는 내가 해봐야지 하다가 3달까지 온 케이스라, 다음 과제를 할 땐 1달 내로 못 하면 바로 다른 분께 여쭤봐야겠다(Born2beroot 간다).

공격적인 평가자

이건 첫 번째 평가자분과 나눴던 이야기 중에 하나이다. 일부 카뎃들은 평가를 다닐 때, 평가지에 없는 내용임에도 불구하고 해당 내용에 걸리는 사항 이 있다면 fail을 주거나, 일부러 fail을 주려고 이런 저런 트집을 잡는다고 한다. 난 아직까지는 만나보진 않았지만, 약 1300명이 모인 공간에서 그런 사람이 한 명도 없을 거라고는 생각하지 않는다. 그저 저런 카뎃을 만나지 않기를 기도하거나, 만나면 “평가지에 있는대로 평가해주셔야 한다.”고 말씀드리고 너무 상처받지 말아야겠다. 실제로도 42에서 평가지에 있는 내용으로 평가를 하는 게 규정이다.

댓글남기기