Ubuntu 12.04 64 bit LTS


* git 을 설치


* server가 실행될 때 항상 실행되도록 Upstart Script 등록

/etc/init/<script name>.conf


<참고>

xxxx.conf : upstart script

xxxx.override : 기존의 xxxx.conf를 override함.


* /etc/init/git-daemon.conf 작성 (git project directory = /work/.prjrepo/)


start on runlevel [2345]                                                                        

stop on runlevel [!2345]                                                                        


expect fork                                                                                     

respawn                                                                                         


exec /usr/bin/git daemon --user=gitro --group=gitro --reuseaddr --base-path=/work/.prjrepo/ /work/.prjrepo/


* export하고자 하는 git repository안에 magic file인 git-daemon-export-ok 파일 생성 (size 0인 dummy file - 그냥 touch로 생성하면 된다.)


* client에서 test.

git clone git://<host>/<project>

<9418 포트 사용>

'Domain > Linux' 카테고리의 다른 글

[Linux] mmap and memory...  (0) 2013.10.07
CoW(Copy on Write) on ARM  (0) 2013.09.26
atime 대신에 strictatime을 사용하자!  (0) 2013.05.31
[Shell] Variable Name Expansion  (0) 2013.03.28
[Linux][Shell] Cleaning PATH environment variable.  (0) 2011.09.15

Ubuntu의 경우 default mount option이 'relatime'으로 되어 있는데, 이것을 바꾸기 위해서 "mount -o remount,atime,xxxx" 식으로 해도 relatime 에서 atime으로 mount상태가 바뀌지 않는다.

대신 strictatime을 사용하면, 정상적으로 원하는 형태로 mount할 수 있다.

(mount의 man page에 상세하게 설명이 나와있긴 하나... atime이상 동작하지 않는다는 설명은 찾을 수 없었다.)


요점은 atime 대신 strictatime을 사용하라!

 요즘 대부분의 경우 compiler의 optimization만으로도 충분하다.

 그렇지만 몇몇 특수한 경우, compiler는 알 수 없지만, programmer는 알고 있는 것들을 적극적으로 이

용할 필요가 있다. 이 글에서는 다음의 내용들을 소개하고자 한다.


* memory barrier
 compile은 file단위로 이루어진다. 그러므로, 비록 programmer가 thread-safety에 대해 고려 하고

coding을 했더라도, compiler가 optimization 과정에서 이를 깨는 경우가 많다.
    가장 대표적인 예가 아래의 경우이다.

    ...
    struct stA {
        int a;
        int b;
        int c;
    };
    ...
    replace (const struct stA* p) {
        struct stA* np = malloc(sizeof(struct stA));
        np->a = 10;
        np->b = 20;
        np->c = 30;
        // <-- A --->
        p = np; // assumes that assign-operation is atomic.
    }
    ...


 programmer는 multi-threading환경에서의 race condition을 고려해서 replace함수를 구현했지만 -

"p = np;"가 가장 아래에 위치해 있다. - compiler의 Out Of Order Optimization으로 인해 programmer의

이러한 의도가 지켜지지 못할 수도 있다.
 (단, 여기서 CPU의 Hardware적인 Out Of Order Execution과 거기에 대응하는, Hardware적인 memory

fence/barrier는 논외로 한다.)
 이런 경우, mutex를 이용해서 해결할 수도 있지만 mutex는 상당히 cost가 비싼 operation이니 만큼

되도록이면 이용하지 않는것이 좋다. 이런 경우 memory barrier를 이용하면 된다.
 memory barrier (membar, memory fence 혹은 fence instruction)에 대한 자세한 내용은 다른 여러

곳에서 잘 설명되어 있으니 따로 언급하지 않겠다.
 다만, 위의 경우 'A'의 위치에 barrier를 두면 mutex를 사용하는 overhead없이 위 문제를 해결할 수

있다.


