[Google Codejam] Qualification Round 2014-Magic Trick


문제 link : https://code.google.com/codejam/contest/dashboard?c=2974486


Google Codejam 이 시작되었다. 참가는 하지 않았지만, 하나씩 풀어 보려고 노력중이다.

이 문제는 처음 시작하는 사람들에게 자신감을 줄 수 있는 문제가 아닌가 한다. :-)


문제의 내용을 요약하면,

마술사가 1~16까지의 수가 적힌 카드를 갖고 4X4 로 무작위(사실 마술사가 트릭을 쓴다는 내용이다. 실제 무작위가 아님)로 배열한뒤 지원자가 선택한 카드가 무엇인지 맞추는 문제이다.


Rule 과 함께 예제를 들면,

1. 마술사가 1~16 까지 숫자를 4X4로 놓는다.

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16


2. 지원자가 원하는 숫자가 어느 열에 있는지 물어본다.

예를 들어 지원자는 7을 생각했고 마술사에게 "2"열이라고 답해준다.


3. 마술사가 카드를 썪어 다시 한번 놓게된다.

1 2 5 4
3 11 6 15
9 10 7 12
13 14 8 16


4. 그럼 지원자는 "3" 열이라고 마술사에게 얘길 하고, 마술사는 지원자가 생각한 숫자 7을 말해주면 된다.


이는 마술사가 1번째와 2번째 카드 배열시 "Trick"을 써 지원자의 생각한 숫자를 맞추는 것이다.

(이런걸 속을 수 있나.. 지원자도 말하면서 눈치를 챌듯..)


이렇게 정상적인 case 만 있는 것은 아니다. 지원자가 실제 위의 예제에서 마술사가 2번째로 카드를 배열했을 때

3열이라고 답해야 하지만, 2열이라고 답하게 되었다면 "Volunteer cheated!" 출력하고,

마술사가 2번째 배열에서 5,6,7,8 을 다른 열로 분리를 해야 하는데, 하나이상 같은 열에 놓게 되어 맞출수 없는 경우에는

"Bad magician!" 이라고 출력하면 이 문제는 완전히 풀수 있게 된다.


문제 link 를 가면, Sample case 가 잘 나와있다. 


일단 Google Codejam 은 input data file을 제공한다. 이 파일을 받아서 code의 input으로 사용하면된다.

input 파일의 한라인씩 읽는다고 했을때,

첫번째 배열에서 지원자가 말한 열(5, 6, 7, 8) 을 저장해놓고 다음 배열에서 말한 (9, 10, 7, 12)를 단순 비교한다


만약

1. 같은 숫자가 하나 라면, 그 값을 return 한다.

2. 같은 숫자가 2 이상 4 이하라면 "Bad magician!" 을 return 한다.

3. 같은 숫자가 하나도 없으면 "Volunteer cheated!" 를 return 한다.


끝.


파일을 첨부하는데 문제가 있어, code를 붙여 넣는다.

def checkCards(first, second):
    sameCount = 0
    sameNum = ""

    for num in range(0, len(first)):
        for snum in range(0, len(second)):
            if first[num] == second[snum]:
                sameCount = sameCount + 1
                sameNum = first[num]

    if sameCount == 1:
        return sameNum
    elif sameCount > 1 and sameCount <= 4:
        return "Bad magician!"
    elif sameCount == 0:
        return "Volunteer cheated!"

    # never reach hear with data from codejam
    return "error"

if __name__ == "__main__":
    testcases = input()
    LineOfCardsFirst = []
    LineOfCardsSeconds = []
    for caseNr in xrange(1, testcases + 1):
        for num in range(0,2):
            selectRow = raw_input()
            #print "selected Row : ", selectRow
            for row in xrange(0, 4):
                if row == int(selectRow) - 1:
                    if (num == 0):
                        LineOfCardsFirst = map(int, raw_input().split())
                        #print "LineOfCards : ", LineOfCardsFirst
                    else:
                        LineOfCardsSeconds = map(int, raw_input().split())
                        #print "LineOfCards : ", LineOfCardsSeconds
                else:
                    raw_input()
 

      print("Case #%i: %s" % (caseNr, checkCards(LineOfCardsFirst, LineOfCardsSeconds)))


Git add -p 로 수정사항 분리하기


