메모리 구조 궁금점

nayana의 이미지

메모리 구조를 공부를 하고 있습니다.
한가지 궁금한점이 생겼습니다. 책에서 보면 이론적으로 아 !!!
이렇게 돌아가는구나 하고 알았습니다. 하지만 여기에서 머물지 않고 실제로 프로그램을 하나 만들어서 돌아가는 원리를 체크해서 몸으로 체험하고 싶어서 입니다.
일단 궁금한점은 메모리구조를 크게 4개( 제가 보느책에서 그렇게 나옵니다. ^^) 로 분류하면 코드메모리, 전역 메모리( data segment ), 스택, 자유저장소( free ) 나누어져 있습니다.
각가 4개의 영역들이 os 에서 메모리 주소를 어느정도 범위를 갖고 있는지 궁금합니다.( 프로그램으로 구현해서 눈으로 확인할려고 합니다. )

2번째 궁금한점은 스택에서 다음과 같은 코드 있다고 할때

int function( int parameter )
{
    int local = 3;
    return 6;
}

스택에서 실행되는 순서는

1. 빈 int 자리 하나가 스택에 쌓인다.
2. parameter가 스택에 쌓인다.
3. local이 스택에 쌓인다.
4. 값 6이 반환값 자리에 들어간다.
5. local 이 뽑힌다.
6. parameter가 뽑힌다.
7. 이 함수를 호출한곳에서 스택의 최상위값, 즉 반환값을 뽑는다.

이론적으로 알겠는데...이러한것들을 눈으로 직접확인 할려고 하니까? 어떻게 짜야 할지 막막합니다.
고수님들 답변 부탁드립니다.
Necromancer의 이미지

gcc -S 로 어셈블리 코드를 뽑아서 보시기 바랍니다.
그리고 어셈블리 강좌 하나 달고 다니시고...

p. s.

486시절 himem.sys없이 1m 이상 메모리 쓰는
도스 익스텐더 만든다고 난리쳤던게 기억이 나는군요. 결국 못만들었지만.

Written By the Black Knight of Destruction

nayana의 이미지

어셈을 공부해야겠군요...^^;
일단 제가 알고 싶은것은 c,c++표현이 불가능한가요?

nayana의 이미지

제나름대로 짜보았습니다.

      1 #include <iostream>
      2 using std::cout;
      3 using std::endl;
      4
      5 #include <cstdio>
      6
      7 int i1=10;
      8 int i2=20;
      9 int i3=30;
     10
     11 void ee()
     12 {
     13     static int i1=10;
     14     static int i2=20;
     15     static int i3=30;
     16
     17     cout << endl << "-----------------";
     18     cout << endl << "static 지역 변수";
     19     cout << endl << &i1;
     20     cout << endl << &i2;
     21     cout << endl << &i3;
     22
     23     int e1;
     24     int e2;
     25     int e3;
     26
     27     cout << endl << "-----------------";
     28     cout << endl << "지역 변수";
     29     cout << endl << &e1;
     30     cout << endl << &e2;
     31     cout << endl << &e3;
     32
     33 }
     34
     35 void f1(){}
     36 void f2(){}
     37 void f3(){}
     38
     39 static void fs1(){}
     40 static void fs2(){}
     41 static void fs3(){}
     42
     43 int main()
     44 {
     45     ee();
     46
     47     cout << endl << "-----------------";
     48     cout << endl << "전역 변수";
     49     cout << endl << &i1;
     50     cout << endl << &i2;
     51     cout << endl << &i3;
     52
     53     cout << endl << "-----------------";
     54     cout << endl << "일반 함수";
     55     printf( "\n%p", f1 );
     56     printf( "\n%p", f2 );
     57     printf( "\n%p", f3 );
     58
     59     cout << endl << "-----------------";
     60     cout << endl << "static 함수";
     61     printf( "\n%p", fs1 );
     62     printf( "\n%p", fs2 );
     63     printf( "\n%p\n", fs3 );
     64
     65     return 0;
     66 }

