커널의 초기 스택위치


커널은 처음 시작될때 OS 없이 구동하는 펌웨어와 동일한 방식으로 구동됩니다.

커널 진입점에서 이런 저런 설정을 하게 됩니다.

대표적으로는 MMU 활성화가 있고 그 외적으로는 일반적인 어플리케이션을 실행시키는 것과

유사한 준비를 하게 됩니다.

커널이 사용하게될 MMU table 의 설정, 스택의 설정, 커널의 bss 영역의 초기화와 data 복사등이 있습니다.

MMU table 의 설정은 swapper_pg_dir  이라고 불리는 특별한 영역이 사용됩니다.

이곳에는 시스템의 전체 메모리에 대한 1:1 매핑 테이블과 CPU 마다 사용되는 고유한 시스템 레지스터들

그리고 IO 에 대한 mapping table 이 생성됩니다.

커널의 bss 영역의 초기화와 data 의 복사는 일반적인 어플리케이션을 로딩시켜줄 때 하는 것과 동일합니다.

마지막으로 스택을 설정하게 되는데 여기에서 설정되는 커널 스택이 init_task 의 스택이 되게 됩니다.

보통 커널스택이라고 불리는 것이 바로 이것입니다.


커널은 모든 아키텍쳐에서 공통적으로 사용되는 init_thread_union 이라는 task 를 사용하게 됩니다.

arm 에서는 이것을 위해서 아래와 같이 합니다.

arch/ar/kernel/head-comm.S 

13     .type   __switch_data, %object

14 __switch_data:

15     .long   __mmap_switched

16     .long   __data_loc          @ r4

17     .long   __data_start            @ r5

18     .long   __bss_start         @ r6

19     .long   _end                @ r7

20     .long   processor_id            @ r4

21     .long   __machine_arch_type     @ r5

22     .long   cr_alignment            @ r6

23     .long   init_thread_union + THREAD_START_SP @ sp

24 

25 /*

26  * The following fragment of code is executed with the MMU on in MMU mode,

27  * and uses absolute addresses; this is not position independent.

28  *

29  *  r0  = cp#15 control register

30  *  r1  = machine ID

31  *  r9  = processor ID

32  */

33     .type   __mmap_switched, %function

34 __mmap_switched:

35     adr r3, __switch_data + 4

36 

37     ldmia   r3!, {r4, r5, r6, r7}

38     cmp r4, r5              @ Copy data segment if needed

39 1:  cmpne   r5, r6

40     ldrne   fp, [r4], #4

41     strne   fp, [r5], #4

42     bne 1b

43 

44     mov fp, #0              @ Clear BSS (and zero fp)

45 1:  cmp r6, r7

46     strcc   fp, [r6],#4

47     bcc 1b

48 

49     ldmia   r3, {r4, r5, r6, sp}

50     str r9, [r4]            @ Save processor ID

51     str r1, [r5]            @ Save machine type

52     bic r4, r0, #CR_A           @ Clear 'A' bit

53     stmia   r6, {r0, r4}            @ Save control register values

54     b   start_kernel


__mmap_switched 라는 레이블은 MMU 가 활성화되고 나서 수행되는 첫 주소입니다.

여기서 수행되는 것들은 __switch_data 레이블을 기본값으로 해서 

data load, bss 클리어영역의 정보를 얻어와서 필요한 내용을 수행하고

init_thread_union + THREAD_START_SP @ sp

위의 값을 sp 에 세팅하게 됩니다.

init_thread_union 은 아키텍쳐마다 선언을 하게 되어 있는데 init thread 가 수행되는 공간입니다.

arch/arm/kernel/init_task.c 에는 

33 union thread_union init_thread_union

34     __attribute__((__section__(".init.task"))) =

35         { INIT_THREAD_INFO(init_task) };

36 

37 /*

38  * Initial task structure.

39  *

40  * All other task structs will be allocated on slabs in fork.c

41  */

42 struct task_struct init_task = INIT_TASK(init_task);

43 

44 EXPORT_SYMBOL(init_task);

이렇게 선언이 되어 있고 일반적으로 커널쓰레드의 사이즈는 8K size 를 갖습니다.

크다면 크고 적다면 적기 때문에 커널에서 수행되는 함수들은 스택을 많이 쓰지 않도록 조심해야 합니다.

조용하게 커널이 망가질수 있으니까요.


그리고 초기 stack 의 위치는 아래와 같이 선언됩니다.

include/asm-arm/thread_info.h

18 #define THREAD_SIZE_ORDER   1

19 #define THREAD_SIZE     8192

20 #define THREAD_START_SP     (THREAD_SIZE - 8)

init_thread_union 은 thread_union 이라는 구조인데 

include/linux/sched.h 에는 아래와 같이 선언되어 있습니다.

1247 union thread_union {

1248     struct thread_info thread_info;

1249     unsigned long stack[THREAD_SIZE/sizeof(long)];

1250 };


결국 init_thread_union 이라는 union 이 하나 선언이 되는데 그 위치는 section 에 존재합니다.

그리고 거기서부터 8192K 뒤쪽 바로 아래위치에 스택이 위치합니다.

그리고 마지막 살펴볼 곳이 있습니다.

init_thread_union 을 하나 선언했는데 그 뒤로 8192 위치에 스택을 잡으면 그 사이 공간은 어떻게 확보될까요...

커널 이미지를 만드는 링크 스트립트에는 아래와 같은 내용이 있습니다.

arch/arm/kernel/vmlinux.lds.S

111 #ifdef CONFIG_XIP_KERNEL

112     __data_loc = ALIGN(4);      /* location in binary */

113     . = PAGE_OFFSET + TEXT_OFFSET;

114 #else

115     . = ALIGN(THREAD_SIZE);

116     __data_loc = .;

117 #endif

118 

119     .data : AT(__data_loc) {

120         __data_start = .;   /* address in memory */

121 

122         /*

123          * first, the init task union, aligned

124          * to an 8192 byte boundary.

125          */

126         *(.init.task)

127 

128 #ifdef CONFIG_XIP_KERNEL

129         . = ALIGN(4096);

130         __init_begin = .;

131         *(.init.data)

132         . = ALIGN(4096);

133         __init_end = .;

134 #endif

135 

136         . = ALIGN(4096);

137         __nosave_begin = .;

해당 변수는 섹션중에서 init.task 라는 위치의 init task union 을 위한 공간을 확보하게 되는데 8192 aligned 를 맞추어 

시작위치에 대한 aling 을 맞추고 그 뒤로는 8192 만큼의 공간을 확보하도록 해 줍니다.

확인을 위해서 System.map 을 보고 확인합니다.

System.map

17384 c0319000 A _etext

17385 c031a000 A __data_loc

17386 c031a000 D __data_start

17387 c031a000 D init_thread_union

17388 c031c000 D __nosave_begin

17389 c031c000 D __nosave_end

17390 c031c000 d acct_globals

17391 c031c000 D tasklist_lock

보는 것처럼 init_thread_union 은 8192 만큼의 공간을 확보하고 있고 

초기 스택의 위치는 0xc031c000 - 8 = 0xC031BFF8 이 될것입니다.