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 에 대한 내용은 좋은 방법이 있다면 포스팅을 할 것이다.



오랜만에 찾은 과천 서울대공원 뒷(?)편에 있는 캠핑장!

좋은날씨에 고기와 새우를 숯불에 구워 먹으려고 당일치기 예약해서 왔다. 예약이나 준비는 같이오신분이 다해주셔서 정말 몸만왔다 ^^

숲속 캠핑장의 모습은


이런식으로 되어있다. 텐트가 비교적 크고 내부는 큰텐트 구조상 마루(?) 같은 공간이있고 누워자는 공간이 구분되어있다.

또한 텐트마다 나무로된-사진을 찍진못했다^^- 식탁이있고 바베큐를 할수있는 공간이있다

바베큐할수있는 그릴고 참숯등 캠핑장에서 필요한 것들은 내부 슈퍼에서 살수있다



전체적인 분위기의 사진만 조금 찍어봤다


나무가 많이 있어 햇볕이 쨍쨍한 날에도 온통 그늘진 공간이라 시원하다. 아이들이 쉬이 놀수있는 얕은 계곡물도 흐른다.



개인적으로 조금위험해 보이긴 한 통나무로 된 놀이시설들이 있다


외나무 다리만 슬쩍 나오긴했는데 많은 시설들이있다. 어른들도 즐길수있고 아이들이 어른들의 보호아래 시설을 이용한다면 재미있을것 같기도 하다.

아래서 부터는 그냥 찍어본 야영장 풍경


생각보다 가파르다~ 조심


계곡에서 내려오는 물~ 잘흐르다보니 깨끗하다.


나무 아래 텐트들이 나란히 줄지어있다

작년 올해 두번째로 오는데 맛있는 음식을 구워먹고 좋은 공기마시며 산책을 하면 좋은 공간이다. 사실 텐트 가지고 움직이고 장소마련하고 등등하는것들 중에 어느정도 누가 해놓은것을 이용하는 캠핑이라 생각하면 된다~

검색어로 네이버나 구글에서 "서울 대공원 자연 캠프장" 이라고 하면 나온다

'Mobile ' 카테고리의 다른 글

[여행-신도림] 디큐브시티 뽀로로 파크 방문기  (1) 2013.10.23
서울 대공원 - 동물원  (0) 2013.10.05
Mobile 글쓰기  (0) 2011.07.28

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
좋은 날씨에 방문한 동물원, 올때마다 느끼는 건 큰공원에 동물들이 있는 것 같다는 것이다 ^^

코끼리열차 어른 인당 천원
동물원 입장료 삼천원



코끼리 열차를 타고 동물원에 입장!
오후 4시쯤 도착해서 5시까지 둘러보고 나왔다 아들녀석이 신기한지 너무 좋아하더니 피곤했던지 금새잠들어 버렸다

사진도 많이 찍질못했다.


입구에서 왼쪽으로 진입하면 기린 근처에있는 홍학


무서울정도로 큼지막한 타조 눈알이 내머리만한 느낌!


누군지모르지만 목소리가 좋으신분들이 노래를 불렀다 동영상촬영을 했어야 하는데 그런 기능을 안 쓰니 그땐 있는지도 몰랐다


뭘까? 올라갈수있던데.. 올라가보진못했다 다음기회에
바로 근처에 미어캣이 있어서 사진을 찍으려고했더니 너무 밝아서 인지 찍어도 잘안나왔다... 귀여웠는데~


집채 만한 코뿔소!!

주말이라 사람들이 너무 많았다. 공원같은 분위기도 맘에 들고~ 다음엔 평일에 조용할 때 한번 더 와야겠다

'Mobile ' 카테고리의 다른 글

[여행-신도림] 디큐브시티 뽀로로 파크 방문기  (1) 2013.10.23
자연 캠핑장 - 과천  (1) 2013.10.08
Mobile 글쓰기  (0) 2011.07.28

Linux kernel physical memory allocator (Buddy) - Part 3


오늘은 할당에 이어 해제과정의 소스 분석을 해본다.


현재 쓰는 시점에서 lxr.linux.no 가 접속 되지 않아 함수에 link 를 붙이지 못했다.


Free 의 호출 순서는 아래의 그림과 같다.




free_pages() 는 parameter 로 virtual address 와 order 를 받게 된다. 이 virtual address 를 page 단위로 변환하고 order 는 size 에 준하여 계산하면 된다.


위의 그림에는 나와있지 않지만 free_pages() 와 __free_pages_ok() 함수가 호출 되는 과정에 free_hot_cold_page() 함수 가 있다. 이는 order 0 일 경우에만 동작하는데, part 2 에서 할당하는 과정에 order 0 일 때, per_cpu_pages 구조체에 있는 page 를 갖고오는 code를 기억할 것이다. 이런 경우(?)는 자세히 다루진 않겠지만 대략적으로 order 0의 cold 한 page 를 per_cpu_pages 구조체에 갖고 있다가 필요한 경우 가져다 쓰는 방식으로 하는 것이 아닐까 한다. 


암튼, __free_pages_ok() ==> free_one_page() ==> __free_one_page() 순으로 호출된다.


__free_one_page() 함수의 소스를 보자.

(한꺼번에 분석하지 않고 적당한 line에서 나눈 것이다.)


