"ARM9는 하버드버스 구조를 사용하는 반면 ARM7은 폰노이만 버스구조를 사용한다. 이런 버스구조의 차이에 따라 ARM7의 경우에는 데이터를 읽거나 쓸때 명령을 패치할 수 없는 이유로 데이터 전송 명령의 경우 실제 메모리를 액세스하는 MEMORY단계가 기본 파이프라인과 별도로 추가되어 사용되었다.
하지만 ARM9는 데이트를 읽거나 쓸때도 명령을 패치할 수 있기 때문에 MEMORY단계가 기본 파이프라인에 추가된다.
따라서 ARM7에서는 불가능 했던 데이터 전송명령이 사용된다 하더라도 파이프라인에 추가되어 매 사이클마다 하나의 명령이 처리되는 장점을 가진다."

(ARM으로 배우는 임베디드시스템 p190)

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,
kldp에 올라온 질문인데.. 흥미로운게 있어서 제가 
몇가지 실험후에 답글을 달았습니다.

#include <stdio.h>

int arr_data[1000] = { 0, };
int arr_bss[1000];

int main()
{
int arr_local1[1000];
int arr_local2[1000] = { 0, };
printf("Hello World!\n");

return 0;
}

위의 소스의 변수들을 각각 추가하면서 size로 실행파일의 각 segment의
크기를 확인해보았더니,
data segment의 크기는 4*1000 + 16만큼 늘어나구요.
bss segment의 크기는 4*1000 + 8만큼 늘어나구요.
text segment의 크기는 초기화안한 경우에는 변화가 없고,
변수를 초기화하면서 선언하면, 8만큼 늘어납니다.

여기서, 변수크기외에 늘어난 크기(16, 8, 8)는
어떤 정보가 추가되기 때문에 생기는 건가요??

-----------------------------------------------------------------

디어셈블 하는 것은 gdb를 사용해도 되고
objdump란 유틸을 사용해도 됩니다.

그럼 일딴 각각의 상태를 정리를 해보지요..

gcc elf.c -s 하면 어셈블리 코드로 생성 해주기는 하는데
실제적으로 어셈블러가 바이너리 정렬같은 것을 하니까..
그냥 바이너리 상태를 덤프해보는게 정확합니다.

-----------------------------------------------------------------
//#include <stdio.h>

int arr_data[1000] ;
int arr_bss[1000];

int main()
{
int arr_local1[1000];
int arr_local2[1000] ;
// printf("Hello World!\n");

return 0;
}

gcc elf.c -o elf

objdump -h elf

Sections: elf init 없음
Idx Name Size VMA LMA File off Algn
11 .text 000000fc 080482f0 080482f0 000002f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .data 0000000c 08049410 08049410 00000410 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .bss 00001f60 08049500 08049500 00000500 2**5
ALLOC

-----------------------------------------------------------------
//#include <stdio.h>

int arr_data[1000] = {0,};
int arr_bss[1000];

int main()
{
int arr_local1[1000];
int arr_local2[1000] ={0,} ;
// printf("Hello World!\n");

return 0;
}

gcc elf2.c -o elf2

objdump -h elf2

Sections: elf2 init 있음
Idx Name Size VMA LMA File off Algn
11 .text 0000010c 080482f0 080482f0 000002f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .data 00000fc0 08049420 08049420 00000420 2**5
CONTENTS, ALLOC, LOAD, DATA
20 .bss 00000fc0 0804a4c0 0804a4c0 000014c0 2**5
ALLOC

-----------------------------------------------------------------
elf, elf2의 사이즈 비교
elf elf2
Idx Name Size Algn Size Algn
11 .text 000000fc(252) 2**4 0000010c(268) 2**4
14 .data 0000000c(12) 2**2 00000fc0(4032) 2**5
20 .bss 00001f60(8032) 2**5 00000fc0(4032) 2**5

일딴 main에서 약간의 코드 차이가 나는데
바이너리를 덤프해봤습니다. objdump -s elf
이런식으로 하면되는데...

아래를 보시면 알겠지만,
초기화 루틴 때문에 조금 차이가 있습니다.
즉 elf.c같은 경우엔 스택에 공간을 잡아두고
(80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp)
바로 사용을 하지만

elf2.c의 경우엔 elf.c와 같이 스택에 자리를 잡아주고
아래와 같이 클리어해주는 작업을 합니다.

80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)

레지스터 eax를 0으로 만든다음
플래그를 리셋하고
ecx에 반복할 횟수 (4000)를 지정하고
eax의 값을 %es:(%edi)에 전송하는 거죠..
repz명령어에 의해서 ecx는 하나씩 줄고,, edi는 하나씩 증가하니까
결과적으로는 스택에 자리잡은 arr_local2가 0으로 클리어 되겠지요..

여기서 한가지 집고 넘어갈 사항이 있는데,
만약에 arr_data를 0이 아닌 특정 다른 데이터로 초기화 한다면
arr_local2[1024] = {1,2,3,4,5,6};
이 내용으 rdata 세그먼트에 저장이 되고
지금처럼 스트링일 0 밀어버리는게 아니라...
메모리 카피하는 어셈블리가 추가됩니다.

그럼 글로벌에 있는 arr_data는 어떨까요?
이것도 초기화 하는 루틴이 있을까요?
요것은 잘 생각해보면 그럴필요가 없습니다.
arr_data는 글로벌 데이터로., 컴파일 타임때 미리 그 값이 정해져서
바이너리에 있는 내용이 그대로 메모리로 올라가니까
초기화 루틴이 필요 없는것이죠..

그럼 결론적으로 사이즈 차이는 왜 생겼을까요?
바로... 이 초기화 하는 루틴때문에 코드가 더 추가 되었기 때문이죠..
그런데 보기좋게 16이란 값으로 차이가 나는걸까요?
그것은... 세그먼트가 효율적인 접근을 위해서 특정 바이트로 정렬이
되기 때문입니다.
즉 elf2는 000000fc 코드사이즈의 임계를 넘어버려서
다음 임계점인 0000010c까지 사이즈가 증가해 버린것이지요..

