Linux의 주요 스택은 무엇입니까? 예를 들어, 인터럽트가 발생하면 어떤 스택이 사용될지, 사용자 프로세스 스택과 커널 프로세스 스택의 차이점은 무엇입니까?
답변1
이는 플랫폼에 따라 다릅니다. 특정 플랫폼에 얽매이지 않는 한 이 질문에 답할 방법이 없습니다(x86-32와 x86-64 간의 차이점도 중요합니다). 그러나 x86으로 제한하는 경우 마지막 의견을 바탕으로 몇 가지 정보를 제안할 수 있습니다.
사용자 모드에서 커널 모드까지의 서비스 요청("시스템 호출")에는 인터럽트 스타일과 시스템 입력 스타일이라는 두 가지 주요 스타일이 있습니다. (이 용어는 설명을 위해 제가 만든 것입니다.) 인터럽트 유형 요청은 외부 인터럽트와 정확히 동일한 방식으로 프로세서에 의해 처리되는 요청입니다. x86 보호 모드에서는 이를 int 0x80
(최신) 또는 lcall 7,0
(이전 변형, SysV 호환) 및 소위 구현 사용이라고 합니다.문s(작업 게이트, 인터럽트 게이트 등), 특수 세그먼트 설명자로 구성됩니다. 작업 전환은 프로세서에 의해 수행됩니다. 이 전환 동안 이전 작업 레지스터(스택 포인터 포함)는 이전 작업 TSS에 저장되고 새 작업 레지스터(스택 포인터 포함)는 새 작업 TSS에서 로드됩니다. 다시 말해서,모두"일반적으로" 레지스터는 저장되고 로드됩니다(따라서 작업 시간이 매우 깁니다). (변경이 연기되는 FPU/SSE/등 상태와 관련된 별도의 문제가 있습니다. 자세한 내용은 설명서를 참조하세요.)
이러한 서비스 요청을 처리하기 위해 커널은 차단 가능한 함수 호출 중에 스레드가 전환될 수 있으므로 각 스레드(일명 LWP - 경량 프로세스)에 대해 별도의 스택을 준비합니다. 이 스택은 일반적으로 크기가 더 작습니다(예: 4KB).
x86 작업 스위치가 항상 스택 포인터를 변경하면 커널에 대한 사용자 모드 스택을 재사용할 기회가 없습니다. 반면에 이러한 재사용은 전혀 허용되지 않습니다(소량의 현재 스레드 데이터 제외). 사용자 프로세스 페이지가 안전하지 않을 수 있기 때문입니다. 다른 활성 스레드가 이를 변경하거나 매핑을 해제할 수도 있습니다. 그렇기 때문에 사용자 영역 스택을 사용하여 커널에서 실행하는 것은 단순히 금지됩니다. 따라서 모든 스레드는다른Userland 및 kernelland 스택은 최신 sysenter 스타일 처리에도 적용됩니다. (반면 위에서 이미 언급했듯이 각 스레드는 다른 스레드의 스택이 아닌 자신의 커널 공간을 위한 스택을 가져야 합니다.)
Sysenter 스타일 처리는 훨씬 나중에 설계되었으며 SYSENTER 및 SYSCALL 프로세서 명령을 통해 구현됩니다. 시스템 호출이 모든 레지스터를 보존해야 한다는 오래된(너무 엄격한) 제한을 고려하지 않고 설계되었다는 점에서 다릅니다. 대신, 그들의 디자인은 함수가 특정 레지스터(대부분의 ABI에서는 이를 "임시" 레지스터라고 함)를 임의로 변경할 수 있도록 허용하고 몇 개의 레지스터만 변경하고 이전 값을 유지하도록 주의하는 일반적인 함수 호출 ABI에 더 가깝습니다. 처리 프로그램을 통해 가져옵니다. SYSENTER/SYSEXIT 명령어 쌍(32비트 및 64비트)은 RDX 및 RCX의 이전 콘텐츠를 파괴하고(이상한 방식으로 사용자 공간은 해당 콘텐츠를 올바른 값으로 미리 채워야 함) 새 RIP 및 RSP가 MSR이 로드되어 스택이 즉시 커널 공간으로 전환됩니다. 대조적으로, SYSCALL/SYSRET(64비트 전용)은 RCX 및 R11을 반환 주소 및 플래그로 사용하고아니요스택을 직접 변경하십시오. 그런 다음 커널은 해당 스택의 일부를 사용하여 일부 레지스터를 저장한 다음 자체 스택으로 전환합니다. 그 이유는 1) 사용자 영역 스택이 필요한 모든 값을 보유할 만큼 충분히 크다는 보장이 없고 2) 보안상의 이유로(위 참조) ). 이 시점부터 우리는 다시 스레드별 커널 스택을 갖게 됩니다.
사용자 공간 스레드 외에도 커널 전용 스레드가 많이 있습니다( ps
출력에서 대괄호 안의 이름으로 볼 수 있음). 이러한 각 스레드에는 자체 스택이 있습니다. 1) 특정 이벤트 또는 시간 초과 시 시작되는 주기적인 루틴, 2) 일시적인 작업 또는 3) 실제 인터럽트 핸들러 요청을 처리하는 작업을 구현합니다. (사례 3의 경우 이전 커널에서는 "bh"로, 새 커널에서는 "ksoftirqd"로 명명되었습니다.) 이러한 스레드의 대부분은 단일 논리 CPU에 연결됩니다. 사용자 토지가 없으면 사용자 토지 스택도 없습니다.
AFAIK, Linux의 외부 인터럽트 핸들러는 논리 CPU당 동시에 실행되는 최대 하나의 핸들러로 제한됩니다. 이러한 핸들러 실행 중에는 IO 인터럽트가 허용되지 않습니다. (NMI는 오류 처리가 쉬운 끔찍한 예외입니다.) 위에서 언급한 것과 동일한 이유로 작업 전환 인터럽트 게이트를 사용하고 각 논리 CPU에 대해 자체 스택을 갖습니다.
이미 지적했듯이, 이것의 대부분은 x86에만 국한됩니다. 스택 포인터 교체를 강제하는 작업 스위치는 다른 아키텍처에서는 거의 볼 수 없습니다. 예를 들어 ARM32는 권한 레벨별로 스택 포인터를 갖고 있기 때문에 커널 모드에서 외부 인터럽트가 발생해도 스택 포인터는 변하지 않는다.
커널 개발의 빠른 속도로 인해 이 답변의 일부 세부 사항은 더 이상 사용되지 않을 수 있습니다. 이는 일반적인 제안 사항으로만 고려하고 탐색할 특정 버전에 대해 확인하십시오. x86 인터럽트 처리 및 작업 전환에 대한 추가 지침은 "인텔® 64 및 IA-32 아키텍처 소프트웨어 개발자 매뉴얼 볼륨 3A: 시스템 프로그래밍 가이드, 1부"(인텔 웹사이트에서 무료로 제공)를 참조하세요.
답변2
Linux에서는 사용자 공간에서 실행될 때 사용자 공간 스택이 사용됩니다. 프로세스가 커널 공간에서 실행 중인 경우 프로세스가 "소유한" 커널 스택을 사용합니다. 인터럽트는 커널에서 처리됩니다.
아마도 여러분이 좋아하는 커널의 비공개 부분에 대한 세부 정보를 찾는 더 좋은 곳은 프로그래밍을 위한 웹사이트를 찾아보는 것입니다.커널의 새로운 기능아니면 주변에서 검색해 보세요저수온망Linux의 "커널 페이지". BSD나 Solaris, 심지어 MacOS에서도 비슷한 것을 찾을 수 있을 것입니다. Windows 정보를 얻기가 더 어려울 수 있습니다.
이러한 정보는 일반적인 운영 체제 텍스트에서 논의되지 않으므로 개발자의 설명을 찾아야 합니다.