2014년 3월 25일 화요일

[Linux Kernel] 47주차(2014.03.22) 후기

10차 ARM 리눅스 커널 스터디 47주차(2014.03.22) 후기입니다. 

# 일시 : 2014.03.22 (47주차)
# 모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
# 장소 : 토즈 타워점
# 장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
# 참여인원 : 5명
# 스터디 진도 : 
 - start_kernel()-> page_alloc_init();
 - start_kernel()-> parse_early_param();
 - start_kernel()-> jump_label_init();
 - start_kernel()-> setup_log_buf(0);
 - start_kernel()-> pidhash_init();
 - start_kernel()-> vfs_caches_init_early();

# 46주차 마지막 부분 논의 (컴파일러 문제점)
## if문의 해석으로 논의하다가 중단했던 부분을 계속 토론하였습니다. 

먼저 함수 선언을 통해서 사용된 매개변수의 형식을 알아보았습니다. 
__mutex_lock_common 함수 선언은 다음과 같습니다. 
<code> 
static __always_inline int __sched                                                                                                       
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,                                                               
                    struct lockdep_map *nest_lock, unsigned long ip,                                                                     
                    struct ww_acquire_ctx *ww_ctx) 
</code>

이 함수에서 if문의 코드의 결과에 따라서 어떻게 되는지 논의한 부분은 이곳입니다. 
<code>
if(!__builtin_constant_p(ww_ctx == NULL)) {
</code>
여기서 *ww_ctx 는 함수를 호출할때 NULL로 매개변수를 전달했고, 중간에 변경된
부분이 없기 때문에 *ww_ctx : NULL 입니다. 
따라서 ww_ctx (NULL) == NULL이 상수 값이 되므로,
!__build_constant_p(상수)로 이 if문은 부정될 것입니다. 
이 가정이 맞는지 검증해 보았습니다. 

## J님의 테스트 과정
내부에 *ww_ctx를 NULL로 선언한 후 컴파일 옵션을 -O0, -O1, -O2, -O3로 변화해
가면서 결과가 어떻게 되는지 검증해 주셨습니다. 

<code>
int main(){
        const int *ww_ctx = NULL;
        if(!__builtin_constant_p(ww_ctx == NULL))
        {
                printf("in \n");
        }else
                printf("out\n");
}
</code>

root@ubuntu:/work/study/module/builtin# gcc builtin_1.c -O0
root@ubuntu:/work/study/module/builtin# ./a.out
in

root@ubuntu:/work/study/module/builtin# gcc builtin_1.c -O1
root@ubuntu:/work/study/module/builtin# ./a.out
out

root@ubuntu:/work/study/module/builtin# gcc builtin_1.c -O2
root@ubuntu:/work/study/module/builtin# ./a.out
out

root@ubuntu:/work/study/module/builtin# gcc builtin_1.c -O3
root@ubuntu:/work/study/module/builtin# ./a.out
out

J님의 검증 결론 컴파일 옵션에 따라 실행 결과는 다르다는 결론을 알 수 있었습니다.  

우리가 사용하는 커널의 Makefile을 보니 HOSTCXXFLAGS = -O2로 최적화합니다.
따라서 if문의 부정된다. 

## 리눅스 토발즈의 이 코드에 대한 회신

질문자의 메일을 보면 좀더 가독성이 좋은 코드가 좋지 않느냐는 의견입니다. 
__buildtin_> __builtin_constant_p(ww_ctx == NULL) is equal 
to __builtin_constant_p(ww_ctx != NULL), 
but the former is more readable, 
since it shows we expect ww_ctx to be null.

토발즈의 회신은 강경(???)합니다. 
Stop the f*cking around already! The  whole "we expect ww_ctx to be
null" thing shows that YOU DO NOT SEEM TO UNDERSTAND WHAT THE TEST
ACTUALLY IS!

The expression

  __builtin_constant_p(ww_ctx == NULL)

has ABSOLUTELY NOTHING to do with whether ww_ctx is NULL or not!
Christ, can you really not understand that?

그리고 계속되는 내용을 보면 컴파일러에 따라 다를 수 있다고 이야기
합니다.  

## 토론의 결론 
 우리의 논의를 거슬러 올라가 보면, 컴파일러가 최적화에 
따라서 이 코드는 실행이 달라 질 가능성이 있기 때문에 컴파일러를 
잘 선택해야 하고 가독성이나 이러한 오해를 줄이기 위해서 코드가 
고쳐지는 것이 맞다는 의견이었습니다.  

## 3.14에서 커널 코드를 알아보았습니다. 
우선 함수의 선언도 바뀌었네요. const bool use_ww_ctx 가 추가되었습니다. 
<code>
__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,   
                    struct lockdep_map *nest_lock, unsigned long ip,         
                    struct ww_acquire_ctx *ww_ctx, const bool use_ww_ctx)    
</code>

그리고 우리가 논의했던 __builtin_constant_p(ww_ctx == NULL) 가 바뀌었습니다.
<code>
if (use_ww_ctx) { 
</code>

우리가 토론했던 것이 반영되었네요. 스터디 시간 중 많은 시간을 할애하여 토론한 
것과 커널의 방향이 같이 간다는 것을 입증한 것이기도 합니다. 
 이것을 보면 오픈소스가 어떻게 발전할 수 있는지 좋은 사례라고 생각합니다.
서로 이상하다고 생각되는 부분이 있으면 의문점을 제시하고 토론해서 
좋은 결론을 이끌어 냅는 것입니다.  

# 스터디 주요 함수의 처리한 내용 요약

- page_alloc_init();
 // cpu_chain에 page_alloc_cpu_notify를 연결함 (mutex lock/unlock 사용) 

- pr_notice("Kernel command line: %s\n", boot_command_line); 
 // "Kernel command line: console=ttySAC2,115200 init=/linuxrc"

- parse_early_param();
 // command arg에서 각 요소들을 파싱하여 early init section으로 
 // 설정된 디바이스 초기화.
 // 우리는 serial device가 검색이 되지만 config설정은 없어서 
 // 아무것도 안함. 

- parse_args("Booting kernel", static_command_line, __start___param,                              
                   __stop___param - __start___param,                                                    
                   -1, -1, &unknown_bootoption);                                                        
  // DTB에서 넘어온 bootargs를 파싱하여 param, val을 뽑아내고 그에 대응되는
  // kernel_param 구조체에 값을 등록함. 

- jump_label_init();
  // HAVE_JUMP_LABEL 이 undefined 이므로 NULL 함수

- setup_log_buf(0); 
  // defalut log_buf의 크기는 __LOG_BUF_LEN: 0x20000 (128KB) 임
  // early_param 에서 호출했던 log_buf_len 값이 있다면 
  // log_buf의 크기를 넘어온 크기로 만듬 

- pidhash_init();
  // pidhash의 크기를 16kB만큼 할당 받고 4096개의 hash list를 만듬 

- vfs_caches_init_early(); 
  // Dentry cache, Inode-cache용 hash를 위한 메모리 공간을 
  // 각각 512kB, 256kB만큼 할당 받고, 
  // 131072, 65536개 만큼 hash table을 각각 만듬

# 48주차는 sort_main_extable(); 부터 시작합니다. 
mm_init(); 을 들어가서 리눅스 커널의 메모리 관리자인 버디, 슬랩에 
대한 공부를 시작할 것으로 예상합니다. 

댓글 없음:

댓글 쓰기