이제 그럼 아래표에서 왜 Algn값을 찍었는지 알겠지요?
Idx Name Size Algn Size Algn
11 .text 000000fc(252) 2**4 0000010c(268) 2**4
14 .data 0000000c(12) 2**2 00000fc0(4032) 2**5
20 .bss 00001f60(8032) 2**5 00000fc0(4032) 2**5

자 아래는 main function의 어셈블리 자료입니다.

080483a0 <main>: elf
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 31 c0 xor %eax,%eax
80483ab: eb 03 jmp 80483b0 <main+0x10>
80483ad: 8d 76 00 lea 0x0(%esi),%esi
80483b0: c9 leave
80483b1: c3 ret
80483b2: 90 nop
80483b3: 90 nop
80483b4: 90 nop
80483b5: 90 nop
80483b6: 90 nop
80483b7: 90 nop
80483b8: 90 nop
80483b9: 90 nop
80483ba: 90 nop
80483bb: 90 nop
80483bc: 90 nop
80483bd: 90 nop
80483be: 90 nop
80483bf: 90 nop

080483a0 <main>: elf2
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 57 push %edi
80483aa: 8d bd c0 e0 ff ff lea 0xffffe0c0(%ebp),%edi
80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)
80483ba: 31 c0 xor %eax,%eax
80483bc: eb 02 jmp 80483c0 <main+0x20>
80483be: 89 f6 mov %esi,%esi
80483c0: 8b bd bc e0 ff ff mov 0xffffe0bc(%ebp),%edi
80483c6: c9 leave
80483c7: c3 ret
80483c8: 90 nop
80483c9: 90 nop
80483ca: 90 nop
80483cb: 90 nop
80483cc: 90 nop
80483cd: 90 nop
80483ce: 90 nop
80483cf: 90 nop


자 그럼 data는 8이 차이가 난다고 했는데
제가 실험을 해보니까. 저는 20이 차이가 나네요..
음.. 이것 역시 정렬때문입니다.

아래의 덤프내역을 보면, elf는 약간의 데이터 영역이
4바이트 단위로 정렬이 되어서... 12바이트로 존재를 합니다.
하지만,,

elf2의 경우엔 12 + 4000 인데. 이것이 4000을 넘어버러서
다음 임계점은 4032까지 커져버린거죠..(elf2의 데이터 정렬은 2^5)

Contents of section .data: elf
8049410 00000000 2c940408 00000000 ....,.......


Contents of section .data: elf2
8049420 00000000 f0a30408 00000000 00000000 ................
8049430 00000000 00000000 00000000 00000000 ................
8049440 00000000 00000000 00000000 00000000 ................
8049450 00000000 00000000 00000000 00000000 ................
8049460 00000000 00000000 00000000 00000000 ................
8049470 00000000 00000000 00000000 00000000 ................
8049480 00000000 00000000 00000000 00000000 ................
8049490 00000000 00000000 00000000 00000000 ................

....


804a380 00000000 00000000 00000000 00000000 ................
804a390 00000000 00000000 00000000 00000000 ................
804a3a0 00000000 00000000 00000000 00000000 ................
804a3b0 00000000 00000000 00000000 00000000 ................
804a3c0 00000000 00000000 00000000 00000000 ................
804a3d0 00000000 00000000 00000000 00000000 ................

elf2에서 일종의 filler가 어디에 들어갔을까요?
그것을 알아보기 위해서 main에 코드를 약간 추가했습니다,
단 여기서 주의할 사항은 코드가 정렬을 위해서 남겨둔
여분보다 커져버리면 전체 코드가 증가해 버리니
절대값 비교가 안됩니다...

//#include <stdio.h>

int arr_data[1000] = {0,};
int arr_bss[1000];

int main()
{
int arr_local1[1000];
int arr_local2[1000] ={0,} ;
arr_data[0] = 1;
// printf("Hello World!\n");

return 0;
}

080483a0 <main>:
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 57 push %edi
80483aa: 8d bd c0 e0 ff ff lea 0xffffe0c0(%ebp),%edi
80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)
80483ba: c7 05 40 94 04 08 01 movl $0x1,0x8049440
80483c1: 00 00 00
80483c4: 31 c0 xor %eax,%eax
80483c6: eb 00 jmp 80483c8 <main+0x28>
80483c8: 8b bd bc e0 ff ff mov 0xffffe0bc(%ebp),%edi
80483ce: c9 leave
80483cf: c3 ret

자.. 답이 나왔네요.

80483ba: c7 05 40 94 04 08 01 movl $0x1,0x8049440

보시면 알겠지만. elf때 있던 12바이트 뒤를 20바이트 0으로 깔고
그다음 부터 0x8049440주욱 연속된 공간을 할당하게 됩니다.


제 실험에서는 bss는 제가 원하는 크기 만큼 동일한 사이즈로 줄었네요..

음... 보시고 다시 잘 이해가 안되면 질문해주세요..
덕분에... 저도 옛날에 배운거 복습도 하고 정리도 하고 좋네요. ^^

ps.
아 한가지 추가.. 자료가 커졌을때 32로 정렬하는것은 무엇 때문일까요?
ㅋㅋ

그건 아마도 캐쉬랑 연관이 클껍니다.
제가 gcc소스를 분석해 보지는 않았지만..
제가 컴파일한 환경의 cpu는 다음과 같은 캐쉬라인을 가지고 있거든요

TLB and cache info:
01: Instruction TLB: 4KB pages, 4-way set assoc, 32 entries
02: Instruction TLB: 4MB pages, 4-way set assoc, 2 entries
03: Data TLB: 4KB pages, 4-way set assoc, 64 entries
43: 2nd-level cache: 512KB, 4-way set assoc, 32 byte line size
08: 1st-level instruction cache: 16KB, 4-way set assoc, 32 byte line size
04: Data TLB: 4MB pages, 4-way set assoc, 8 entries
0c: 1st-level data cache: 16KB, 4-way set assoc, 32 byte line size