결과는 다음과 같이 나왔습니다.
-----------------
static 지역 변수
0x8049db4
0x8049db8
0x8049dbc
-----------------
지역 변수
0xbffff614
0xbffff610
0xbffff60c
-----------------
전역 변수
0x8049da8
0x8049dac
0x8049db0
-----------------
일반 함수
0x80487f6
0x80487fc
0x8048802
-----------------
static 함수
0x8048808
0x804880e
0x8048814

주소값 자체가 지역변수를 제외하고 같은영역을 사용하는것 같은데..메모리에 대한 지식이 부족하다 보니까...대충 감 밖에 오지 않습니다. 저를 가르쳐 주실 고수님 안계신지요?
그리고
Quote:
int function( int parameter )
{
int local = 3;
return 6;
}

스택에서 실행되는 순서는
코드:
1. 빈 int 자리 하나가 스택에 쌓인다.
2. parameter가 스택에 쌓인다.
3. local이 스택에 쌓인다.
4. 값 6이 반환값 자리에 들어간다.
5. local 이 뽑힌다.
6. parameter가 뽑힌다.
7. 이 함수를 호출한곳에서 스택의 최상위값, 즉 반환값을 뽑는다


이부분을 주소값을 뽑을려고 생각중인데 감이 잘 안잡힙니다.
dopesoul의 이미지

컴파일러와 링커를 아셔야 할것같은데
링커에서는 변수를 크게 몇부류로 나누어 메모리 영역을
씁니다.
그리고 특이하게 어떤 경계를 넘어서 Heap 영역이라고하는
미할당된 영역을 잡아놓습니다.

어셈코드를 뽑아보면 .section 을 살펴보실수있습니다.

.text 영역은 머쉰코드가 위치하는부분
.L 영역은 보통 printf 에서 pop 해가는 부분...(가변인자)

뭐 그런식으로 쓰입니다.

void f()
{
int i;
}

int main(void)
{
f();
}

        .file   "main.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .align 4
.globl f
        .type    f,@function
f:
        pushl %ebp
        movl %esp,%ebp
        subl $24,%esp
.L2:
        leave
        ret
.Lfe1:
        .size    f,.Lfe1-f
        .align 4
.globl main
        .type    main,@function
main:
        pushl %ebp
        movl %esp,%ebp
        subl $8,%esp
        call f
.L3:
        leave
        ret
.Lfe2:
        .size    main,.Lfe2-main
        .ident  "GCC: (GNU) 2.95.4 20011002 (Debian prerelease)"

함수를 호출할때 보면 pushl %ebp 는 base pointer 기능을
하는 레지스터입니다. esp 에는 현재 함수의 base pointer
를 넣고(movl) 변수의 크기만큼 스택포인터를 차감합니다.
이는 함수를 호출하기전의 prelude 라고 합니다.

f 레이블 즉 함수 호출을 살펴보면

역시 ebp 를 push 하고 스택포인터를 차감하는식으로..
진행됩니다.

ret 명령어는 직전의 eip 값을 저장했다가 다시 return 할때
참고하여 pc(program counter) 를 호출되는 부분의
바로 다음 코드로 반환시킵니다.

버퍼오버플로우라고 하는 공격은 보통 eip 를 변조시키는
것으로 일어납니다. frame pointer overflow 는 1byte 만
변조시킴으로써도 오버플로우가 가능하다는 것을 보여줍니다.

보통 프로그램을 짤때 int main(void) 와 같은식으로 짜는것도
main 함수가 프로그램의 처음이 아니기 때문입니다

내부적으로는 더 많은 함수들이 프로세스를 fork 시키는데
들어가므로, 그 함수에 옳은값을 반환하기 위해서 입니다.

메모리구조는 링커의 이해가 없이는 불가능하다고 봐도 과언이 아닙니다.^^
makeile 과 링커를 공부하시는것도 메모리 이해에 도움이 될 것입니다.
[/code]

nayana의 이미지

답변 감사합니다.
어셈 압박 ^^;
메모리구조를 공부하려면 어셈공부는 필수인가보네요?
그러면 일단 크게 나누어 메모리 4개영역이 어떻게 되고 그것을
c,c++로 표현하려면 어떻게 해야하나요?

