맨위로가기

재진입성

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

재진입성은 함수가 실행 중에 중단되었다가 다시 시작될 수 있는 특성을 의미하며, 초기 컴퓨터 시스템의 인터럽트 처리 루틴에서 중요하게 다뤄졌다. 멱등성과 유사하지만, 여러 번 호출해도 결과가 같은 멱등성과 달리, 재진입성은 실행의 중단을 허용한다. 재진입 가능한 코드는 정적 변수나 전역 변수를 수정하거나, 자기 자신을 수정하는 코드를 포함해서는 안 되며, 다른 재진입 불가능한 루틴을 호출해서도 안 된다. 전역 변수 사용 시에는 원자적 연산이나 동기화 메커니즘을 사용해야 한다. 재진입성은 스레드 안전성과 밀접하게 관련되지만, 동일한 개념은 아니며, C 언어 예제를 통해 재진입 가능성과 스레드 안전성의 차이를 설명한다. POSIX 표준은 재진입 가능 함수를 정의하고 있으며, 비동기 시그널 안전성에 대한 내용을 포함한다.

더 읽어볼만한 페이지

  • 병행성 - 세마포어
    세마포어는 데이크스트라가 고안한 정수 변수로, P/V 연산을 통해 자원 접근을 제어하고 동기화 문제를 해결하며, 계수 세마포어와 이진 세마포어로 나뉘어 멀티스레드 환경에서 자원 관리 및 스레드 동기화에 기여한다.
  • 병행성 - 기아 상태
    기아 상태는 컴퓨터 과학에서 프로세스가 필요한 자원을 할당받지 못해 무한정 대기하는 현상으로, 단순한 스케줄링 알고리즘, 우선순위 역전, 교착 상태 등으로 인해 발생하며 시스템 효율성을 저하시키고 작업 완료를 지연시키지만, 에이징 기법과 같은 공정한 스케줄링 알고리즘이나 우선순위 조정으로 해결할 수 있다.
  • 재귀 - 무한 루프
    무한 루프는 컴퓨터 프로그램에서 루프가 종료되지 않고 무한히 반복되는 현상으로, 프로그램 오류나 의도적인 경우에 발생하며 시스템 응답 불능을 초래하거나 특정 상황에서 사용되기도 한다.
  • 재귀 - 점화식
    점화식은 수열의 각 항을 이전 항들의 함수로 표현하는 방정식으로, 피보나치 수열, 로지스틱 맵, 이항 계수 등의 예시가 있으며, 선형대수학이나 Z변환 등을 이용하여 풀고 다양한 분야에 응용된다.
  • 함수 (프로그래밍) - 사용자 정의 함수
    사용자 정의 함수는 프로그래밍 언어와 데이터베이스 시스템에서 사용자가 직접 정의하여 재사용할 수 있는 코드 블록이다.
  • 함수 (프로그래밍) - 코루틴
    코루틴은 실행을 멈췄다가 다시 시작할 수 있는 서브루틴의 특별한 형태로, 로컬 데이터를 보존하며 다양한 방식으로 구현되고 여러 프로그래밍 상황에서 유용하게 쓰인다.
재진입성
재진입성
정의재진입성(reentrancy)은 컴퓨터 프로그래밍에서 여러 사용자가 동시에 공유하면서 실행해도 안전한 컴퓨터 프로그램 또는 루틴의 속성을 말한다.
조건프로시저가 실행 중 자체 수정하지 않는다.
프로시저가 전역 데이터를 변경하는 경우, 프로시저가 실행될 때마다 독점적인 접근 권한을 얻는다.
필요성재진입성은 멀티태스킹 운영 체제에서 공유 라이브러리를 사용하는 데 필수적이다.
재진입 함수 조건
조건공유되지 않은 지역 변수에서만 작동한다.
전역 변수를 참조하지 않는다.
재진입하지 않는 함수를 호출하지 않는다.
인터럽트 또는 타이머와 같은 모든 하드웨어 장치를 사용하지 않는다.
특징위 조건들을 충족하지 못하는 경우, 함수는 재진입이 불가능하다.
예시
설명간단한 예로, 재진입하지 않는 함수를 여러 번 호출하는 프로그램이 있다. 첫 번째 호출이 완료되기 전에 두 번째 호출이 발생하면 예기치 않은 동작이 발생할 수 있다. 재진입성은 공유 자원에 대한 접근을 제어하는 데 사용되는 상호 배제와 관련된 개념이다.
추가 설명
관련 개념프로그램이 자체 수정되도록 직렬화되면 재진입이 가능할 수 있다. 순수 프로시저가 적절한 직렬화 없이 전역 데이터를 업데이트하면 재진입에 실패할 수 있다.

