Let's assume that a engineer is working at same domain for a long time.

Then, based on my experience, relation among those three is something like this.


<Performance/Productivity> = <Talent> * <Time> ^ <Attitude>


What this means?


At early stage, Talent dominates Performance.

But, after all, Attitude dominates Performance.


Then, what is attitude means for engineer?
In my opinion, good attitude of engineer, is
    - studying continuously.
    - trying to establish strong knowledge base for domain.
    - trying to understand as many / deep as possible regarding his/her task and domain.
    .. .and so on.


Let's see below graph.



Y-axis : Performance / Productivity
X-axis : Time

Red line : Talent value = 1, Attitude value = 4
Green line : Talent value = 10, Attitude value = 2



At this graph, there is special period (0 < Time < 1).

At this period, Attitude hinders Performance.

My interpretation is, engineer who has great attitude, always tries to understand nature and fundamental knowledge of tasks.

So, during this period, in terms of so-called output, he/she may be worse than other engineers.

But, based on this strong knowledge and understanding, he/she accelerates productivity (Time > 1)

And finally, his/her performance overcome talent.


This is my opinion based one only my experience.

Reader may or may not agree with it.
But, I strongly believe this. :)




* 가장 중요한 것은, 내가 맡은 업무가 무슨 업무인지 이해하는 것이다. 따라서 업무를 할당할때, 그리고 할당해 줄때 모두 내가/타인이 주어진 Project와 그에 따른 할일을 제대로 이해하고 있는지 확인하는게 중요하다.


* 그렇다면 위의 내용을 확인하는 가장 좋은 방법은 무엇인가?

Project에 대한 내용을 말로 설명하고 말로 확인하는거? 당연히 그렇게 해야 한다. 그렇지만 단순히 여기에 그칠 경우 한계가 명확하고, mis-communication도 종종 생긴다.

따라서 좀더 확실한 방법은, "난/넌 어떻게 내/니 일이 완료되었다는 것을 확인할래?"라는 질문을 던지는 것이다.

여기서 '어떻게'를 좀더 기술적인 용어로 변환해 보면, 'Test/Verification'이 될 수 있다.

즉, '어떤 Test/Verification을 통과하면, 이 일을 완료되었다고 확신할 수 있나?'가 된다.
(Project Management 쪽에서는 이것을 'Definition of done' 이라고 표현하고 있다.)


* 위의 과정의 핵심은 '어떤 Test/Verification'이다. 즉 Test design이다.


정리하면, 해당 업무를 '완료'했다고 말할 수 있는 'Test'를 design할 수 있다면, 그 업무를 완전히 이해했다고 볼 수 있다.


따라서, 업무를 받을때/할때 모두, 다음과 같은 질문/확인 을 통해서 업무에 대한 이해도를 확인할 필요가 있다.

업무를 할당 받았을때: "제가 <이런이런>시험을 했을때 문제가 없으면 되는거죠?"

업무를 할당할때:

(저급)1. <이런이런>시험을 했을때 문제가 없으면 됩니다.

- 문제의 본질을 이해하지 못하고, 단순히 시험에 통과하기위한 업무만을 진행할 가능성이 있음.

(고급)2. 업무는 <이러이러>합니다. 자, 당신은 어떻게 이 업무가 완료되었다는 것을 확인할 예정입니까?

- 제대로 된 답을 한다면, 이 사람은 업무를 완전히 이해하고 있다고 볼 수 있다.


#include <stdio.h>


template<int N> class A {

public:

        A() { a = N;}

        int a;

};


int

main() {

        A<10> *a = new A<10>();

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

        return 0;

}


type이 아니라, 변수 값을 전달할 수도 있다. 이 경우 장점은?

compile time에 값이 정해지므로 compiler가 code를 optimize할 수 있다.



예.


[ a. cpp ]

#include <stdio.h>


template<int N> class A {

public:

        A(int n) { mN = n; }

        void speak() {

                if (N > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

        }

        int mN;

};


int

main() {

        A<10> *a = new A<10>(10);

        A<2> *b = new A<2>(2);

        a->speak();

        b->speak();

        return 0;

}


<< objdump >>

...

        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (N > 5)

                        printf("I'm BIG\n");

   c: bf 00 00 00 00       mov    $0x0,%edi

  11: e8 00 00 00 00       callq  16 <_ZN1AILi10EE5speakEv+0x16>

                else

                        printf("I'm SMALL\n");

        }

  16: c9                   leaveq 

  17: c3                   retq   


...


        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (N > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

   c: bf 00 00 00 00       mov    $0x0,%edi

  11: e8 00 00 00 00       callq  16 <_ZN1AILi2EE5speakEv+0x16>

        }

  16: c9                   leaveq 

  17: c3                   retq   


...






[ b. cpp ]

#include <stdio.h>


