Linux v6.18.37 · 개념과 코드 읽기

Signal frame과 user context 복원

이 코드는 어떤 문제를 푸나요?

시그널 처리기는 원래 프로그램의 실행을 잠깐 빌려 실행됩니다. 처리기가 끝난 뒤 돌아가려면 중단한 상태를 사용자 스택에 보존해야 합니다. arm64는 확장 context record, x86은 FP/xstate를 포함한 프레임, RISC-V는 해당 ABI의 레지스터와 벡터 상태를 사용합니다. 대표로 arm64 프레임 배치 초기화를 읽습니다.

읽을 범위: v6.18.37 · arch/arm64/kernel/signal.c · init_user_layout 156–169행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.

먼저 알아둘 개념

사용자 프레임

사용자가 수정할 수 있는 메모리입니다. sigreturn은 이 내용을 신뢰하지 않고 복원 가능한 상태인지 검사해야 합니다.

가변 길이 확장

SVE/SME 등 큰 상태를 담기 위해 고정 구조체 뒤에 확장 record 배치가 필요할 수 있습니다.

정렬

스택과 record의 ABI가 요구하는 주소 경계입니다. 단순히 필요한 바이트 수만 더하면 되는 것은 아닙니다.

처음 읽을 때

기본 프레임, 확장 공간, 종료 표시를 한 메모리 그림에 표시하십시오.

더 깊이 살펴볼 때

예약 공간의 한계, extra_context 연결, 복귀 시 권한 검사의 필요성을 이어서 검토하십시오.

그림으로 보는 변화

Signal frame과 user context 복원의 단계별 개념 그림
각 단계에 화살표 의미와 생략 범위를 표시했습니다. 주소·숫자 예제는 실제 장치 값을 뜻하지 않습니다.
1단계 설명

1단계 고정

GIF 원본 열기

1. 배치 계산 초기화

레이아웃 계산 구조를 0으로 초기화합니다.

화살표는 커널의 계산 상태 변경이며 사용자 스택 쓰기와 구분합니다.

2. 고정 영역 뒤에서 시작

__reserved까지의 offset을 현재 크기로 삼습니다.

메모리 그림에서 낮은 offset에서 높은 offset으로 공간을 잡습니다.

3. 종료·확장 여유 확보

terminator와 extra_context 공간을 한계에서 뺍니다.

남은 크기는 일반 context record를 배치할 수 있는 여유입니다.

init_user_layout를 한 줄씩 읽기

줄 번호는 v6.18.37 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.

static void init_user_layout(struct rt_sigframe_user_layout *user)
{
	const size_t reserved_size =
		sizeof(user->sigframe->uc.uc_mcontext.__reserved);

	memset(user, 0, sizeof(*user));
	user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);

	user->limit = user->size + reserved_size;

	user->limit -= TERMINATOR_SIZE;
	user->limit -= EXTRA_CONTEXT_SIZE;
	/* Reserve space for extension and terminator ^ */
}
static void init_user_layout(struct rt_sigframe_user_layout *user)

시그널 프레임을 배치할 때 사용할 커널 측 계산 구조를 초기화합니다. 매개변수 이름 user가 곧 __user 메모리라는 뜻은 아닙니다.

	const size_t reserved_size =

고정 시그널 구조 안에 예약된 확장 공간의 크기를 계산할 상수를 선언합니다.

		sizeof(user->sigframe->uc.uc_mcontext.__reserved);

sizeof는 __reserved 필드의 타입 크기를 구합니다. 이 표현식은 user->sigframe의 주소를 실제로 역참조하여 사용자 데이터를 읽는 동작이 아닙니다.

	memset(user, 0, sizeof(*user));

레이아웃 관리 구조 자체를 0으로 만듭니다. 아직 사용자 스택 전체에 시그널 프레임을 복사하는 단계는 아닙니다.

	user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);

고정 프레임 시작에서 __reserved까지의 바이트 거리를 구해 현재 사용 크기의 출발점으로 정합니다.

	user->limit = user->size + reserved_size;

고정 부분 끝에 reserved_size를 더해 확장 기록이 들어갈 초기 한계를 구합니다.

	user->limit -= TERMINATOR_SIZE;

마지막 종료 표시가 들어갈 자리를 미리 빼 둡니다. 가변 기록을 가득 채워도 끝을 표시할 공간이 남아야 합니다.

	user->limit -= EXTRA_CONTEXT_SIZE;

추가 확장 영역을 가리킬 extra_context 공간도 미리 확보합니다. 일반 기록의 여유 공간과 관리용 공간을 구분합니다.

함께 생각해 볼 질문

memset(user)는 사용자 스택을 지웁니까?

이 함수의 user는 레이아웃 관리 구조체 포인터입니다. 이름만 보고 사용자 주소라고 판단하면 안 됩니다.

시그널 처리기가 return만 하면 커널 상태가 복원됩니까?

트램펄린과 rt_sigreturn 경로가 프레임을 읽고 검사하여 복원합니다.

큰 벡터 상태는 시그널 비용에 영향을 줍니까?

예. 저장할 데이터와 프레임 크기가 증가할 수 있습니다.

출처와 읽은 범위

Linux stable v6.18.37 · arch/arm64/kernel/signal.c

해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고

맨 위로 ↑