그래서 캐쉬라인에 걸치지 말라고 저렇게 해주는거 같네요..

[출처] : http://www.ezdoum.com/stories.php?story=02/08/24/6098584

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,
뭐지?

 
((struct s_a *) p_void)          이것은 캐스팅

((struct s_a *) p_void)->c 이것은 p_void 주소부터 c 가 객체에서
차지하는 바이트만큼 떨어져(?) 있는 곳의
있는 자료

&(((struct s_a *) p_void)->c) 이것은 p_void 주소부터 c 가 객체에서
차지하는 바이트만큼 떨어진 곳의 자료의
주소

가 됩니다. 따라서
&(((struct s_a *) 0)->c) 의 값이 4 였다면
&(((struct s_a *) 1)->c) 은 5(4+1) 가 출력되고
&(((struct s_a *) 2)->c) 은 6(4+2) 가 출력됩니다.
단 (((struct s_a *)0)->c) 이렇게 c 멤버값을 알아낼려고 하면 안되겠죠



WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,
C++ 약간 알고, 주로 자바 프로그래밍을 하는 사람입니다. 요새 C로 뭘 짤일이 생겼는데, 정렬 알고리즘이 필요해서 하나 짰습니다. 그런데 문제는...이거 타입이 바뀌면 일일이 다시 짜야하는건가요?
C++ 이라면 템플릿을 사용하면 되고, 자바라면 5.0 에 추가된 제너릭을 사용하거나 아님 캐스팅으로 해결할 수 있는데, C에서는 어떻게 할지 모르겠습니다.
일단 int 배열을 정렬하도록 짰는데, double 을 정렬하려니 이거 다시 짜는 것 밖에는 방법이 없어보이네요. void 포인터를 사용하는 방법도 생각해봤지만 매번 포인터를 사용해 간접적으로 접근하는 것은 효율에 문제가 있을 것 같아서요. 제법 큰 배열을 자주 정렬해야하기 때문에 효율 문제가 중요합니다. 정렬 말고도 C++ 벡터, 자바의 ArrayList 같은 녀석도 있으면 좋겠어서 int 에 대해 하나 짰는데 이것도 도무지 다른 타입으로 확장할 방법이 안보이는군요...
C를 주로 사용하시는 분들은 어떻게 처리하는지 궁금합니다.


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

매크로를 쓰시면 됩니다.

다음은 wikipedia에 있는 버블소트 pseudocode입니다.

procedure bubbleSort( A : list of sortable items ) defined as:
do
swapped := false
for each i in 0 to length( A ) - 2 do:
if A[ i ] > A[ i + 1 ] then
swap( A[ i ], A[ i + 1 ] )
swapped := true
end if
end for
while swapped
end procedure

C로 하면
void bsort(int* A, size_t sz)
{
int swapped;
int temp;
size_t i;
do {
swapped = 0;
for (i=0; i < sz-1; ++i) {
if (A[i] > A[i+1]) {
temp = A[i];
A[i] = A[i+1];
A[i+1] = temp;
swapped = 1;
}
}
} while (swapped);
}

얘를 다시 매크로로 하면
#define BSORT(type)\
void bsort_##type(type* A, size_t sz)\
{\
int swapped;\
type temp;\
size_t i;\
do {\
swapped = 0;\
for (i=0; i < sz-1; ++i) {\
if (A[i] > A[i+1]) {\
temp = A[i];\
A[i] = A[i+1];\
A[i+1] = temp;\
swapped = 1;\
}\
}\
} while (swapped);\
}
 
BSORT(int)
BSORT(double)
...
 
int main()
{
bsort_int(xx, sizeof(xx)/sizeof(xx[0]));
bsort_double(xxx, sizeof(xxx)/sizeof(xxx[0]));
return 0;
}


WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,

[Q/A] container_of() 매크로

QnA 2010. 2. 27. 05:47

커널 소스를 보다가 list_entry() 매크로를 자세히 보게되었다.

#define list_entry(ptr,type,member) container_of(ptr,type,member)

이하. containger_of() 매크로를 보던중, 관련 스레드이다.


 

embedding list를 구현할때 사용하는 기법(?)입니다.(BSD의 sys/queue.h에서 처음 등장했고요, Linux는 몇가지 다른 버전이 있더군요)

흔히 보통의 자료구조 교재에서 접하는 linked list등의 구현 방법은, 구조체에 다음 노드의 "시작"점을 포인팅하게 구현하죠.
이 방법은 직관적이고 이해하기 쉽지만, 일반 타입에 대해서 만들려면 노가다가 필요합니다.(c++ 등은 템플릿이 있어서 상관없고요)

그래서 나온 또 다른 방법이, link 정보를 구조체 안에 있는 변수에 넣어두고(위에 예제에선 member), 이 변수들이 다른 구조체에 접근할 수 있도록 하는 것입니다. 이렇게 하면.. 실제로 리스트에서는 다음 노드의 링크 변수에만 접근할 수 있습니다.(member)

그러나 우리가 원하는 것은 링크 변수가 아닌, 링크 변수를 가진 구조체의 시작점이죠. 이 구조체의 시작점을 계산하는 매크로가 list_entry입니다. gcc extension에서는 offsetof라는 연산자를 지원하기도 하는데, 그렇지 않을 경우에는 가상의 구조체 만든 후(0을 구조체 타입으로 캐스팅했죠) 링크 변수의 주소에서 링크 변수의 구조체 시작점에서의 오프셋을 뺍니다. 이렇게 계산하면 실제 구조체의 시작점 주소가 반환되겠죠.




[Q]

보통
#define MAX(a,b) ( (a>b) ? (a) : (b) )

이런식으로 매크로 함수를 정의합니다.
하지만 약간 복잡한 형태의 함수의 return값을 명시적으로 주고자 했을때 어떻게 해야합니까?

