재진입(컴퓨팅)
Reentrancy (computing)컴퓨팅에서 컴퓨터 프로그램 또는 서브루틴은 여러 개의 프로세서에서 동시에 여러 개의 호출을 안전하게 실행할 수 있는 경우 또는 하나의 프로세서 시스템에서 재진입 절차를 실행 도중에 중단하고 이전 호출이 완료되기 전에 안전하게 재진입(재진입)할 수 있는 경우 재진입(재진입)이라고 부릅니다.on. 새로운 호출이 내부 호출에 의해서만 발생할 수 있는 재귀와는 달리, 이 중단은 점프나 호출과 같은 내부 액션이나 인터럽트나 신호와 같은 외부 액션에 의해 발생할 수 있습니다.
이 정의는 여러 프로세스가 동시에 활성화될 수 있고 제어 흐름이 인터럽트에 의해 중단되어 Interrupt Service Route(ISR; 인터럽트 서비스 루틴) 또는 "handler" 서브루틴으로 전송될 수 있는 멀티프로그래밍 환경에서 비롯됩니다.인터럽트가 트리거되었을 때 실행되었을 가능성이 있는 핸들러에 의해 사용되는 서브루틴은 재진입해야 합니다.마찬가지로 2개의 프로세싱과 공유 데이터 접근에 의해 공유되는 코드는 재진입해야 합니다.대부분의 경우 운영 체제 커널을 통해 액세스할 수 있는 서브루틴은 재진입되지 않습니다.따라서 인터럽트 서비스 루틴은 실행할 수 있는 액션이 제한되어 있습니다.예를 들어 보통 파일시스템에 액세스 할 수 없고 메모리 할당이 제한되는 경우도 있습니다.
이러한 재진입의 정의는 멀티 스레드 환경에서의 스레드 안전성과는 다릅니다.재진입 서브루틴은 스레드 [1]안전성을 실현할 수 있지만 재진입만으로는 모든 상황에서 스레드 안전성이 충분하지 않을 수 있습니다.반대로 스레드 세이프 코드는 반드시 재입력할 필요는 없습니다(아래 예 참조).
재진출 프로그램에 사용되는 다른 용어로는 "공유 가능한 코드"[2]가 있습니다.재진입 서브루틴은 참조 자료에서 "신호 안전"[3]으로 표시될 수 있습니다.재입학 프로그램은 종종 "순수한 절차"입니다[a].
배경
재진입은 함수가 여러 번 호출될 수 있지만 한 번만 호출된 것과 완전히 동일한 출력을 생성하는 idempotence와는 다릅니다.일반적으로 함수는 몇 가지 입력 데이터를 기반으로 출력 데이터를 생성합니다(다만 일반적으로 둘 다 옵션입니다).공유 데이터는 언제든지 모든 기능을 통해 액세스할 수 있습니다.어떤 함수에 의해서도 데이터를 변경할 수 있는 경우(아무도 이러한 변경을 추적하지 않는 경우), 그 데이터가 이전과 동일하다는 보장은 없습니다.
데이터에는 스코프라고 하는 특성이 있습니다.이 특성은 프로그램에서 데이터를 사용할 수 있는 위치를 나타냅니다.데이터 범위는 글로벌(함수의 범위 밖이며 무한 익스텐트) 또는 로컬(함수가 호출되어 종료될 때마다 생성됨) 중 하나입니다.
로컬 데이터는 재입력 여부에 관계없이 어떤 루틴에서도 공유되지 않습니다.따라서 재입력에는 영향을 주지 않습니다.글로벌 데이터는 함수 외부에 정의되며, 여러 함수에 의해 액세스될 수 있습니다.글로벌 변수(모든 함수 간에 공유되는 데이터) 또는 스태틱 변수(같은 함수의 모든 호출에 의해 공유되는 데이터) 중 하나입니다.객체 지향 프로그래밍에서 글로벌 데이터는 클래스의 범위에서 정의되며 개인 데이터가 될 수 있으므로 해당 클래스의 함수에만 액세스할 수 있습니다.클래스 변수가 클래스 인스턴스에 바인딩되는 인스턴스 변수의 개념도 있습니다.이러한 이유로 객체 지향 프로그래밍에서 이 구별은 일반적으로 클래스 외부에서 액세스할 수 있는 데이터(퍼블릭)와 클래스 인스턴스(스태틱)에 대해 예약됩니다.
재진입은 스레드 안전과는 구별되지만 스레드 안전과 밀접하게 관련되어 있습니다.함수는 스레드 세이프이지만 재진입하지 않을 수 있습니다.예를 들어 함수는 뮤텍스로 둘러쌀 수 있지만(멀티스레딩 환경에서 문제가 발생하지 않도록), 인터럽트 서비스 루틴에서 해당 함수가 사용되면 뮤텍스를 해제하기 위한 첫 번째 실행을 기다리지 못할 수 있습니다.혼란을 피하기 위한 열쇠는 재진입이 실행되는 쓰레드는 1개뿐이라는 것입니다.멀티태스킹 운영체제가 존재하지 않았던 시절의 개념입니다.
재진입 규칙
- 재입력 코드는 동기화 없이 정적 또는 전역 비정수 데이터를 보유할 수 없습니다.
- 재진입 기능은 글로벌 데이터로 작동할 수 있습니다.예를 들어 재진입 인터럽트 서비스 루틴은 글로벌할 뿐만 아니라 휘발성이 높은 하드웨어 상태(시리얼 포트 읽기 버퍼 등)를 취득할 수 있습니다.단, 동기화된 코드의 섹션을 제외하고 이러한 변수에는 아토믹 읽기-수정-쓰기 명령만 사용해야 한다는 점에서 스태틱 변수와 글로벌데이터의 일반적인 사용은 권장되지 않습니다(이러한 명령 실행 중에는 인터럽트 또는 신호가 발생할 수 없습니다).C에서는 읽기 또는 쓰기조차도 원자성이 보장되지 않으므로 여러 읽기 또는 [4]쓰기로 분할될 수 있습니다.C 표준 및 SUSv3는 다음을 제공합니다.
sig_atomic_t
단, 단순한 읽기 및 쓰기에 대해서만 보장되며 증가 또는 [5]감소에 대해서는 보장되지 않습니다.C11에서는 보다 복잡한 원자 연산을 이용할 수 있으며, 이는stdatomic.h
. - 동기화하지 않으면 재입력 코드가 수정되지 않을 수 있습니다.
- 운영 체제에서 프로세스가 코드를 수정할 수 있습니다.여기에는 다양한 이유(예를 들어 그래픽이 빠르게 깜박이는 것)가 있지만, 일반적으로 재진입 문제를 피하기 위해 동기화가 필요합니다.
다만, 독자적인 메모리에 있는 경우는, 그 자체를 변경할 수 있습니다.즉, 새로운 호출마다 원래 코드의 복사가 이루어지는 다른 물리 머신 코드 위치를 사용하는 경우, 특정 호출(스레드) 실행 중에 자신을 변경하더라도 다른 호출에는 영향을 주지 않습니다.
- 재진입자 코드는 재진입자가 아닌 컴퓨터 프로그램 또는 루틴을 호출할 수 없습니다.
- 사용자, 오브젝트 또는 프로세스의 priority나 멀티프로세싱의 여러 레벨은 보통 재진입 코드의 제어를 복잡하게 합니다.재진입을 위해 설계된 루틴 내에서 수행된 액세스나 부작용을 추적하는 것이 중요합니다.
운영 체제 리소스 또는 로컬이 아닌 데이터에 대해 작동하는 서브루틴의 재진입은 각 작업의 원자성에 따라 달라집니다.예를 들어 서브루틴이 32비트 머신 상에서 64비트 글로벌 변수를 변경했을 경우 동작을 2개의 32비트 동작으로 분할할 수 있으며, 따라서 서브루틴이 실행 중에 중단되어 인터럽트 핸들러에서 다시 호출되었을 경우 글로벌 변수는 32비트만 갱신된 상태가 될 수 있습니다.프로그래밍 언어는 점프나 콜 등의 내부 액션에 의한 중단에 대한 원자성 보증을 제공할 수 있습니다.그러면 함수가f
같은 표현으로(global:=1) + (f())
프로그래밍 언어에서는 서브 표현의 평가 순서가 임의일 수 있습니다.여기서 글로벌 변수는 1 또는 이전 값으로 설정되지만 부분만 업데이트된 중간 상태에서는 표시되지 않습니다.(식에는 시퀀스 포인트가 없기 때문에 후자는 C에서 발생할 수 있습니다).오퍼레이팅시스템은 신호로 중단되는 시스템콜 등 부분적인 영향이 없는 신호에 대한 원자성 보증을 제공할 수 있습니다.프로세서 하드웨어는 중단되는 프로세서 명령이 부분적인 영향을 미치지 않는 등의 인터럽트에 대한 원자성 보증을 제공할 수 있습니다.
예
이 문서에서는 재진입을 설명하기 위해 C 유틸리티 함수를 예로 사용합니다.swap()
2개의 포인터를 사용하여 값을 바꿉니다.또, 스왑 함수를 호출하는 인터럽트 처리 루틴도 있습니다.
재진입도 스레드 세이프도 없음
이것은 재진입 또는 스레드 세이프가 되지 않는 스왑 기능의 예입니다.그 이후로는tmp
변수는 동기화가 이루어지지 않은 상태에서 함수의 동시 인스턴스 간에 글로벌하게 공유됩니다.한 인스턴스가 다른 인스턴스가 의존하는 데이터를 방해할 수 있습니다.따라서 인터럽트 서비스 루틴에서는 사용하지 말았어야 합니다.isr()
:
인트 tmp; 무효 바꾸다(인트* x, 인트* y) { tmp = *x; *x = *y; /* 하드웨어 인터럽트는 여기서 isr()을 호출할 수 있습니다.*/ *y = tmp; } 무효 인식하다() { 인트 x = 1, y = 2; 바꾸다(&x, &y); }
스레드는 안전하지만 재진입하지 않음
함수swap()
위의 예에서 스레드 세이프를 만들 수 있습니다.tmp
스레드 로컬그래도 재진입에 실패하여 다음과 같은 경우 문제가 계속 발생합니다.isr()
이미 실행 중인 스레드와 동일한 컨텍스트에서 호출됩니다.swap()
:
스레드_로컬 인트 tmp; 무효 바꾸다(인트* x, 인트* y) { tmp = *x; *x = *y; /* 하드웨어 인터럽트는 여기서 isr()을 호출할 수 있습니다.*/ *y = tmp; } 무효 인식하다() { 인트 x = 1, y = 2; 바꾸다(&x, &y); }
재진입은 가능하지만 스레드 세이프가 아님
종료 시 글로벌데이터를 일관된[disputed ] 상태로 유지하도록 주의하는 스와프 함수의 다음 (일부 의도된) 변경은 재진행됩니다.다만, 스레드 세이프가 아닙니다.잠금이 채용되어 있지 않기 때문에 언제든지 중단될 수 있습니다.
인트 tmp; 무효 바꾸다(인트* x, 인트* y) { /* 글로벌 변수를 저장합니다.*/ 인트 s; s = tmp; tmp = *x; *x = *y; *y = tmp; /* 하드웨어 인터럽트는 여기서 isr()을 호출할 수 있습니다.*/ /* 글로벌 변수를 복원합니다.*/ tmp = s; } 무효 인식하다() { 인트 x = 1, y = 2; 바꾸다(&x, &y); }
재진입 및 스레드 세이프
의 실장swap()
할당하다tmp
이 파라미터는 스레드 세이프와 재진입이 모두 가능하기[b] 때문에 공유되지 않은 변수에서만 호출됩니다.스택이 스레드에 대해 로컬이며 로컬 데이터에 대해서만 작동하는 함수가 항상 예상된 결과를 생성하므로 스레드 안전합니다.공유 데이터에 액세스할 수 없으므로 데이터 경합이 발생하지 않습니다.
무효 바꾸다(인트* x, 인트* y) { 인트 tmp; tmp = *x; *x = *y; *y = tmp; /* 하드웨어 인터럽트는 여기서 isr()을 호출할 수 있습니다.*/ } 무효 인식하다() { 인트 x = 1, y = 2; 바꾸다(&x, &y); }
재진입 인터럽트 핸들러
재진입 인터럽트 핸들러는 인터럽트 핸들러로 인터럽트를 조기에 재이네이블로 하는 인터럽트 핸들러입니다.이로 인해 인터럽트 [6]지연이 줄어들 수 있습니다.일반적으로 인터럽트 서비스 루틴을 프로그래밍하는 동안 인터럽트 핸들러에서 인터럽트를 가능한 한 빨리 다시 활성화하는 것이 좋습니다.이 방법은 [7]인터럽트 손실을 방지하는 데 도움이 됩니다.
기타 예
다음 코드에서는 둘 다f
도 아니다g
기능은 재진입합니다.
인트 v = 1; 인트 f() { v += 2; 돌아가다 v; } 인트 g() { 돌아가다 f() + 2; }
위의 경우,f()
비표준 글로벌 변수에 의존합니다.v
; 즉, 만약f()
ISR에 의해 실행 중에 중단되어, ISR이 변경된다.v
에 재진입합니다.f()
잘못된 값을 반환하다v
의 가치v
따라서 수익률은f
는 확실하게 예측할 수 없습니다.인터럽트의 변경 여부에 따라 달라집니다.v
동안f
의 실행.이런 이유로,f
재진입이 되지 않습니다.둘 다 아니다g
를 호출하기 때문입니다.f
재진입이 아닙니다.
다음과 같이 약간 변경된 버전이 다시 적용됩니다.
인트 f(인트 i) { 돌아가다 i + 2; } 인트 g(인트 i) { 돌아가다 f(i) + 2; }
다음에서는 기능은 스레드 세이프이지만 (필요한) 재진입은 아닙니다.
인트 기능.() { 뮤텍스_잠금(); // ... // 함수 본문 // ... mutex_mutex(); }
위의 경우,function()
는 다른 스레드로 문제없이 호출할 수 있습니다.단, 함수가 재진입 인터럽트 핸들러에서 사용되고 함수 내부에서 두 번째 인터럽트가 발생하면 두 번째 루틴은 영원히 정지됩니다.인터럽트 서비스는 다른 인터럽트를 무효로 할 수 있기 때문에 시스템 전체에 장애가 발생할 수 있습니다.
메모들
「 」를 참조해 주세요.
레퍼런스
- ^ 케리스크 2010, 페이지 657
- ^ 랄스턴 2000, 페이지 1514–1515.
- ^ "pthread_cond_init()--Initialize Condition Variable". IBM Knowledge Center. Retrieved 2019-10-05.
- ^ Preshing, Jeff (2013-06-18). "Atomic vs. Non-Atomic Operations". Preshing on Programming. Archived from the original on 2014-12-03. Retrieved 2018-04-24.
- ^ 케리스크 2010, 페이지 428
- ^ Slossloss et al. 2004년, 페이지 342
- ^ Regehr, John (2006). "Safe and Structured Use of Interrupts in Real-time and Embedded Software" (PDF). Handbook of Real-Time and Embedded Systems. CRC Press. Archived (PDF) from the original on 2007-08-24 – via the author's website at the University of Utah School of Computing.
인용된 작품
- Kerrisk, Michael (2010). The Linux Programming Interface. No Starch Press.
- Ralston, Anthony, ed. (2000). "Reentrant Program". Encyclopedia of Computer Science (4th ed.). Nature Publishing Group.
- Sloss, Andrew N.; Symes, Dominic; Wright, Chris; Rayfield, John (2004). ARM System Developer's Guide. Morgan Kaufmann Publishers. ISBN 9780080490496.
추가 정보
- Chen, Raymond (2004-06-29). "The Difference Between Thread-safety and Re-entrancy". The Old New Thing. Microsoft Developer Network. Archived from the original on 2018-04-24. Retrieved 2018-04-24.
- Ganssle, Jack (2001-03-15). "Introduction to Reentrancy". Embedded.com. Archived from the original on 2013-01-21. Retrieved 2018-04-24.
- IBM (2018). "General Programming Concepts" (PDF). AIX Version 7.2 Manual. p. 636–641. Retrieved 2018-04-24.
- Jha, Dipak (2005-01-20). "Use Reentrant Functions for Safer Signal Handling". IBM DeveloperWorks. Archived from the original on 2014-07-07. Retrieved 2018-04-24.