Kernel mailing list 활용 방법


예전 arm linux kernel 에 mailing list 에 subscribe 하여 email 로 patch 의 내용을 볼 수 있도록 하는 방법을 포스팅 한 적이 있다. (http://woodz.tistory.com/27


하지만 하루에도 너무 많은 내용의 patch 와 답글들이 난무하여 모두 보기엔 너무 많고, 골라보기엔 뭘 골라야 하는지도 몰라서 그냥 한달동안 받은 편지함에 쌓이는 메일을 방치하다 unsubscribe 를 하여 더이상 메일을 받지 않았다.


얼마전에 알게된 것인데, kernel mailing list 는 전체가 아닌 부분(part 별로)으로 mailing service 를 신청할 수 있는 방법이 있었다. 관심있는 것 한두개 정도만 등록해서 메일을 받아 보는 것이 효과적일 듯 하여 한번 신청을 해보았다.


두개를 등록했는데, 하루에 많으면 30개 정도 적으면 10개도 안되어 틈틈히 보면 좋을 것으로 보인다.


등록 절차는 간단하다.

1. http://vger.kernel.org/vger-lists.html 에 접속하여, 어떤 mailing list group 가 있는지 확인 한다.

   약 150개 group 이 있었다. group 별로 따로 설명은 없지만, group 이름만 봐도 어떤 내용인지 알수 있을 것이다.


2. 그 중에 일단 고른다. 저는 linux-mm(memory management) 를 선택했다.

아래 처럼 나와있다. 설명은 없지만, archives link 가 있으니 방문하여 어떤 patch 들이 들어오는지 확인 해보고 구독해도 될 것이다.

List: mm-commits;     ( subscribe / unsubscribe )
Info:
  
Archives:
	http://marc.info/?l=linux-mm-commits
	http://www.spinics.net/lists/mm-commits/
Footer:
  
--- 
To unsubscribe from this list: send the line "unsubscribe mm-commits" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

121 


3. subscribe 를 누르면 PC 의 emali client 가 구성해주지만, 없다면 gmail 이나 쓰는 메일에서 편지 쓰기하여 보낼 수 있다.

편지 쓰기를 하여, 


보내는 사람 : <your-email>@mail.com

받는 사람 : majordomo@vger.kernel.org

제목은 안씁니다.

내용 : subscribe mm-commits


위에 처럼 간단히 메일을 써서 보내면 됩니다. 물론 "subscribe mm-commits" 는 plan/text 로 보내야 하며, space 이외에 enter 등으로 분리하시면 안될 것입니다.


그러면 약 3분 뒤에,

Majordomo results 의 제목에 mail 이 옵니다. 메일을 정확히 보냈다면 이메일은 신경 안써도 됩니다.

(봇이 보내는 check mail 입니다. 다 읽어 보지도 않았습니다. ㅎㅎ)

그리고 조금 더 뒤에 아래와 같이 확인 메일이 옵니다.
(확인이 되어야 가입이 됩니다.)

--

Someone (possibly you) has requested that your email address be added
to or deleted from the mailing list "mm-commits@vger.kernel.org".

If you really want this action to be taken, please send the following
commands (exactly as shown) back to "Majordomo@vger.kernel.org":

        auth abcdefg subscribe mm-commits your-email@mail.com

If you do not want this action to be taken, simply ignore this message
and the request will be disregarded.

If your mailer will not allow you to send the entire command as a single
line, you may split it using backslashes, like so:

        auth abcdefg subscribe mm-commits \
        your-email@mail.com

If you have any questions about the policy of the list owner, please
contact "mm-commits-approval@vger.kernel.org".

Thanks!


위의 메일을 받게 된다면 다시한번 majordomo@vger.kernel.org 에 메일을 보내야 한다.

메일은,
받는 사람 : majordomo@vger.kernel.org
보내는 사람 : your-email@mail.com
제목은 비워두시면 됩니다.
내용 :
auth abcdefg subscribe mm-commits your-email@mail.com

제가 잘보이라고 빨간색으로 한겁니다. auth  에서 시작해서 맨 한 줄을 메일 내용에 복사 & 붙여넣기 하면 됩니다.
이것또한 줄바꿈없이 복사 만 딱해서 보내시면 됩니다. 아래의 줄바꿈을 하려면 역슬래쉬(\) 를 넣어주세요 하는데 그냥 한줄로 보내자.

위에 처럼 보내면 약 5분내로 가입 메세지를 보내주고 이제 메일링 서비스를 받을 수 있습니다.

Welcome to mm-commits


Welcome to the mm-commits mailing list!

Please save this message for future reference.  Thank you.

If you ever want to remove yourself from this mailing list,
you can send mail to <Majordomo@vger.kernel.org> with the following
command in the body of your email message:

    unsubscribe mm-commits

or from another account, besides your-email@mail.com:

    unsubscribe mm-commits your-email@mail.com

If you ever need to get in contact with the owner of the list,
(if you have trouble unsubscribing, or have questions about the
list itself) send email to <owner-mm-commits@vger.kernel.org> .
This is the general rule for most mailing lists when you need
to contact a human.

 Here's the general information for the list you've subscribed to,
 in case you don't already have it:

Archives:
        http://marc.info/?l=linux-mm-commits


이제 틈틈히 올라오는 patch 들을 확인 할 수 있습니다. 저는 linux-mm 과 linux-janitor 두가지만 했습니다. ^^



'Development Tip' 카테고리의 다른 글

const char* vs. char const*  (0) 2014.02.19
Fish shell environment  (0) 2013.12.03
PuTTY 설정 값 공유하는 방법  (0) 2013.10.07
Git with eclipse  (0) 2013.04.04
Tips on Linux  (0) 2013.03.11

How does get_current() work?


Kernel 을 보다 보면, "current"를 사용한 코드들이 보인다. 이에 관련해서 어떻게 현재 수행중인 process의 task struct 를 갖고 올 수 있는지 확인해 보자.


참고 url : http://kernelnewbies.org/FAQ/get_current

             http://kernelnewbies.org/FAQ/current


위의 두 가지를 참고하여 재구성 해봤다. 


일단 process 마다 virtual address space 를 갖고 있다. 이는 User/Kernel 의 영역이 나뉘어져 있는데(대게 3G(user)/1G(kernel) 을 많이 사용한다), 이중 kernel 영역에 get_current() 를 통해 현재 process 정보를 얻어올 수 있도록 하는 것이 get_current() 이다.


Kernel 영역의 어디에 이와 같은 정보가 저장되느냐면, Kernel 이 갖고 있는 stack 의 가장 아래쪽에 저장되어 있다.(stack 이 높은 주소에서 낮은 주소 방향으로 이동하며 사용되니까 가장 아래쪽이라 하면.. 아래 그림을 보자)



대게 32bit 에서는 8KB stack 을 사용한다고 한다. 위에서 처럼 stack 의 맨 아래의 정보를 얻기 위한 macro가 get_current() 이다.


일단 get_current() 는 thread_info struct 정보를 넘기고 thread_info struct 는(architecture 마다 다른 듯 함.)

struct thread_info {

unsigned long flags; /* low level flags */

int preempt_count; /* 0 => preemptable, <0 => bug */

mm_segment_t addr_limit; /* address limit */

struct task_struct *task; /* main task structure */

struct exec_domain *exec_domain; /* execution domain */

__u32 cpu; /* cpu */

__u32 cpu_domain; /* cpu domain */

struct cpu_context_save cpu_context; /* cpu context */

__u32 syscall; /* syscall number */

__u8 used_cp[16]; /* thread used copro */

unsigned long tp_value;

struct crunch_state crunchstate;

union fp_state fpstate __attribute__((aligned(8)));

union vfp_state vfpstate;

#ifdef CONFIG_ARM_THUMBEE

unsigned long thumbee_state; /* ThumbEE Handler Base register */

#endif

struct restart_block restart_block;

};


get_current() 로 얻은 thread_info 를 통해 현재 process의 상태 및 task_struct 도 갖고 올 수 있다.


Kernel Stack 은 항상 고정된 주소이고 이 thread info가 저장되는 위치는 항상 같은 곳이다. 그래서 얻어오는 과정의 소스를 분석해보자.

//  code 에서 current->flag |= ... 이라고 사용하면

// current macro는

// include/asm-generic/current.h

#define get_current() (current_thread_info()->task)

#define current get_current()


간단하다. 위에서 처럼 current->XXX 로 사용하면 된다.(thread_info 구조체에 있는 녀석으로)


current_thread_info() 의 함수는 architecture 마다 따로 구현이 되어 있다. ARM 것을 보면,

// arch/arm/include/asm/thread_info.h

static inline struct thread_info *current_thread_info(void)

{

register unsigned long sp asm ("sp");

return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));

}