이 함수는 parameter 가 4개이다. 이 때까지 migratetype은 신경 안썼으니 pass 하자. 

첫번째 parameter 는 free_pages 로 넘어온 virtual address 를 page 로 변환한 주소를 넘겨 받았을 것이다.

그리고 해제 하려는 size 를 받았을 것이고 size 를 order(세번째 parameter) 로 변환하여 받는다.


line 16 에서 page_idx (page frame number) 를 구하는 공식이다. 

간략히 살펴 보면 __page_to_pfn(page) 를 보면 (page 주소 - mem_map) 을 한뒤 ARCH_PFN_OFFSET 을 더해주고 return 해준다. 


ARCH_PFN_OFFSET 은 대게 물리메모리 시작 주소가 0이 아닌 경우에 사용된다. 

(예를 들어 물리 메모리의 시작 번지가 8192 이고 그 시스템의 PAGE size가 4096 이라면 ARCH_PFN_OFFSET 은 2 가된다.) 여기서 mem_map 은 간략히 시스템이 갖고 있는 물리 메모리를 page 단위로 mapping 해놓은 map이다. 그렇다는 것은 mem_map(struct page* 형) 을 현재 page 주소값에다 빼주면 말 그대로 page 가 mem_map 으로 부터 몇 번째 있는 page 인지 알 수 있는 것이다.


현재 page_idx = PFN & ((1 << 11) -1); // 11은 MAX_ORDER 값이다.

다시 적으면 page_idx = PFN & 0x1FFFF 가 된다. pfn max 131071 인 듯.(정확히 왜 제한을 두는 것인지 확인 하지 못했음).


line 21 현재 order 에서 MAX_ORDER - 1까지 loop 을 수행하면서, 

line 22 __find_buddy_index() 를 통해 buddy page frame number를 구해 온다. 

part 2-1 을 보면 알겠지만, buddy 와 함체를 위해 찾는 것이다. 

현재 free 하려는 index 가 2 이고 order 가 1이라면, 2 ^ (1 << 1) = 0 이 된다. 즉 order 1(page 2개가 묶음)에서 2번 page frame의 buddy는 0번 page frame이다. 


line 23 page index(page frame number) 기준으로 page 구조체 주소를 얻어 올 수 있다. 현재 해제 하려는 기준의 page 구조체는 parameter 로 받아왔으니 buddy 의 page 구조체를 구하는 것은 index 를 더하고 빼면 나올 것이다.


line 24 page_is_buddy() 함수에서 몇 가지를 check 한다.

  a. buddy page 가 유효한 page 인지 확인

  b. 현재 해제 요청한 page 와 buddy 가 같은 zone 에 있는 지 확인.

  c. buddy 의 order 가 해제 요청한 page 의 order 와 같은지 비교 및 guard 상태인지 확인.

      guard 상태의 확인은 kernel option에서 CONFIG_DEBUG_PAGEALLOC 가 enable 되어 있다면 뭔가 정보를 확인하겠지만 아니라면 무조건 false 를 return 한다.


buddy 가 맞고, 같은 order 의 free_list 에 있다면,

line 35~42 free_area[order] 의 nr_free 값을 하나 줄이고 buddy 의 page 속성 중 lru list 에서만 제거한다. 

  merge 는 위에서 보듯이 order 1의 index 2 의 요청이면 buddy가 0번 page 일 테고, merge 가 되면 0번 page 가 index 로 된다.(합쳐 졌으니)

그리고 상위 order 로 이동하여 위의 작업을 다시 하고, buddy check 에서 buddy가 아니거나 없으면 그만 둔다. 이 while loop 에서 최대한 상위 order 로의 merge 가 완료 된 후, 다음에 설명할 code 에서 실제 free_area 구조체에 정보를 update 한다.


free_area 정보 update code.


사실 위의 while 문 내부에서 현재 해제 시도에서 최상위 merge 까지 했다면 line 22 에 list_add 의 단 한 줄이면 정리가 끝난다. line 9~20 에 있는 내용이 잘 이해가 되지 않는다. :( (이부분의 내용도 __free_one_page() 함수 내부이다.) 그래도 code의 내용만을 보자면, 현재 계산된 order 가 합쳐질 수 있는 최상의 order 가 맞는지 확인하고(order 9), pfn_valid_within() 함수로 pfn 이 zone 내부에 있는 지 확인하는 것인데, zone 내부에 memory hole을 포함하고 있지 않다면 무조건 1 을 return 한다. 

line 10 ~ 15 위의 while 문과 아주 비슷한 행동을 하는데, 실제로 상위 order 의 free_area 를 확인해서 뭔가 처리하는 code가 아닌 그냥 확인용(?) 같은 느낌이 든다. 

line 15 에서 page_is_buddy 로 현재 구해진 order 에 +1 하여 상위 order 에 page buddy 를 확인해서 buddy page 가 같은 order 에 있는 상태를 확인했음에도 line 16 에서 free_area는 order + 1이 아니라 order 를 갖고 와서 적용한다. 한번 고민을 해봐야 할 듯 한 code이다.


일단 line 22 를 통해 계산된 order 의 free_area[order].free_list 에 page 를 잘 붙여 준다.


여기까지 buddy 초기화/할당/해제 를 알아봤다.


다음에는 할당하는 과정에서 memory 가 부족했을 경우에 수행되는 code를 살펴 보고자 한다.



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