ARM Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

201239
1625
172598


[리눅스커널] 프로세스: struct thread_info 구조체 주소 위치는 어디일까? 4. 프로세스(Process) 관리

앞에서 thread_info 구조체의 필드를 소개했습니다. 이어서 이 구조체가 어느 위치에 있는지 알아봅시다.

프로세스 스택에서 thread_info 구조체의 위치 확인

thread_info 구조체는 프로세스의 세부 실행 속성 정보를 담고 있으며 프로세스마다 1개씩 존재합니다. 다음 그림을 보면서 thread_info 구조체의 위치를 알아봅시다.

 
 그림 4.15 프로세스 스택에서 thread_info 구조체의 위치

그림 4.15에서 프로세스 스택의 최상단 주소는 0x80C00000이고, 스택의 최하단 주소는 0x80C02000입니다. 그런데 thread_info 구조체의 주소는 그림 4.15와 같이 프로세스 스택의 최상단 주소인 0x80C00000입니다. 

이 정보를 일반화하면 다음과 같은 사실을 이끌어낼 수 있습니다.

thread_info 구조체는 프로세스 스택의 최상단 주소에 있다.
thread_info 구조체는 프로세스마다 1개씩 있다.

---
스택의 최하단 주소가 0x80C02000인 이유는 뭘까요? ARM 아키텍처에서 프로세스가 실행되는 스택의 크기는 0x2000바이트로 고정돼 있기 때문입니다.
---

이어서 프로세스가 자신에게 주어진 스택 공간을 어떻게 쓰는지 확인해보겠습니다.

프로세스가 스택을 어떻게 쓰는지 알아보기 

리눅스 커널에서 프로세스 스택은 높은 주소에서 낮은 주소 방향으로 자랍니다. 그림 4.15에서 화살표 방향을 눈여겨봅시다. 함수는 프로세스 스택의 최하단 주소(0x80C02000)에서 최상단 주소(0x80c0000) 방향으로 호출됩니다.

먼저 프로세스가 실행할 때는 커널 함수를 호출합니다. sys_write() 함수에서 vfs_write() 함수 방향으로 함수를 호출하는 것입니다. 가장 먼저 호출된 함수는 프로세스 스택의 최하단 주소에 위치해 있고 프로세스 스택의 최상단 주소 방향으로 스택을 사용합니다.

또한 함수를 실행하면서 로컬 변수나 복귀 레지스터(링크드 레지스터: 자신을 호출한 함수 주소)도 프로세스 스택에 저장(푸시)합니다. 이때 필요한 스택 메모리를 프로세스 스택 공간을 최하단 주소에서 최상단 주소 방향으로 사용합니다. 즉, 프로세스 스택은 최하단 주소에서 최상단 주소 방향으로 자란다고 볼 수 있습니다.

__wake_up_common_lock() 함수가 실행을 마치면 pipe_write() 함수로 복귀하고, pipe_write() 함수의 실행이 끝나면 __vfs_write() 함수로 복귀합니다. 자료구조 시간에 배운 스택(푸시, 팝)과 같은 동작입니다.

이번에는 자료구조 중 하나인 스택을 통해 프로세스가 스택 공간에서 어떻게 동작하는지 조금 더 알아봅시다. 프로세스가 새로운 함수를 호출하면 프로세스의 스택 공간에 이전에 호출했던 복귀 레지스터를 푸시합니다. 이렇게 하는 이유는 함수 실행이 끝나고 자신을 호출한 함수 주소를 알아야 이전 함수로 복귀할 수 있기 때문입니다. 

프로세스가 함수 실행을 마치고 이전에 자신을 호출했던 함수로 돌아가려고 할 때 복귀 레지스터를 스택에 팝(Pop)합니다. 

여기서 한 가지 의문이 생깁니다. 바로 프로세스가 새로운 함수를 실행할 때 프로세스 스택을 대상으로 푸시와 팝을 수행하는 리눅스 커널 코드를 볼 수 없다는 점입니다.