Quote:
int function( int parameter )
{
int local = 3;
return 6;
}

스택에서 실행되는 순서는
코드:
1. 빈 int 자리 하나가 스택에 쌓인다.
2. parameter가 스택에 쌓인다.
3. local이 스택에 쌓인다.
4. 값 6이 반환값 자리에 들어간다.
5. local 이 뽑힌다.
6. parameter가 뽑힌다.
7. 이 함수를 호출한곳에서 스택의 최상위값, 즉 반환값을 뽑는다


이것을 c,c++표현하려면 어떻게 해야하나요...
정녕 방법이 없는것인가?
cdpark의 이미지

nayana wrote:

이것을 c,c++표현하려면 어떻게 해야하나요...
정녕 방법이 없는것인가?

C 언어의 함수가 이런 스택 관리를 abstract화한 겁니다.

연습삼아 함수 호출 없이 goto만 가지고 하노이탑 같은 재귀호출 프로그램 하나 짜 보세요.

dopesoul의 이미지

윗분말대로 goto 로 하노이탑같은
재귀호출을 짜보시면 대략 이해하실수도있겠네요.

매크로 어셈이 그런구조니까요.

이준의 이미지

메모리구조라고 안하고 메모리맵 이라고 하지 않나요? ^^;

좀더 프로페셔날한 프로그래머가 되려면 메모리맵에 대한 내용과..

심볼등에 대한 내용을 잘 알고 있어야 하지요..

프로그램을 잘 짜는사람일수록 링커에 대한 사용이 능숙합니다.^^

메모리맵이 무조건 4개 영역이되는것또한 아니구요..

언어에 따라 링크하는 방법에 따라 틀려지기도 합니다.

어셈블리과 링커에 대한 내용 컴파일러에 대한 내용을 공부하시면 좋을거 같네요^^

Hyo-Sung Lee(李曉星/Mark Lee)

KRSF Certified Inline Skate Instructor
Fitness Inline Skate Trainer
Mogul&Freeride Skier
IDOne ski rider
Cafe MogulBuddy/KoreaMogul
E-Leader(C) Programmer

nayana의 이미지

일단 c,c++로 표현할수 없다는거로 이해해야겠네요?
하이노이드 탑을 함수 호출없이 goto문으로 짜려고 하니까..
여기서도 막힙니다. ^^;
그래서 함수호출로 한번 짜보았습니다.

      1 #include <iostream>
      2 using std::cout;
      3 using std::cin;
      4 using std::endl;
      5
      6 #include <cstdio>
      7
      8 void Hanoi( int Dish, int OnePillar, int ThreePillar, int TwoPillar )
      9 {
     10     if( Dish > 0 )
     11     {
     12         Hanoi( Dish - 1, OnePillar, TwoPillar, ThreePillar );
     13         cout << " 접시 " << Dish << " Moving from " << OnePillar << " to " << ThreePillar << endl;
     14         Hanoi( Dish - 1, TwoPillar, ThreePillar, OnePillar );
     15     }
     16 }
     17
     18 int main( void )
     19 {
     20     int Dish;
     21     cout << "숫자를 입력하세요: ";
     22     cin >> Dish;
     23
     24     Hanoi( Dish, 1, 3, 2 );
     25
     26     cout << "Press Enter" << endl;
     27     getchar();
     28
     29     return 0;
     30 }

goto문으로 변경시키면서 스택관리를 비주얼한 화면으로 볼수 있는 방법이.....^^
제가 너무많은것을 요구하는것 같아서 죄송합니다.
nayana의 이미지

      1 #include <cstdio>
      2
      3 int function( int parameter )
      4 {
      5     int local = 3;
      6
      7     return 5;
      8 }
      9
     10 int main( void )
     11 {
     12     function( 4 );
     13
     14     return 0;
     15 }