2. 배경

재진입성의 개념은 멀티태스킹 운영 체제가 등장하기 이전부터 존재했다. 초기 컴퓨터 시스템에서는 인터럽트 처리 루틴이 주요 관심사였으며, 이 루틴이 실행 도중 다시 호출될 수 있는 상황에 대한 안전성을 확보하는 것이 중요했다. 재진입성은 멱등성과 유사하지만, 멱등성은 함수가 여러 번 호출되어도 결과가 동일함을 보장하는 반면, 재진입성은 실행 중인 함수가 중단되었다가 재개될 수 있음을 의미한다.

재진입성은 스레드 안전성과 밀접하게 관련되어 있지만, 동일한 개념은 아니다. 스레드 안전성은 여러 스레드가 동시에 함수를 실행해도 안전함을 보장하는 것이며, 재진입성은 단일 스레드에서도 문제가 발생할 수 있다. 예를 들어, 함수가 뮤텍스를 사용하면 스레드 안전하지만, 재진입 인터럽트 핸들러에서 사용될 경우 데드락이 발생할 수 있는데, 이는 뮤텍스가 인터럽트된 처리와 인터럽트 서비스 루틴 사이에서 공유되기 때문이다.

3. 재진입성의 원칙

재진입 가능한 코드는 Static variable|정적 변수영어전역 변수동기화 없이 수정하거나 보존하지 않아야 한다.[1] 또한, 자기 자신을 수정하는 코드를 포함해서는 안 된다.[2] 단, 각 호출마다 코드의 복사본이 메모리의 다른 위치에서 실행되는 경우는 예외이다.[3] 재진입 가능한 코드는 재진입 가능하지 않은 다른 프로그램이나 루틴을 호출해서는 안 된다.[4]

전역 변수나 정적 변수를 사용해야 하는 경우, 원자적 연산을 사용하거나 동기화 메커니즘(예: 뮤텍스, 세마포어)을 통해 접근을 제어해야 한다.[5]

4. 예제

C와 Pthreads를 사용한 코드 예제를 통해 재진입성과 스레드 안전성의 개념을 살펴볼 수 있다.

C 코드의 `swap()` 함수를 예로 들어보자. 개선된 `swap()` 함수는 실행 완료 시 전역 데이터의 일관성을 유지하여 재진입성을 갖는다. 하지만 실행 중간에는 전역 데이터의 일관성이 보장되지 않아 스레드 안전하지 않다. 또한 `int`형 변수 읽기/쓰기가 원자적 연산이어야 한다는 전제가 필요하다.[2][3]

반면, 아래의 `swap()` 함수는 재진입성과 스레드 안전성을 모두 갖춘 코드이다.

```c

void swap(int *x, int *y)

{

int t;

t = *x;


  • x = *y;

// 여기서 하드웨어 인터럽트가 발생하여 isr()이 호출될 가능성이 있다.

  • y = t;

}

void isr()

{

int x = 1, y = 2;

swap(&x, &y);

}

```

Pthreads를 사용한 C 코드에서 `function` 함수는 스레드 안전하지만, 재진입 가능하지 않다(비동기 시그널 안전이 아니다). Pthreads의 뮤텍스 함수가 재진입성을 보장하지 않기 때문이다.[6]

4. 1. C 언어 예제

다음 C 코드에서 `f`와 `g` 함수는 재진입이 불가능하다.[2]

```c

int g_var = 1;

int f()

{

g_var = g_var + 2;

return g_var;

}

int g()

{

return f() + 2;

}

```

위 코드에서 `f` 함수는 전역 변수 `g_var`에 의존하고 있다. 만약 두 개의 스레드가 이 함수를 실행하여 `g_var`에 동시에 접근할 경우, 결과는 실행되는 시점에 따라서 바뀌게 된다. 그러므로 `f`는 재진입성을 가지지 않는다. `g` 함수는 비-재진입 함수 `f`를 호출하기 때문에 역시 재진입이 불가능하다.[2]