C 코드에서는 스택 푸시와 팝을 수행하는 코드를 볼 수 없습니다. 대신 이러한 역할을 하는 코드를 어셈블리 코드에서 확인할 수 있습니다. 프로세스 스택 공간에 푸시/팝을 수행하는 동작은 CPU 아키텍처(ARM, x86, ARM64)마다 다르기 때문입니다. 예를 들어, write 시스템 콜 핸들러인 sys_write() 함수를 봅시다.

https://elixir.bootlin.com/linux/v4.19.30/source/fs/read_write.c
SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
size_t, count)
{
return ksys_write(fd, buf, count);
}

보다시피 C 코드로는 스택에 팝/푸시하는 과정을 알 수는 없습니다. 이번에는 sys_write() 함수를 어셈블리 코드로 보겠습니다.

01 8026abc4 <sys_write>:
02 8026abc4: e1a0c00d mov ip, sp
03 8026abc8: e92ddbf0 push {fp, ip, lr, pc}
04 8026abcc: e24cb004 sub fp, ip, #4
...
05 8026abdc:   ebfa300c    bl ksys_write
06 8026abe0:   e89da800    ldm sp, {fp, sp, pc}

02번째 줄 코드를 보면 현재 스택(sp) 주소를 ip(r12) 레지스터에 저장합니다.

02 8026abc4: e1a0c00d mov ip, sp

03번째 줄을 보면 push 명령어를 써서 프로세스 스택 공간에 {fp, ip, lr, pc} 레지스터를 푸시합니다.

03 8026abc8: e92ddbf0 push {fp, ip, lr, pc}

이번에는 함수 실행을 마치고 스택에 푸시된 레지스터를 팝하는 06번째 줄 코드를 보겠습니다.

06 8026abe0:   e89da800    ldm sp, {fp, sp, pc}

위 명령어를 실행하면 스택 주소를 기준으로 이미 스택 주소에 푸시된 {fp, sp, pc} 레지스터 값을 ARM 레지스터에 다시 로딩합니다.

여기서 주의깊게 봐야 할 점이 있습니다. 

01 8026abc4 <sys_write>:
02 8026abc4: e1a0c00d mov ip, sp
03 8026abc8: e92ddbf0 push {fp, ip, lr, pc}
...
06 8026abe0:   e89da800    ldm sp, {fp, sp, pc}

sys_write() 함수를 처음 실행했을 때 03번째 줄을 보면 lr(r14) 레지스터를 스택에 푸시했습니다. lr(r14) 레지스터는 자신을 호출한 함수 주소를 저장합니다. 이 주소를 프로세스 스택에 푸시했습니다.

이번에는 스택에 푸시한 값을 팝(로딩)하는 06번째 줄 코드를 보겠습니다. 

ldm sp, {fp, sp, pc}

명령어의 맨 오른쪽을 보면 pc로 지정돼 있습니다. 03번째 줄에서 저장한 lr(r14) 레지스터를 이제 ARM 프로그램 카운터인 pc로 로딩하겠다는 의미입니다. 06번째 줄을 실행하면 이전에 자신을 실행한 함수로 복귀합니다. 그림 4.15를 기준으로 ret_fast_syscall 함수(레이블)로 복귀합니다.

스택 메모리에 저장된 주소를 ARM 프로세스 PC(Program Counter)에 저장하면 PC에 저장된 주소에 있는 어셈블리 코드를 가져와서(fetch) 실행합니다.

다음 절에서는 thread_info 구조체의 필드 중 preempt_count가 어떤 코드에서 변경되고 리눅스 커널에서 어떤 의미를 갖고 있는지 깊이 있게 살펴보겠습니다.

* 유튜브 강의 동영상도 있으니 같이 들으시면 좋습니다. 




#프로세스

프로세스 소개 
프로세스 확인하기  
프로세스는 어떻게 생성할까?  
유저 레벨 프로세스 실행 실습  
커널 스레드  
커널 내부 프로세스의 생성 과정   
프로세스의 종료 과정 분석  
태스크 디스크립터(task_struct 구조체)  
스레드 정보: thread_info 구조체  
프로세스의 태스크 디스크립터에 접근하는 매크로 함수  
프로세스 디버깅  
   * glibc의 fork() 함수를 gdb로 디버깅하기  


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

Thanks,
Austin Kim(austindh.kim@gmail.com)


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


 




핑백

덧글

댓글 입력 영역