인터럽트 컨텍스트에서 실행되는 커널 코드/스레드가 왜 잠을 잘 수 없습니까?

StackOverflow https://stackoverflow.com/questions/1053572

  •  20-08-2019
  •  | 
  •  

문제

Robert Love의 다음 기사를 읽고 있습니다

http://www.linuxjournal.com/article/6916

그것은 말한다

"... 작업 대기열이 프로세스 컨텍스트에서 실행되었다는 사실에 대해 논의합시다. 이것은 인터럽트 컨텍스트에서 실행되는 다른 하위 절반 메커니즘과 대조적이다. 인터럽트 컨텍스트에서 실행되는 코드는 잠을 잘 수 없거나 차단할 수 없다. 컨텍스트는 조정할 수있는 후원 프로세스가 없습니다. 따라서 인터럽트 핸들러는 프로세스와 관련이 없기 때문에 스케줄러가 잠을자는 것이없고, 더 중요한 것은 스케줄러가 깨어날 것이 없습니다 ... "

나는 그것을 얻지 못한다. 커널의 스케줄러 인 Afaik은 O (1)이며 비트 맵을 통해 구현됩니다. 그렇다면 SCEHDULER가 인터럽트 컨텍스트를 잠들고 다음 예약 가능한 프로세스를 취하고 통제를 전달하는 것을 막는 것은 무엇입니까?

도움이 되었습니까?

해결책

디자인 아이디어라고 생각합니다.

물론, 당신은 인터럽트에서 잠을 잘 수있는 시스템을 설계 할 수 있지만, 시스템을 이해하기 어렵고 복잡한 (많은 상황을 고려해야하는 많은 상황)를 제외하고는 아무것도 도움이되지 않습니다. 따라서 디자인보기에서 수면 할 수없는 인터럽트 핸들러를 선언하는 것은 매우 명확하고 구현하기 쉽습니다.


Robert Love (커널 해커) :http://permalink.gmane.org/gmane.linux.kernel.kernelnewbies/1791

인터럽트에는 후원 프로세스 컨텍스트가 없기 때문에 인터럽트 핸들러에서 잠을 잘 수 없으므로 다시 조정할 것이 없습니다. 다시 말해, 인터럽트 핸들러는 작업과 관련이 없으므로 "잠을 자고"(더 중요하게는) "깨어날 것이 없다"는 것이 없습니다. 그들은 원자 적으로 실행해야합니다.

이것은 다른 운영 체제와 다릅니다. 대부분의 운영 체제에서는 인터럽트가 나사화되지 않습니다. 그러나 바닥 반쪽은 종종 있습니다.

페이지 결함 핸들러가 잠들 수있는 이유는 프로세스 컨텍스트에서 실행중인 코드에 의해서만 호출되기 때문입니다. 커널 자체 메모리는 pagable이 아니기 때문에 사용자 공간 메모리 만 액세스하면 페이지 오류가 발생할 수 있습니다. 따라서 COPY_ {TO, FROM} _USER ()에 대한 호출과 같은 특정 장소 만 있으면 커널 내에서 페이지 오류가 발생할 수 있습니다. 이러한 장소는 모두 잠을 잘 수있는 코드로 만들어야합니다 (예 : 프로세스 컨텍스트, 자물쇠 없음 등).

다른 팁

그렇다면 SCEHDULER가 인터럽트 컨텍스트를 잠들고 다음 예약 가능한 프로세스를 취하고 통제를 전달하는 것을 막는 것은 무엇입니까?

문제는 인터럽트 컨텍스트가 프로세스가 아니므로 잠을 잘 수 없다는 것입니다.

인터럽트가 발생하면 프로세서가 레지스터를 스택에 저장하고 인터럽트 서비스 루틴의 시작으로 점프합니다. 이는 인터럽트 핸들러가 실행될 때 인터럽트가 발생할 때 실행중인 프로세스의 맥락에서 실행되고 있음을 의미합니다. 인터럽트가 해당 프로세스의 스택에서 실행되고 있으며 인터럽트 핸들러가 완료되면 해당 프로세스가 재개됩니다.

인터럽트 핸들러 내부에서 잠을 자거나 차단하려고하면 인터럽트 핸들러를 중지 할뿐만 아니라 중단 된 프로세스도 정화됩니다. 인터럽트 핸들러가 중단 된 프로세스가 무엇을하고 있는지 알 수 없거나 해당 프로세스가 중단되기에 안전한 경우에도 이것은 위험 할 수 있습니다.

상황이 잘못 될 수있는 간단한 시나리오는 인터럽트 핸들러와 인터럽트하는 프로세스 사이의 교착 상태가 될 것입니다.

  1. 프로세스 1 커널 모드로 들어갑니다.
  2. 프로세스 1 인수 로카.
  3. 인터럽트가 발생합니다.
  4. ISR은 사용을 실행하기 시작합니다 프로세스 1스택.
  5. ISR은 인수하려고합니다 로카.
  6. ISR은 잠을 기다리며 기다립니다 로카 발매 예정.