#define func(a) {\

//bla~ bla~ bla; \

/*return*/ result;
}

이렇게 하게되면 {}로 둘러쌓인 부분이 result로 평가 받게 되나요?

리눅스 커널에 있는 container_of 라는 매크로를 풀어 보니 대략 이런 류의 코드가 되네요.

대략 어떻게 되는거다 라는건 알겠는데..

이거 원래 C 문법에 맞는 코드 인가요?

어떤 식의 처리가 되는거죠?



[A]

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

#define func(a) ({\ 

//bla~ bla~ bla; \

/*return*/ result; \
})

요런식으로 하면 result가 ({ blahblah...; result; }) 표현의 평가값이 되니까

ret = func(something);

식으로 사용할 수는 있을 것 같습니다만, 아무래도 이건 좀...-.-a

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

http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

GCC extension이라는 걸로 봐서 표준은 아닌 듯 합니다. -pedantic 옵션으로 컴파일 하니까 뭐라뭐라 그러네요. 하지만 다음과 같이 정의하는 건 표준 C 문법에 부합합니다.

#define func(a) ( \
tmp1 = another_func1(a), \
tmp2 = another_func2(a), \
result = tmp1 + tmp2, \
/* return */ result )

...
int tmp1, tmp2, result; // for use by macro func()
...
ret = func(a);

즉, 선언'문' 없이, '식'으로만 이뤄져 있으면 GCC뿐 아니라 모든 ANSI C 컴파일러에서 문제가 없게 됩니다... 만, 원하시는 것에는 이프로 부족하지 않을까 싶네요-

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

매크로는 확장(expansion)될 뿐 그 이상도 이하도 아닙니다.
소스에 그대로 대치되는 것입니다.
실제로 전처리기가 매크로를 다 풀어헤친(?) 상태의 소스를 컴파일하게 됩니다.
매크로의 리턴 값이라고 하면 말이 앞뒤가 안맞는 느낌이 드네요.

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

Submitted by 익명 사용자 on 금, 2005/10/14 - 1:06am.

동의합니다. 8)

매크로에서의 리턴값이란거부터가 이미 말이 안되는거죠.
스스로 pre-processor입장이 되어서 매크로가 삽입되는 과정을
시뮬레이션 해보시면 잘 이해할 수 있을겁니다.

그럼 매크로에서 return 문을 쓰게 되었을 때 매크로가 값을
리턴하는게 아니라 매크로가 삽입된 함수가 값을 리턴하며
끝나게 된다
는것도 이해가 가실줄로 압니다.

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

몇가지 정리하면 다음과 같습니다.

첫째, 표준에 따라, statement를 expression이라고 할 수 없습니다. 물론 statement의 정의에 따라, expression-statement가 될 수 있기는 하지만, 모든 statement가 expression이라고 할 수 없습니다. 따라서

인용:

과연 { ~~~~~; ~~~~; result;}의 평가값이 result가 되는냐는 것이죠

는, 표준에 따라 잘못된 질문입니다.

둘째, 복잡한? 매크로를 만들고 싶다면 inline function을 쓰기 바랍니다. inline function을 쓰는 것이 여러모로 편리하고, 안전합니다. 다음과 같이 쓰면 좋습니다:

static inline void
foo(void)
{
...
}

세째, gcc C 언어 확장 기능으로, (즉, 표준이 아니며, 다른 컴파일러에서 지원하지 않는..) compound statement (중괄호로 둘러싼 여러 statement)를 expression으로 해석하는 기능이 있습니다. 예를 들면:

({ int y = foo(); int z;
if (y > 0) z = y;
else z = - y;
z; })

로 쓴 것처럼, foo() 리턴 값의 절대값을 얻는 코드를 위와 같이 작성할 수 있습니다. 이 것은 C99 표준에서 inline function이 나오기 전에 매우 유용하게 썼던 기능입니다. expression에서 switch, for 등의 statement를 쓸 수 있는 등의 장점을 얻을 수 있었습니다. 실제로 GNU C library에 포함되어 있는 obstack에 관계된 대부분의 함수가 이 compound-statement expression을 써서 macro로 구현되어 있습니다. 물론 맨 마지막 statement의 값이, 이 compound statement의 값이 됩니다.

그런데, 이 기능은 현재 C++에서 쓸 경우, 임시 변수의 destructor가 불리는 순서가 바뀌는 등의 단점이 있기 때문에, C++로 쓸 우려가 있는 코드에서는 이 compound statement expression을 쓰지 말라고 권하고 있습니다.

개인적으로 간단한 수식으로 나올 수 있는 것이 아니라면 do { ... } while (0)을 써서 macro로 만들고, 어떤 값을 리턴할 필요가 있는 macro라면 inline function을 쓰는 것이 좋다고 생각합니다.


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

매크로 함수 기능은 대단히 주의깊게 이용되어야만 합니다.

http://groups.google.co.kr/group/comp.os.linux/browse_frm/thread/9690464...

#define toupper(c) (_ctmp=c,islower(_ctmp)?_ctmp-('a'-'A'):_ctmp)

아무 문제 없을것 같은 코드지만

-------------------bug.c-------------------------
#include <linux/ctype.h>
main()
{
printf("%d\n", toupper('x') == toupper('y'));
printf("%d\n", toupper('x') == toupper('X'));
}
-------------------------------------------------

he program below prints:
1
1
but should print:
0
1

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

매크로와 함수는 각각 할 일이 있는 거라고 봅니다. 함수 호출의 오버헤드가 아주 중요한 요인으로 작용하는 경우가 아니라면, 함수를 쓰는 것이 맞다고 생각하구요. (그리고 그런 경우는 별로 없더군요..)

말씀하신 상황에서 굳이 매크로를 쓰고 싶으시다면 SOME_MACRO(input, output) 처럼 매크로를 정의해서 argument 에 바로 값을 넣도록 만드는 것이 옳은 방법이라고 생각합니다.