template<int N> class A {

public:

        A(int n) { mN = n; }

        void speak() {

                if (mN > 5)

                        printf("I'm BIG\n");

                else

                        printf("I'm SMALL\n");

        }

        int mN;

};


int

main() {

        A<10> *a = new A<10>(10);

        A<2> *b = new A<2>(2);

        a->speak();

        b->speak();

        return 0;

}



<< objdump >>

...

        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (mN > 5)

   c: 48 8b 45 f8           mov    -0x8(%rbp),%rax

  10: 8b 00                 mov    (%rax),%eax

  12: 83 f8 05             cmp    $0x5,%eax

  15: 7e 0c                 jle    23 <_ZN1AILi10EE5speakEv+0x23>

                        printf("I'm BIG\n");

  17: bf 00 00 00 00       mov    $0x0,%edi

  1c: e8 00 00 00 00       callq  21 <_ZN1AILi10EE5speakEv+0x21>

  21: eb 0a                 jmp    2d <_ZN1AILi10EE5speakEv+0x2d>

                else

                        printf("I'm SMALL\n");

  23: bf 00 00 00 00       mov    $0x0,%edi

  28: e8 00 00 00 00       callq  2d <_ZN1AILi10EE5speakEv+0x2d>

        }

  2d: c9                   leaveq 

  2e: c3                   retq   


...


        void speak() {

   0: 55                   push   %rbp

   1: 48 89 e5             mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 48 89 7d f8           mov    %rdi,-0x8(%rbp)

                if (mN > 5)

   c: 48 8b 45 f8           mov    -0x8(%rbp),%rax

  10: 8b 00                 mov    (%rax),%eax

  12: 83 f8 05             cmp    $0x5,%eax

  15: 7e 0c                 jle    23 <_ZN1AILi2EE5speakEv+0x23>

                        printf("I'm BIG\n");

  17: bf 00 00 00 00       mov    $0x0,%edi

  1c: e8 00 00 00 00       callq  21 <_ZN1AILi2EE5speakEv+0x21>

  21: eb 0a                 jmp    2d <_ZN1AILi2EE5speakEv+0x2d>

                else

                        printf("I'm SMALL\n");

  23: bf 00 00 00 00       mov    $0x0,%edi

  28: e8 00 00 00 00       callq  2d <_ZN1AILi2EE5speakEv+0x2d>

        }

  2d: c9                   leaveq 

  2e: c3                   retq   

...




===============================================


위의 코드를 보면, template 의 value를 사용할 경우, 'if'문에서, 사용되지 않는 코드가 compile시 object에 반영되지 않고 삭제되어 버리는 것을 알 수 있다.

즉 compile time에 충분히 optimize되어 버린다. (if 문의 condition이 compile time에 결정될 수 있으므로...)

직장생활을 하다보면, 제목과 같은 현상을 경험할 수 있다.

이게 '일반적'이라고 말하기에는 근거가 부족하지만, 개인적으로는 위와 같은 경험을 많이 해 본거 같다.


왜 그럴까?

가장 쉽게 생각할 수 있는 것은, 회사의 고정비를 줄이기 위해서 인력을 감축시키기 때문일 것이다.

업무량은 변하지 않았는데, 일하는 사람이 줄어드니, 남은 직원들은 더 바빠진다.


그런데, 개인적인 의견으로, 이것보다 더 중요한 요인은 "살아남기 위해서 일을 벌리기 때문" 이라고 생각한다.

회사의 고위층은, 실적이 나빠지면 살아남기 위해서 실적을 만들어야 한다.

그리고, 보통 '실적'은 '기존에 해 오던 일을 잘 하는 것' 보다는 '새로운 일을 해 내는 것'이 더 높은 평가를 받기 마련이다.

그렇다보니, 소위 '윗선'에서 자꾸 새로운 일을 만들어 내게 된다.


생각하기에 따라서는, 긍정적인 시각으로 이런 현상을 바라볼 수 있겠지만, 난, 부정적인 측면이 더 많다고 생각한다.

회사의 실적이 안 좋아지고, 고정비를 줄여야 하는 상황이라면, 일을 만들어 내는 것 보다는, 기존 일들에 대해서 우선순위를 정하고 "선택과 집중"을 통해서 어디에 회사의 역량을 집중할 것인가를 판단하는게 더 중요한 일이 아니겠는가?


물론, '기존에 해 오던 일을 잘 하는 것' 보다는 '새로운 일을 해 내는 것'에 대해서 압도적으로 높은 평가를 하는 것이 이런 현상의 근본 원인이긴 하겠지만, 그것과 별개로, 위기상황이라면, 좀더 합리적인 선택 - 선택과 집중 - 을 해야하지 않을까?

그렇지 못할 경우, 아마도 회사는 더 빠른 속도로 몰락의 길을 걷게 될 것이라고 본다.



[Makefile]