이 시점에서 교착 상태가 있습니다. 프로세스 1 ISR이 스택으로 수행 될 때까지 실행을 재개 할 수 없습니다. 그러나 ISR은 대기가 막혔습니다 프로세스 1 해제 로카.

그 시점에서 스레드 스위칭 인프라를 사용할 수 없기 때문입니다. 인터럽트를 서비스 할 때 우선 순위가 높은 것만 실행할 수 있습니다. 인터럽트, 작업 및 프로세서 우선 순위에 대한 인텔 소프트웨어 개발자 매뉴얼. 다른 스레드가 실행되도록 허용 한 경우 (질문에서 쉽게 수행 할 수 있음을 암시하는 경우) 아무것도 할 수 없을 것입니다. 페이지 결함을 일으킨 경우 서비스를 사용해야합니다. 인터럽트가 서비스되는 동안 사용할 수없는 커널에서 (이유는 아래 참조).

일반적으로 인터럽트 루틴의 유일한 목표는 장치가 중단을 중단하고 더 낮은 인터럽트 레벨에서 무언가를 대기하는 것입니다 (UNIX에서는 전형적으로는 중단되지 않은 레벨이지만 Windows의 경우 Dispatch, APC 또는 수동 레벨)에서 커널/OS의 더 많은 기능에 액세스 할 수있는 곳에서 무거운 리프팅을하십시오. 보다 - 핸들러 구현.

Linux에 내재 된 것이 아니라 O/S가 어떻게 작동 해야하는지의 속성입니다. 인터럽트 루틴은 어느 시점에서든 실행할 수 있으므로 중단 된 상태가 일관되지 않습니다. 스레드 스케줄링 코드를 중단 한 경우 상태가 일관되지 않으므로 "수면"및 스레드를 전환 할 수 있는지 확인할 수 없습니다. 스레드 스위칭 코드가 중단되는 것을 방지하더라도 스레드 스위칭은 O/S의 매우 높은 레벨 기능이며 모든 것을 보호하면 인터럽트가 이름이 암시하는 명령보다 더 많은 제안이됩니다.

그렇다면 SCEHDULER가 인터럽트 컨텍스트를 잠들고 다음 예약 가능한 프로세스를 취하고 통제를 전달하는 것을 막는 것은 무엇입니까?

일정은 타이머 인터럽트에서 발생합니다. 기본 규칙은 한 번에 하나의 인터럽트 만 열 수 있다는 것입니다. "장치 X의 GOT 데이터"인터럽트에서 잠을 자면 타이머 인터럽트가 실행되어 예약을 할 수 없습니다.

인터럽트도 여러 번 발생하고 겹치게됩니다. "GOT 데이터"를 잠들게 한 다음 더 많은 데이터를 얻는다면 어떻게됩니까? 캐치-모든 규칙이 : 인터럽트에서 자지 않는 것만 큼 혼란스럽고 연약한 것입니다. 당신은 그것을 잘못 할 것입니다.

ISR을 잠들 수 있다고해도하고 싶지 않을 것입니다. 당신은 당신의 ISR이 후속 인터럽트가 누락 될 위험을 줄이기 위해 가능한 빨리 빠르게되기를 원합니다.

인터럽트 핸들러를 차단하지 않는 것은 설계 선택입니다. 일부 데이터가 장치에 있으면 인터럽트 핸들러는 현재 프로세스를 가로 채고 데이터의 전송을 준비하고 인터럽트를 활성화합니다. 핸들러가 현재 인터럽트를 활성화하기 전에 장치가 매달려 야합니다. 우리는 I/O를 바쁘게 유지하고 시스템 반응을 유지하려면 인터럽트 핸들러를 차단하지 않는 것이 좋습니다.

나는 "불안정한 상태"가 필수적인 이유라고 생각하지 않습니다. 프로세스는 사용자 모드 또는 커널 모드에 있든간에 인터럽트에 의해 중단 될 수 있음을 알고 있어야합니다. 인터럽트 핸들러와 현재 프로세스 모두에서 일부 커널 모드 데이터 구조에 액세스하고 레이스 조건이 존재하는 경우 현재 프로세스는 로컬 인터럽트를 비활성화하고 다중 프로세서 아키텍처의 경우 중요한 섹션에서 스핀 록을 사용해야합니다. .

또한 인터럽트 핸들러가 차단되면 깨어날 수 없다고 생각합니다. "블록"이라고 말하면 기본적으로 차단 된 프로세스가 일부 이벤트/리소스를 기다리고 있음을 의미하므로 해당 이벤트/리소스의 대기 시간에 연결됩니다. 리소스가 릴리스 될 때마다 방출 프로세스는 대기 프로세스를 깨우는 일을 담당합니다 (ES).

