Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

C 확장에서 Ruby 메소드를 호출할 때 메모리 누수 방지

메모리 누수는 gem 사용자에게 골칫거리입니다. 추적하기 어렵고 인프라 비용이 많이 들 수 있습니다.

C 확장 내의 메모리 누수는 더욱 심각합니다. Ruby에서 누수를 찾는 방법에 대한 많은 도구와 기사를 볼 수 있습니다. 그러나 C에서는 내부에 동일한 액세스 권한이 없습니다.

rb_funcall의 순진한 사용법 메모리 누수가 발생할 수 있습니다. rb_protect를 사용하는 것이 훨씬 좋습니다. 대신. 따라서 C 확장 작성자라면 gem을 사용할 개발자를 위해 읽어보시기 바랍니다.

시작해 보세요!

rb_funcall의 문제 그리고 C

rb_funcall Ruby와 라이브러리의 C 부분 간에 상호 작용해야 하지만 약간의 C만 작성하면 되는 경우 훌륭한 도구가 될 수 있습니다.

그러나 rb_funcall을 실행하면 , 당신은 더 이상 모든 것이 간단한 C에 있지 않습니다. 호출된 함수의 경우 진흙탕에 빠질 수 있습니다:

  1. 런타임 중에 정의가 완전히 변경됩니다.
  2. 전화 걸기

1번이 가장 잡기 쉽습니다. 세그폴트가 발생할 가능성이 높으며 테스트 스위트가 충분히 완성되면 게시하기 전에 이를 포착해야 합니다.

그러나 후자는 메모리 누수를 일으키고 코드베이스를 읽기 어렵게 만들 수 있습니다. 지금부터 살펴보겠습니다.

C 메모리 누수를 유발하는 Ruby 인상

Ruby의 발생 메커니즘은 한 범위에서 오류를 포착한 첫 번째 상위 코드 부분 사이를 점프합니다. 이는 longjmp를 사용하여 MRI에서 구현됩니다. 및 setjmp .

이것이 어떻게 구축되는지에 관심이 있다면 Ruby 해킹 가이드의 평가자 장을 읽어보세요. 간단히 말해서 begin..ensure를 사용하면 차단해, 너 setjmp() , 그리고 이 블록 내에서 레이즈하면 longjmp() 저장된 위치로 이동합니다.

따라서 rb_funcall로 함수가 발생하면 , C 코드는 결코 실행되지 않은 후에 호출됩니다.

아래 예는 잠재적인 누출을 보여줍니다. json_parse인 경우 올리면 누출됩니다.

 

물론 위의 예는 약간 어리석습니다. 해제 및 Ruby 처리 부분을 뒤집을 수 있습니다. 그러나 이것이 항상 가능한 것은 아니며, 기능 본체가 길어질수록 더 얽힐 수 있습니다.

begin..ensure 사용 루비에서

Ruby를 사용하는 경우 대신 begin..ensure를 사용하여 위의 예를 작성할 수 있습니다. :

 

이 API는 C에서도 rb_rescue로 사용할 수 있습니다. 및 rb_ensure :

 

하지만 이는 다소 번거로운 작업이므로 rescue를 추가하려는 경우 파티를 차단하면 읽기가 훨씬 어려워집니다. begin..rescue..ensure..end를 사용하려면 Peter Zhu의 'A Rubyist's Walk Along the C-side(Part 8):Exceptions &Error Handling'을 읽어 보시기 바랍니다. C의 API

rb_protect 사용 C의 경우

또 다른 옵션이 있습니다. 먼저, Ruby에서 어떻게 보이는지 살펴보겠습니다:

 

이는 Ruby에서는 이상해 보이지만 C에는 매우 적합한 작업 흐름입니다. MRI에는 이를 위한 API(rb_protect)가 있습니다. , C 함수는 다음과 같습니다:

 

위의 방법은 모든 것을 해제한 후 Ruby 오류를 다시 발생시킵니다.

rescue를 사용하여 오류를 무시하도록 선택할 수도 있습니다. Ruby의 블록:

 

경고: 오류를 발생시키지 않으면 rb_set_errinfo(Qnil) 사용자가 알아야 할 오류에 대한 정보를 계속 제공하지 않도록 하는 단계가 중요합니다.

또는 rescue My::Error와 같이 조건부로 오류를 발생시키도록 선택할 수 있습니다. :

 

실제로 rb_errinfo()을 고려해 볼 수 있습니다. $!와 동일 전역변수입니다.

모두 훌륭하지만 rb_funcall 하나로 요약하면 오직 API를 단순화할 수 있습니다.

rb_protect 사용에 대한 전반적인 아이디어 함수가 있을 때 API를 올려 가독성을 높이기 위함입니다. 함수가 발생할 수 있는지 여부를 확인할 필요가 없으며, 발생할 수 있다고 가정하고 상태를 사용하여 작업합니다.

rb_protect_funcall 제안

rb_funcall을 분리해 보겠습니다. , 유일한 위험이기 때문입니다. 사용 방법. 이를 수행하는 API는 다음과 같습니다:

 

이 API는 rb_funcall과 동일합니다. , state 포함 rb_protect에서 . 따라서 사용법은 매우 간단합니다:

 

이 API는 아직 Ruby에서 사용할 수 없으며 앞으로도 없을 수도 있습니다. RGeo(MIT 라이센스)에서 가져오실 수 있습니다.

실제 사례

실제 예를 보려면 최근 rb_protect 전체 버전으로 전환한 RGeocodebase를 읽어 보시기 바랍니다. . rgeo_convert_to_geos_geometry와 같은 일부 기능도 있습니다. , 더 간단한 사용을 위해 이 상태를 전파합니다. 이 기능은 탐색을 시작하기에 좋은 곳입니다.

RGeo에서 이슈를 열어 우리가 내린 선택에 대해 토론해 보세요.

마무리

이 게시물에서는 rb_funcall 사용에 대해 경고했습니다. C를 사용하면 메모리 누수가 발생할 수 있습니다. begin..ensure를 사용하여 탐색했습니다. 또는 rb_protect 대신.

즐거운 코딩 되세요!

추신 Ruby Magic 게시물이 보도되는 즉시 읽으려면 Ruby Magic 뉴스레터를 구독하고 단 하나의 게시물도 놓치지 마세요!

C 확장에서 Ruby 메소드를 호출할 때 메모리 누수 방지

율리스 부오노모

우리의 객원 저자인 Ulysse는 대부분의 시간을 전 세계를 여행하는 데 바치는 전직 Ruby 개발자입니다. 그는 여가 시간을 RGeo와 Ruby에 전념하며 Ruby의 내부를 만지는 것을 좋아합니다.

Ulysse Buonomo의 모든 기사