출처 : http://kldp.org/node/58409


WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,


#define을 사용한 매크로 함수에서 do{...}while(0) 문을 사용하는 경우를 볼 수 있다. 왜 굳이 의미없는 do{...}while(0)를 사용하는지 아래 예제를 보면서 살펴보자.

//do{...}while(0)문을 사용하지 않은 경우
#define Inc2Each(x,y) { x+=2;y+=2;}

//do{...}while(0)문을 사용한 경우
#define Inc2Each(x,y) do{ x+=2;y+=2}while(0)

do{...}while(0)을 사용하지 않은 첫번째 방식의 경우 어떤 문제가 사용하는지 살펴보자

if ( x > y )
 Inc2Each(x,y);
else
 x=y;

위 코드는 아래와 같이 확장될 것이다.

if ( x > y )
 { x+=2;y+=2;};
else
 x=y;

즉 if 와 else 사이에 원치 않는 ; 가 포함됨으로써 예상치 못했던 오류를 만들어 내게 된다.
하지만 do{...}while(0)을 사용하면 아래와 같이 문제가 깨끗하게 해결된다.

if ( x > y )  
 do{ x+=2;y+=2;}while(0);
else  
 x=y;

혹시 아직도 뭐가 문제인지 모르겠다면...

if ( x > y )
{ 
 x+=2;
 y+=2;
}     //if 문은 여기서 끝난다.
;     //빈 라인이 되고...
else    //이 else는 if문이 없는 else가 되므로 컴파일 에러 발생할 것임.
 x=y;


출처 : http://kernelnewbies.org/FAQ/DoWhile0


WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,
속도를 분석할 때 n 이라는 변수에 붙은 지수가 1 이나 2 처럼 미리 정해진 값, 즉 상수(constant) 로 표현되는
경우에는 그 알고리즘을 '쉬운' 문제라고 간주한다. 지수의 값이 아무리 크다고 해도 그것이 미리 정해진 상수 값이라면 그
알고리즘은 컴퓨터가 적당한 시간 안에 계산해 낼 수 있는 쉬운 알고리즘에 해당하는 것이다. 이렇게 변수 위에 붙은 지수가 미리
정해진 상수인 수학 공식을 우리는 다항식 (polynomial) 이라고 부른다.
ex)2n³ x n² - 3&nbsp;

복잡성 이론에서 다항식의 반대말은 '지수 함수(exponential function)'다. 지수함수란 변수 위에 붙은 지수가 미리 정해져 있는 상수 값이 아니라 그 자신도 변수로 표현되는 함수를 의미한다. </p>
ex)2^n, 3^n
복잡성(Complexity) 이론에서는 알고리즘의 속도가 다항식으로 표현되는 문제들을 묶어서 'P'라고 부르고,
다항식으로 표현될 수 있는지 여부가 알려지지 않은 문제들을 묶어서 'NP'라고 부른다. 여기서 P는
polynomial(다항식)의 머리 글자고, NP는 nondeterministic polynomial(비결정성 다항식)의 머리
글자를 의미한다.

이 때 NP가 none-polynomial(비다항식)을 의미하지 않는다는 사실에 유의할 필요가 있다. 다항식이
아니라는 사실과 다항식으로 표현되는지 여부가 아직 알려지지 않았다는 사실 사이에는 엄청난 차이가 존재하기 때문이다. 다항식으로
표현되는 알고리즘은 오늘날의 컴퓨터가 적당한 시간 내에 해결할 수 있는 문제기 때문에 P에 속한 문제들은 '쉬운' 문제들이고,
NP는 그와 반대로 '어려운' 문제를 의미한다.</p>

'NP-hard'라고 불리는 문제들은 세일즈맨의 여행 문제처럼 모든 경우의 수를 전부 확인해보는 방법 이외에는 정확한 답을 구할 수 있는 뾰족한 수가 없는 문제들을 뜻한다. </p>

어떤 문제가 NP에 속하면서 즉, 다항식으로 표현될 수 있는지 여부가 알려지지 않았으면서 동시에 NP-hard에
속한다면, 즉 '무식한 힘'의 방법 말고 다른 절묘한 알고리즘이 알려져 있지 않다면 그 문제는 'NPC(NP-complete)
문제'라고 부른다.

컴퓨터 학자들과 프로그래머들은 대개 NP 완전 문제를 실용적인 관점에서 해결하기 위해서 진짜 정답을 찾기를
포기하는 대신 훨씬 적은 양의 계산을 통해서 정답에 가까운 값을 찾는 데 만족한다. 이러한 알고리즘은 근사
알고리즘(approximation algorithm) 혹은 발견적 알고리즘(heuristic algorithm)이라고 부른다.
MST, 탐욕적 알고리즘, 평면 절단 방법 등은 모두 이러한 알고리즘의 예인데, 실전 프로그래밍의 세계에서는 이러한 근사
알고리즘이 생각보다 많이 사용된다.

복잡성 문제를 연구하는 학자들에게 가장 어려운 질문중의 하나는 바로&nbsp;"NP에 속하는 문제들이
궁극적으로는 모두 다항식, 즉 쉬운 알고리즘을 이용해서 해결될 수 있을까?" 라는 질문이다. 만약 그렇다면 NP 에 속한 문제나
P 에 속한 문제가 모두 종국에는 다항식으로 표현되기 때문에 'P = NP' 라는 등식이 성립하게 될 것이다. 하지만 NP 에
속하는 문제가 모두 다항식으로 해결될 수 있을지 여부를 파악하거나 증명하는 것이 너무나 어렵기 때문에 이 등식은 아직도 완전하게
입증되지 않은 어려운 명제증의 하나로 통하고 있다.



WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,

에르고딕 이론이란 무엇인가

 

(한국과학기술원 수학과 최건호 교수, 최종 수정일 2006년 4월 17)

 