그러나 실제로 성가신 것은 차단 된 프로세스가 차단 시간 동안 아무것도 할 수 없다는 것입니다. 불공평 한이 처벌에 대해 아무런 문제가 없었습니다. 그리고 아무도 차단 시간을 예측할 수 없었으므로 무고한 프로세스는 불분명 한 이유와 무제한 시간을 기다려야합니다.

본질적으로 문제는 인터럽트 핸들러에서 유효한 "현재"(현재 프로세스 주소 task_structure)를 얻을 수 있는지 여부입니다. 그렇다면 "수면"상태로 만들기 위해 콘텐츠를 수정할 수 있습니다. 상태가 어떻게 든 변경되면 나중에 스케줄러로 돌아갑니다. 대답은 하드웨어에 따라 다를 수 있습니다.

그러나 '전류'가 인터럽트 모드에서 처리하는 것과 관련이 없기 때문에 불가능합니다. 아래 코드를 참조하십시오.

#linux/arch/arm/include/asm/thread_info.h 
94 static inline struct thread_info *current_thread_info(void)
95 {
96  register unsigned long sp asm ("sp");
97  return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
98 }

사용자 모드의 SP 및 SVC 모드는 "동일"입니다 (여기서는 동일 "(여기서는 동일하지는 않습니다. 대신 사용자 모드의 SP 포인트 포인트 사용자 공간 스택 지점, SP Mode의 SP R13_SVC는 사용자 프로세스가 커널 스택을 가리 킵니다. task_structure는 이전 작업 스위치에서 업데이트되었으며, 시스템 호출이 발생하면 프로세스가 다시 커널 공간에 다시 들어가고 SP (SP_SVC)가 여전히 변경되지 않으면이 2 SP가 서로 연관되어 있으며, 이러한 의미에서 동일합니다. '), 따라서 SVC 모드에서 커널 코드는 유효한'current '를 얻을 수 있습니다. 그러나 다른 권한이있는 모드에서 인터럽트 모드는 SP가 '다르다고'CPU_INIT ()에 정의 된 전용 주소를 가리 킵니다. 이 모드에서 계산 된 '현재'는 중단 된 프로세스와 관련이 없으므로 액세스하면 예기치 않은 동작이 발생합니다. 그렇기 때문에 시스템 호출은 잠을 잘 수 없지만 인터럽트 핸들러는 할 수 없으며 시스템 호출은 프로세스 컨텍스트에서 작동하지만 인터럽트하지는 않습니다.

높은 수준의 인터럽트 처리기는 시스템 타이머 인터럽트를 포함하여 모든 우선 순위가 낮은 인터럽트의 작동을 마스킹합니다. 결과적으로 인터럽트 핸들러는 잠을 자게 할 수있는 활동에 참여하는 것을 피해야합니다. 핸들러가 잠자면 타이머가 마스크되어 수면 스레드를 예약 할 수 없기 때문에 시스템이 매달릴 수 있습니다. 이게 말이 되요?

더 높은 수준의 인터럽트 루틴이 일정 기간 후에해야 할 일이 일어나야하는 시점에 도달하면 타이머 큐에 요청을 넣어 다른 인터럽트 루틴을 실행하도록 요청해야합니다 (최우선 순위가 낮습니다. 레벨) 얼마 후.

인터럽트 루틴이 실행되면 원래 인터럽트 루틴 수준으로 우선 순위 레벨을 다시 올리고 실행을 계속할 것입니다. 이것은 수면과 같은 영향을 미칩니다.

Linux 커널에는 인터럽트 스택을 할당하는 두 가지 방법이 있습니다. 하나는 중단 된 프로세스의 커널 스택에 있고 다른 하나는 CPU 당 전용 인터럽트 스택입니다. CPU 당 전용 인터럽트 스택에 인터럽트 컨텍스트가 저장되면 실제로 인터럽트 컨텍스트는 프로세스와 완전히 관련이 없습니다. "현재"매크로는 일부 아키텍처가있는 "현재"매크로가 스택 포인터로 계산되기 때문에 현재 실행중인 프로세스에 대한 유효하지 않은 포인터를 생성합니다. 인터럽트 컨텍스트의 스택 포인터는 일부 프로세스의 커널 스택이 아니라 전용 인터럽트 스택을 가리킬 수 있습니다.

Linux OS의 설계/구현 선택 일뿐입니다. 이 디자인의 장점은 간단하지만 실시간 OS 요구 사항에는 좋지 않을 수 있습니다.

다른 OS에는 다른 설계/구현이 있습니다.

예를 들어, Solaris에서는 인터럽트가 다른 우선 순위를 가질 수 있으며, 이는 대부분의 장치 인터럽트가 인터럽트 스레드에서 호출 될 수 있습니다. 인터럽트 스레드는 각 인터럽트 스레드가 스레드의 맥락에서 별도의 스택을 갖기 때문에 수면을 허용합니다. 인터럽트 스레드 설계는 인터럽트보다 우선 순위가 높은 실시간 스레드에 적합합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top