이 함수를 다음과 같이 고치면 재진입이 가능하다.

```c

int f(int i)

{

return i + 2;

}

int g(int i)

{

return f(i) + 2;

}

```

새로운 버전에서는 전역 변수 `g_var`가 사용되지 않는다. 인수를 전달하여, 그것을 기반으로 처리를 하고 결과를 반환한다. 공유될 가능성이 있는 객체에 접근하지 않도록 되어 있다. 그 대신, 호출 측이 이전 반환값을 인수로 전달해야 한다. 이처럼 재진입성 서브루틴에서는 필요한 정적 데이터는 호출 측이 관리해야 한다.[2]

다음은 재진입성을 갖지 않는 `swap()` 함수의 예시이다.

```c

int t;

void swap(int *x, int *y)

{

t = *x;

  • x = *y;

// 여기서 하드웨어 인터럽트가 발생하여 isr()이 호출될 가능성이 있다.

  • y = t;

}

void isr()

{

int x = 1, y = 2;

swap(&x, &y);

}

```

`swap()`는 `t`를 스레드 지역 저장소에 저장함으로써 스레드 안전성을 확보할 수 있다. 그러나 그렇게 하더라도 재진입성을 갖지는 못하며, `swap()` 실행 중에 같은 스레드의 컨텍스트에서 `isr()`이 호출되면 문제가 발생할 가능성이 남아 있다.[2]

다음과 같이 개선된 swap 함수는 실행 완료 시의 전역 데이터의 일관성을 주의 깊게 유지하므로 완전히 재진입성을 갖는다. 다만 실행 중간의 전역 데이터의 일관성은 보장되지 않으므로 스레드 안전하지 않다. 또한 `int`형 변수의 읽기 및 쓰기가 원자적 연산이어야 한다(읽기 처리 및 쓰기 처리는 각각 1개의 명령으로만 실행되며, 처리 중에 인터럽트가 발생하지 않음)는 전제가 있다[2][3]

```c

int t;

void swap(int *x, int *y)

{

int s;

s = t; // 전역 변수를 저장

t = *x;

  • x = *y;

// 여기서 하드웨어 인터럽트가 발생하여 isr()이 호출될 가능성이 있다.

  • y = t;

t = s; // 전역 변수를 복원

}

void isr()

{

int x = 1, y = 2;

swap(&x, &y);

}

```

다음 `swap` 함수는 재진입성이 있으며 스레드 안전하다.

```c

void swap(int *x, int *y)

{

int t;

t = *x;

  • x = *y;

// 여기서 하드웨어 인터럽트가 발생하여 isr()이 호출될 가능성이 있다.

  • y = t;

}

void isr()

{

int x = 1, y = 2;

swap(&x, &y);

}

```

다음 Pthreads를 사용한 C 언어 코드에서 함수 `function`은 스레드 세이프하지만 리엔트란트(비동기 시그널 안전)가 아니다. Pthreads의 뮤텍스 함수가 리엔트란트(비동기 시그널 안전)라는 보장은 없기 때문이다[6]

```c

void function(pthread_mutex_t mutex)

{

pthread_mutex_lock(mutex);

/* ... */

/* 어떤 처리를 함 */

/* ... */

pthread_mutex_unlock(mutex);

}

```

이 `function`은 여러 스레드에서 호출되어도 아무 문제가 없다. 하지만 `pthread_mutex_init()`에 의한 뮤텍스 초기화 시에 `PTHREAD_MUTEX_NORMAL`을 설정한 속성을 사용하고, 리엔트란트 인터럽트 핸들러가 이 함수를 호출하는 경우, 두 번째 인터럽트가 이 함수 실행 중에 발생하면, 두 번째 호출은 뮤텍스를 획득할 수 없게 되어 영구적으로 정지(데드락)한다. 인터럽트 서비스에서는 다른 인터럽트를 디세이블하므로 시스템 전체가 멈추게 된다.