먼저‘에르고딕(ergodic)’이라는 단어의 어원을 살펴보면 오스트리아의 통계물리학자인 볼츠만(Ludwig Boltzmann)이 그리스어의 어근인‘ergon(, 작용)과‘hodos(, 경로)를 붙여서 만들었습니다. (참고문헌: The Words of Mathematics, S. Schwartzman, Mathematical Association of America, 1994) 볼츠만은 에르고딕 가설을 내세웠는데, “주어진 하나의 입자는 상태 공간에서 같은 에너지를 갖는 곡면의 모든 부분을 골고루 돌아다닌다”라는 것입니다. 참고로 일러두자면 자신의 원자 이론이 학계에서 받아들여지지 않아 우울하게 지냈던 볼츠만의 전기는 ‘볼츠만의 원자’라는 제목으로 이미 우리말로 번역되어 나와 있습니다. (생각해 보면 큰 길에서 슈뢰딩어 연구소와 Wien 대학 쪽으로 접어드는 길의 이름이 Boltzmanngasse이었습니다. Wien 에서는 역사적인 업적을 남긴 위인들의 이름을 길에 붙였습니다.)

 

동역학계(dynamical system)란 간단히 말하자면 사상 f : X --> X 주어져 있을 때 f를 반복하여 적용하여 생길 수 있는 현상을 연구하는 분야입니다. 비유를 하자면 컴퓨터 프로그래밍에서 DO loop 연상하면 되겠습니다. 물리적인 의미로는 f가 시간이 1초 지날 때 마다 주어진 점의 위치를 주는 사상이라고 하면 fn번 적용한다는 것은 n 초 후에 점이 어디에 놓이게 될 것인가 하는 것입니다. 가장 흔한 예는 X 미분다양체이고, f상미분 방정식을 풀었을 때 나타나는 유선 (flow)으로 정의될 때 f의 점근적(asymptotic) 행동을 연구하는 것입니다. 이 분야를 미분동역학계라고 하며, 한편 집합 X가 위상공간이고 f가 연속일 때를 위상적 동역학계이론이라고 합니다. 마지막으로 X가측(measurable) 구조를 갖고, 사상f가측적인(measurable) 경우를 연구하는 분야를 에르고딕 이론이라고 합니다. 이러한 동역학계들을 물리적 현상의 무질서의 관점에서 연구하는 것을 카오스 이론이라고 합니다. 참고로 말하자면 이상의 구조에 덧붙여 환(ring)이니 체(field) 등의 대수적 구조도 동시에 갖는 역학계를 연구하는 경우도 있습니다. 예를 들면 G.A. Margulis 필즈 상을 받은 업적도 이런 분야에 속합니다.

 

확률측도 공간에서 정의된 에르고딕 변환의 의미는 ‘불변인 부분집합은 그 측도값이 0 이거나 1 이다’라는 것입니다. 이것은 주어진 불변집합이 더 분해될 수 없다는 (indecomposable) 뜻입니다. 동치인 조건은 ‘불변함수는 상수 밖에 없다’라는 것인데, 주어진 변환이 에르고딕하지 않다면 정의역을 분해하여 각각의 성분이 에르고딕이 되도록 할 수 있습니다. 주어진 변환이 에르고딕이면 Birkhoff 의 에르고딕 정리가 성립하는데 이것은 변환의 궤도에 따라 구한 시간 평균이 불변측도에 대한 공간 평균과 같다는 것이며 에르고딕 이론에서 가장 기본이 되는 사실입니다.

from : http://shannon.kaist.ac.kr/choe/Articles/Ergodic.htm

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,

rts 에 대한 메뉴얼을 읽어 봤는데 사용하기 어려운 것 같긴한지만 구현 이론을 보면 select 의 문제점을 잘 해결 한 듯 하더군요.
포퍼먼스도 좋을 듯 하고요.
그런데 epoll 과 비교 해서 포퍼먼스가 어떨지 궁금합니다.
libevent 라이브러리의 벤치마킹 결과를 보면 rts 에 대한 벤치마킹이 없어서 서로간의 성능차를 알 수가 없더군요.
어느쪽이 얼마나 효율적인가요?

libevent 를 사용해 볼려고 하는데, select 를 사용할지 poll, epoll, kqueue 를 사용할지의 여부를 선택할 수 없는 듯 한데 맞나요?
컴파일 타임에서 사용 할 수 있는 가장 좋은 것을 선택해서 컴파일이 되는건가요?

솔라리스 8은 epoll 이 지원되지 않는 것 같던데 epoll 이 지원되는 솔라리스 버전은 어떤건가요?


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


libevent 에서 어떤 대기 함수를 사용하지를 설정할 수 있습니다.

libevent 에서 어떤 대기 함수를 사용하지를 설정할 수 있습니다. 환경 변수로 조작할 수 있습니다. libevent make 중에 test 할 수 있도록 하는 타킷이 있습니다.

RTS 는 언뜻 봐서 자세히는 모르겠지만, 코드의 복잡성과 프로그래머가 알아야 할 상황이 좀 많은 것 같습니다. 저는 libevent 가 이식성도 좋고 사용하기도 편해서 잘 사용하고 있습니다.


좀 상관없는 이야기지만rts의 경우 스레드에서 시그널을 잘 받아 드리

좀 상관없는 이야기지만
rts의 경우 스레드에서 시그널을 잘 받아 드리는지요?

같은 코드를 커널 2.4에서는 스레드가 시그널을 잘 받아드리는데.
2.6에서 pthread_kill하면 받는데 rts로 발생되는 시그널은 받지를
못하더군요.

pid문제일거라고 생각했지만.. 이건 아무레도 아닌듯 하군요.

한참을 문제점을 찾아다니다가 포기하고
지금은 epoll로 하고 있는데.. 정말 궁금하네요..


rts보다 epoll이 성능이 더 뛰어나다고 합니다

조인씨 위키에서 epoll관련 문서를 본적이 있는데

epoll이 10~20%정도 성능이 좋다고 하더군요.

(뭐 구현하기 나름이겠지만...^^)

참고문서입니다.