* compiler hint

 이미 앞서 이야기한대로, compiler는 알지 못하지만, programmer는 알고 있는 내용들을 이용하는 또 다른 방법이 'compiler hint'이다.

 compiler hint는 branch문 - 특히 if/else - 에서 많이 쓰이는데, programmer가, runtime에 분기하는 빈도수가 높은 쪽을 compiler에게 미리 hint를 주는 것이다. 예를 들면, "if (<cond>) { A } else { B }" 이고, runtime에 대다수의 경우 B쪽으로 분기하게 된다는 것을 programmer가 안다면, <cond>에 이러한 힌트를 함께 언급해 주는 것이다.

 그럼 왜 branch문에 이런 compiler hint가 많이 쓰이는 것일까? 그리고 어떠한 잇점이 있는 걸까?

 대부분의 Architecture - 특히 RISC - 에서, 성능향상을 위해, pipeline을 사용한다. 그리고 일반적으로 이 pipeline의 단계를 늘림으로서 성능향상을 기대할 수 있다.

 그런데, pipelining을 위해서, 다음에 수행할 instruction을 미리 읽어오는 방법을 쓰게 되는데 - instruction prefetch - branch문에서는 다음에 어떤 쪽으로 수행될지가 runtime에 결정되기 때문에 - 즉 compiler가 알지 못한다 -  compiler는 자체적으로 판단하여 prefetch를 수행할 쪽을 결정하고, 이를 반영해서 compile하게 된다. 이런 예측이 옳은 경우는 pipeline이 깨어지지 않고 잘 수행되어 별 문제가 없는데, branch가 예측한 곳과 다른 곳으로 일어나게 되면, 미리 prefetch했던 instruction이 무효한 것이 되므로 이 순간 pipelining이 깨어지게 된다. (앞의 내용들이 이해가 되지 않는다면, pipeline에 대해서 wikipedia등을 이용하길 바란다.) 즉, performance측면에서 손해를 보게 되는 것이다.

 이런 손해를 최소화 하기 위해서, programmer가 어느 쪽으로 분기하는 경우가 많은지를 compiler에게 hint를 줄 필요가 있는 것이다.

 아래의 예는 Linux kernel에서 볼 수 있는 'likely/unlikely'를 사용한 예이다.


#define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) if (likely(n > 1)) { // 자주 수행되는 routine } else {

// 상대적으로 적은 빈도로 수해오디는 routne }