단지 현재 stack pointer register를 읽어다가, sp & ~(THREAD_SIZE - 1) 했다.

현재 32bit 의 ARM 에서 stack 의 크기는 8KB 이다. 그렇다면 stack 의 12bit 만 0으로 만들어 주면 제일 낮은 주소의 stack 주소가 나오고 거기에는 현재 thread_info 의 address가 있다는 것이다.(현재 stack pointer 가 위의 그림에서 중간쯤에 위치 하더라도 하위 12bit 만 0으로 해주면 맨 아래의 thread_info 를 접근 할 수 있을 것이다.)


이렇게 해서 current macro를 이용해서 thread_info 구조체의 내용을 갖고와 process 의 status 확인 및 control 이 가능 한것이다.


define 으로  do { ... } while(0) 많이 쓰는 이유


Kernel code 를 보면 do { // some codes.. } while(0) 를 많이 쓰고 있다.


이에 대한 이유를 보면,

   1. 빈 구문은 compiler 로 부터 왜 #define FOO do {} while(0). 같이 썼는지 warning 을 받을 수 있다.

      (뭔가 구현 중에 있는 code 나 특정 define 이 정의되지 않을 때의 code가 없는 경우에 대체 후, compile 시 알림 같은 것으로 사용이 되려나.. 싶다.)

   2. 지역 변수 선언을 위해 쓰는 기본 block 으로 사용될 수 있다.

   3. 복잡한 macro를 이용해서 조건부 코드를 만들려고 할 때, 아래와 같이 만들 수 있을 것이다. 이것에 문제점을 보안하기 위해 do-while-0 블락을 사용할 것이다.

#define FOO(x) \
        printf("arg is %s\n", x); \
        do_something_useful(x);

위의 define으로 조건부 code를 만들어보자. 예를 들어,

if (blah == 2)
        FOO(blah);

위에서 처럼 만들었다면, interpreter 는 FOO 를 정의된 내용으로 대체를 할 것이다.

if (blah == 2)
        printf("arg is %s\n", blah);
        do_something_useful(blah);;

위에서 보듯이 printf 와 do_something_useful() 함수를 같이 쓰려는 목적과는 다르게, blah 가 2일 경우에만 printf 와 do_something_useful 가 같이 불릴 것이다. 이를 예방하기 위해, do { ... } while(0) 으로 define을 해두면,

if (blah == 2)
        do {
                printf("arg is %s\n", blah);
                do_something_useful(blah);
        } while (0);

위와 같이 변경 될 것이고 한번에 호출 될 수 있을 것이다.


  4. 또 다른 예제로는, define으로 지역변수 선언과 사용되는 것을 만들어 놓으면 아래와 같이 구현이 될 것인데(물론 여기에 나온 예제는 간단하고 억지스러움이 있을 수 있다.)

#define exch(x,y) { int tmp; tmp=x; x=y; y=tmp; }

위의 정의를 아래와 같이 사용한다면,

if (x > y)
        exch(x,y);          // Branch 1
else  
        do_something();     // Branch 2

이런식으로 구현을 하려고 할 것이고 이는 컴파일에 문제가 발생할 것이다. 이유는, 아래와 같이 번역(?) 되기 때문이다.

if (x > y) {                // Single-branch if-statement!!!
        int tmp;            // The one and only branch consists
        tmp = x;            // of the block.
        x = y;
        y = tmp;
}
;                           // empty statement
else                        // ERROR!!! "parse error before else"
        do_something();

세미콜론(;) 이 블락이 끝나자 마자 오게되어 발생하는 문제이다. 이것의 해결또한 do { ... } while(0) 으로 해결 할 수 있단다.

if (x > y)
        do {
                int tmp;
                tmp = x;
                x = y;
                y = tmp;
        } while(0);
else
        do_something();

위에서 보듯이 정리가 될 것이다. 이는 습관적인(?) rule 을 통해 code에 문제를 해결하기 위한 것이라 생각이 든다. 이미 kernel에는 많은 do { ... } while(0) 있다. 


추가적으로 GCC 는 do-while-0 블락을 대체 할 수 있는 것을 제공한다. 아래와 같이 사용하면 위에서 쓰는 것과 동일한 효과를 보는 것이다.(참고 자료 : http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Statement-Exprs.html#Statement-Exprs)

#define FOO(arg) ({         \
           typeof(arg) lcl; \
           lcl = bar(arg);  \
           lcl;             \
    })

알아두면 편리한 code 작성 법이다. 원본 URL 은 http://kernelnewbies.org/FAQ/DoWhile0 이다.


Likely and Unlikely


Likely() 와 Unlikely() 의 사용법과 용도를 알아보자. 원본 URL : http://kernelnewbies.org/FAQ/LikelyUnlikely


이 함수들은 뭐하는 건가?


예제를 보자.

bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx);
if (unlikely(!bvl)) {
  mempool_free(bio, bio_pool);
  bio = NULL;
  goto out;
}

위와 같이, 특정 condition 을 확인하는 용도로 사용되는데 위의 code는 bvec_alloc으로 할당 받고 bvl 이 유효하지 않는 address이라면 free 하고 NULL 로 만들어주는 code이다. 


likely(), unlikely() 는 include/linux/compiler.h 에 정의된 macro 이다. 이것의 용도는 컴파일러에게 branch 예측을 도와 주는 용도로 사용이된다. 즉, 대부분 0으로 예측이 된다면 unlikely(x) 의 형태로 쓰고, 1로 예상되는 값을 likely(x) 로 쓴다. 예측을 도와 줌으로써 성능의 향상을 볼 수 있도록 하는 것이다.


#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