git commit 은 하나의 수정 사항을 반영 하는 것이 Kernel 과 같은 대형 project 에 merge 될 확률이 높다. 이는 kernel에 Documentation/SumittingPatches 의 3번(Separate your changes.)의 내용을 보면 알 수 있다.


하지만 수정을 하다 보면, 하나의 commit 내부에 두 개 이상의 bug 수정이 있을 수 있다. 이런 경우 여러 개의 Commit 으로 분리하는 방법을 작성해 본다.


최근 Kernel patch 를 만드는 과정에서 Maintainer 가 두 개로 분리해달라는 요청이 있었고, 이를 위해 고민하다 검색으로 찾아낸 방법이다.


이미 하나의 commit 으로 내 local branch 에 있다. 이 commit 을 두 개로 분리했던 과정을 살펴본다.


1. 내 commit 이전으로 돌아간다.

  - 이 때, 분리하려는 commit 이 최상위에 있어야 한다. 만약 최상위가 아니라면 http://woodz.tistory.com/75 여기를 참고하시면 된다.

$ git reset HEAD^

이렇게 하면, 최상위에서 바로 전의 commit 으로 reset 이 되고 내 수정 사항은 un-stage 된다.


현재 상황은, 아래와 같다. 아래서 위쪽 code block 과 아래쪽을 분리하여 commit 할 것이다.

$ git diff

diff --git a/kernel/workqueue.c b/kernel/workqueue.c

index 0ee63af..0679854 100644

--- a/kernel/workqueue.c

+++ b/kernel/workqueue.c

@@ -4087,10 +4087,7 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu,

                if (cpumask_equal(cpumask, pwq->pool->attrs->cpumask))

                        goto out_unlock;

        } else {

-               if (pwq == wq->dfl_pwq)

-                       goto out_unlock;

-               else

-                       goto use_dfl_pwq;

+               goto use_dfl_pwq;

        }


        mutex_unlock(&wq->mutex);