general_var:= my-value

target: dep1 dep2

    <do something> $(general_var)

위의 makefile이 단독으로 사용되는 경우는, 전혀 문제가 되지 않는다. 그렇지만, makefile이 서로 서로 include되는 등의 복잡한 system에서는

경우에 따라, 위의 makefile이 parsing된 이후, general_var 의 값이 다른 값으로 바뀌게 될 수도 있다.(include된 다른 make file에서...)

이럴 경우, 'target'의 command section이 수행될 때는 'general_var'가 이미 다른 값으로 치환된 이후이기 때문에, 기대와 다른 동작을 하게 될 수 있다.

('blog의 variable expansion at command section on GNUMake 참조)

이것을 막기 위해서는 아래와 같은 형태로 make file을 구성해야 한다. 


[Makefile]

general_var:= my-value

target: private_var := $(general_var)

target: dep1 dep2

    <do something> $(private_var)

위의 makefile은, makefile이 parsing되고 target을 만들기 위한 rule이 수행되기 전에 아래와 같은 상태가 된다.


[Makefile]

general_var:= my-value

target: private_var := my-value

target: dep1 dep2

    <do something> $(private_var)


따라서, target 의 command section이 수행되기 전에, dependency section이 수행되면서 private_var값이 my-value로 set 되고

target 의 command section은 기대했던 'my-value' 값을 가지고 수행된다.

단, 아래와 같은 syntax는 "*** commands commence before first target.  Stop." error를 발생시키니 주의해야 한다.

(즉 dependency section에서 variable assignment를 수행할 경우, command section과 함께 있으면 안된다.) 


general_var:= my-value

target: private_var := my-value

    <do something> $(private_var)




[Makefile]

$(shell echo hello) # <--- (*A)

all:

    echo all

above make file gives following results.

     

Makefile:1: *** missing separator.  Stop.

error is issued.

But, we can resolve this error by changing line (*A) into below line.

dummy := $(shell echo hello)

or

$(shell echo)


What is root cause of this?

Let's see source code of 'func_shell' function in GNUMake-v4.0.


func_shell_base(...)

{

    ...

      child_execute_job (FD_STDIN, pipedes[1], errfd, command_argv, envp); <= stdout을 pipe로 redirecdtion

    ...

          EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i)); <= pipe를 읽음

    ...

          /* The child finished normally.  Replace all newlines in its output

             with spaces, and put that in the variable output buffer.  */

          fold_newlines (buffer, &i, trim_newlines);

          o = variable_buffer_output (o, buffer, i);    <= buffer값(pipe값 -> child의 stdout값)을 variable_buffer에 저장. 

    ...

}


That is, 'shell' function stores it's result(stdout) into variable_buffer.

variable_buffer is buffer that is used to store result of variable-expandsion.

What does this mean?

See below samples.


[Makefile]

$(shell echo hello) <= (*1)


[Makefile]

temp:=hello

$(temp) <= (*2)


Above analysis means, (*1) and (*2) is same expression in gnumake's point of view.


Actually, above rules are same on all other functions in gnumake.

But, most function gives empty string - that is, it doesn't write string value to stdout.

So, it doesn't write anything into variable_buffer. That's reason why above issues are not shown at other functions.

And, in gnumake's point of view, 'function' and 'variable' is same in concept.

So, we can say that 'function' is just special variable that can get arguments - a kind of 'predefined variable'.

Therefore, we should use same concept for 'variable' and 'function' to understand gnumake.


[[ Tested on GCC 4.x ]]


static void

test01_00(char buf[TEST01_SZ]) {

        printf("In func: %lu\n", sizeof(buf));

}


static void

test01(void) {

        char buf[TEST01_SZ];

        printf("In local: %lu\n", sizeof(buf));

        test01_00(buf);

}


int

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

        test01();

        return 0;

}


<< Run on 64bit linux >>

$ ./a.out

In local: 16   <== array size

In func: 8     <== pointer size


'sizeof(<array>)' works as expected in the scope where it is declared. But, array that is declared as parameter, is regarded same as 'pointer' (as you can see above).

I don't think this is defined in C specification.

So, I think it is just one of special characteristics of GCC.

Is it interesting,isn't it?


즉 array를 선언한 곳에서 sizeof(<array>)는 기대한 대로 동작하는데, argument로 넘긴 array의 경우, pointer와 동일하게 취급하는 것을 알 수 있다.

개인적인 생각으로, 이것이, C spec 사항일 것 같지는 않다.

그러니, 그냥 GCC 특성 중 하나라고 생각한다....

꽤나 재미있는 내용이라, 일단 적어 둔다. ^_^

'Language > C&C++' 카테고리의 다른 글

[C/C++] Small tip - Argument passed to main() function.  (0) 2015.10.19
[C/C++] Template with value  (0) 2015.04.28
[Macro] '##' macro operator  (0) 2015.02.11
'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25