__builtin_expect() 함수는 GCC 문서의 설명을 살펴보면,(원본은 http://kernelnewbies.org/FAQ/LikelyUnlikely 여기에서 보시길)

-- Built-in Function: long __builtin_expect (long EXP, long C)

'__builtin_expect' 를 사용하는 것은 compiler 에게 branch prediction 정보를 제공해주기 위한 것이다. 일반적으로 개발자들은 자신의 program이 실제로 어떻게 수행되는지 알기가 힘들기 때문에 '-fprofile-arcs' option을 통해 profile 을 feedback 받는 것을 선호합니다. 그렇지만, 어떤 application들은 이것 조차도 수집하기 힘든경우가 있긴 하다.


반환 값은(return value)는 EXP 인 완전한(?) 표현이어야 한다.(아래의 예제를 통해..) EXP와 C는 서로 같아야 합니다.(EXP == C)


if (__builtin_expect (x, 0))

foo ();

이렇게 구현한 것의 의미는 foo() 라는 함수가 불리지 않기를 기대하는 것입니다. 즉, 'x' 는 대부분 0 의 값을 가졌으면 한다는 것이다. 또 다른 방법으로는 EXP 에 "식"으로 사용할 수 있다.


if (__builtin_expect (ptr != NULL, 1))

error ();

예제가 조금 이상하다. ptr != NULL 인 경우가 대부분이라고 알려주는 것인데, NULL 이 아닐때는 error() 가 실행된다. 아닌 경우라면 bypass.

아래의 예제를 보자.
#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)

int main(char *argv[], int argc)
{
   int a;

   /* Get the value from somewhere GCC can't optimize */
   a = atoi (argv[1]);

   if (unlikely (a == 2))
      a++;
   else
      a--;

   printf ("%d\n", a);

   return 0;
}

위의 code를 편집기에 붙여 넣고 저장 한뒤,(test.c)

$ gcc -o test test.c -O2

$ objdump -S test

하면 많은 내용들이 나오는데 그 중에 <main> 이라는 부분을 찾아보면

080483b0 <main>:
 // Prologue
 80483b0:       55                      push   %ebp
 80483b1:       89 e5                   mov    %esp,%ebp
 80483b3:       50                      push   %eax
 80483b4:       50                      push   %eax
 80483b5:       83 e4 f0                and    $0xfffffff0,%esp
 //             Call atoi()
 80483b8:       8b 45 08                mov    0x8(%ebp),%eax
 80483bb:       83 ec 1c                sub    $0x1c,%esp
 80483be:       8b 48 04                mov    0x4(%eax),%ecx
 80483c1:       51                      push   %ecx
 80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
 80483c7:       83 c4 10                add    $0x10,%esp
 //             Test the value
 80483ca:       83 f8 02                cmp    $0x2,%eax
 //             --------------------------------------------------------
 //             If 'a' equal to 2 (which is unlikely), then jump,
 //             otherwise continue directly, without jump, so that it
 //             doesn't flush the pipeline.
 //             --------------------------------------------------------
 80483cd:       74 12                   je     80483e1 <main+0x31>
 80483cf:       48                      dec    %eax
 //             Call printf
 80483d0:       52                      push   %edx
 80483d1:       52                      push   %edx
 80483d2:       50                      push   %eax
 80483d3:       68 c8 84 04 08          push   $0x80484c8
 80483d8:       e8 f7 fe ff ff          call   80482d4 <printf@plt>
 //             Return 0 and go out.
 80483dd:       31 c0                   xor    %eax,%eax
 80483df:       c9                      leave
 80483e0:       c3                      ret

이것도 원본 URL 에서 붙여 넣은 것인데, 직접 해보니 조금 짤린 부분이 있는 것으로 보인다. a == 2가 같은 것이 참이라면 0x80483e1 으로 jump 를 하게 되는데 jump 되는 주소의 instruction 이 복사가 안된 듯 하다. 거기에 mov command 로 a++ 인 "3"을 직접 넣어주고 printf 를 호출해야 하니, 80483d0의 주소로 바로 jump 하는 instruction 이 있을 것이다.


주석에서 보듯이, unlikely() 의 경우 a의 값과 2가 같지 않는것이 대부분이라고 해준것이다. 만약 2와 같지 않으면 jmp instruction 을 수행하지 않고 pipeline flush 가 일어나지 않도록 하여 성능 향상을 주는 것이다.


반대로 likely() 로 변경하여 진행해보자.

컴파일을 다시 하고, objdump 로 disassem 해보면,


080483b0 <main>:
 //             Prologue
 80483b0:       55                      push   %ebp
 80483b1:       89 e5                   mov    %esp,%ebp
 80483b3:       50                      push   %eax
 80483b4:       50                      push   %eax
 80483b5:       83 e4 f0                and    $0xfffffff0,%esp
 //             Call atoi()
 80483b8:       8b 45 08                mov    0x8(%ebp),%eax
 80483bb:       83 ec 1c                sub    $0x1c,%esp
 80483be:       8b 48 04                mov    0x4(%eax),%ecx
 80483c1:       51                      push   %ecx
 80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
 80483c7:       83 c4 10                add    $0x10,%esp
 //             --------------------------------------------------
 //             If 'a' equal 2 (which is likely), we will continue
 //             without branching, so without flusing the pipeline. The
 //             jump only occurs when a != 2, which is unlikely.
 //             ---------------------------------------------------
 80483ca:       83 f8 02                cmp    $0x2,%eax
 80483cd:       75 13                   jne    80483e2 <main+0x32>
 //             Here the a++ incrementation has been optimized by gcc
 80483cf:       b0 03                   mov    $0x3,%al
 //             Call printf()
 80483d1:       52                      push   %edx
 80483d2:       52                      push   %edx
 80483d3:       50                      push   %eax
 80483d4:       68 c8 84 04 08          push   $0x80484c8
 80483d9:       e8 f6 fe ff ff          call   80482d4 <printf@plt>
 //             Return 0 and go out.
 80483de:       31 c0                   xor    %eax,%eax
 80483e0:       c9                      leave
 80483e1:       c3                      ret

여기도 마지막 두 line 이 짤려져 있다. jne 로 jump 하게 되면 거기에 2 라는 값을 eax 에 넣어주고 printf 를 호출 하도록 하는 code일 것이다. 

위에서 보듯이 likely() 로 하면 대부분 2와 같을 것이라고 컴파일러에게 알려줘서 최대한 jmp instruction 이 수행되지 않게 될 것이다.


이렇게 개발자가 예측할 수 있는 부분에 대해 거의 대부분이 x와 같을 것이다 혹은 다를 것이다라는 것을 컴파일러에게 알려주는 방식으로 적용하여 program을 만들 수 있을 것이다.



Write and Submit My First Kernel Patch


Kernel 의 Buddy 알고리즘 할당 방법을 위한 소스 분석을 하면서 발견한 문제를 큰 맘(?)먹고 진행을 해봤다. 처음이라 2~3 줄 고치는데 굉장히 여러 번 다시 보고 맞는지 확인 또 했다. 너무 간단한 내용의 수정이지만 patch 하고 검사받는 과정을 알아본다고 생각하시면 된다.


문제는 Buddy part 1 을 쓰면서 발견했다. 간단히 요약하면 while loop 내부에서 한번 설정되면 update 나 수정 사항이 없는 변수를 매 loop 매다 설정하는 것을 while 밖으로 빼낸 것이다.


이 코드는 굉장히 오랫동안 이런 상태로 유지되고 있었으며, 소스 분석 당시에 봤던 kernel 이 3.4.X 였으니 최신버전에는 반영되었겠지 하고 확인해봤는데 아직도 그대로 인 듯 하여 Patch 를 진행했다.


Patch를 하는 방법과 Coding Style 확인 하는 방법을 기존 포스트에서 쓴 적이 있으니 참고하시면 된다.


어떻게 하고 결과를 받았는지 적어 두려한다.


우선, Code를 수정한다.

수정한 위치는 mm/bootmem.c의 free_all_bootmem_core() 함수이다.


$ vi mm/bootmem.c

  수정...


$ git status

# On branch master

# Changes not staged for commit:

#   (use "git add <file>..." to update what will be committed)

#   (use "git checkout -- <file>..." to discard changes in working directory)

#

#       modified:   mm/bootmem.c

#

no changes added to commit (use "git add" and/or "git commit -a")


$ git add mm/bootmem.c


$ git commit

mm: unnecessary set a variable in while loop.


Signed-off-by: Daeseok Youn <daeseok.youn@gmail.com>


# Please enter the commit message for your changes. Lines starting

# with '#' will be ignored, and an empty message aborts the commit.

# On branch master

# Changes to be committed:

#   (use "git reset HEAD <file>..." to unstage)

#

#   modified:   mm/bootmem.c

#


git commit 하면 "# Please..." 만 있다. 맨 윗 줄에 제목을 쓰고 한줄 띄우고 더 추가적인 comment 가 있다면 쓴다음에

마지막 줄에 Signed-off-by: Your Name <email@email.com> 형식으로 한 줄 추가해 주면 된다.

편집기를 저장 하고 나오자


$ git log .

commit 8baed0442191f87c0c500f124576f3a409c91f25

Author: Daeseok Youn <daeseok.youn@gmail.com>

Date:   Thu Oct 17 10:22:45 2013 +0900


    mm: unnecessary set a variable in while loop.


    Signed-off-by: Daeseok Youn <daeseok.youn@gmail.com>


그러면 위와 같이 나온다.


이제 patch 파일을 만들고 maintainer 를 확인 한 다음에 메일을 보내면 된다. 

물론 사전에 확인해야 할 사항들이 있는데, 너무 간단한 내용이라 빌드 확인 만 했다. 기본적으로 확인 해야 하는 사항은 

Kernel/Documentation/SubmitChecklist 를 열어보면 10가지 정도가 나와있다. 기본적인 빌드 테스트와 확인 사항이 있다.


이제 patch file 을 만들어 보자.


사실 이렇게 하나정도 수정하는 경우에는 branch 를 따로 만들지 않고 해도 무방하다.


$ git format-patch HEAD^

0001-mm-unnecessary-set-a-variable-in-while-loop.patch


위와 같은 파일이 만들어진다.

내용을 열어 보면,

From 8baed0442191f87c0c500f124576f3a409c91f25 Mon Sep 17 00:00:00 2001

From: Daeseok Youn <daeseok.youn@gmail.com>

Date: Thu, 17 Oct 2013 10:22:45 +0900

Subject: [PATCH] mm: unnecessary set a variable in while loop.


Signed-off-by: Daeseok Youn <daeseok.youn@gmail.com>

---

 mm/bootmem.c |    6 +++---

 1 file changed, 3 insertions(+), 3 deletions(-)


diff --git a/mm/bootmem.c b/mm/bootmem.c

index 6ab7744..0b96fea 100644

--- a/mm/bootmem.c

+++ b/mm/bootmem.c

@@ -172,11 +172,12 @@ void __init free_bootmem_late(unsigned long physaddr, unsigned long size)

 static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)

 {

    struct page *page;

-   unsigned long start, end, pages, count = 0;

+   unsigned long *map, start, end, pages, count = 0;


    if (!bdata->node_bootmem_map)

        return 0;


+   map = bdata->node_bootmem_map;

    start = bdata->node_min_pfn;

    end = bdata->node_low_pfn;


@@ -184,10 +185,9 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)

        bdata - bootmem_node_data, start, end);


    while (start < end) {

-       unsigned long *map, idx, vec;

+       unsigned long idx, vec;

        unsigned shift;


-       map = bdata->node_bootmem_map;

        idx = start - bdata->node_min_pfn;

        shift = idx & (BITS_PER_LONG - 1);

        /*

--

1.7.9.5


요런 식으로 만들어진다. 


자 이제 메일을 보내보자. 근데 누구한테 보내야 되는지 확인을 먼저 해야 한다.

$ ./scripts/get_maintainer.pl 0001-mm-unnecessary-set-a-variable-in-while-loop.patch

Andrew Morton <akpm@linux-foundation.org> (commit_signer:10/12=83%)

Jiang Liu <jiang.liu@huawei.com> (commit_signer:4/12=33%)

Joonsoo Kim <js1304@gmail.com> (commit_signer:3/12=25%)

Johannes Weiner <hannes@cmpxchg.org> (commit_signer:1/12=8%)

Daeseok Youn <daeseok.youn@gmail.com> (commit_signer:1/12=8%)

linux-mm@kvack.org (open list:MEMORY MANAGEMENT)

linux-kernel@vger.kernel.org (open list)


여러 사람과 mailing list 가 나온다. 사실 to 로 누구를 보내야 하고 cc 로 누굴 넣어야 할 지 모르는 상태이다.

그래서 사실 맨위의 한사람에게 메일을 보내버렸다. 하루정도 지나서 Andrew 라는 사람이 다음부터는 cc 에 관련자를 꼭넣어야 merge 해준다고 한다. 사람이름은 to 로 보내고 단체 이름은 cc 로 보내면 맞을 것이다.


일단 mail 보내는 방법은 git send-mail command를 이용해도 좋고, Documentation/email-clients.txt 파일을 열어보면 여러 linux email client 가 있고, patch를 보내는 방법이 나와있다.


나는 그중에 Kmail client 를 이용해서 메일을 보냈다. kernel patch 관련 메일은 patch 파일로 첨부해서 보내거나 하면 안된다. patch 를 plan text 로 보내야 하는데, indentation 이 달라지면 안되기 때문에 따로 보내는 rule 과 방법이 있는 것이다. 

그냥 git send-email command를 이용하도록 하자. 난 어쩔수 없는 상황이라 Kmail로 한것임.


git send-mail command 사용법은(물론 설치해야 한다.)

$ git help send-email 

해서 마지막 예제 부분을 보던가, google 검색을 하면 설정하는 방법들이 나온다.


$ git send-email --to akpm@linux-foundation.org --to daeseok.youn@gmail.com --cc linux-mm@kvack.org --cc linux-kernel@vger.kernel.org 0001-mm-unnecessary-set-a-variable-in-while-loop.patch 이렇게 보내면 된다.


나의 경우에는 하루만에 메일이 왔다.

실제로 보낸 commit 의 제목을 알맞게 변경도 해주었고, CC를 붙이라는 comment 로 따로 해주었다.


최종 적으로, http://marc.info/?l=linux-mm-commits&m=138195455814828&w=4 에 등록이 되었고, 3~4 일뒤에 merge 작업이 있을 것이라고 한다.


이렇게 해서 첫 patch 가 성공적으로 merge 되었다. 한번 해봤으니 다음번에는 잘 찾아서 commit 을 해볼 예정이다.


Google 애드센스 신청 TIP


최근에 Google Adsence 를 신청해서 승인을 받았다. (승인 받는 시점에 글 개수는 60개)

신청 과정에서 "컨텐츠 불충분" 이라는 메일을 3번이나 받았다. (1차에서 두번 2차에서 한번)



내가 발견했던 문제를 공유하고자 한다.


1. 가입 신청서에 입력 URL 오류




위의 가입 신청서에 적는 blog url 의 입력에 "http://" 와 함께 입력 하셨다면 1시간 이내로 아래와 같이 구글에서 "컨텐츠 불충분"이라는 메일을 받을 수 있을 것이다.(chrome 주소 창에서 복사해다가 붙여넣기 하면 기본적으로 http:// 가 들어간다.)

안녕하십니까,

가입축하 이메일에 나와 있듯이 사이트에 애드센스 코드를 삽입하시면 애드센스 신청서의 2차 검토가 진행됩니다. 검토 결과 다음과 같은 위반사항이 발견되어 귀하의 계정이 승인되지 않았습니다.

문제:

콘텐츠 불충분
---------------------

세부 정보:

콘텐츠 불충분: 애드센스에서 승인을 받고 귀하의 사이트에 관련 광고를 게재하려면 웹페이지에 있는 텍스트의 양이 Google 전문가가 검토하고 Google 크롤러가 페이지의 주제를 파악할 수 있을 만큼 충분해야 합니다.

이 문제를 해결하는 방법은 다음과 같습니다.

- 페이지에 충분한 양의 텍스트가 있는지 확인하세요. 콘텐츠의 대부분이 이미지, 동영상 또는 플래시 애니메이션인 웹사이트는 승인되지 않습니다.
- 콘텐츠에는 완전한 문장이나 구문이 있어야 하며 제목만 나열하는 것은 허용되지 않습니다.
- 애드센스 신청서를 제출하기 전에 귀하의 웹사이트가 제작이 완료되어 게시된 상태인지 확인하세요. 사이트가 베타 테스트 또는 '공사 중' 단계에 있거나 웹사이트 템플릿만으로 구성되어 있다면 신청서를 제출하지 마세요.
- 웹사이트의 라이브 페이지에 광고 코드를 삽입하세요. 메인 페이지가 아니어도 무방하지만, 애드센스 광고 코드만 삽입되어 있고 다른 콘텐츠가 전혀 없는 테스트 페이지는 승인되지 않습니다.- 방문자가 웹사이트의 모든 섹션과 페이지를 쉽게 찾을 수 있도록 분명한 탐색 방법을 제공해야 합니다.
- YouTube 동영상에서 수익을 올리려면 YouTube 수익 창출 프로그램(http://support.google.com/youtube/bin/answer.py?&answer=2490020&hl=ko)을 신청하세요. 단, 동영상만 게시된 블로그와 웹사이트는 승인되지 않습니다.

필요한 변경을 해주시면 신청서를 바로 다시 검토해 드리도록 하겠습니다.

...


다른 블로그에도 나와있지만, 사람이 직접 확인을 하는 것이 아니니 접속 시도 후

예상컨데 http:// 가 두번 들어간 url 을 접속하려고 했을 것이고 이로 인해 접속 실패하여 더이상 "봇"같은 것이 확인을 못해 바로 답장을 날린 것으로 예상된다.


http:// 를 빼고 바로 신청했다. 물론 구글 검색으로 찾다 보면 현재 신청하려는 gmail 계정의 프로필 정보에 blog url 을 등록해야한다는 얘기도 있어 진행하고 다시 하다보니 발견한 것이다. (프로필은 수정 안해도 될 것 같은데... 확실치는 않다.)


2. 1차 승인 성공.

애드센스에 오신 것을 환영합니다.

현재 신청서의 일부만 검토가 완료된 상태이지만 이제 사이트에 광고 코드를 삽입할 수 있습니다. 그러나, 사이트에 대한 검토가 완전히 끝날 때까지는 woodz.tistory.com에 실제 광고가 게재되지 않습니다. 대신 빈 광고만 사이트의 배경과 섞여 표시됩니다.

사이트에 애드센스 코드를 추가하고 Google에 광고 요청을 보내야만 신청서 검토가 완료됩니다. 검토가 완료되면 귀하의 신청서에 대한 최종 승인 또는 비승인 여부가 결정됩니다. 승인되는 경우 실제 광고가 게재되기 시작합니다.

사이트에 실제 광고가 게재된 후에도 귀하의 계정이 애드센스 정책을 준수하는지 여부가 지속적으로 검토됩니다. 위반사항이 발견되면 귀하의 페이지에서 광고 게재가 중단되거나 계정이 비승인될 수 있습니다. 사이트에 광고가 보이지 않을 경우 애드센스 계정에 로그인하여 수신된 알림이 있는지 확인하시기 바랍니다.

다음 단계에 따라 사이트에 광고 코드를 삽입하세요.

...


요런 메일을 받게 된다. 빈 박스의 광고를 넣는다. 2차 인증을 받기 위한 광고 삽입 방법은 다른 블로그를 참고하시는게 더 좋다.


3. 2차 승인 실패.

여기서 문제는 내가 이것 저것 해보는 과정에서 발생한 문제가 있었다. 가만히 있었으면 되었을 지도 모르지만 아무튼...



내가 왜 그런행동을 했는지 기억은 나질 않지만, 위의 메뉴에서 티에디션을 사용하면 메인 페이지에 간략 목록(?) 같은 것이 포스트에 있는 특정 이미지의 thumb nail 과 함께 보여진다. (단지 사용하기를 눌렀다.)

이것이 문제다. 봇이 딱 진입하자마자 본 페이지가 그림뿐인 페이지며, 간략한 포스팅 설명은 그녀석이 보기엔 완성된 문장이 아니었겠지.


그래서 

- 페이지에 충분한 양의 텍스트가 있는지 확인하세요. 콘텐츠의 대부분이 이미지, 동영상 또는 플래시 애니메이션인 웹사이트는 승인되지 않습니다.
- 콘텐츠에는 완전한 문장이나 구문이 있어야 하며 제목만 나열하는 것은 허용되지 않습니다.


이 부분에서 걸리지 않았나 하는 생각이다. 그래서 티에디션을 승인과정에서 사용하는 것은 안된다.


4. 재요청


 위의 문제를 다 해결하고 다시 1차 승인 부터 하기 위해 재신청을 했다. 


이후에 1차 승인은 바로 났고(물론 게시물을 추가적으로 없었다.) 2차 승인을 잘 받기 위해, 전체 글에 대한 광고를 넣지 않고

몇개 텍스트 위주로 이루어진 포스트 3개정도에만 광고 script 를 넣어놨다.


5. 승인 완료


축하합니다.

귀하의 Google 애드센스 가입 신청이 승인되었습니다. 몇 시간 내에 실제 광고가 게재되기 시작합니다. 신규 애드센스 게시자를 위한 자세한 안내를 보려면 애드센스 아카데미(https://support.google.com/adsense/bin/static.py?page=checklist.cs&tab=1187443&hl=ko)를 참조하세요.

....


6. 결론.


1. 방문자 수와 글의 개수는 중요한 요소가 아니다. 검색해보니 대략 20 개이상이고 고정적으로 방문자 수만 있다면야 가능하다는 것이다.


2. 신청서 작성 시, example 을 잘 보고 고것대로 정확히 진행을 해야 한다.

    http:// 를 넣지 말아야 하는 부분에는 넣지 않도록 한다. 


3. 봇이 확인하려고 입장하는 시기(?)에는 thumb nail의 집합으로 이루어진 메인 화면은 사용하지 말자.


4. 모든 포스트에 광고를 넣을 필요는 없지 않을까 싶다. 맘에 드는 포스트에 광고를 하나씩 넣을 예정이다.


사실 글을 많이 써놓은 것도 아니고 방문자 수가 많은 블로그도 아니다. 근래 들어 애드센스를 넣기 위해 글쓰는 횟수도 늘긴했고

광고로 수익이 나면야 좋겠지만 나지 않더라도, 수익을 낼수 있는 블로그를 갖게 된 것만 해도 기분 좋은 일이고 더 자주 쓰려고 노력하지 않을까 하는 생각이 든다.


'BlogTip' 카테고리의 다른 글

Ubuntu 에서 우 클릭(Right-Click) 메뉴 screen shot  (0) 2012.02.20
불펌 방지 HTML(LINK)  (0) 2011.03.03
Tag Cloud 만들기  (0) 2010.12.22
Syntax highlight test  (1) 2010.12.14

[Algorithm] Python 배우기


CodeChef, TopCoder, Google Codejam 등 Algorithm Contest 를 하는 site 들이 많다. 내 개인적인 생각으로는 앞서 말한 세개의 site 에서 제공하는 문제를 푸는 것에 있어 언어(C++, Java, Python 등)가 그렇게 중요하진 않다고 생각한다.


그래서 제가 선택한 것은 Python. 아주 배우기도 좋고 Algorithm 문제를 푸는데 적합한(물론 C++/JAVA 도 괜찮다-개인적인 생각이다.) 언어인 것 같아 선택했다. Python는 CodeChef 와 Google Codejam 에서는 지원을 하고 있고, 내가 덤벼볼수 있는 초급문제가 많은 CodeChef 에서도 Python 의 기본적인 function 으로 풀 수 있다. 


하지만 python 도 따로 배우긴 해야한다. 다른 C++ 이나 JAVA를 다루어 보았다면 쉽게 접근하고 사용할 수 있을 것이다. 그리고 python 에서 제공되는 함수의 기능으로 검색하면 왠만큼 나온다. ^^


그래서 python 을 배우기 위한 추천하는 site는 http://www.learnstreet.com 이다.

browser 내에서 python code를 입력해보고 결과를 다 확인 할 수 있다는 장점이 있다. 

(eclipse, python 설치등이 필요없다!!)


어떻게 사용하는지 알아보자. 아래의 이미지가 너무 작아 세세히 보이지 않는다면 클릭하여 크게 보도록 합니다.


1. site 에 진입한다. Click ==> learnstreet



맛보기만 하려면 가입할 필요없지만, 끝까지 한번 해보고 어디까지 했는지 기록을 다 남기려면 가입하는 것이 좋다. 물론 가입은 Google/Facebook 등의 계정이 있다면 클릭 한번에 가입이 가능하다.


2. 가입

메인 페이지의 오른쪽 상단에 Sign in / Sign up 이 있다 아무거나 클릭해도 같은 page가 나온다.



난 Google 계정으로 로그인했다.


3. 배우려는 언어 선택

오른쪽 상단 로고 옆 "Courses" 를 클릭한다. 이사이트는 JavaScript/Python/Ruby 언어를 지원한다.



그중 Python을 선택해보자


4. Emulator(?) 같은 녀석이 나온다. 



시작한다고 얘기해주면서 화면 구성에 대한 설명을 해준다. (Next 를 눌러주자.)

설명을 차근차근 읽어보고 마지막 설명에서 Next 버튼이 비활성화 되면 Close 를 눌러주자.




5. 이제 하나씩 시키는 대로 풀어보자.

하나만 풀어보자. 오른쪽 까만 화면에 어떻게 하라고 지시가 나온다. 시키는대로 하나씩 하면 된다.

아래의 그림에서는 interpreter에 "Python" 이라고 입력해봐 라는 지시였고 그렇게 하면 바로 다음 문제가 나온다.



총 course 는 아래와 같다.




위의 course 대로 다 하게 되면 기본적인 것은 다룰 수 있다.


이렇게만 끝나는 site라면 추천하지 않는다. 간단한 project 를 할 수 있도록 도와준다.


왼쪽 상단 LearnStreet 오른쪽에 "Project"라는 탭으로 진입한다.

그러면 아래처럼 간단한 project 들이 보인다.



여기에 나오는 project 는 학습을 위한 것이다. code를 작성하고 수행하면 이미지에 반영이 되는 재미를 더해 집중을 할 수 있도록 만들어준다. 


이중 하나를 풀어보자.


나는 Project : Number Converter 라는 것을 선택했다.


시작 화면



이 project의 설명이 나온다. 설명을 보면 숫자를 입력했을 때 숫자를 로마자로 변경하는 것이다.(주어진 룰대로)


Start를 클릭하면



오른쪽 소스 view에 layout 이 나오고 이 함수내에 code를 구현하고 소스 view 왼쪽 하단에 "Run" 을 수행해주면 된다. (소스의 layout인 함수 내부에만 구현해서 원하는 값을 return 해주는 구조로 작성하면 된다.)

Course 를 잘 진행했다면 이것도 진행하는데 무리가 없을 것으로 생각된다.



완성이 되어 수행하면, 왼쪽화면에 결과를 알수 있도록 숫자를 입력하고 로마자로 변경(Convert 화살표 클릭)이 완료되면 OK

이런식으로 하나씩 하다보면 어느새 시간은 훌쩍지나 있고 기본기를 어느 정도 닦을 수 있다.


그리고 하는 과정에서 바로 입력하고 Run 수행하고 이것을 반복하는 것보다 소스 view 하단에 있는 "Scratchpad" 를 실행해서 소스를 작성하고 작성된 소스를 선 검토하는 것이 좋다. 

물론 Scratchpad는 함수 구현 및 호출부까지 작성해야 한다. 아래의 "scratchpad" 로 이동한 모습.

오른쪽 view에서 interpreter 로 바로 이용할 수 있고, 오른쪽에서 python으로 작성 후 사이에 있는 "체크"버트을 클리하면 interpreter console에서 결과를 알수 있는 구조이다. 



최대한 자세히 쓴다고 했으나 부족한 부분이 있을 수 있을 것이다. 어렵지 안은 UX 이니 차근차근 진행하시면 된다.  그다음 level 에 대한 내용은 좋은 방법이 있다면 포스팅을 할 것이다.



PuTTY 설정 값 공유하는 방법

Window 에서 ssh/serial 등 다양한 접속환경을 위해 사용하는 유명한 PuTTY application 의 설정 값을 저장하고 다른 컴퓨터에서 PuTTY application 에도 같은 설정값으로 사용할 수 있는 방법을 공유한다.


window 7 환경에서 작성됨.


1. 현재 설정값을 Local 파일로 저장

  - puTTY 는 설정값을 registry 에 저장한다

  - 설정값을 찾아보자. 찾는 방법은,

     a. Window Key  + r 또는 시작 ==> 실행

     b. regedt32 입력




   - puTTY 설정 값 찾기.

     a. 위치는 HKEY_CURRENT_USER/Software/Simon Tatham 이다.

     b. 간단히 Ctrl + F 를 눌러 검색어에 "Simon Tatham" 입력하면 찾아준다.(따옴표는 빼고 입력)



   - 찾기 완료.



   - 내보내기




   - 저장 하기




파일을 저장하고 다른 컴퓨터를 사용할 때, puTTY 설정 값을 그대로 갖고 오기 위해서는 그냥 실행만 하면 된다.

그럼 그 컴퓨터에 putty 설정값이 바로 load 된다.


Good!!!


참조 url:http://downloadsquad.switched.com/2007/02/01/howto-transfer-your-putty-settings-between-computers/


'Development Tip' 카테고리의 다른 글

Fish shell environment  (0) 2013.12.03
Kernel mailing list 활용 방법  (0) 2013.11.08
Git with eclipse  (0) 2013.04.04
Tips on Linux  (0) 2013.03.11
Tips for VIM  (0) 2013.02.21

Linux kernel physical memory allocator (Buddy) - Part 2-1


Physical memory allocating 방식인 buddy algorithm에 관련된 소스 분석을 쓰는 과정에서 코드 분석에만 너무 초첨을 맞춘 상태라는 것을 다시 읽으면서 느끼게 되었다.


그래서 이번 part 는 Buddy 알고리즘에 대해 간략하게 한번 정리 하고 넘어 가도록 한다.


Buddy 란?


이 포스트를 다읽으시고 부족하신 분들은 wiki 의 buddy memory allocation 을 읽어보시길 바랍니다.

Buddy 란 의미는 "동료", "친구" 이정도 되겠다.

왜 이런 이름을 붙였냐면, 기존 포스트 part 1, part 2 를 보면 order 란 단어를 사용하고 2^order 의 개수로 free_list 에서 관리 되고 있다는 얘길 했었다. 


아래 그림을 한번 보자.

(그림을 보실 때, 조금 부연설명을 하자면, free_area 라고 되어 있는 주황색 박스 맨위에 0번은 신경쓰지 말고 박스안의 숫자는 order 를 나타낸다. 0~15로 되어 있는 부분은 실제 memory 인 것이다.)




총 16개의 page 가 시스템에 있다고 가정한 그림이다. 빨간색은 이미 할당된 상태인 것이고 파란색은 할당되지 않은 상태이다.


여기에서 buddy 라는 의미를 찾아보자.

기준은 order 로 한다. 기준이라는 의미는 몇개의 page 를 연속적으로 묶어 두느냐는 것이다. 예를 들어 order 0 의 입장에서는 0 과 1번이 서로 buddy 가 된다.

그렇다면 order 2 에서의 0번의 page 의 buddy 는 몇번 page 인지 알려면 <page number> + 2^order 하면 될 것이다. 즉 order 2 의 0번 page 의 buddy 는 0 + 2^2 = 4 번이 된다. 

(하지만 4번의 buddy는 4+2^2 = 8 이 아니다. 반대인 0이 된다. 현재 order 의 갯수 순서대로 buddy가 정해진다고 보면됨.) 현재 16개 page 에서의 buddy 를 알아보면

order 0 의 입장에서는 0, 1 이 서로 buddy,
                               2, 3 이 서로 buddy,

....

order 1 의 입장에서는 0, 2 이 서로 buddy,

                               4, 6 이 서로 buddy,

...

order 2의 입장에서는 0, 4 가 서로 buddy,

                              8, 12 가 서로 buddy,

... 

이런식으로 된다. 


이렇게 buddy 로 관리 하려는 이유는 buddy 알고리즘을 이용한 할당은 메모리 외부 단편화를 줄여주기 위한 방식이라고 알려드렸다. 이렇게 하면 연속적인 page 할당 관리가 용이 하고, 작은 단위의 order 0, 1,2 등(하위 order)의 할당/해제가 많이 있었다면 buddy 끼리 분리 및 합체(?) 등으로 메모리 관리가 가능하다는 것이다.


왜 그런지 그림으로 알아보도록 하자. 다시 위의 그림을 보았을 때, 시나리오를 보자.


Scene 1.

   a. 3번 page 할당 해제.

   b. 0번 page 할당 해제.

   c. 1번 page 할당 해제.


a 번 이후 상태를 보자.




3번이 해제 되고(파란색으로 변경) order 0번 list 에 추가적으로 add 가 된다.

3번의(order 0) 해제됨과 동시에 3번의 buddy 인 2번 page 가 해제 되어있는지 확인 한다. buddy(2번 page)가 할당 상태이다. 그냥 free_list 에 add만 하고 해제를 종료.


다음 b, c 번을 진행하면,



0번이 해제되는 시점에서 0번의 buddy인 1번page 가 할당 상태이지만, 1번이 해제되는 시점에 1번의 buddy 인 0번 page 가 해제 상태이다. 그럼 0, 1번은 병합이 가능한 상태인 것이다. 같은 buddy 끼리 합체 후 상위 order 로 함께 올라 가는 것이다. 

결과 적으로 아래의 그림 처럼 0번 page 가 order 1 (2^1) 로 add 된다. 



0번 1번 page 가 합체하여 상위 order 에 올라갔다. 그 다음 order 1에서 0번 page 의 buddy 를 확인하자. 2번이 할당 상태이다. 그럼 합체를 종료한다.


해제를 통해 buddy 들이 어떻게 합쳐지는지 확인 했다.

이번에는 할당을 통해 어떻게 buddy 알고리즘이 적용되는지 확인 해보자. 위의 마지막 상황을 다시 한번 그림으로 정리한다.




이제 할당을 요청한다 일반적으로 요청한 order 에 딱 그 pgae가 있으면 그 page 를 할당 해 주면 끝이다.


Scene 2.

  a, order 1 (page 2개)의 할당 요청

이는 order 1의 free_list 를 확인해서 "있냐?" 고 물어본다. "있어" 하면 그 page 를 주면 된다.

order 1에는 0번 page 가 있었다.(0,1번이 할당 상태가 되고 free_area[1].free_list 에서 delete 함)

결과는,



간단하다.


Scene 3.

Scene 2 가 완료된 상태에서, 다시

  a. order 1(page 2개)를 요청


order 1을 요청하는 시점에서는 order 1의 free_list 에 할당 해줄만한 page 가 없다. 이런 경우에는 상위 order 에서 갖고 오는데, 그냥 갖과 왔다가는 낭패다. 상위 order는 최소 2배가 많은 page 를 갖고 있기 때문이다. 추가 작업이 필요하다는 의미이다. 


현재 order 1 의 page 가 없으므로 상위 order 를 검사한다(order 2). order 2에 마침 12번 page가 있다. (이는 12번 + 2^2개의 page 포함). 그렇다면 12번~13번 page를 할당 해주면 된다. 그렇다면 14번 15번은 당연히 하위 order 에 안착(?) 을 하면 되는 것인데, 아래의 그림으로 확인 해보자. 

12번 page 를 할당 해주고 요청이 2개의 page였으므로 그냥 2개의 page까지만 사용한다고 생각하고 그냥 14번 부터는 하위 order 설정을 하는 겁니다.



위의 그림과 같이 12번 13번이 할당되고, 14번이 order 1로 연결되게 된다.


최종적으로 아래와 같이 나올 것이다.




이렇게 buddy 를 physical memory 할당/해제 에 어떻게 이용되는지 확인 해보았다. code도 이 logic 을 이용한 것이라고 보면된다.


이렇게 하면 최대한 할당을 연속적으로 할 수 있고, 해제 후 병합을 통해 지속적인 관리가 가능하다는 것이다.



Linux kernel physical memory allocator (Buddy) - Part 2


이전 buddy 초기화 관련 글에 이어 Buddy allocation/free 를 중심으로 Code 분석을 진행한다. 

(쓰다보니 allocation 만 해도 소스를 붙여 넣다 보니 분량이 조금 나와서 free 는 다음 파트로 넘기기로 했습니다. :) )


1. Allocation

  - alloc_page() 함수 call 에서 실제 buddy를 만지는(?) code까지 call flow 를 보면 아래와 같다.




  사실 call flow 의 __alloc_pages_nodemask() 까지는 특별히 내가 설명할 내용은 없어 보인다. 실제 __alloc_pages_nodemask() 함수 내에서 get_page_from_freelist() 함수를 통해 page를 받아 return 하는 것이다. 

code를 보자.(이 blog 에 syntax highlight를 위해 plugin 을 사용하는데, 아래의 code가 그냥 하얀 박스에 성의없이 나온다면 lxr.linux.no site 의 함수 link를 남겨 놓을테니 들어가서 보시면 됩니다. line number는 이 블로그의 code에 맞춘 것입니다.)


line 35 선호하는 zone 을 preferred_zone 으로 갖고 온다. 

line 44 get_page_from_freelist() 를 통해 특정 zone으로 부터 page 를 받아 온다.

line 48 get_page_from_freelist() 를 통해 page 를 할당 받지 못하는 경우에 수행하는 함수이다. 다음 part에서 살펴 보겠지만, 내가 아는 page allocation 실패의 주요 이유는 driver level 에서 kmalloc 을 통해 연속적인 page 를 요청했는데 free memory 는 충분하나 연속적인 메모리가 없는 경우이거나 정말 나눠줄 메모리가 없는 경우 등이 있다. 이런 경우에 정리 및 retry 를 위해 __alloc_pages_slowpath() 함수를 호출 하여 COMPACTION 을 하거나 process를 죽이고 메모리 확보등의 여러가지 행동 들을 한다. compaction 관련해서 lwn 을 번역해 놓은 문서가 이 블로그에 있다. ^^

line 61~64 정상적으로 할당이 되어얐다면 page structure pointer 를 넘기고 아니면 NULL 넘긴다. NULL 이라면 allocation 실패다. 


call flow 를 참조하시고, 이제 buffered_rmqueue() 함수를 살펴 보자.


line 2 order 가 0인 요청(page 1개 == 4096byte)의 경우와 아닌 경우로 분리하여 할당 요청함.

line 7 per_cpu_pages 구조체의 list 에서 바로 할당 해줄 수 있다면

line 20 에서 page 를 바로 얻고 return 해줄 수 있다. 하지만 per_cpu_pages 가 없다면 __rmqueue_bulk() 에서 한번에 page 들을 할당 해놓는다. 예상 컨데 per_cpu_pages 의 list 에 매달린 page의 단위는 order 0일 뿐일 것이다.

line 24 order 가 0 보다 크다면(요청 크기 > 4096) __rmqueue() 를 호출하여 Buddy를 이용하여 연속적인 physical memory를 할당해준다. buddy 를 이용하는 부분을 다음 소스에서 살펴 보자.


__rmqueue()__rmqueue_smallest() 함수의 순으로 호출하는데, 일반적으로 잘 되는 경우에는 __rmqueue_smallest() 에서 free_list 의 page 를 return해준다. 아래는 __rmqueue_smallest()


이 함수로 넘어오는 경우의 시나리오를 먼저 세우면,(가장 간단한 case)

1. order 는 1 이상(4라고 하자)

2. 현재 order 4의 free_list 에는 free 한 page 가 여럿 있다.


line 14 에서 current_order 는 4 이고 MAX_ORDER 는 11 이다.

line 15 area 는 order 4의 free_area 의 주소를 갖고 있다. (free_area 그림을 다시 보고 싶다면 지난 part 1 블로그를 보시는 게 좋을 듯 한다.)

line 16 은 free_area 의 free_list 가 비어있는지 아닌지 확인 한 후에, 있다고 가정 했으니 다음으로

line 19 free_list 에서 page structure 를 갖고 온다. 

line 21 은 page lru list 에서 현재 할당될 page 를 빼주고

line 23 free_area structure 내부에서 page 하나가 빠졌으니 nr_free 를 하나 감소시킨다

line 23 expand() 함수는 현재 요청한 order(4) 에 free 한 page 가 하나도 없다면 상위의 order 에서 제공되고 제공된 뒤에 상위의 order 에서 갖고 왔으니 남은 부분은 하위의 order 에 다시 붙여 줘야 한다. 그 하위 order 에 남은 page 를 붙여주는 작업이 expand() 에서 일어난다.


이제 expand() 함수가 수행되는 과정으로 시나리오를 다시 잡아 보자.

1. order 는 1이상(4라고 하자)

2. 현재 order 4의 free_list 의 nr_free 값은 0 이다 (즉 가능한 page 가 없다는 것임.)


line 14 에서 current_order 4 에서 시작하는 것은 같다. 하지만

line 16 에서 order 4의 free_area의 free_list 에는 page 가 없기 때문에 

line 17 의 continue가 수행될 것이다.

line 14 로 다시 돌아가서 이제 current_order 가 5가 된다.

line 16 에서 확인 해보니 몇개가 있단다

line 19 에서 현재 order 5의 free_list 에서 page 를 갖고 온다.

자 여기서 문제(?)가 발생했다. 요청한 것은 order 4 인 (4K * 2^4) 인데 order 5에서 할당 해줬다.

일단 page 는 넘기되 상위 order 의 page 는 바로 하나 아래 하위 order 보다 딱 2배 더 많은 page 를 갖고 있으니까 반짤라서 남은 page 를 하위에 붙여 주면 된다. 


그일은 expand() 에서 하게 된다. expand() 를 살펴보자.



간단합니다. expand() 를 호출 하기 전에 low 는 실제로 요청한 4가 되고 high 는 order 4에서 받지 못해 상위에 요청을 했었으니 5가되고 area structure 는 order 5의 free_area[5] 입니다. migratetype 은 신경 안쓰도록 합니다. page 구조체는 order 5의 free_list 에서 가져온 page 입니다.


line 5 의 size 값은 1 << 5 가 되니 32 가 됩니다.

line 7 은 high(5) > low(4) 일때 까지~

line 8~11 area-- 합니다. 그렇게 되면 area는 order 4의 free_area 가 됩니다.(free_area[4]), high-- 합니다. high 는 4가 됩니다. size >>=1 은 32 에서 16으로 바뀝니다. VM_BUG_ON 은 그 page 의 address 가 유효한지 테스트 합니다.


CONFIG_DEBUG_PAGEALLOC 부분은 pass 합니다.


line 29 order 4의 free_list 에 &page[size] 를 메달아 줍니다. page[size] 는 현재 page address 는 order 5에 있던 것입니다. 그렇다는 것은 page address 에서 부터 32의 page 가 연속적인 상태로 있었단는 것이고, order 4에 내려와서 16개의 page를 할당 해줬으니 0~15까지는 allocated 상태인것이고 16~31 까지는 하위 order 인 4에 메달아 줘야 하는 것이다. 그래서 &page[size] 를 달아주면 16~31 까지를 order 4에 달아 주는 것이다.

line 30 order 4의 nr_free 를 증가.

line 31 현재 page 를 현재 order 에 맞게 설정함.


여기 까지가 buddy 알고리즘을 이용한 할당 소스 분석입니다. 


+ Recent posts