댓글 8
-
홍문화
2011.10.21 22:52
-
홍문화
2011.10.22 12:22
저도 __u32 cpu에 값을 대입 하는 부분을 지금까지 분석한 우리 소스에서 찾지 못했습니다.
아마도 이 부분이 문제의 소지가 될 수도 있다고 판단해 패치가 적용된게 아닌가 합니다.
그럴 경우는 거의 없겠지만 부트 CPU ID가 0이 아닐 수도 있거나 또는 명시적으로 레지스터에서 값을
읽어서 대입 해주는게 바람직한 방법이라고 개발자가 생각한게 아닐까 합니다.
-
유경환
2011.10.22 10:02
네 그런식으로 찾아봤는데 cpu설정은 못찾았어요 ^^;
-
무명
2011.10.24 04:27
유경환 님, 현재 ARM 커널 소스 코드에서는 Logical CPU ID가 항상 0으로 설정되어 부팅되는 것이 맞습니다.
어떻게 0으로 설정되는지는 이미 이야기 나누었었고
그래서 Physical CPU ID가 0이 아닌 CPU에서 부팅되는 경우를 위해 위의 패치를 만들었다고 하네요.
그런데 위의 패치는 아쉽게도 commit 되지는 않았습니다.^^
메일링리스트에 패치한다고 다 commit 되는 건 아니니까요.
메일링리스트에서 사람들이 위의 패치에 대해 몇가지 문제를 지적했는데 이미 내부에서도 말이 많았었다고 하네요.
새로운 패치가 나왔다고 하니까 원하신다면 찾아드리겠습니다. 물론 구글링으로요. :)
-
무명
2011.10.24 09:55
그렇게 생각합니다.
저희가 이야기를 나누었던 것은 구조체 전역변수의 경우,
하나의 멤버도 초기화되지 않으면 모든 멤버가 BSS 영역에서 0으로 초기화되고
하나의 멤버라도 초기화되면 다른 멤버들은 데이터 영역에서 0으로 초기화된다는 그런 이야기를 했던 것 뿐입니다.
-
유경환
2011.10.24 10:17
문화님이 잠시 빠진동안 얘기를 나누었습니다. 아시다시피 init_task는 data 영역의 일부영역을 (data.init_task section)
할당하여 만들어지고 init_task의 주요 값들도 컴파일 타임에 매크로에의해서 이미 설정되어져 버립니다.
(홍문화씨가 앞글에서 적은대로)
그런데 아무리 찾아도 thread_info의 cpu값을 할당하는 코드는 없었습니다. (메일링리스트에서 찾은 패치코드 빼고)
BSS 영역이야 0으로 초기화 하는 코드가 start-up code에 명시적으로 들어가 있으니 문제가 없는데,
아무것도 할당하지 않은 data 영역에는 무슨값이 들어가나가 문제였으며 짐작은 0으로 초기화 된다는 심증은 가는데
확증이 없어서 헤매던 차에 한일님이 테스트 코드 실험을 통해 data영역은 아무 값도 초기화 하지 않은 부분은
컴파일타임에 이미지에다가 0으로 채워진다는 결론을 얻었습니다.
따라서 init_task의 cpu 값은 0입니다.
테스트 코드
----------------------------------------------------------------
struct aa
{
int a;
int b;
};
static struct aa ccc = {1};
int main()
{
printf("%dn", ccc.b);
}
----------------------------------------------------------------
-
홍문화
2011.10.24 09:51
"어떻게 0으로 설정되는지는 이미 이야기 나누었었고"
CPU ID가 초기화 되지 않은 전역 변수라서 0의 값을 가지게 되는거 아닌가요?
저도 좀 알려주세요.ㅋ
저는 제가 잘못 알고 있는 지식을 바로잡아 주시는 분께, 혹은 바로잡기 위해 같이 고민 해주시는 분께
진심으로 감사히 생각합니다. ^^;
-
홍문화
2011.10.24 11:28
감사합니다.
본의 아니게 지난 주에는 중간에 공백기?를 만들어 버렸네요.
스터디 시간에 좀 더 충실하도록 노력하겠습니다.
.
벌써 패치된 내용이 있군요. 버전 업 문제도 생각을 해봐야 겠네요.
저는 일단 head-common.S에서부터 접근을 해봤습니다.
__mmap_switched: 의 맨 마지막 라인에 다음과 같이 스택 포인터를 얻어오는 부분이 있습니다.
.long init_thread_union + THREAD_START_SP @ sp
THREAD_START_SP는 thread_info.h에 THREAD_SIZE(8192) - 8로 정의 되어 있습니다.
스택 크기를 8KB로 잡고 8바이트를 빼주고 있습니다. 8바이트를 빼는 이유는 아마도 스택을 DA(decrement after)
방식으로 사용 하기 때문이 아닌가 생각합니다.
문제는 init_thread_union 이 녀석이 나타내는 주소인데 상당히 난감한 상황이 벌어집니다.
trace 해보면 sched.h의 extern union thread_union init_thread_union; 이 녀석을 가리킵니다.
이 녀석은 init_task.c에서 아래와 같이 초기화를 해서 #include <linux/sched.h>를 통해 외부로 노출 시킨 것입니다.
union thread_union init_thread_union __init_task_data = { INIT_THREAD_INFO(init_task) };
__init_task_data는 init_task.h에 아래와 같이 정의 되어 있습니다.
#define __init_task_data __attribute__((__section__(".data..init_task")))
즉, init_thread_union를 초기화 해서 .data..init_task 섹션으로 만들겠다는 것입니다.
INIT_THREAD_INFO(init_task)은 thread_info.h에 아래와 같이 정의 되어 있습니다.
즉, 공용체 init_thread_union의 멤버인 struct thread_info thread_info의 멤버 변수 중 특정 변수들을 초기화 합니다.
#define INIT_THREAD_INFO(tsk)
{
.task = &tsk,
.exec_domain = &default_exec_domain,
.flags = 0,
.preempt_count = INIT_PREEMPT_COUNT,
.addr_limit = KERNEL_DS,
.cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) |
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) |
domain_val(DOMAIN_IO, DOMAIN_CLIENT),
.restart_block = {
.fn = do_no_restart_syscall,
},
}
INIT_THREAD_INFO(init_task)의 매개변수 init_task는 init_task.c에서 아래와 같이 선언과 동시에 초기화 되었습니다.
struct task_struct init_task = INIT_TASK(init_task);
그리고 .task = &tsk를 통해 thread_info의 멤버로 대입 되었습니다.
이제 vmlinux.lds를 살펴보겠습니다.
. = ALIGN(8192); *(.data..init_task)
init_task.c 에서 union thread_union init_thread_union __init_task_data = { INIT_THREAD_INFO(init_task) };를 통해
init_thread_union을 전역 변수로 만들고 초기화 함과 동시에 .data..init_task 섹션으로 만들었습니다.
즉, 초기화한 전역변수 init_thread_union를 데이터 섹션에 ALIGN(8192);로 정렬해 배치하고 있습니다.
결국 head-common.S에서 초기화한 스택 포인터는 데이터 섹션의 *(.data..init_task)를 시작 주소로 해서
+ THREAD_START_SP 한 위치를 가리키게 되는 것입니다. 이 상태에서 바로 start_kernel()로 점프를 한 것입니다.
current_thread_info()는 thread_info.h에 아래와 같이 정의 되어 있습니다.
register unsigned long sp asm ("sp");
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
즉, 데이터 섹션 .data..init_task으로 초기화 되어 배치된 전역 변수 init_thread_union의 주소를 리턴합니다.
그리고 current_thread_info()->cpu에 의한 cpu 변수 참조 동작은 init_thread_union 내의 thread_info 내의
__u32 cpu를 참조하게 되는 것입니다. 그런데 init_thread_union은 전역 변수이고 cpu 멤버에 대해 특별한 초기 값을
대입하지 않았기 때문에 0의 값을 가지고 있게 될 것입니다.
앞에서 말씀 드린 난감한 상황이 바로 이러한 상황인데 스택을 데이터 영역 내의 8KB 공간으로 사용하고 있다는
것입니다. 즉, start_kernel 호출 이후 스택 포인터 SP는 데이터 영역 내에서 움직이고 있는 것입니다.
잘못된 내용 지적 부탁 드립니다.