이코드를 어셈블리어 변형 시켜 어셈블리어 웹사이트하나 띄어놓고 나름대로 주석을 달아 보았습니다. ^^;
주석이 맞는것인지도 확신 서지 않고 주석안단 부분이 많습니다.
이코드 설명해주실수 없나요?

#include <cstdio>

int function( int parameter )
{
 8048344:       55                      push   %ebp                  //⑥ ebp레지스터를 스택에 넣는다.
 8048345:       89 e5                   mov    %esp,%ebp             //⑦ 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 8048347:       83 ec 04                sub    $0x4,%esp             //⑧ esp에 4byte만큼뺀다.
        int local = 3;
 804834a:       c7 45 fc 03 00 00 00    movl   $0x3,0xfffffffc(%ebp)

        return 5;
 8048351:       b8 05 00 00 00          mov    $0x5,%eax
}
 8048356:       c9                      leave
 8048357:       c3                      ret

08048358 <main>:

int main( void )
{
 8048358:       55                      push   %ebp               //① ebp레지스터를 스택에 넣는다.
 8048359:       89 e5                   mov    %esp,%ebp          //② 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 804835b:       83 ec 08                sub    $0x8,%esp          //③ esp에 8byte만큼 뺀다.
 804835e:       83 e4 f0                and    $0xfffffff0,%esp
 8048361:       b8 00 00 00 00          mov    $0x0,%eax
 8048366:       29 c4                   sub    %eax,%esp
        function( 4 );
 8048368:       83 ec 0c                sub    $0xc,%esp
 804836b:       6a 04                   push   $0x4                   //④ 스택에 4를 넣는다.
 804836d:       e8 d2 ff ff ff          call   8048344 <_Z8functioni> //⑤ function함수를 호출한다.
 8048372:       83 c4 10                add    $0x10,%esp

        return 0;
 8048375:       b8 00 00 00 00          mov    $0x0,%eax
}
nayana의 이미지

주석문추가하여 올립니다.

#include <cstdio>
int function( int parameter )
{
 0x 8048344:  push   %ebp            //⑩ ebp레지스터를 스택에 넣는다.
 0x 8048345:  mov   %esp,%ebp      //⑪ 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x 8048347:  sub    $0x4,%esp       //⑫ esp에 4byte만큼뺀다.
        int local = 3;
 0x 804834a: movl     $0x3,0xfffffffc(%ebp) //⑬ 3을 ebp에 넣는다.
        return 5;
 0x 8048351: mov      $0x5,%eax         // ⑭5를 eax에 집어넣는다.
}
 8048356:  leave
 8048357:  ret

08048358 <main>: // 메인함수 시자주소
int main( void )
{
 0x8048358:  push    %ebp                //① ebp레지스터를 스택에 넣는다.
 0x8048359:  mov   %esp,%ebp          //② 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x804835b:  sub    $0x8,%esp            //③ esp에 8byte만큼 뺀다.
 0x804835e:  and    $0xfffffff0,%esp      //④ 0xfffffff0와 esp와 and 비트연산을 하여 esp에 집어 넣는다. 
 0x8048361:  mov    $0x0,%eax           //⑤ 0x0의 값을 eax에 넣는다.
 0x8048366:  sub    %eax,%esp            //⑥ esp에 0바이트 만큼뺀다.
        function( 4 );
 0x8048368:  sub    $0xc,%esp            //⑦ esp에 12바이트 만큼뺀다.  
 0x804836b:  push   $0x4                  //⑧ 스택에 4를 넣는다.
 0x804836d:  call   8048344 <_Z8functioni> //⑨ function함수를 호출한다.( 0x8048344주소를 호출한다. )
 0x 8048372:  add    $0x10,%esp
        return 0;
 0x 8048375:  mov    $0x0,%eax
}

제 나름대로 정리하고 이것을 그림으로 표현하려고 하니까..
잘안됩니다.

Quote:
1. 빈 int 자리 하나가 스택에 쌓인다.
2. parameter가 스택에 쌓인다.
3. local이 스택에 쌓인다.
4. 값 6이 반환값 자리에 들어간다.
5. local 이 뽑힌다.
6. parameter가 뽑힌다.
7. 이 함수를 호출한곳에서 스택의 최상위값, 즉 반환값을 뽑는다