@@ -4100,7 +4097,8 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu,

        if (!pwq) {

                pr_warning("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n",

                           wq->name);

-               goto out_unlock;

+               mutex_lock(&wq->mutex);

+               goto use_dfl_pwq;

        }


        /*


이 상태에서 "git add -p" 를 입력하면, 

$ git add -p

diff --git a/kernel/workqueue.c b/kernel/workqueue.c

index 0ee63af..0679854 100644

--- a/kernel/workqueue.c

+++ b/kernel/workqueue.c

@@ -4087,10 +4087,7 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu,

                if (cpumask_equal(cpumask, pwq->pool->attrs->cpumask))

                        goto out_unlock;

        } else {

-               if (pwq == wq->dfl_pwq)

-                       goto out_unlock;

-               else

-                       goto use_dfl_pwq;

+               goto use_dfl_pwq;

        }


        mutex_unlock(&wq->mutex);

Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? n


알아서 한 블럭씩 분리하여 더 나눌 것인지, 아니면 이부분을 하나의 Commit 으로 만들 것인지 물어본다.

나는 아래의 block 을 먼저 commit 하고 이 부분을 나중에 commit 하려고 한다. 그래서 "n" 을 입력했다.

n 을 입력하면, 

@@ -4100,7 +4097,8 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu,

        if (!pwq) {

                pr_warning("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n",

                           wq->name);

-               goto out_unlock;

+               mutex_lock(&wq->mutex);

+               goto use_dfl_pwq;

        }


        /*

Stage this hunk [y,n,q,a,d,/,K,g,e,?]? y

다음 block 의 code를 할 것인지 연속적으로 물어본다. 여기서 "y" 를 입력하면 이 부분만 git add 가 된다.


확인을 해보면,

$ git status

# Not currently on any branch.

# Changes to be committed:

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

#

#       modified:   kernel/workqueue.c

#

# 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:   kernel/workqueue.c

#

위에서 처럼 일부는 add 가 되고 나머지는 un-stage 상태가 된다. 


그 다음에

$ git commit -s

제목 쓰고.. 내용 쓰고.. 저장 하고 나온다.


또 그 다음에 나머지 수정 사항을 기록 한다.

$ git add

$ git commit -s

제목 쓰고.. 내용 쓰고.. 저장하고 나온다.


그렇게 하면 의도했던 대로 하나였던 commit 이 두 개로 분리가 된다.


사실 이렇게 하지 않아도 되는데, 지저분하거나 혹은 새로 작성해야 하는 문제가 발생한다. 물론 위와 같은 예제는 간단해서 다시 작성해도 되긴 하지만, 이와 같은 방법을 사용하면 이미 잘 작성된 code 에 실수 등이 발생하지 않을 것이다.(code는 건들지도 않으니.. 물론 분리를 잘못하면 발생하는 문제는 있긴 하다. :-) )


이렇게 해서 patch 를 보내면 된다. 



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

VIM 에서 spelling 확인하기  (0) 2016.07.08
Git: 특정 commit 으로 이동 후, amend 하기  (0) 2014.02.28
const char* vs. char const*  (0) 2014.02.19
Fish shell environment  (0) 2013.12.03
Kernel mailing list 활용 방법  (0) 2013.11.08

Git: 특정 commit 으로 이동 후, amend 하기


Git 으로 local branch 를 생성 한 후, 특정 source 를 수정함에 있어 여러개의 commit 이 만들어 질 수 있다.

이 때, 기존에 했던 commit 에 추가적인 수정을 하려면 git add/commit --amend 를 하면 되는데 최상위에 있는

commit 은 바로 수정 후, git add/git commit --amend 하면 적용이 되지만 현재 수정하려는 내용이 이전에 있던 commit 에 반영되었으면 하는 경우가 발생한다.


예를 들어, 아래와 같이 commit 들이 있다고 가정하자.

commit ec88cc08f5a29f98a3c5f14ca642f235fb1c0fb8

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

Date:   Fri Feb 28 15:34:22 2014 +0900


    staging: cxt1e1: fix checkpatch errors with open brace '{'


    clean up checkpatch.pl error:

     ERROR: that open brace { should be on the previous line


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


commit 6007a41fffd430b79775871a2c9eb6c35eb3e6a6

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

Date:   Fri Feb 28 15:27:51 2014 +0900


    staging: cxt1e1: fix checkpatch error 'assignment in if condition'


    checkpatch.pl error:

     ERROR: do not use assignment in if condition


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


commit aa877600a98048d056b65d0c9e7426616e9ccc2f

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

Date:   Fri Feb 28 15:22:13 2014 +0900


    Staging: cxt1e1: Fix line length over 80 characters in hwprobe.c


    clean up checkpatch.pl warnings:

     WARNING: Line length over 80 characters


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


commit 2f61d921981a45c687e5dc26d6f7e6a3925ca0e5

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

Date:   Fri Feb 28 15:14:39 2014 +0900


    staging: cxt1e1: Fix no spaces at the start of a line in hwprobe.c


    clean up checkpatch.pl warnings:

    WARNING: please no spaces at the start of a line in


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


이 때, 추가적인 수정사항이 발생 되었는데, 이 내용이 맨 아래에 있는 "staging: cxt1e1: Fix no spaces at the start of a line in hwprobe.c" 에 적용되었으면 하는 바램이 생겼다.


그렇다면, 일단 그 commit 을 수정할 수 있는 mode(?) 로 가야한다.

$ git rebase --interactive 2f61d921981a45c687e5dc26d6f7e6a3925ca0e5^

라고 한다. 맨 마지막에 "^" 를 넣어줘야 그 commit 을 포함하여 rebase  를 진행한다.


위에서 처럼 입력하면, 나의 경우는 vim 이 열리면서 아래와 같은 내용이 입력된다.

pick 2f61d92 staging: cxt1e1: Fix no spaces at the start of a line in hwprobe.c

pick aa87760 Staging: cxt1e1: Fix line length over 80 characters in hwprobe.c

pick 6007a41 staging: cxt1e1: fix checkpatch error 'assignment in if condition'

pick ec88cc0 staging: cxt1e1: fix checkpatch errors with open brace '{'


# Rebase b9ea35b..ec88cc0 onto b9ea35b

#

# Commands:

#  p, pick = use commit

#  r, reword = use commit, but edit the commit message

#  e, edit = use commit, but stop for amending

#  s, squash = use commit, but meld into previous commit

#  f, fixup = like "squash", but discard this commit's log message

#  x, exec = run command (the rest of the line) using shell

#

# If you remove a line here THAT COMMIT WILL BE LOST.

# However, if you remove everything, the rebase will be aborted.

#


여기서 내가 수정하고 싶은 것은 맨 위에 있는 commit 이다. "pick" 을 "edit" 로 변경하고 저장 하고 나와 git log를 하면

최상위에 그 commit 이 올라와 있다. 


이제 원하던 수정을 하자.


수정이 완료되면,

$ git add <file>

$ git commit --amend


이렇게 하면 일단 현재 원하던 commit 에 추가 반영이 된다.


이제 원래 갖고 있던 commit 들을 복구 해야 한다.

$ git rebase --continue


이렇게 하면 수정한 commit 이 후에 생성된 commit 들을 merge 하기 시작한다. 하나씩 merge 를 하다가 conflict 날 수 있다.

그러면 rebase 가 중지 되고, conflict 를 해결 한 후에 다시 git rebase --continue 를 입력하라고 한다.


이렇게 해서 마지막으로

Successfully rebased and updated ref/heads/<local branch name>

이라고 나오면 성공한 것이다.



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

VIM 에서 spelling 확인하기  (0) 2016.07.08
Git add -p 로 수정사항 분리하기  (0) 2014.04.16
const char* vs. char const*  (0) 2014.02.19
Fish shell environment  (0) 2013.12.03
Kernel mailing list 활용 방법  (0) 2013.11.08

const char* vs. char const*


code 를 보다 보니 C/C++ 에서 사용하는 const 에 대한 내용은 알고 있지만, 막상 보면 어디가 상수로 결정되어 수정될 수 없는지 계속 찾아 보게된다. 


그래서 검색을 해보았더니, 예제로 잘 정리된 내용이 있어 갖고 왔다.

original url : http://stackoverflow.com/questions/162480/const-int-vs-int-const-as-function-parameter-in-c-and-c


아래서 처럼 const int 와 int const 는 같은 의미로 사용되어 진다.

const int a = 1; // read as "a is an integer which is constant"
int const a = 1; // read as "a is a constant integer"

그래서 아래와 같이 2를 넣을 수 없다.

a = 2; // Can't do because a is constant

하지만 pointer 변수일 때는 다르게 해석이 되는데, 하나는 주소값을 변경하지 못하는 것과 다른 하나는 주소가 가리키고 있는 값의 변경을 할 수 없는 것이다. 아래의 주석을 잘 읽어보면 이해가 될 것이다.

const char *s;      // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a char"

*s = 'A'; // Can't do because the char is constant
s++;      // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++;      // Can't do because the pointer is constant


Install Fish Shell on Ubuntu


Fish shell 이라는 것이 생겼다. 이 shell 의 가장 큰 특징은 자동 완성기능인데, 특정 command 의 man page를 parsing 해서 option 까지 자동완성해준다. 기본적으로 현재 typing 하고 있는 command 의 hint 기능도 있다. 


또 하나 큰 특징은 shell 에서 script 작성 및 실행이 가능하다는 것이다.(command line 에서 편집기 editor 처럼 동작이 가능하다는 것이다!) 


이러한 모든 기능의 설명은 http://fishshell.com/docs/current/index.html 에서 확인할 수 있다.


여기서는 ubuntu 에 fish shell 의 설치 방법과 간단한 기능들을 살펴 본다.

(Bash shell 에서 지원하는 것은 일단 다 지원하는 듯 하다)


1. Installation.

sudo apt-add-repository ppa:fish-shell/release-2

$ sudo apt-get update

$ sudo apt-get install fish


2. 실행

$ fish


3. 기본 shell 로 등록

$ chsh -s /usr/bin/fish


2 번 실행으로 사용하다가 괜찮다 싶으면 기본 shell 로 등록해서 사용하면 될 것이다.


4. Commands hint



위에 그림 처럼 최근 history base 로 vi 를 입력했을 때 회색으로 되어 있는 부분이 자동 완성 될 문장이다. 간단히 화살표 오른쪽을 누르면 그  command 를 바로 이용할 수 있다.(화살표 위/아래를 하면 vi 와 관련된 history command 들간 이동이 가능하다.)


5. Edit script



쉘에서 바로 script 를 작성할 수 있다. 위의 그림처럼 간단히 작성 가능하고 위/아래로 움직여서 수정도 가능하다. 정말 editor 처럼 동작한다.

실제 예로는 현재 시점에서 prompt 모양을 변경할 경우 이렇게도 사용할 수 있을 것이다.

(나머지는 http://fishshell.com/docs/current/index.html 으로 이동해서 확인 바란다.)


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

Git: 특정 commit 으로 이동 후, amend 하기  (0) 2014.02.28
const char* vs. char const*  (0) 2014.02.19
Kernel mailing list 활용 방법  (0) 2013.11.08
PuTTY 설정 값 공유하는 방법  (0) 2013.10.07
Git with eclipse  (0) 2013.04.04

Qemu booted kernel debugging with GDB


책을 읽고, 그 내용의 소스를 분석하다 보면 어떤 값에 의해 진행이되고 예제를 만들어 보는 과정에서 도저히 답이 안나오는 상황들이 생긴다. 그 때 대충 시나리오를 만들고 만든 시나리오에 값을 넣어 code를 분석하는데 값이 명확하게 떨어지면 굉장히 분석이 잘된다.(지난 buddy 관련 code를 분석할 때 그러했다.http://woodz.tistory.com/57)


하지만 그렇게 분석이 순조롭게 흘러가지 못하고 방황을 하다보면 집중도 안되고 하다 말아버리는 경우가 다반사다. 그래서 얼마전에 찾다보니 gdb 를 연결하여 kernel code를 쫓아가는 방법을 알게 되었다. 


eclipse + CDT 에 gdb를 연결하여 추적하는 방법은 UI 사용성이 좋고 보기 편하지만 너무 무겁다는 것이 단점이다. 조금 빨리 보고 싶은데 답답해서 eclipse 를 빼고 최대한 편한 방법을 찾았다.

(eclipse 를 이용해 kernel debugging 및 trace 하고 싶으신 분은, http://www.sw-at.com/blog/2011/02/11/linux-kernel-development-and-debugging-using-eclipse-cdt/ 여기를 참조하여 하면 좋다. 그림으로 잘나와있기도 하고)


이 포스팅은 kernel target 을 x86 으로 build 하고 qemu 로 순조롭게(?) 부팅하여 gdb 연결하는 것까지 준비했다.


1. kernel 준비

  - kernel source 를 받자.

$ mkdir ~/work/Kernel

$ cd ~/work/Kernel

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git


  요샌 30분 이내로 다운로드 가능하더라.


  - kernel build

$ make ARCH=i386 menuconfig

   다른 것들은 default 로 내비두고, gdb 를 사용해야 하므로 두가지 option 이 check 되어 있는지만 확인하자.

  a. Kernel hacking ==> Compile-time checks and compiler options ==> [*] Compile the kernel with debug info

  b. Kernel hacking ==> Compile-time checks and compiler options ==> -*- Compile the kernel with frame pointers


b는 default 로 되어 있었다. 안되어 있는 분은 check 해주자.

% menuconfig 하면 ncurses-devel 이 설치 안되어 있다고  error를 낼때는,

  ==> $ sudo apt-get install ncurses-dev


$ make ARCH=i386 -j4


2. qemu 준비
 - Ubuntu 의 경우

sudo apt-get install qemu qemu-system


3. kernel 부팅.(x86_64 system)

$ qemu-system-x86_64 -no-kvm -kernel arch/x86/boot/bzImage -hda /dev/zero -append "root=/dev/zero console=ttyS0" -serial stdio

% 32bit machine 의 경우 qemu-i386 command 로 하면 된다.


위에처럼 하면 kernel 이 부팅된다. 물론 root filesystem 이 없기 때문에 mount 를 못하고 panic 을 발생시킨다. 

(control + C 하면 qemu 가 종료한다.)


root file system 없이도 kernel 을 gdb 에 연결할 수 있다.

우선 rootfs 없이 진행을 해보자.


4. gdb 연결

$ qemu-system-x86_64 -s -S -no-kvm -kernel arch/x86/boot/bzImage -hda /dev/zero -append "root=/dev/zero console=ttyS0" -serial stdio


위의 command 에서 -s 는 gdb 를 default 로 쓰되 tcp port 1234 으로 연결하겠다는 의미의 옵션이다.(-gdb tcp::1234)

-S 옵션은 cpu 를 start 해줄때까지 멈춰있겠다는 의미이다.


실행하면 검은 화면만 떨렁 있는 화면을 볼껏이다. 그럼 이제 gdb 를 연결해보자.

$ cd ~/work/Kernel/linux/

$ gdb ./vmlinux

GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04

Copyright (C) 2012 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-linux-gnu".

For bug reporting instructions, please see:

<http://bugs.launchpad.net/gdb-linaro/>...

Reading symbols from /home/daeseok/work/Kernel/linux/vmlinux...done.

(gdb) target remote localhost:1234

Remote debugging using localhost:1234

0x0000fff0 in ?? ()


gdb command 이후에 "target remote localhost:1234" 를 치면, qemu 와 연결이 완료된다.

아직도 qemu 는 검은색화면이다. 

그다음에는 break point 하나를 잡자. 

(gdb) b start_kernel

Breakpoint 1 at 0xc18cb6f2: file init/main.c, line 484.

(gdb) c

Continuing.


Breakpoint 1, start_kernel () at init/main.c:484

484 {

(gdb)


break point 잡고 c(continue) 를 입력하면 실제 진행하다 start_kernel() 에서 멈춰있는 것을 확인 할 수 있다.

gdb command 로 요리하면서 부팅 sequence 를 살펴봐도 되고.. 뭐 암튼 그러하다.


(gdb) list

479 pgtable_init();

480 vmalloc_init();

481 }

482

483 asmlinkage void __init start_kernel(void)

484 {

485 char * command_line;

486 extern const struct kernel_param __start___param[], __stop___param[];

487

488 /*


현재 멈춰있는 곳의 소스 보기.

gdb 를 잘쓰면 아주 훌륭하다고 하는데... gdb 에 text ui 를 붙인 version 이 있는데 맨 아래에 추가적으로 소개하겠다.


5. simple rootfs 만들기.

  - get rootfs 

  http://downloads.yoctoproject.org/releases/yocto/yocto-1.2.1/machines/qemu/qemux86/ 이 링크에 접속하면

미리 만들어진 rootfs 가 있다. tar 로 묶여져있는 것과 ext3 확장자를 가진 것이 있는데, 지금 만들어진 image는 download 가 안된다. --;


일단 제일 작은 tar.gz로 된 rootfs 를 받자.

core-image-minimal-dev-qemux86.tar.bz2 이걸 받았다.

$ mkdir ~/work/rootfs_qemu

$ tar zxf core-image-minimal-dev-qemux86.tar.bz2 -C ~/work/rootfs_qemu


압축을 풀면, 아래와 같이 구성이 되어 있다.

bin  boot  dev  etc  home  lib  media  mnt  proc  sbin  sys  tmp  usr  var


이것을 qemu 가 mount 할 수 있도록 만들어 주면 된다. 이 방법도 eclipse CDT 에 참조했던 link 에 나와있다.

$ BLOCKS=$(((1024*$(du -m -s rootfs | awk '{print $1}')*12)/10))

$ genext2fs -z -d rootfs_qemu -b $BLOCKS -i 1024 rootfs.ext3

$ resize2fs rootfs.ext3 1G

$ tune2fs -j -c 0 -i 0 rootfs.ext3

사실 위의 command 들 모두가 뭐하는 것인지 자세히는 모른다. :)


암튼 만들어진 rootfs.ext3 로 mount 하여 기존 빌드한 kernel image로 부팅해보자.

qemu-system-x86_64 -no-kvm -kernel arch/x86/boot/bzImage -hda ~/work/rootfs.ext3 -append "root=/dev/sda console=ttyS0" -serial stdio 


부팅을 다하면,

.....

Starting system message bus: dbus.

Starting syslogd/klogd: done

Stopping Bootlog daemon: bootlogd.


Yocto (Built by Poky 7.0.1) 1.2.1 qemux86 ttyS0


qemux86 login: 


위에처럼 login 화면이 뜬다. root 라고 입력하면 shell 을 만날 수 있을 것이다.


여기서 kernel debugging을 하려면, qemu option 에 -s 만 옵션으로 넣고 gdb를  붙이면 된다. 이래저래 해보시길...


6. cgdb

검색을 해보면 아시겠지만, text based(gdb -tui) gdb 에 syntex highlight 를 넣은 것이다. 

$ sudo apt-get install cgdb


사용은, 

$ cgdb ./vmlinux

하고 위에서 remote 에 연결하는 command 를 하면 된다. break point를 잡고 run 했을 때, 어떻게 보이냐면,



위의 예제는

break point 를 compact_zone 함수에 걸어놓고, 

shell 에서 $ echo 1 > /proc/sys/vm/compact_memory 하면 그 함수가 불리고 break 가 걸린다.

그럼 source level 에서 쫓아갈 수 있을 것이다.


gdb 사용이 아직 익숙치 않아 봐야 할 부분들이 많다.



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 이 가능 한것이다.


지난 일요일에 신도림역에 위치한 뽀로로 테마파크를 방문했다. 이제 17개월 된 아이가 조금은 뽀로로를 인지한 것같아서 데리고 갔는데 인지하고 안하고를 떠나서 너무 좋아하는 것이었다.

위치는 신도림역 하차하여 외부로 나가지말고 1번출구 방향으로 이동하면 디규브 시티 백화점 지하1층으로 갈수있다 거기서 엘리베이터를 타고 4층으로 가면 된다.
주의!! 유모차타고 지상으로 올라가서 1번출구로 가면 돼지~ 하는생각을 하고 2번출구 쪽에만 있는 엘리베이터를 타고 올라가면 1번출구로 가는 방법찾기가 쉽지않고 그냥멀다! 그렇게 가면 더 고생한다.

일단 1번 출구방향으로 가면


디큐브시티가 보인다. 보이는길 따라 가면 입구가있고 엘리베이터를 잘 찾아서(?) 4층으로 가자.

4층 엘리베이터를 내리면 일단 뽀로로 부위기가 많이 난다. 입구를 보면,


줄을 설수있다. 아이가 자고있어서 들어가기전에 주위를 돌아보니


포토존 처럼보이는 곳에 포비가 인사를 한다.

입장료에 대한 내용은 기억나는데로 정리하면,
   1. 2시간 기본이고 어른 6천원 아이가 1만 6천원이다. 2시간 이후 십분당 어른 5백원 아이 천원이다. 비싸긴하다
   2. 18개월미만 아이들은 50% 할인이다. 할인율이 센만큼 애매한 아이는 증명할만한 서류(저는 등본 출렸했음)를 갖고가야한다.
   3. 입구 쪽에 컴퓨터가 있는데 멤버쉽가입하면 첫방문시 20% 할인이고 다음부터는 10% 할인이다.


여기까지이고 시간은 대략 오전 10:30 분 부터 오후 8:30까지로 봤다. 내가 11시반 쯤에 갔는데 점심시간이라 그런지 한산했다. 1시반에서 2시에 입장하는 사람들이 엄청많았다. 일찍가서 놀고 빠지는 것이 좋을 것 같다.

입장하자 마자 정면에 보이는 ?? 뭘라불러야 할지..


이 넓은 공간을 뛰어 놀다 미끄럼틀을 타고 공이있는 곳으로 이동이된다.

일단 뽀로로네 집으로 가보자. 아이랑 둘이서 가서 사진찍을 타이밍을 잘잡지 못했다.





들어가면 티비에서 보는 방이랑 거의 똑같다.


주위를 살펴보면
카페가 있는데 공사중이었다



크롱이 있는 쪽으로 가면 간단한 장난감 정도 있다.

뽀로로 친구들 집에서 컵케익도 만들고 하는 이벤트등이 있지만 나이제한이 있다.

기차도 타보자. 시간이 정해져 있으니 잘보고 타자.


짧게 두바퀴 돈다.

그리고 흔히 키즈카페있는 놀이 시설이 있는데 조금크다는것이 좋다.

중간에 갑작스런 뽀로로 등장! 질서정연히 아이들은 같이 춤을 춘다
더 많은 볼꺼리와 놀이시설이 있었으나 너무 힘들어서 2시간을 못채우고 나왔다. 가족들과 가기 좋은곳이다 백화점에서 쇼핑도 할수있다.



'Mobile ' 카테고리의 다른 글

자연 캠핑장 - 과천  (1) 2013.10.08
서울 대공원 - 동물원  (0) 2013.10.05
Mobile 글쓰기  (0) 2011.07.28

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 이다.


+ Recent posts