http://www.xmailserver.org/linux-patches/nio-improve.html


WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,

에.... 제목이 너무 장황하네요. 근데, 사실인지라... -_-;

우선, 제 상황은요, 어제까지 시리얼 포트 두개로 파일 전송하는 프로그램 만들려다가...실패하고, 자료를 다시 찾아보는 중입니다.

그래서, 자료를 이해가 안되는 것이 있어서, 고수님들의 명쾌한 해설을.... ^^

아. 그전에 실패했던 원인이, 데이터를 전송했는데 받는 쪽에서 그걸 다 받지 못하는, 즉, 전송이 100만큼되면 80정도만 받는 것이였습니다.

자.... 여기서부터 질문인데요.
1. 우선 , 송신부 데이터보다 수신부 데이터가 작은 점, 또한 송신부에 딜레이(sleep()등) 을 두면 수신율이 올라가는 점(예를들면 80 받던게 85나 90받는다던가...)으로 보아, 이유는 몰라도 송신되는 데이터를 수신부에서 전부 받지 못하는 것이 아닌가 판단했습니다.

그런데... 이게 실제로 가능한가요? 제가 시리얼로 송수신하는 걸 많이 안 해봐서 그런지 몰라도, 꼴랑 2미터짜리 널모뎀 케이블로 PC랑 타겟 보드 연결했는데, 수신부(이게 보드던, 호스트 PC던 증상은 같더군요) 송신하는 걸 다 못 받는다는 게, 상상이 안되더라구요. 실제로 이런 일이 있나요?

2. [비동기 송수신]을 이용하면 된다... 는 해결책이 많이 나와있고, 실제로 많이 이용하시는 듯 한데요. 이해가 안되는 것이,
[serial programming HOW-TO] 에 나와 있는 3가지 방식이면 어느 거라도 되어야 하는 게 정상 아닌가...하는 것이 제 생각이었거든요. 즉, 어떤 모드던 송신측에서는 데이터를 전송할 테고, 수신부에서는 데이터가 버퍼에 들어오면 수신할 테니, 결국 어플리케이션 레벨에서 보면 결국 write 로 보내고 read로 읽어내는 것이 아닌가(물론 , 중간에 0을 보낸다던가 혹은 데이터가 들어오면 signal을 보낸다던가 하는 차이는 있겠지만요) 하는데요. 그런데, 실제로는 다른가요?

3. select()함수를 써서 데이터가 들어올 때 까지 일정 시간 기둘리는 것이 해결책으로 많이 등장하던데요. 이것이 이해가 안 가는 것이,
오히려 시간지연함수(물론 select가 시간지연함수는 아닙니다만)는 송신부에 써야 하는 것 아닌가요? 즉, 송수신 환경에서 수신측이 데이터를 다 못 받고 있으니까, 송신부에서 느리게 송신하고, 수신부에서는 빨리 수신해야 하는데, select()를 쓰면, 물론 시간 안에 데이터가 수신되면 signal을 보내지만, 그래도 그거 처리하는 데도 시간이 걸릴거고, 결정적으로 받는 데이터보다 보내는 데이터가 더 많은 데 왜 수신부에 select()를 쓰는지 이해가 안 가더라구요.

4. 간단한 방법중 하나가 수신부에 버퍼를 늘리는...것을 생각할 수 있다...라는 것이 있던데요.
그런데...그 버퍼라는 것이, rs-232c를 물리적으로 구현하는 칩 안에 있는 버퍼를 이야기하는 것이 아닌가요? 즉, 이미 하드웨어적으로 구현되어 있는 버퍼를 , 어떻게 소프트웨어에서 늘린다는 것인지요? 아니면 이 문장 자체가 다른 의미를 가지고 있나요?

질문이 길어 죄송합니다. 어쩌면 제가 serial 통신의 개념 자체를 잘 못 가지고 있는지도 모르겠습니다만... 고수님들의 조언 부탁드립니다.

그럼. 좋은 하루 되시기 바라며.... 행복하세요. ^^


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


질문 1) 송신데이터양 &lt;&gt; 수신데이터양답변) 시리얼통신은

질문 1) 송신데이터양 <> 수신데이터양
답변) 시리얼통신은 TCP처럼 데이터 수신후 제대로 수신했는지 확인, 재전송, 에러검출, 흐름제어가 안됩니다.
따라서, 이를(신뢰적인 통신) 응용프로그래머가 구현해야 합니다.
질문 2) 비동기 송수신
답변) 언제 데이터를 보낼지는 알지만, 언제 데이터가 도착할지는 수신 프로그램입장에서 알수 없습니다. 그렇다고 수신하는 측에서 계속 데이터가 오기만 기다릴 수는 없고(다른 작업도 수행해야 하므로), 수신 프로그램이 다른 작업(바이트 수신하는 작업말고)을 수행하는 와중에 도착된 데이터는 일단 특정한 버퍼로 받아놓게 코딩하는 것이 필요합니다. 특정 버퍼의 크기는 클수록 좋겠지요.
*참고: 비동기 송수신이란, 송신측에서 send()할때, 상대가 반드시 recv()를 호출하고 있지 않아도 send()는 동작한다!라는 것입니다. 어떻게? 누가 대신 임시적으로 받아주면 되지요. 누가? 디바이스 드라이버(커널)가. 어디에? 커널 버퍼에. 버퍼크기는? 그때 그때 다른데, 프로그래머가 세팅하기에 달렸지요.
tcp/ip도 비동기 프로토콜임을 참고하시길..
질문 3) select ?
답변) select는 시간지연을 위한 목적이 아니고, 입력(수신데이터가 있나?)을 검사해주는 루틴입니다. 데이터가 수신되면 select에서 탈출하게 되지요. 그러나, 데이터가 만일 하루, 이틀, 10년동안 안오면, 계속 기다릴 수 없겠지요? 따라서, 일정기간동안 입력이 있나를 검사하게 됩니다. 그후 다시 또 시간주고 기다리던가....
또는, 출력을 위해서도 사용되는데, 현재 송신버퍼가 꽉찼는데, 거기다가 데이터를 쓰게 된다면(over run) 문제가 되겠지요? 그래서 송신파일 디스크립터에 select를 걸어 쓸수(보낼수) 있는지를 검사하는 용도로 사용하기도 합니다.
* sleep처럼 대충~ 눈가리고 아웅으로 시간측정해서... 할 수 있으나, 이는 송수신속도(baud rate)나, 시스템사양(예를 들어, 펜티엄3->펜티엄4)이 틀려질 경우, sleep으로 코딩한 부분은 다시 시간측정해서 다시 sleep들어가야 한다는 아픔이 생깁니다.
* 동기화를 위해 sleep을 사용하는 것은 절대 지양해야 합니다.(송수신 속도 바뀔경우 없고, 하드웨어 사양 안바뀐다? 쿵~ ....)
질문 4) 수신버퍼 ?
답변) RS-232C의 수신버퍼 크기는 1바이트 입니다. 하드웨어적으로 말이지요.
디바이스 드라이버에서 데이터가 수신되면 보다 큰 버퍼 영역으로 복사를 해놓을 수 있겠지요? 물론, 버퍼 크기는 1바이트 보다야 크게잡아서리..... 이때, 수신버퍼 영역의 크기가(디바이스 드라이버에서 지정한) 기본크기가 있는데, 경우에 따라, 이 버퍼 크기를 늘려줘야 하는 경우가 있겠지요?
아~ 물론 버퍼 크기야 클수록 좋습니다만........