데드락을 회피하려면, 뮤텍스 초기화 시에 동일 스레드에 의한 여러 번의 락을 허용하는 `PTHREAD_MUTEX_RECURSIVE`를 설정한 속성을 사용할 필요가 있다[7]。하지만 `PTHREAD_MUTEX_RECURSIVE`를 설정한다고 해서 `pthread_mutex_lock()` 및 `pthread_mutex_unlock()`이 비동기 시그널 안전이 되는 것은 아니다. 또한, 뮤텍스 초기화 시에 `PTHREAD_MUTEX_INITIALIZER`를 사용하면 `PTHREAD_MUTEX_DEFAULT`를 설정한 기본 속성이 사용되어 데드락에 관해서는 미정의 동작이 된다.

4. 2. 스레드 안전하지만 재진입 가능하지 않은 예제

c

int function()

{

mutex_lock();

// ...

// function body

// ...

mutex_unlock();

}

```

위의 코드는 `function()`이 다른 스레드에 의해 문제없이 호출될 수 있도록 뮤텍스(mutex)를 사용한다. 그러나 이 함수가 재진입 인터럽트 핸들러에서 사용되고 함수 내부에서 두 번째 인터럽트가 발생하면, 두 번째 루틴은 영원히 멈추게 된다. 인터럽트 처리가 다른 인터럽트를 비활성화할 수 있기 때문에 전체 시스템에 영향을 줄 수 있다.[2]

5. POSIX와 재진입성

POSIX 표준은 `_r` 접미사가 붙은 C 함수들을 제공하여, 기존 함수의 재진입 가능 버전을 제공한다.[9] POSIX는 재진입 가능 함수를 "둘 이상의 스레드에서 호출되었을 때, 실행이 인터리브(interleave)된 경우에도 스레드가 각각 정의되지 않은 순서로 해당 함수를 차례로 실행한 것과 같도록 결과가 보장되는 함수"로 정의한다.

이는 멀티 스레드 환경에서 실행 순서에 의존하지 않고 병렬 실행이 가능(스레드 안전)하다는 의미로 재진입 가능이라는 용어를 정의하며, 비동기 시그널 안전성에 대해서는 언급하지 않는다.

한편, POSIX는 비동기 시그널 안전(async-signal-safe)한 함수들을 규정하며, 시그널 핸들러 내에서는 이러한 함수들만 호출해야 한다.[10][11]

6. 한국의 관점

한국의 소프트웨어 개발 환경에서 재진입성은 임베디드 시스템, 실시간 운영체제, 서버 프로그램 등 다양한 분야에서 중요하게 다루어진다. 특히, 인터럽트 처리 빈도가 높은 시스템이나 다수의 스레드가 동시에 동작하는 환경에서는 재진입성 확보가 시스템 안정성에 큰 영향을 미친다. 재진입성에 대한 이해 부족은 예측 불가능한 오류, 시스템 다운, 데이터 손실 등의 심각한 문제로 이어질 수 있으므로, 개발자들은 재진입성의 원칙을 숙지하고 코드를 작성해야 한다.

참조

[1] 웹사이트 pthread_cond_init()--Initialize Condition Variable https://www.ibm.com/[...] 2019-10-05
[2] 웹사이트 Use reentrant functions for safer signal handling https://www.ibm.com/[...] IBM Developer
[3] 웹사이트 安全なシグナル処理のために再入可能ファンクションを使う https://www.ibm.com/[...] IBM Developer
[4] 서적 ARM System Developer's Guide https://books.google[...]
[5] 서적 Safe and structured use of interrupts in real-time and embedded software http://www.cs.utah.e[...]
[6] 웹사이트 Man page of PTHREAD_MUTEX https://linuxjm.osdn[...]
[7] 웹사이트 pthread_mutexattr_settype(3) - Linux man page https://linux.die.ne[...]
[8] 웹사이트 EnterCriticalSection function | Microsoft Docs https://docs.microso[...]
[9] 웹사이트 Thread-safety and POSIX.1 http://www.unix.org/[...]
[10] 웹사이트 Signal Handlers and Async-Signal Safety (Multithreaded Programming Guide) | Oracle https://docs.oracle.[...]
[11] 웹사이트 シグナルハンドラと「非同期シグナル安全」 (マルチスレッドのプログラミング) | Oracle https://docs.oracle.[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com