이 이론만 갖고도 그림을 그리려고 하면 그릴수는 있겠습니다.
하지만 위의소스를 참고하면서 주소값 정확하게 달고 전체적을 돌아가는 루틴을 작성할려니까 쉽지가 않네요^^;
고수님들 답변 부탁드립니다.
cocas의 이미지

13번 주석이 잘못되었습니다. %ebp에서 0xfffffffc ( -4 맞나요? ) 를 더한 메모리 주소 상에 3을 넣는 것입니다.

nayana의 이미지

수정해서 다시 올립니다. -4는 무엇을 말씀 하시는건가요?

#include <cstdio>
int function( int parameter )
{
 0x 8048344:  push   %ebp            //⑩ ebp레지스터를 스택에 넣는다.
 0x 8048345:  mov   %esp,%ebp      //⑪ 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x 8048347:  sub    $0x4,%esp       //⑫ esp에 4byte만큼뺀다.
        int local = 3;
 0x 804834a: movl     $0x3,0xfffffffc(%ebp) //⑬ ebp에서 0xfffffffc를 더한 메모리주소상에 3을  넣는다.
        return 5;
 0x 8048351: mov      $0x5,%eax         // ⑭5를 eax에 집어넣는다.
}
 8048356:  leave
 8048357:  ret

08048358 <main>: // 메인함수 시자주소
int main( void )
{
 0x8048358:  push    %ebp                //① ebp레지스터를 스택에 넣는다.
 0x8048359:  mov   %esp,%ebp          //② 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x804835b:  sub    $0x8,%esp            //③ esp에 8byte만큼 뺀다.
 0x804835e:  and    $0xfffffff0,%esp      //④ 0xfffffff0와 esp와 and 비트연산을 하여 esp에 집어 넣는다. 비트연산 결과 esp는 0x8048360이 된다.
 0x8048361:  mov    $0x0,%eax           //⑤ 0x0의 값을 eax에 넣는다.
 0x8048366:  sub    %eax,%esp            //⑥ esp에 0바이트 만큼뺀다.
        function( 4 );
 0x8048368:  sub    $0xc,%esp            //⑦ esp에 12바이트 만큼뺀다.  
 0x804836b:  push   $0x4                  //⑧ 스택에 4를 넣는다.
 0x804836d:  call   8048344 <_Z8functioni> //⑨ function함수를 호출한다.( 0x8048344주소를 호출한다. )
 0x 8048372:  add    $0x10,%esp
        return 0;
 0x 8048375:  mov    $0x0,%eax
}

ebp 값이 0x8048358 인가요 ? 만약에 맞다면 그런 가정하에
나름대로 추정을 해보았습니다.
ebp값이 1이라고 가정하면 ③까지는 esp값이 8이되고 ④에서 esp값을 3으로 바꾸고 ⑦에서 esp는 14가 됩니다. 제가 잘분석한것인지는 모르겠지만 만약에 맞다면 왜이렇게 돌아가는것인가요?

cocas의 이미지

3, 4, 5, 6, 7의 이유는 정확하게 모르겠습니다. 일종의 aling 맞추기와 다른 경우에 쓰이는 것을 최적화 하지 않고 그냥 쓰는데 필요가 없는 경우라고 추측할 뿐입니다. 최적화 옵션을 주게 되면 보통 사라지거나 다른 형태로 바뀌게 됩니다.

nayana의 이미지

그러면

Quote:
1. 빈 int 자리 하나가 스택에 쌓인다.
2. parameter가 스택에 쌓인다.
3. local이 스택에 쌓인다.
4. 값 5이 반환값 자리에 들어간다.
5. local 이 뽑힌다.
6. parameter가 뽑힌다.
7. 이 함수를 호출한곳에서 스택의 최상위값, 즉 반환값을 뽑는다

이 부분을 인용하면 2, 3번은 코드를보면 대충 알겠는데..
1, 4번 역할을 하는곳은 어디인가요?
cocas의 이미지