if (unlikely(n > 1)) { // 상대적으로 적은 빈도로 수해오디는 routne

} else {

// 자주 수행되는 routine }


ㅇㅇㅇㅇ


특정 코드 구간이 수행되는 시간을 측정해야 할 경우가 종종 생기는데, 대부분 아래와 같은 방법을 사용한다. (ex. Java)

 

long tm = System.currentTimeMillis();
< do something >
tm = System.currentTimeMillies() - tm;
System.out.println("Time : " + tm);
그렇지만, 약간만 응용을 하면 아~주~ 조금이지만, 위의 코드를 더 간단하게 만들 수 있다.

long tm = -System.currentTimeMillis();
< do something >
tm += System.currentTimeMillis();
System.out.println("Time : " + tm);

어떤가? ^_^

 Variable(변수)와 Function(함수)는 programming을 하는 사람이라면, 누구에게나 익숙한 개념이다.

 그렇지만, 조금 더 살펴보면 흥미로운 요소들을 많이 찾을 수 있다.


 먼저, Variable에 대해서 이야기해 보자.

 거의 모든 programming language에서, variable은 type과 name 두 가지로 구성된다.

 비록 Static/Dynamic typing, 혹은 Strong/Week typing 등의 차이는 있으나, 기본적으로 type과 name이 존재한다는 점에서는 차이가 없다.

 그럼, 혹시 "variable은 왜 필요하지?", "왜 거의 모든 programming language에서 변수는 type과 name으로 구성되어 있지?"등의 의문을 가져본 적이 있는가?

 이는 분명 한번 쯤은 생각해 볼 문제이다.


 사실 이런 의문들에 대한 대답은 programming 발전의 역사와 함께한다고 할 수 있다.

 따라서, 이런 부분에 대한 자세한 내용은 넘어가고, 간략하게, "왜 type과 name"으로 나뉘어지는 지를 고민해보는 것으로 대체하기로 하자.

 

이런저런 자세한 내용은 차치하고, 기본적으로 Programming이란 MCU(혹은 CPU)가 메모리에서 data를 읽어서 그것으로 연산을 수행하고 다시 그 결과를 메모리에 저장하는 일련의 과정이다. 이때 메모리에서 데이타를 읽고 그것을 가공하기 위해서는, 두 가지가 필수적으로 필요하다. "어디서 data를 읽고, 그 data가 어떻게 해석되어야 할 것인가?"가 그것이고, 그것이 바로, "메모리 주소" 와 "메모리 data type"이다.

그 결과, "메모리 주소 = Symbol Address"이고, "data type = Symbol Type"으로 해석되는 것이다.
 또한, 변수, 함수 모두 memory address의 alias 역할을 하는 symbol + memory data의 해석 방법을 알려주는 type, 두 가지로 이루어져 있다는 측면에서 보면 근본적으로 같은 개념이다. 단, variable은 data이고, function은 execution code라는 측면이 다를 뿐인데, 특정언어 - ex. LISP(data와 code가 구분되지 않는다.) - 에서는 이것 마저도 구분하지 않는 경우도 있다.

 즉, 함수라는 것 역시 일련의 데이터를 execution code로 해석하는 것! 그 이상도 그 이하도 아니다 (일반적인 Programming lanuage에서는 함수의 type이란, 함수의 signature로 표현된다.)

 

요점은 다음과 같다.

오늘날 사용되는 거의 모든 programming이 CPU, memory구조를 가지는 (넓은 의미의)폰-노이만(Von-Neumann)방식에 근간을 둔 하드웨어 구조위에서 이루어지고 있다. 그리고, 이를 전제한 programming language는 memory address의 alias인 'symbol'과 memory data의 해석 방식인 'type' 두 가지에 기반한 형태로 자연스럽게 발전하게 되었다...라는 개인적인 의견을 피력해본다. ^_^


 소프트웨어쪽 업무의 대부분은, 바쁜 일정 속에서, 로직(logic)구현 및 디버깅을 반복하는 일이다.

 그러다 보니, 입사초기에, 실무적으로 필요한 내용을 최대한 빨리 습득하고, 이를 바탕으로 업무에서 성과를 내는 것을 우선시 하게 된다.

 그렇지만, 이런식으로 프로그래밍을 배운 사람들은, 마치 언어에 대한 깊은 이해나 지식, 이론적은 배경 없이 "우리나라 말"을 통해서 의사소통을 하는 초등학생과 같이, 당장 필요한 몇가지 일들은 해낼 수 있을지 모르겠으나 어느 순간 한계에 부딫히게 되기 마련이다.


 이 강좌는, 프로그래밍에 대한 기초적인 지식을 보유한 사람들을 대상으로 하며, 프로그래밍의 바탕에 깔린 깊은 내용을, 구체적인 예를 통해서 case-by-case로 짚어보는 방식으로 기술될 것이다.


혹시 부정확한 부분이나, 잘못 기술된 내용이 있다면... 부담없이 태클 걸어 주세요~~

 자기 개발 서적에서 많이 나오는 이야기 중 하나가. "모든 것을 '자기 탓'으로 돌려라."이다. 그래야 자기 발전이 있고, 문제해결의 실마리가 보이기 때문이다. 문제의 원인을 외부의 탓으로 돌리면, "어쩔 수 없다."는 결론 이외에는 얻을 수 있는 것이 없다.


 이런 관점에서 보면, 국내 정치환경이 나아지기 까지는 앞으로도 많은 시간이 필요한 듯 하다. 


 정치에 불만을 가지는 대부분의 글이 "저 정치인은 이래서 문제고, 저래서 문제고..." 등의 글이다. 즉, 문제의 원인을 "잘못된 정치인" - 외부 - 에서 찾고 있다. 그래서, "정치인은 어쩔 수 없다." 혹은 "정치가 다 그렇지 뭐" 식의 이야기가 나오게 되는 것이다. 위에서 "자기 개발 서적"을 예로 든 내용과 거의 유사한 흐름이다.


정말 문제를 해결하고 싶다면, 원인을 "자기 탓"으로 돌려야 한다. 즉 "내가 왜 저런 정치인에게 표를 줬을까?" 혹은 "왜 저 정치인에 표를 준 많은 사람들 중 한명이라도 더 설득하지 못했던가?" 등의 방식으로...


정치에 대한 불만을 표출하는 글과 말들이, 정치인을 주어로 하지 않고, 자기 자신을 주어로 하는 식으로 변해갈 때, 비로소 우리는 "변화"를 이야기 할 수 있지 않을까?...


Variable operator

Action 

Description 

${varname}

Nonambiguous variable substitution 

Simple variable substitution occurs with the value of varname being substituted

${varname:=value}

Assign a default value for the variable if a value does not exist.

If varname does not have a value or is set to null, then varname is set to value.

Varname is then substituted in the statement.

${varname:+value} 

Utilize value if varname is set 

If the variable, varname, contains a value and is not null, then the alternate value, value, is substituted instead of the value of the variable varname. Otherwise nothing is substituted.

${varname:-value} 

Assign a temporary default value for a variable if one does not exist 

If the variable, varname, contains a value and is not null, then it is substituted; otherwise the value, value, is substituted but is not assigned to varname. (different from = operator)

${varname:?value} 

Issue an error message if the value of variable is not set to any value. 

If the variable, varname, containsa value and is not null, then it is substituted; otherwise an error message containing the value, value, is printed and the Shell exits.

${#varname}
(Korn and Bash only) 

Return the length of the value contains in varname or the number of positional parameters.

If varname is set, the length of the value of varname is returned. If the special variable * or @ is used as varname then the number of positional parameters are returned. 

${varname#pattern}

${varname##pattern}

(Korn and Bash only) 

Substitue varname with pattern removed from the beginning of varname .

If the pattern matches the begining of varname, then pattern is removed from varname. If the # form is used, then the shortest match is removed. If the ## form is used, then the longest match is replaced. 

${varname%pattern}

${varname%%pattern}

(Korn and Bash only) 

Substitute varname with pattern removed from the end of varname. 

If the pattern matches the end of varname, then pattern is removed from varname. If the % form is used, then the shortest match is removed. If the %% form is used, then the longest match is replaced. 

${#arrayname[*]}
(Korn only) 

Substitute the number of elements in the array. 

The number of elements in the array arrayname is substituted. 


<From : UNIX Shell Programming, FOURTH EDITION, LOWELL JAY ARTHUR, TED BURNS - WILEY COMPUTER PUBLISHING>

Software업계에서 Programming skill 이라는 단어가 많이 사용되고 있는데 비해, 그 정의가 명확하지 않은 것 같다.

아니, 정의가 명확하지 않은게 아니라, 정의를 실체화 하는 방법이 명확치 않다고 해야 하나?


일단 "좋은 코드/소프트웨어"라는 말에 대한 정의는 어느 정도 합의된 실체가 있는 것 같다.

그럼에도 불구하고, 정량적인 측정 기준이 없기 때문에 여전히 관념적인 형태에 머물러 있긴 하지만...

그런데, "좋은 소프트웨어"를 만드는 것 => Programming skill 이 뛰어난 것"이 되지 못하는 이유는 현실적으로, 보통 '시간'으로 대표되는 '비용'이라는 요소가 들어가기 때문이다.

그래서 결국, Programming skill은 "적은 비용으로 좋은 소프트웨어를 만드는 기술"이라고 정의되어 질 수 밖에 없다.


문제는 앞서 이야기한 것처럼, "좋은 소프트웨어"라는 요소는 정량적인 기준이 없고, 측정방법 또한 모호하다. 그렇지만, '비용'은 객관적이고 명확한 측정 방법을 가진다. 그러다보니, 두 요소 중 '비용'이라는 요소에 더 집중하게 되고, 그것을 기준으로 Programming skill 을 말하는 경우가 많다.

그러다 보니, 극단적으로, "적인 비용으로 소프트웨어를 만드는 기술"이 Programming skill 로 왜곡되어 정의되어 버리게 된다.

(이 경우, 소프트웨어의 품질은 전혀 고려 대상이 아니다.)


실제, 계약에서도, 측정할 수 없는 "좋은 소프트웨어"라는 항목을 계약서에 기술할 수 없으므로, "비용"에 초점이 맞추어 질 수 밖에 없게되고, "적은 비용"이라는 기준을 만족시키는 쪽이 "기술력이 있는 곳"이라는 평가를 받게 되는 기이한 현상이 벌어지게 된다.

이는 계약에서 뿐만아니라, 국내 기업의 업무 형태에서도 쉽게 찾아볼 수 있다 - 물론 모든 기업이 그렇다는 이야기는 아니지만, 대부분(특히 소프트웨어가 주 제품이 아닌 곳들)의 경우 해당될 것이다.

소프트웨어를 만드는데, 연봉이 높은 우수 인력을 사용하는 것은 '과다 비용'으로 받아들이고, 경력이 짧은 미숙한 엔지니어에게 일을 맞겨 소위 "돌아가는 소프트웨어"를 만들어 내는 것을 "잘 했다"고 평가한다. 즉, 소프트웨어의 "품질"은 무시되고, "비용"부분만 고려된 "평가"인 것이다.


이와 같은 현상은 계속 반복된다.

위와 같은 방식으로 만든 소프트웨어는 필연적으로 수많은 문제점(버그)를 내포하게 된다. 이제 이 문제점들을 수정해야 하는데, 이 역시 '비용'만을 고려해서, 미숙한 엔지니어가 처리하게 되고, 수많은 fix-on-fix를 양산하는 악순환을 반복한다.

그러면서, "소프트웨어는 당연히 많은 버그를 내포할 수 밖에 없고, 약간의 수정도 많은 문제점을 동반할 수 밖에 없다."라는 이상한 결론을 내려 버린다.


위의 이야기는 좀 극단적인 측면이 있지만, 국내 현실을 상당히 잘 반영한다고 생각한다.

"소프트웨어의 비용 감소"라는 측면에서 시작된 것이 아니라, 순수하게 "소프트웨어 품질의 정량화"라는 측면에서 시작된, 소프트웨어 관련 연구가 활발하게 이루어 졌다는 이야기는 들어 본적이 없다. 물론, 내가 잘못 알고 있는 것일 수도 있지만...

각설하고... 슬프지만... Programming skill에 대한 가장 현실적인 정의는 "적은 비용으로 소프트웨어를 만들어 내는 기술"이라고 할 수 밖에....


주저리 주저리... 문맥도 안맞고... 내용도 정리가 안되어 있고.. 쩝..

Android version ICS.

PagerAdapter에서 중간 item하나를 없애기 위해서... item data를 update하고 notifyDataSetChanged를 하면,

(편의를 위해서 'N'개의 item을 가진 PagerAdapter를 가정.... )

ViewPager는 그냥 마지막 'N-1' 번째 position의 item을 destory해 버린다. => destoryItem이 불린다. 헐...

그러니... 현재 ViewPager의 구조에서 PagerAdapter로부터 중간에 있는 item을 하나 없애는건 너무너무 곤란한 상황을 만든다...

따라서 중간에 있는 item을 하나 없애고, 차라리 그냥... 'ViewPager'를 새로 만드는게 더 편한 방법이다!!

(ex. activity전체를 새로 시작한다던가....)


+ Recent posts