ARM Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

203239
1625
172600


[리눅스커널] 프로세스는 언제 시그널을 받을까? __irq_usr 레이블 코드 분석 12. 시그널

프로세스가 자신에게 시그널이 전달됐는지 확인하는 다른 지점은 __irq_usr 레이블입니다.
유저 공간에서 코드를 실행 중 인터럽트가 발생하면 __irq_usr 레이블을 실행합니다.  

이번 소절에서는 __irq_usr 레이블 소스 분석을 통해 프로세스가 시그널을 받는 과정을 살펴보겠습니다. 


유저 공간에서 코드 실행 중 인터럽트가 발생했을 때 __irq_usr 이란 인터럽트 벡터를 실행합니다.

이 동작은 다음과 같은 ftrace 로그로 확인할 수 있습니다.
01 chromium-browse-1322 [000] d.h. 1519.742814: irq_handler_entry: irq=86 name=mmc1
02 chromium-browse-1322 [000] d.h. 519.742815: bcm2835_mmc_irq+0x14/0x754 <-__handle_irq_event_percpu+0xbc/0x224
03 chromium-browse-1322  [000] d.h.  1519.742826: <stack trace>
04 => bcm2835_mmc_irq+0x18/0x754
05 => __handle_irq_event_percpu+0xbc/0x224
06 => handle_irq_event_percpu+0x3c/0x8c
07 => handle_irq_event+0x54/0x78
08 => handle_level_irq+0xc0/0x16c
09 => generic_handle_irq+0x34/0x44
10 => bcm2836_chained_handle_irq+0x38/0x50
11 => generic_handle_irq+0x34/0x44
12 => __handle_domain_irq+0x6c/0xc4
13 => bcm2836_arm_irqchip_handle_irq+0x60/0xa8
14 => __irq_usr+0x4c/0x60
15 => 0x9fe19e 

위 ftrace 로그를 한 문장으로 요약해 해석하면 다음과 같습니다.

    chromium-browse(pid: 1322) 프로세스 실행 도중 86번 mmc1 인터럽트가 발생했다.

여기서 12번째 줄 로그를 보면 __irq_usr 레이블을 시작으로 인터럽트에 대한 처리를 시작하며 인터럽트 핸들러 함수인 bcm2835_mmc_irq() 함수는 2번째 줄 로그에서 볼 수 있습니다.


그런데 __irq_usr 레이블에서 프로세스가 자신에게 시그널이 전달됐는지 체크하는 과정은 ret_fast_syscall 레이블 로직과 거의 유사합니다. 
1단계: 프로세스 struct thread_info 구조체 flags가 _TIF_WORK_MASK인지 체크
2단계: do_work_pending() 함수 호출

이제부터 __irq_usr 레이블 코드를 분석해볼까요? 
[https://github.com/raspberrypi/linux/blob/rpi-4.19.y/arch/arm/kernel/entry-armv.S]
1 __irq_usr:
2 usr_entry
3 kuser_cmpxchg_check
4 irq_handler
5 get_thread_info tsk
6 mov why, #0
7 b ret_to_user_from_irq
8 UNWIND(.fnend )
9 ENDPROC(__irq_usr)
10
11 ENTRY(ret_to_user_from_irq)
12 ldr r2, [tsk, #TI_ADDR_LIMIT]
13 cmp r2, #TASK_SIZE
14 blne addr_limit_check_failed
15 ldr r1, [tsk, #TI_FLAGS]
16 tst r1, #_TIF_WORK_MASK
17 bne slow_work_pending
18 slow_work_pending:
19 mov r0, sp @ 'regs'
20 mov r2, why @ 'syscall'
21 bl do_work_pending

1단계: 프로세스 struct thread_info 구조체 flags가 _TIF_WORK_MASK인지 체크
__irq_usr 레이블 코드에 대한 전반적인 이해를 돕기 위해 1번째 줄 코드부터 분석하겠습니다.
1 __irq_usr:
2 usr_entry
3 kuser_cmpxchg_check
4 irq_handler
5 get_thread_info tsk
6 mov why, #0
7 b ret_to_user_from_irq

1~4번째 줄 코드에서 인터럽트 핸들링을 실행합니다. 이후 7번째 줄 코드와 같이 ret_to_user_from_irq 레이블로 브랜치합니다. 처리 과정을 요약하면 다음과 같습니다.

    유저 모드에서 인터럽트가 발생하면 인터럽트 핸들링 후 ret_to_user_from_irq 
     레이블을 실행합니다. 

다음 ret_to_user_from_irq 레이블 코드를 보겠습니다.
11 ENTRY(ret_to_user_from_irq)
12 ldr r2, [tsk, #TI_ADDR_LIMIT]
13 cmp r2, #TASK_SIZE
14 blne addr_limit_check_failed
15 ldr r1, [tsk, #TI_FLAGS]
16 tst r1, #_TIF_WORK_MASK
17 bne slow_work_pending

프로세스 최상단 주소를 갖고 있는 tsk(r9) 레지스터에 접근해서 struct thread_info 구조체 flags 필드에 저장된 값을 읽습니다. 이 값이 _TIF_WORK_MASK 플래그로 설정됐는지 점검합니다.

이번에도 ret_fast_syscall 레이블에서와 같이 flags 필드에 _TIF_WORK_MASK 플래그가 설정됐는지 확인합니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/arch/arm/include/asm/thread_info.h]
#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
 _TIF_NOTIFY_RESUME | _TIF_UPROBE)

struct thread_info flags 필드가 _TIF_WORK_MASK 플래그에 선언된 플래그 중 하나이면 slow_work_pending 레이블로 브랜치합니다. 이 코드를 시그널 처리 관점으로 해석하면 다음과 같습니다.

     struct thread_info flags 필드가 _TIF_SIGPENDING로 설정됐으면 
   slow_work_pending 레이블로 브랜치합니다. 

만약 struct thread_info flags 필드가 _TIF_SIGPENDING가 아니면 다른 처리를 하지 않고 유저 공간으로 복귀합니다.

2단계: do_work_pending() 함수 호출
slow_work_pending 레이블 코드를 봅시다. 
18 slow_work_pending:
19 mov r0, sp @ 'regs'
20 mov r2, why @ 'syscall'
21 bl do_work_pending

r0 레지스터에 스택 주소, r2에는 시스템 콜 번호를 저장한 후 do_work_pending() 함수로 브랜치합니다.

여기까지 프로세스가 자신에게 시그널이 전달됐는지 어떻게 확인하는지 알아 봤습니다.
소스 분석으로 다음 내용을 알게 됐습니다.
1. 시스템 콜을 핸들링 마무리 한 후 유저 공간으로 복귀하기 전 시그널 전달 확인
2. 유저 프로세스가 인터럽트 핸들링 후 유저 공간으로 복귀하기 전 시그널 전달 확인

#Referene 시그널
시그널이란
시그널 설정은 어떻게 할까
시그널 생성 과정 함수 분석
프로세스는 언제 시그널을 받을까
시그널 전달과 처리는 어떻게 할까?
시그널 제어 suspend() 함수 분석 
시그널 ftrace 디버깅

"혹시 궁금점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답글 올려드리겠습니다!" 





핑백

덧글

댓글 입력 영역