main 함수에서는 반환값을 사용하는 코드가 전혀 없습니다. 그러므로 반환값이 어디에 들어가는지 main함수의 어셈블리 코드에 나오지 않습니다. function 함수 안에서는 mov 5, %eax 코드가 있음을 알 수 있습니다. 보통 위와 같이 4바이트이하의 크기를 같는 형태의 데이터들은 %eax 레지스터를 통해 반환값을 주고 받습니다.

1번은 무엇인지 모르겠습니다. 빈 int 자리가 쌓이는 게 무엇인가요?

nayana의 이미지

Quote:
1번은 무엇인지 모르겠습니다. 빈 int 자리가 쌓이는 게 무엇인가요?

일단 스택에 쌓일때 파라미터가 먼저 싸이는것이 아니고 반환값이 먼저 스택에 먼저 쌓입니다. 일단 자리를 먼저 차지 하는것이지요 그리고 나서 파라미터, 지역변수 그리고 미리 잡아놓은 반환값에 5를 채우는 것입니다.
nayana의 이미지

Quote:
main 함수에서는 반환값을 사용하는 코드가 전혀 없습니다

그래서 다시 수정해서 올립니다.
#include <cstdio>
int function( int parameter )
{
 0x 8048344:  push   %ebp            //⑩ ebp레지스터를 스택에 넣는다.
 0x 8048345:  mov   %esp,%ebp      //⑪ 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x 8048347:  sub    $0x4,%esp       //⑫ esp에 4byte만큼뺀다.
        int local = 3;
 0x 804834a: movl     $0x3,0xfffffffc(%ebp) //⑬ ebp에서 0xfffffffc를 더한 메모리주소상에 3을  넣는다.
        return 5;
 0x 8048351: mov      $0x5,%eax         // ⑭5를 eax에 집어넣는다.
}
 8048356:  leave
 8048357:  ret

08048358 <main>: // 메인함수 시자주소
int main( void )
{
 0x8048358:  push    %ebp               //① ebp레지스터를 스택에 넣는다.
 0x8048359:  mov   %esp,%ebp          //② 현재의 esp의 값을 ebp에 넣는다. 그러므로 esp와 ebp같게 된다.
 0x804835b:  sub    $0x8,%esp            //③ esp에 8byte만큼 뺀다.
 0x804835e:  and    $0xfffffff0,%esp      //④ 0xfffffff0와 esp와 and 비트연산을 하여 esp에 집어 넣는다. 비트연산 결과 esp는 0x8048360이 된다.
 0x8048361:  mov    $0x0,%eax           //⑤ 0x0의 값을 eax에 넣는다.
 0x8048366:  sub    %eax,%esp            //⑥ esp에 0바이트 만큼뺀다.
int a = function( 4 );
 0x8048368:  sub    $0xc,%esp            //⑦ esp에 12바이트 만큼뺀다.  
 0x804836b:  push   $0x4                  //⑧ 스택에 4를 넣는다.
 0x804836d:  call   8048344 <_Z8functioni> //⑨ function함수를 호출한다.( 0x8048344주소를 호출한다. )
 0x8048372:  add    $0x10,%esp
0x 8048375:  mov    %eax,0xfffffffc(%ebp) //⑮ eax에서 0xfffffffc를 더한 메모리 주소에 ebp 를 넣는다.
        return 0;
 0x 8048375:  mov    $0x0,%eax
}

오늘 하루종일 이것만 잡고 있네요^^
생각보다 이해하는것이 어렵네요...
빨리 머리속에 그려지고 싶은데^^
M.W.Park의 이미지

함수 호출에서 stack을 관리하는 것은 callee가 할 수도 있고,caller가 할 수도 있습니다.
그리고, call stack의 깊이가 좀더 깊은 예제 (함수내에서 다른 함수를 한번더 호출한다든지, 재귀호출이 일어난다든지...)를 이해해보시는 것도 도움이 될듯합니다. 8)

-----
오늘 의 취미는 끝없는, 끝없는 인내다. 1973 法頂

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.