1. 송신부에서 100을 보냈는데, 수신부에서 80만을 받았다.

1. 송신부에서 100을 보냈는데, 수신부에서 80만을 받았다.
- 정상적으로 짜여진 프로그램이라면 거의 이런 일은 발생하지 않습니다.
- 하드웨어 셋팅을 확인해 보세요 boud, parity...
- 시리얼은 단순한 하드웨어 흐름제어는 가능하지만 TCP와 같은 신뢰성은 매우 떨어집니다. (대신 매우 심플하지만요)
2. 비동기송수신은 받던지 말던지 보낸다로 생각하면 될 것같은데...이것과는 별개 인것 같습니다. 시리얼은 일부러 그러지 않는한 비동기 송수신 입니다.
(TCP의 경우 핸드쉐이킹이 필요합니다만...)
3. select는 머... 여러가지 기능이 있는데 또 위 문제와는 별개입니다. read 함수는 일부러 그러지 않는한 특정 사이즈 만큼 받을 때까지 블락되니깐요.
4. 수신부의 버퍼가 무엇을 말하는 것인지는 모르겠지만, 디바이스 드라이버에서의 수신 버퍼는 우선 생각하지 마시고(아마도 최적화 되어 있지 않을까요?) read 함수의 버퍼만 생각하는게 낫지 않을까요?(시리얼의 수신 버퍼의 문제일리는 없을 것 같아서....)

경험상 통신시 데이터가 적게 수신되는 경우는 생각하시는 것처럼 복잡하지 않습니다. 즉 간단한 실수 였다는 거죠
1. 케이블 문제
2. 시리얼 셋팅 문제
3. 송신측 APP 단의 버퍼 크기 문제
4. 보내려고 하는 size 문제
5. 수신측 APP 단의 버퍼 문제
6. 받으려고 하는 size 문제
7. 매우 드물지만 케이블의 길이 등이 스펙의 권고안보다 긴경우 에러가 생길 수 있지만 100:80의 비율은 아닙니다.


1. 답변송신부에서 100을 보냈는데 수신부에서 80만 받는 경우는

1. 답변
송신부에서 100을 보냈는데 수신부에서 80만 받는 경우는 나올수 있습니다. 물론 지금 말씀하신 데이터 정도는 크게 문제될 것 같지는 않지만 가능성은 있습니다. 아실테지만 시리얼통신은 신뢰성 통신 방식이 아닙니다. 따라서 시리얼 데이터를 받는 부분에서 하드웨어 버퍼 또는 디바이스 드라이버 버퍼의 양이 너무 빨리 들어오는 데이터양에 못견뎌 데이터가 손실될 수 있습니다. 따라서 송신측에서 시간 간격을 두면 데이터가 다 받아지는 것입니다.

2. 답변
동기 방식이냐 비동기 방식이냐는 효율적 측면에서 다릅니다. 우리가 TCP/IP에서 동기/비동기 방식을 구분해서 사용하는 것또 같은 맥락이라고 생각합니다.

3. 답변
이 부분은 말씀하신 분께서 뭔가 착각하고 계신것 같은데요... 송신측에서 시간을 지연시켜 전송하는 것은 통신을 정삭적으로 하기 위한 하나의 트릭일 뿐이지 그런 방식을 언젠가는 큰 재앙을 불러옵니다. 따라서 양측의 APP 프로토콜 설계시 이런 것들을 고려해야합니다. 그 프로토콜을 정의하고 데이터를 안전하게 수신할 수 있도록 해야겠지요... 데이터를 천천히 보냈더니 수신측에서 데이터를 다 받네... 이런 방법은 옳지 않습니다. 시리얼통신 속도를 높이는 것도 한 방법이 될 수 있겠네요... 아니면 하드웨어 또는 소프트웨어 흐름제어를 사용하는 것도 하나의 방법입니다.(성능은 좀 떨어지겠지만...)

4. 답변
버퍼의 크기를 늘리는 것도 한 방법입니다. 하지만 시리얼 통신은 전송속도가 매우 느린 통신 방법중 하나입니다. 9600 baud를 사용할 경우 1초에 보낼수 있는 데이터의 양은 약 900 바이트 정도입니다. 따라서 이런 이론적인 내용을 분석하고 본인의 프로그램에 적용하는 것이 옳다고 생각되네요...

미흡한 답변이지만 도움이 되었으면 합니다.


WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,