ext4에서 'readdir' system call을 이용해서 directory를 읽으면, directory가 가지는 file list를 읽을 수 있다.

이때, 읽히는 file의 순서는 어떻게 정해지는가?

경험적으로 대부분의 개발자라면, 이 순서가 일정하지 않다는 것을 알고 있을 것이다.

이 부분에 대해서 좀더 깊게 살펴보기도 하자.


linux kernel에서 ext4 관련 부분의 코드를 분석해 보면 아래의 사실들을 알아 낼 수 있다.

- ext4는 directory내에서 file search등을 빠르게 하기 위해서 htree(hash tree)를 이용한다.

- 이때 사용되는 hash값의 order가 곧 readdir 로 읽어들이는 file의 order이다.

- hash 값은 'HashFunc(<hash algorithm>, <hash seed>, <file name>)"을 통해서 구한다.

- hash algorithm과 hash seed의 경우, super block에 적힌 값을 사용하면, 없는 경우 default 값을 사용한다.

- directory내의 각 file의 hash값은 directory file의 'f_pos'값 (file position값)으로 사용된다.


즉, super block에 적힌 hash algorithm과 hash seed에 따라서, directory가 같은 이름들의 file을 가지고 있다고 하더라도, readdir이 읽어 오는 file의 순서가 달라진다는 말이다.


실제로 이를 확인하기 위해서는 'dumpe2fs' 와 'debugfs' tool을 사용할 수 있다.


Assumption : '/' 가 '/dev/sda1' 에 mount 되어 있음.


>$ sudo dumpe2fs -h /dev/sda1
...
Default directory hash:   half_md4     <== hash algorithm
Directory Hash Seed:      5841608b-14fe-405e-8d28-76236cc8c496  <== seed (UUID format)
...


이후 아래와 같은 방법으로 각 file name에 해당하는 hash 값을 알 수 있다.

>$ sudo debugfs /dev/sda1
dx_hash -h half_md4 -s c773a461-6150-4fe8-abe8-96acc6086d7e vmlinuz
Hash of /vmlinuz is 0xf097024e (minor 0x0)
추가적으로 'ls -f' 를 사용하면, sorting되지 않은 순서로 file list를 읽을 수 있는데, 이 순서가 바로, hash 값에 따른 순서 즉 readdir로 읽어 들이는 순서이다.
이를 확인하기 위해서는
>$ rm dxhashs; for e in $(ls -f /); do echo "dx_hash -h half_md4 -s 5841608b-14fe-405e-8d28-76236cc8c496 $e" >> dxhashs; done; sudo debugfs -f dxhashs /dev/sda1 | grep "Hash of" | cut -d' ' -f5

와 같이 하면, 정확하게 ascending order로 출력되는 hash 값을 볼 수 있다.
즉 'ls -f' 를 통해서 보여지는 file name의 순서가 hash값이 ascending order라는 뜻이고, hash 값이 ascending order에 따라 읽어 들이는 file name의 순서가 'readdir'로 읽어 들이는 순서와 일치한다.

Hash Seed를 바꾸는 방법은 아래와 같다.(간단한데, 나중에 분명히 잊어 먹을테니...)
/dev/sda2 가 /opt에 mount되어 있고, ext4 file system인 경우.

># unmount /opt
># debugfs -w /dev/sda2
>debugfs: ssv hash_seed ceeba115-4091-4b79-aac9-36ccee4a5349  <= UUID of hash seed to change


/libcore/luni/src/main/java/java/util/jar/JarVerifier.java

여기 source를 확인해 보면,

+ '.RSA', '.DSA', '.EC' 로 끝나는 file을 찾고, 같은 이름의 '.SF' 파일을 찾는다.(Android의 경우는, 'CERT.RSA' 와 'CERT.SF' 파일)

    : '.RSA', '.DSA', '.EC' : Certification File.

    : '.SF' : Signature File

    : Signature file을 certification file을 통해서 먼저 verification한다.

+ .SF 파일의 각 file entry에 대해서 '-Digest' 값을 읽어 들인다.


<< PackageParser >>

'Signing Verifier'에서 읽어들인 "file entry:<HASH>" 값 mapping table을 이용해서, zip file안에 있는 file들에 대해 hash를 검사한다.

    : System APK에 대해서는 Manifest의 hash만 검사 (trusted app.) 그렇지 않은 경우 모든 file - 'META-INF/' directory제외 - 에 대해서 검사.

    



일반 적으로...

exit code = 128 + (signal number)


ex.

exit code가 139(128  + 11)라면, 11번 signal이 뭔지 알아야 하고 "kill -l"로 확인할 수 있다.

보통의 경우 11번은 SIGSEGV  이다. 즉, segmentation fault로 죽었다는 말...



+ Recent posts