임베디드 면접 용어 사전
C 언어reentrant

재진입 함수

여러 실행 흐름이 동시에 들어와도 안전한 함수. 전역·정적 상태를 쓰지 않아야 한다.

실행 도중에 다시 호출되어도 안전한 함수(reentrant function). 인터럽트가 있는 환경에서 필수 개념이다.

어떤 상황을 말하는가

메인 루프가 f() 를 실행하는 중
   ↓
인터럽트 발생 → ISR 이 같은 f() 를 호출
   ↓
ISR 의 f() 가 끝나고 메인의 f() 로 돌아옴
   ↓
메인의 f() 가 올바르게 이어지는가?  ← 이것이 재진입성

깨지는 예

static char buf[32];                  // ★ 공유 상태

char *format(int n) {
    sprintf(buf, "%d", n);
    return buf;
}
메인: format(100) 실행 → buf = "100"
      ↓ 인터럽트!
ISR:  format(999) 실행 → buf = "999"
      ↓ 복귀
메인: buf 를 읽는다 → "999"   ✗ 조용히 잘못된 값

크래시가 나지 않고 값만 틀리기 때문에 원인을 찾기가 매우 어렵다.

재진입 함수의 조건

  • ① static / 전역 변수를 수정하지 않는다
  • ② 자기 자신의 주소를 반환하지 않는다 (호출자마다 다른 결과여야)
  • ③ 하드웨어 자원을 배타적으로 점유하지 않는다
  • ④ 재진입하지 않는 함수를 호출하지 않는다 ← 전염된다

④가 놓치기 쉽다. 내가 아무리 잘 만들어도 안에서 malloc이나 printf를 부르면 재진입성이 깨진다.

표준 라이브러리 — 대부분 재진입하지 않는다

함수
strtok내부 static 포인터로 위치를 기억
malloc / free전역 힙 자료구조를 수정
printf전역 스트림 버퍼
rand내부 시드 상태
asctime, ctimestatic 버퍼 반환
errno전역 변수(스레드 로컬로 구현되기도)

ISR 안에서 printf를 부르면 안 되는 이유가 여기에 있다 (느린 것도 이유지만 재진입성이 더 근본적이다).

재진입 버전이 별도로 제공되기도 한다 — strtok_r, rand_r. newlib은 -DREENTRANT 계열 설정으로 재진입 변형을 제공한다.

재진입성과 스레드 안전성 — 다르다

자주 혼동되지만 같은 개념이 아니다.

int counter = 0;
pthread_mutex_t m;

void increment(void) {
    pthread_mutex_lock(&m);
    counter++;
    pthread_mutex_unlock(&m);
}
스레드 안전?재진입?
위 함수 (락으로 보호)

왜 재진입하지 않나 — ISR이 락을 잡은 상태에서 같은 함수에 재진입하면 자기가 잡은 락을 자기가 기다린다 → 데드락. 락은 다른 스레드의 개입을 막지만, 같은 실행 흐름의 재진입은 못 막는다.

스레드 안전 : 여러 스레드가 동시에 불러도 안전 (락으로 달성 가능)

  • 재진입 — : 하나의 실행 흐름이 중첩해 불러도 안전 (락으로 달성 불가)

재진입 ⊂ 스레드 안전 (인 경우가 많지만 위처럼 예외가 있다)

이 구분을 설명할 수 있으면 면접에서 확실히 좋은 인상을 준다.

어떻게 만드나

// ✗ 재진입 불가
char *format(int n) { static char buf[32]; … return buf; }

// ✓ 호출자가 버퍼를 제공한다  ← 임베디드의 표준 패턴
void format(int n, char *buf, size_t len) { snprintf(buf, len, "%d", n); }

"상태를 함수 밖으로 빼서 호출자에게 맡긴다" 는 것이 핵심이다. 지역 변수(스택)만 쓰면 호출마다 별도 공간이 생기므로 자동으로 재진입 가능하다.

실무 원칙

✅ ISR에서 부르는 함수는 재진입 가능해야 한다 ✅ 공유 데이터는 임계 구역으로 보호하고, 그 구간을 최대한 짧게 ✅ ISR은 플래그만 세우고 실제 처리는 메인 루프로 (deferred 처리) ❌ ISR에서 printf, malloc, 긴 라이브러리 호출 금지

가장 안전한 설계는 "ISR과 메인이 같은 함수를 공유하지 않는 것" 이다. 공유가 불가피하면 재진입 가능하게 만들거나 인터럽트를 차단한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — C 언어 핵심 — 포인터·메모리클래스·const/volatile·비트·struct 패딩