'Linux'에 해당하는 글 56건

● 맹글링

C++에서는 함수 중복을 하기 때문에 함수 이름이 같더라도 구별되는 인자열에 따라 다른 이름을 만들어 준다.

이를 함수 이름 맹글링(function name mangling)이라고 한다. 맹글링은 사전적 의미로 '토막토막 베다/알아듣지 못하게 만들다'라는 의미를 가진다.


맹글링의 실제 의미는 컴파일러가 어떤 일관된 규칙에 따라 함수를 부호화하는 것이다.


에러 메시지를 자세히 보면 VC++의 "


?swap@@YAXPAH0@Z"와 g++의 "swap_FPiT0"은 void swap( int*, int* ) 함수가 맹글링된 이름이다.


이 맹글링 방법은 컴파일러마다 다르지만 Ellis와 StrouStrup이 쓴 책 "The Annootated C++ Reference Manual"에  name encoding이라는 내용으로 소개되어 있다. 맹글리의 기본 원칙은 함수의 인자 타입, 지정자(modifier), 클래스의 멤버함수에 따라 부호를 할당한다.


int형은 i, double형은 d, char형은 c, float형은 f등으로, unsigned는 U, signed는 S, const는 C등으로 부호화한다.


또 포인터 타입은 P, 레퍼런스 타입은 R, 전역함수인 경우에는 F, 클래스 멤버함수인 경우에는 클래스 이름의 글자 개수에다 클래스 이름, 그리고 F를 연속해서 붙여준다. 클래스 이름과 같은 생성장와 소멸자는 함수 이름으로 ct, dt로 붙여준다.


예를 들어보자.

1    void f( int, const char * ) ;                         → f_FiPCcv

2    void f( unsigned, const String& ) ;             → f_FUiRC6Stringv

3    Complex::Complex( double, double ) ;      → _ct_7ComplexFdd

4    Complex::~Complex() ;                             → _dt_7ComplexFv

5    double Complex::csin() ;                           → csin_7ComplexFvd


이러한 원리로 C++에서는 컴파일을 할 때 함수 정의나 호출시 맹글링이 일어난다.

위의 첫 두 함수처럼 함수 이름은 'f'로 같지만 인자가 다르기 때문에 서로 다른 이름으로 맹글링된다.

따라서 함수 중복이 가능하며 타입 검사를 할 수 있는 것이다.

그렇지만 C에서는 함수 이름의 유일성이 보장되므로 맹글링이 일어나지 않고 단지 함수 이름 앞에 밑줄 문자(_)를 붙이는 것이 고작이다.


● extern "C"

그러면  C 함수에 대해 맹글링이 일어나지 않도록 하려면 어떻게 해야 하는가?


C++에서는 이를 타입 보장 링크(type-safe linkage)라는 메커니즘으로 제공한다. 에러를 고치려면 다음과 같이 고치도록 한다.


extern "C"

{

        #include "swap.h"

}


함수 하나에 대해서만 사용할 경우에는 extern "C" 문장 다음에 바로 함수 원형을 적어도 된다.

extern "C" int swap( int *, int * ) ;


함수 선언이 여러 개일 때는 중괄호로 감싸주어야 한다.


extern "C"

{

        int f() ;

        void g( int t ) ;

}


[extern "C"의 의미]

extern "C"문장은 맹글링을 하지 말라는 지시문이다.

그렇다면 C의 표준 라이브러리 함수, 예를 들어 printf()를 사용할 때도 extern "C"문장을 항상 적용해야 하는가?

만약, 함수 원형을 개별적으로 선언하고 사용하려면 적용해야 한다.


1    // printf.cpp

2    

3    extern "C" int printf( const char *... ) ;

4

5    void main()

6    {

7        printf( "야!" ) ;

8    }


반면 C 표준 헤더 파일 자체를 포함시킬 때는 적용하지 않아도 된다.

1    // printf.cpp

2

3    #include                  // c 표준 헤더 파일을 포함할 경우에는 extern "C" 지시어가 필요 없음

5    void main()

6    {

7        printf( "야!" ) ;

8    }


왜냐하면 stdio.h를 포함한 모든 표준 C 헤더 파일에는 다음과 같은 형태로 extern"C" 문장이 들어있기 때문이다.


#ifndef    __STDIO_H               // 조건부 컴파일:헤더 파일이 중복 포함되는 것을 방지

#define   __STDIO_H  


#ifdef    __cplusplus              // C++ 모드로 컴파일할 때

extern "C" {                            // 맹글링 방지

#endif

        ...                                     // C 함수 원형

int printf( cont char *, ...) ;      // C++모드로 컴파일할 때

        ...

#ifdef    __cplusplus

}

#endif

#endif        /* __STDIO_H */


__cplusplus는 예약된 매크로 상수로서 C++ 모드로 컴파일할 때 설정되어 extern "C" {} 문장을 수행하고,

C모드로 컴파일을 할 때는 매크로 상수를 정의하지 않기 때문에 extern "C" {} 문장을 수행하지 않는다.

따라서 C/C++ 두 언어로 모두 컴파일할 수 있다.


● type-safe linkage의 의미

 extern "C" 문장의 의미에 대해 한 가지 조심할 점은 C함수를 사용하더라도 C 컴파일러가 아닌 C++ 컴파일러가 해석하기 때문에 그 함수의 원형에 따라 타입 검사는 아주 정확히 한다는 것이다.

결코 C 프로그램에서 함수를 사용할 때처럼 타입 검사를 느슨하게 하지 않는다. 따라서 함수 원형을 정확하게 기술할 필요가 있다.

타 입 보장 링크(type-safe linkage)라는 말의 의미처럼 C++ 내에서 사용되는 C 함수는 컴파일 단계에서 C++ 함수와 동등하게 엄격한 타입 검사를 받는 것을 보장하고 링크 단계에서 이름을 알 수 있는 C 함수를 찾아 넣을 수 있다는 것이다. 이로써 유효한 타임을 갖는 C함수 호출이 가능해지는 것이다.


예 를 들어 두 행렬의 합을 구하는 함수 madd()를 C함수로 정의할 때, 임의의 행과 열을 갖는 행렬 연산을 위해 일차원 배열로 구현한다고 하자. 이 함수의 인자에 개념적으로 수학에서의 행렬과 논리적으로 개념이 동등한 이차원 배열을 실인자로 넘기는 경우, C에서는 에러를 내지 않지만 C++에서는 엄격한 타입 검사를 통해 타입 불일치 에러를 낸다.


따라서 extern "C" 지시어의 의미는 C++ 맹글링을 하지 않음으로써 C 함수를 사용할 수 있고 또 타입 검사는 C++ 규칙을 따르기 때문에 올바르지 못한 타입을 넘기는 것을 방지한다고 요약할 수 있다.


[extern "C"의 적용]

여기에서는 extern "C"를 함수에 국한해서 설명하였지만 사실 C 함수에만 적용되는 것이 아니다. C의 맹글링 규칙이 적용되는 심볼이 모두 해당된다



출처 : http://wen21.springnote.com/pages/2412238

'Linux > Programming in UNIX env.' 카테고리의 다른 글

multi-core에서 spinlock 없이 프로그래밍 하기  (0) 2010.07.17
extern "C"  (0) 2010.05.18
Linux Kernel Linked List Explained  (0) 2010.03.01
alarm 과 sleep 그리고 usleep  (0) 2009.08.24

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

,

C뿐만 아니라 모든 프로그래밍 언어는 목적 파일(obj)을 만들고 링크에 의해 목적 파일을 연결함으로써 최종 실행 파일을 만든다. 이때 목적 파일로 자신이 정의한 함수의 명칭과 주소를 공개하도록 되어 있고 이 명칭 공개 방식이 언어에 상관없는 표준 포맷으로 규정되어 있다. 그래서 C와 파스칼처럼 각각 다른 언어로 컴파일된 오브젝트 파일들이 링크될 수 있는 것이다.

초기의 C++도 마찬가지로 이런 범용 링커(linker)를 사용하도록 디자인되었다. 그런데 범용 링커는 함수를 오로지 이름으로만 찾기 때문에 C++의 오버로딩된 함수들을 제대로 구분하지 못한다. 그래서 C++은 함수의 이름을 외부로 공개할 때 인수의 개수와 타입에 대한 정보까지 함수명에 포함시키는데 이런 명칭을 작성하는 것을 이름 장식(name mangling)이라고 한다. 그래서 C++로 컴파일한 목적 파일의 함수명을 보면 함수 이름외에도 앞뒤로 이상한 기호들이 붙어 있다. 앞에서 만든 Overload.cpp의 목적 파일에는 세 개의 Add함수의 이름이 다음과 같이 작성된다.


?Add@@YANNN@Z

?Add@@YAHHHH@Z

?Add@@YAHHH@Z


Add라는 함수 이름 외에도 뒤쪽에 인수의 개수나 타입에 대한 정보들이 부호화되어 표기되어 있다. 오버로딩된 함수들의 외부 명칭이 이렇게 작성되므로 링크 단계에서는 각 함수들이 다른 함수처럼 인식되는 것이다. 그런데 모든 언어가 오버로딩을 지원하지 않기 때문에 C++의 이런 기능이 다른 언어와 혼합 프로그래밍을 할 때는 문제가 될 수 있다. 그래서 다른 언어와 링크되어야 하는 모듈은 이런 이름을 작성하지 말고 함수의 명칭을 이름만으로 외부로 공개할 필요가 있다.

이때 사용하는 지시자가 바로 extern "C"이다. 함수앞에 이 지시자를 사용하면 오버로딩 기능은 사용할 수 없지만 다른 언어와 링크될 수 있는 범용 함수를 만들 수 있다. 이렇게 만들어진 함수는 C에서도 호출 가능하다. 지금 당장 여러분들이 이 지시자를 사용할 일은 없겠지만 차후에 윈도우즈 환경에서 DLL을 만들거나 할 때는 이 지시자가 필요할 것이다. 윈도우즈의 DLL 포맷은 이름으로 함수를 찾기 때문에 오버로딩을 지원하지 않는다.

extern "C" 지시자는 C++이 C나 다른 언어를 위해 이름 장식을 하지 않도록 할 때 사용하기도 하지만 반대의 경우에도 이 지시자가 필요하다. 즉, 이미 C로 만들어진 함수를 C++에서 호출하고자 할 때 이 함수의 원형 앞에 extern "C"가 있어야 한다. 그렇지 않으면 링커가 장식된 이름으로 함수를 찾게 되므로 C의 함수를 제대로 연결하지 못한다. C++로만 함수를 작성한다면 굳이 이 지시자를 사용할 필요가 없다.


WINAPI의 의미(__stdcall)
 
function calling에 있어서 convention이 여럿 있게 마련인데요.

그런 게 있는 이유는 언어별로 다 틀리기 때문이죠.

따라서 님이 만약 어떤 dll을 만들어서 이런 저런 s/w에서 쓸 수 있게 하려면,
basic용, c++용, pascal용 등등을 따로 만들어야겠죠?

그렇게 되면 시간 및 비용이 많이 들게 됩니다.

그리고 dll의 본래 취지에서 벗어나게 되죠.

그래서 WINAPI라게 선언을 하는 거죠.

WINAPI를 찾아보시면..

#define WINAPI __stdcall

라고 되어 있는데요..

__stdcall 이라는 keyword로 선언된 함수는 함수가 return되기 전에 stack을 정리하게 됩니다.

좋은 비교 대상으로 __cdecl이라는 keyword가 있는데요..

__cdecl 이라는 keyword로 선언된 함수는 함수가 return된 후에 stack을 정리하게 됩니다. ( c/c++ )

따라서 __cdecl keyword를 사용하게 되면 stack을 정리하는 code가 필요하게 되죠.

이런 이유로, CALLBACK macro나 PASCAL macro도 찾아보면 __stdcall 이죠.
( 문자 그대로 standard calling 입니다. )


이외에도 차이점은 있을 거라고 생각됩니다만, 어쨌거나 님께서 질문하신 WINAPI,
즉 __stdcall의 용도는 서로 다른 compiler로 제작된 binary에 공통된 interface를
( - 정확히는 pascal 형식으로 알고 있습니다.. ) 제공하기 위한 것에 목적이 있다고 보시면 됩니다...

참고가 되셨길..


- 네이버에서 greathjm 님의 글을 발췌했습니다.


main.cpp 와 hello.c 그리고 hello.h 로 이루어진 화일이 있다. hello.c 에는 void 형의 hello() 란 함수가 정의되어 있고, hello.h 에는 hello() 함수의 원형이 선언되어 있다.
main.cpp 함수에서 이 hello 함수를 사용하길 원한다면, 언뜻 아래와 같은 방법으로 컴파일 하려고 시도할 것이다.

gcc -c hello.o
g++ -o hello main.cpp hellow.o

그러나 이를 컴파일 해보면 아래와 같은 에러가 발생한다.

/tmp/ccr6TqS7.o: In function `main':
/tmp/ccr6TqS7.o(.text+0x7): undefined reference to `hello(void)'

이런 일이 발생하는 이유는 C와 C++의 각 컴파일러가 알고 있는 함수 이름이 서로 틀릴수 있다는 데에서 발생한다. C++ 에서는 같은 함수이름이 overloading 를 통해서 여러개 존재할수 있지만 C에서는 오직 하나만 존재할수 있음으로, 실지 사용할 함수명을 리턴값과 인자값 까지 명시 해서 컴파일러에게 정확하게 알려줄 필요가 있다.
이럴경우 extern "C" {함수} 를 사용하면 된다.

extern "C" { void hello(); }

예제 코드
main.cpp
extern "C" { void hello(); }
int main()
{
hello();
}
hello.c
#include "hello.h"
#include <stdio.h>

void hello()
{
printf("hello world!!");

출처 : joinc
}

'Linux > Programming in UNIX env.' 카테고리의 다른 글

multi-core에서 spinlock 없이 프로그래밍 하기  (0) 2010.07.17
name mangling  (0) 2010.05.18
Linux Kernel Linked List Explained  (0) 2010.03.01
alarm 과 sleep 그리고 usleep  (0) 2009.08.24

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

,
List:       linux-arm-kernel
Subject: Re: (no subject)
From: Russell King - ARM Linux <linux () arm ! linux ! org ! uk>
Date: 2006-09-25 14:01:24
Message-ID: 20060925140123.GB6168 () flint ! arm ! linux ! org ! uk

[Download message RAW]


On Mon, Sep 25, 2006 at 03:40:23PM +0200, Tak?cs ?ron wrote:
> Hi,
>
> I want to compile a 2.6.18 kernel, but I get the following error:
>
> CHK include/linux/compile.h
> LDS arch/arm/kernel/vmlinux.lds
> CC mm/page_alloc.o
> /tmp/ccHUQAqC.s: Assembler messages:
> /tmp/ccHUQAqC.s:5477: Error: .err encountered
> make[1]: *** [mm/page_alloc.o] Error 1
> make: *** [mm] Error 2
>
> What could be the problem here? Thanks for your help!
>
> ?ron Tak?cs
>
> p.s.: I am using gcc-3.3.2 and binutil 2.17.

gcc 3.4.3 is the minimum gcc version for the kernel.

-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php

'Linux > Trial and Error' 카테고리의 다른 글

Yum 의 의존성.  (0) 2009.05.25
i18n????  (0) 2009.05.04
~/.config/user-dirs.dirs  (0) 2009.05.04

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

,

CSCOPE settings for vim

Linux 2010. 4. 2. 18:19
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" CSCOPE settings for vim
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" This file contains some boilerplate settings for vim's cscope interface,
" plus some keyboard mappings that I've found useful.
"
" USAGE:
" -- vim 6: Stick this file in your ~/.vim/plugin directory (or in a
" 'plugin' directory in some other directory that is in your
" 'runtimepath'.
"
" -- vim 5: Stick this file somewhere and 'source cscope.vim' it from
" your ~/.vimrc file (or cut and paste it into your .vimrc).
"
" NOTE:
" These key maps use multiple keystrokes (2 or 3 keys). If you find that vim
" keeps timing you out before you can complete them, try changing your timeout
" settings, as explained below.
"
" Happy cscoping,
"
" Jason Duell jduell@alumni.princeton.edu 2002/3/7
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""


" This tests to see if vim was configured with the '--enable-cscope' option
" when it was compiled. If it wasn't, time to recompile vim...
if has("cscope")

""""""""""""" Standard cscope/vim boilerplate

" use both cscope and ctag for 'ctrl-]', ':ta', and 'vim -t'
set cscopetag

" check cscope for definition of a symbol before checking ctags: set to 1
" if you want the reverse search order.
set csto=0

" add any cscope database in current directory
if filereadable("cscope.out")
cs add cscope.out
" else add the database pointed to by environment variable
elseif $CSCOPE_DB != ""
cs add $CSCOPE_DB
endif

" show msg when any other cscope db added
set cscopeverbose


""""""""""""" My cscope/vim key mappings
"
" The following maps all invoke one of the following cscope search types:
"
" 's' symbol: find all references to the token under cursor
" 'g' global: find global definition(s) of the token under cursor
" 'c' calls: find all calls to the function name under cursor
" 't' text: find all instances of the text under cursor
" 'e' egrep: egrep search for the word under cursor
" 'f' file: open the filename under cursor
" 'i' includes: find files that include the filename under cursor
" 'd' called: find functions that function under cursor calls
"
" Below are three sets of the maps: one set that just jumps to your
" search result, one that splits the existing vim window horizontally and
" diplays your search result in the new window, and one that does the same
" thing, but does a vertical split instead (vim 6 only).
"
" I've used CTRL-\ and CTRL-@ as the starting keys for these maps, as it's
" unlikely that you need their default mappings (CTRL-\'s default use is
" as part of CTRL-\ CTRL-N typemap, which basically just does the same
" thing as hitting 'escape': CTRL-@ doesn't seem to have any default use).
" If you don't like using 'CTRL-@' or CTRL-\, , you can change some or all
" of these maps to use other keys. One likely candidate is 'CTRL-_'
" (which also maps to CTRL-/, which is easier to type). By default it is
" used to switch between Hebrew and English keyboard mode.
"
" All of the maps involving the <cfile> macro use '^<cfile>$': this is so
" that searches over '#include <time.h>" return only references to
" 'time.h', and not 'sys/time.h', etc. (by default cscope will return all
" files that contain 'time.h' as part of their name).


" To do the first type of search, hit 'CTRL-\', followed by one of the
" cscope search types above (s,g,c,t,e,f,i,d). The result of your cscope
" search will be displayed in the current window. You can use CTRL-T to
" go back to where you were before the search.
"

nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>
nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR>
nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR>
nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR>
nmap <C-\>e :cs find e <C-R>=expand("<cword>")<CR><CR>
nmap <C-\>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
nmap <C-\>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
nmap <C-\>d :cs find d <C-R>=expand("<cword>")<CR><CR>


" Using 'CTRL-spacebar' (intepreted as CTRL-@ by vim) then a search type
" makes the vim window split horizontally, with search result displayed in
" the new window.
"
" (Note: earlier versions of vim may not have the :scs command, but it
" can be simulated roughly via:
" nmap <C-@>s <C-W><C-S> :cs find s <C-R>=expand("<cword>")<CR><CR>

nmap <C-@>s :scs find s <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>g :scs find g <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>c :scs find c <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>t :scs find t <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>e :scs find e <C-R>=expand("<cword>")<CR><CR>
nmap <C-@>f :scs find f <C-R>=expand("<cfile>")<CR><CR>
nmap <C-@>i :scs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
nmap <C-@>d :scs find d <C-R>=expand("<cword>")<CR><CR>


" Hitting CTRL-space *twice* before the search type does a vertical
" split instead of a horizontal one (vim 6 and up only)
"
" (Note: you may wish to put a 'set splitright' in your .vimrc
" if you prefer the new window on the right instead of the left

nmap <C-@><C-@>s :vert scs find s <C-R>=expand("<cword>")<CR><CR>
nmap <C-@><C-@>g :vert scs find g <C-R>=expand("<cword>")<CR><CR>
nmap <C-@><C-@>c :vert scs find c <C-R>=expand("<cword>")<CR><CR>
nmap <C-@><C-@>t :vert scs find t <C-R>=expand("<cword>")<CR><CR>
nmap <C-@><C-@>e :vert scs find e <C-R>=expand("<cword>")<CR><CR>
nmap <C-@><C-@>f :vert scs find f <C-R>=expand("<cfile>")<CR><CR>
nmap <C-@><C-@>i :vert scs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
nmap <C-@><C-@>d :vert scs find d <C-R>=expand("<cword>")<CR><CR>


""""""""""""" key map timeouts
"
" By default Vim will only wait 1 second for each keystroke in a mapping.
" You may find that too short with the above typemaps. If so, you should
" either turn off mapping timeouts via 'notimeout'.
"
"set notimeout
"
" Or, you can keep timeouts, by uncommenting the timeoutlen line below,
" with your own personal favorite value (in milliseconds):
"
"set timeoutlen=4000
"
" Either way, since mapping timeout settings by default also set the
" timeouts for multicharacter 'keys codes' (like <F1>), you should also
" set ttimeout and ttimeoutlen: otherwise, you will experience strange
" delays as vim waits for a keystroke after you hit ESC (it will be
" waiting to see if the ESC is actually part of a key code like <F1>).
"
"set ttimeout
"
" personally, I find a tenth of a second to work well for key code
" timeouts. If you experience problems and have a slow terminal or network
" connection, set it higher. If you don't set ttimeoutlen, the value for
" timeoutlent (default: 1000 = 1 second, which is sluggish) is used.
"
"set ttimeoutlen=100

endif


'Linux' 카테고리의 다른 글

리눅스 원격접속 VNC 서버 사용하기  (0) 2010.07.15
리눅스에서 시디이미지 만들기  (0) 2010.07.08
BogoMIPS  (0) 2009.12.27
스크립트(awk,sed,vi,gcc..)  (0) 2009.10.24
고급 타이머  (0) 2009.07.30

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

,
typedef struct {  int counter;} atomic_t;

형식은 리소스 카운터 용도로 사용한다.ATOMIC_INIT() 매크로로 값을 초기화한다.
64비트형을 따로 쓰는 아키텍처에서는 ATOMIC64_INIT()을 쓴다.
일부 아키텍처에서는 atomic_t와 atomic64_t 두 가지 형식을 제공하지만, x86 형식에서는
atomic_t만 지원한다. 대신 atomic_32.h와 atomic_64.h 헤더 파일로 나누어 32비트와 64비트
커널에 따라 정의를 달리 쓰지만, 어느 쪽이든 32비트 부호형(32bit signed) 정수로 선언하므로
사실상 같다.(i386 아키텍처는 현재 2.6.20 이상의 커널에서 쓰이지 않으며 x86으로 변경되었다. i386은 현재 심볼 링크 형태로만 그 흔적이 남아있다)
atomic_read(), atomic_set(), atomic_inc(), atomic_dec() 등의 다양한 매크로로 값을 조작한다.
접미어 _t를 붙이는 형식은 데이터 추상화(ADT, Abstract Data Type)를 위해 C 언어에서 자주 쓰는 방법이다.

typedef int atomic_t;정도로 써도 될텐데, 그렇게 하지 않는다.
struct으로 감싼 것과 아닌 것의 차이가 뭘까? typedef int로 선언하면 ++ 연산을 이용할 수 있고,
이는 설계자가 의도하지 않은 코드 작성이 된다.
이를 막고 안전하게 atomic_inc() 등으로 접근하라는 의도가 코드에 포함된 설계다.
i++이나 atomic_int(i)나 차이가 뭐가 크다고? 생각할지도 모르겠다. i++은 i = i + 1의 형태이고
 이는 값 읽어오기, 연산, 저장의 과정으로 이뤄진다.
atomic_inc()의 구현은 각 아키텍처별 어셈블리어로 되어 있다. 어셈블리 명령 단 하나로
구현하였다. CPU를 어떻게 쪼개든 어셈블리 명령 하나(보통 incl 명령)는 반드시 실행된다.
이는 CPU에서 실행할 수 있는 최소 단위(atomic) 명령이기 때문이다.

커널 2.6.22 이전까지의 코드나 2.6.22 이후 일부 아키텍처(xtensa 등)에서는 atomic_t를
volatile int로 선언한다.
typedef struct {
      volatile int counter;
}atomic_t;
이전까지는 volatile로 선언했으나 2.6.23 이후 버전은 int로만 선언한다.<Professional Linux Kernel Architecter>(Wrox, 2008)에서도 volatile int로 소개하는데, 이는 틀린 내용이다.
(책은 커널 2.6.24를 기준으로 설명한다지만, 해당 부분의 원고는 아마도 2.6.22 이하 버전일 때
작성했다고 추측한다)
volatile int가 int로 바뀐 이유는 무엇일까?
atomic_read() 동작 방식 때문이다. 컴파일러 최적화를 이용하면 레지스터에 저장된 값을
재사용하게 되고, 이는 레지스터에서 어떤 값이든 읽을 수 있음을 뜻한다.
루프를 처리하는 데 atomic_t 형식의 값을 외부 코드에서 변경하게 되면 코드는 엉망이 되고
만다. barrier()를 써서 이런 버그를 피할 수 있지만, 이는 루프에서 사용하는 모든 레지스터 값을
 다시 읽어와야 한다(reload). 이는 성능 측면에서 바람직하지 않으며, 이런 동작이 모든 아키텍처
에서 꼭 필요한 것도 아니다. 루프 안에서 barrier()를 써야 한다는 불필요한 의존성도 제거하고,
atomic_t 루프 외부에서 값을 변경할 수 있다고 생각해 버리자는 의도.



출처 : http://stnzone.com/gboard/bbs/board.php?bo_table=rss&wr_id=12696

'Linux > Kernel Programming' 카테고리의 다른 글

__builtin_constant_p  (0) 2010.08.31
asmlinkage, __attribute__  (0) 2010.03.02

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

,
리눅스 커널 코드를 분석하다보면 분명 작성된 언어는 C언어인데 처음보는 낯선 키워드등 분석이 잘 안되는 경우가 있습니다.

이는 메크로함수나 함수 포인터 등 #define으로 새로 정의되어 사용되는 키워드들 때문에 가독성이 떨어지기 때문입니다.
 
그 중 한 예로 리눅스의 스케줄링을 담당하는 schedule() 함수를 보면,

asmlinkage void __sched schedule(void)

이렇게 함수가 구성되어 있습니다.

이를 하나씩 살펴 보겠습니다.

asmlinkage는 어셈블리와 링크가 가능하다는 뜻 즉, 어셈블리어로 짜여진 코드에서 이 함수를 호출 할 수 있다는 뜻입니다.

__sched 이 키워드는 linux/include/linux/sched.h에 정의 되어 있습니다.

#define __sched __attribute__((__section__(".sched.text")))

다음과 같은 형태로 정의되어 있는데 ( 점점 더 복잡하게 되는 것 같습니다. )

__attribute__ 는 윈도우의 #pragma 와 비슷한 것으로 c의 표준은 아닙니다.

#pragma는 cl컴파일러에서 작동하며, __attribute__는 gcc컴파일러에서 작동하며 사용되는 용도는 비슷합니다.

__attribute__ 키워드의 대표적인 예로는

typedef struct
{
char a;
int b;
}asdf;

이런 구조체가 있을때 sizeof(asdf) 라고 하면 결과는 8이 나옵니다.

int a = sizeof(char)+sizeof(int);

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

라고 했을 때 결과는 5가 나옵니다.

이는 packing(natural boundary)때문에 일어나는 현상입니다.

이를 정확한 값을 얻고 싶다면

typedef struct
{
char a;
int b;
}__attribute__(packed) asdf;

이렇게 하면 정확한 값이 나옵니다.  (윈도우에서는 #pragma pack(1) 라고 하면 됩니다.)

이 외에 __attribute__(x) 안에 들어갈 수 있는 것은 여러가지가 있습니다.

그럼 다시 돌아와서 살펴 보도록 하겠습니다.
 
__attribute__((__section__(".sched.text")))

이 키워드는 ".sched.text라는 섹션을 만들어서 그 섹션안에 schedule()함수의 내용을 넣어라" 라는 뜻이 됩니다.

이렇게 함으로써 얻을 수 있는 이점으로 섹션은 build 타임에 relocation이 가능하게 되는데,
 
초기화 역할을하는 특정 섹션을 만들어 그 안에 넣은 후 사용하다가 더이상 필요 없을 시 이 섹션을 날려 버립니다.

이런 방식으로 메모리 공간을 아낄 수 있습니다.
 
리눅스에서는 보통 커널공간이 1GB이고, 유저공간이 3GB입니다.(설정에 따라 달라질 수는 있습니다.)

만약 task_struct등 커널 오브젝트의 증가나 컨택스트 스위칭 등 커널 자원을 소비하는 작업을 할 때 커널 공간이 부족하다면 시스템 운영상 곤란 할 것입니다.

실제로 start_kernel() 함수도

asmlinkage void __init start_kernel(void)

#define __init __attribute__ ((__section__ (".text.init")))

이렇게 정의되어 있습니다.

즉 .text.init섹션에 start_kernel함수의 내용을 넣었다가 초가화가 끝나고 나면 이 섹션을 날리면서 메모리 공간을 절약합니다.

이렇게 리눅스에서는 커널공간을 효율적으로 관리하고 있습니다.

관련된 pdf파일을 첨부합니다. 관련된 내용은 아주 조금 있지만 전반적으로 읽어두면 커널을 이해하는데 조금 도움이 될듯합니다.




출처 : http://teamcrak.tistory.com/130

'Linux > Kernel Programming' 카테고리의 다른 글

__builtin_constant_p  (0) 2010.08.31
atomic_t in Linux Kernel  (0) 2010.03.06

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

,

Linux Kernel Linked List Explained


(Note: This is a working copy, last updated on April 5th, 2005. Feel free to email your comments.)


Introduction:

Linux kernel is mostly written in the C language. Unlike many other languages C does not have a good collection of data structures built into it or supported by a collection of standard libraries. Therefore, you're probably excited to hear that you can borrow a good implementation of a circularly-linked list in C from the Linux kernel source tree.

The file include/linux/list.h in the source tree implements a type oblivious, easy-to-use, circularly-linked list in the C language. The implementation is efficient and portable-- otherwise it would not have made it into the kernel. Whenever someone needs a list in the Linux kernel they rely on this implementation to strung up any data structure they have. With very little modifications (removing hardware prefetching of list items) we can also use this list in our applications. A usable version of this file is available here for download.

Some of the advantages of using this list are:
  1. Type Oblivious:
    This list can be used to strung up any data structure you have in mind.
  2. Portable:
    Though I haven't tried in every platform it is safe to assume the list implementation is very portable. Otherwise it would not have made it into the kernel source tree.
  3. Easy to Use:
    Since the list is type oblivious same functions are used to initialize, access, and traverse any list of items strung together using this list implementation.
  4. Readable:
    The macros and inlined functions of the list implementation makes the resulting code very elegant and readable.
  5. Saves Time:
    Stops you from reinventing the wheel. Using the list really saves a lot of debugging time and repetitively creating lists for every data structure you need to link.
Linux implementation of the linked list is different from the many linked list implementations you might have seen. Usually a linked list contains the items that are to be linked. For example:
struct my_list{
void *myitem;
struct my_list *next;
struct my_list *prev;
};
The kernel's implementation of linked list gives the illusion that the list is contained in the items it links! For example, if you were to create a linked list of struct my_cool_list you would do the following:
struct my_cool_list{
struct list_head list; /* kernel's list structure */
int my_cool_data;
void* my_cool_void;
};
Couple of things to note here:
  1. List is inside the data item you want to link together.
  2. You can put struct list_head anywhere in your structure.
  3. You can name struct list_head variable anything you wish.
  4. You can have multiple lists!
So for example, the declaration below is also a valid one:
struct todo_tasks{
char *task_name;
unsigned int name_len;
short int status;

int sub_tasks;

int subtasks_completed;
struct list_head completed_subtasks;/* list structure */

int subtasks_waiting;
struct list_head waiting_subtasks; /* another list of same or different items! */

struct list_head todo_list; /* list of todo_tasks */
};
Here are some examples of such lists from the kernel: While we are at this, kernel's list structure is declared as follows in include/linux/list.h:
struct list_head{
struct list_head *next;
struct list_head *prev;
}
Having said that this is probably a good time to delve into the details. First let us see how we can use this data structure in our programs. Then, we will see how the data structure actually works.

Using the List:

I think the best way to get familiar with the list functions is to simply scan the file for them. The file is well commented so there should not be any trouble understanding what is available to a user.

Here is an example of creating, adding, deleting, and traversing the list. You can download the source code here.
#include <stdio.h>
#include <stdlib.h>

#include "list.h"


struct kool_list{
int to;
struct list_head list;
int from;
};

int main(int argc, char **argv){

struct kool_list *tmp;
struct list_head *pos, *q;
unsigned int i;

struct kool_list mylist;
INIT_LIST_HEAD(&mylist.list);
/* or you could have declared this with the following macro
* LIST_HEAD(mylist); which declares and initializes the list
*/

/* adding elements to mylist */
for(i=5; i!=0; --i){
tmp= (struct kool_list *)malloc(sizeof(struct kool_list));

/* INIT_LIST_HEAD(&tmp->list);
*
* this initializes a dynamically allocated list_head. we
* you can omit this if subsequent call is add_list() or
* anything along that line because the next, prev
* fields get initialized in those functions.
*/
printf("enter to and from:");
scanf("%d %d", &tmp->to, &tmp->from);

/* add the new item 'tmp' to the list of items in mylist */
list_add(&(tmp->list), &(mylist.list));
/* you can also use list_add_tail() which adds new items to
* the tail end of the list
*/
}
printf("\n");


/* now you have a circularly linked list of items of type struct kool_list.
* now let us go through the items and print them out
*/


/* list_for_each() is a macro for a for loop.
* first parameter is used as the counter in for loop. in other words, inside the
* loop it points to the current item's list_head.
* second parameter is the pointer to the list. it is not manipulated by the macro.
*/
printf("traversing the list using list_for_each()\n");
list_for_each(pos, &mylist.list){

/* at this point: pos->next points to the next item's 'list' variable and
* pos->prev points to the previous item's 'list' variable. Here item is
* of type struct kool_list. But we need to access the item itself not the
* variable 'list' in the item! macro list_entry() does just that. See "How
* does this work?" below for an explanation of how this is done.
*/
tmp= list_entry(pos, struct kool_list, list);

/* given a pointer to struct list_head, type of data structure it is part of,
* and it's name (struct list_head's name in the data structure) it returns a
* pointer to the data structure in which the pointer is part of.
* For example, in the above line list_entry() will return a pointer to the
* struct kool_list item it is embedded in!
*/

printf("to= %d from= %d\n", tmp->to, tmp->from);

}
printf("\n");
/* since this is a circularly linked list. you can traverse the list in reverse order
* as well. all you need to do is replace 'list_for_each' with 'list_for_each_prev'
* everything else remain the same!
*
* Also you can traverse the list using list_for_each_entry() to iterate over a given
* type of entries. For example:
*/
printf("traversing the list using list_for_each_entry()\n");
list_for_each_entry(tmp, &mylist.list, list)
printf("to= %d from= %d\n", tmp->to, tmp->from);
printf("\n");


/* now let's be good and free the kool_list items. since we will be removing items
* off the list using list_del() we need to use a safer version of the list_for_each()
* macro aptly named list_for_each_safe(). Note that you MUST use this macro if the loop
* involves deletions of items (or moving items from one list to another).
*/
printf("deleting the list using list_for_each_safe()\n");
list_for_each_safe(pos, q, &mylist.list){
tmp= list_entry(pos, struct kool_list, list);
printf("freeing item to= %d from= %d\n", tmp->to, tmp->from);
list_del(pos);
free(tmp);
}

return 0;
}


How Does This Work?

Well most of the implementation is quite trivial but finesse. The finesse relies on the fact that somehow we can obtain the address of an item that contains the list (struct list_head list) given the pointer to the list. This trick is done by the list_entry() macro as we saw above. Let us now understand what it does.
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
Macro expansion for the above example is as follows:
	((struct kool_list *)((char *)(pos) - (unsigned long)(&((struct kool_list *)0)->list)))
This is what confuses most people but it is quite simple and a well-known technique (See Question 2.14). Given a pointer to struct list_head in a data structure, macro list_entry() simply computes the pointer of the data structure. To achieve this we need to figure out where in the data structure the list_head is (offset of list_head). Then, simply deduct the list_head's offset from the actual pointer passed to the macro.

Now the question is how can we compute the offset of an element in a structure? Suppose you have a data structure struct foo_bar and you want to find the offset of element boo in it, this is how you do it:
(unsigned long)(&((struct foo_bar *)0)->boo)
Take memory address 0, and cast it to whatever the type of structure you have-- in this case struct foo_bar. Then, take the address of the member you're interested in. This gives the offset of the member within the structure. Since we already know the absolute memory address of this element for a particular instance of the structure (for example, by way of pos) deducting this offset points us to the address of the structure itself. That's all there is to it. To get a better handle on this I suggest you play around with this piece of code.
#include <stdio.h>
#include <stdlib.h>

struct foobar{
unsigned int foo;
char bar;
char boo;
};

int main(int argc, char** argv){

struct foobar tmp;

printf("address of &tmp is= %p\n\n", &tmp);
printf("address of tmp->foo= %p \t offset of tmp->foo= %lu\n", &tmp.foo, (unsigned long) &((struct foobar *)0)->foo);
printf("address of tmp->bar= %p \t offset of tmp->bar= %lu\n", &tmp.bar, (unsigned long) &((struct foobar *)0)->bar);
printf("address of tmp->boo= %p \t offset of tmp->boo= %lu\n\n", &tmp.boo, (unsigned long) &((struct foobar *)0)->boo);

printf("computed address of &tmp using:\n");
printf("\taddress and offset of tmp->foo= %p\n",
(struct foobar *) (((char *) &tmp.foo) - ((unsigned long) &((struct foobar *)0)->foo)));
printf("\taddress and offset of tmp->bar= %p\n",
(struct foobar *) (((char *) &tmp.bar) - ((unsigned long) &((struct foobar *)0)->bar)));
printf("\taddress and offset of tmp->boo= %p\n",
(struct foobar *) (((char *) &tmp.boo) - ((unsigned long) &((struct foobar *)0)->boo)));

return 0;
}
Output from this code is:
address of &tmp is= 0xbfffed00

address of tmp->foo= 0xbfffed00 offset of tmp->foo= 0
address of tmp->bar= 0xbfffed04 offset of tmp->bar= 4
address of tmp->boo= 0xbfffed05 offset of tmp->boo= 5

computed address of &tmp using:
address and offset of tmp->foo= 0xbfffed00
address and offset of tmp->bar= 0xbfffed00
address and offset of tmp->boo= 0xbfffed00

See Also

  • Please also have a look at the hash table that uses the linked list.
  • /sutff/src/ for more source code

TODO

  • Figure to explain list_entry() better
  • Post the C Data Structures Library (CDSL) that contains hashtables, maps etc. for peer review. Think of it as the Java.Util for C. Clean syntax, prepackaged data structures to make your C life easy!






출처 : http://isis.poly.edu/kulesh/stuff/src/klist/

'Linux > Programming in UNIX env.' 카테고리의 다른 글

multi-core에서 spinlock 없이 프로그래밍 하기  (0) 2010.07.17
name mangling  (0) 2010.05.18
extern "C"  (0) 2010.05.18
alarm 과 sleep 그리고 usleep  (0) 2009.08.24

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

,

BogoMIPS

Linux 2009. 12. 27. 08:10

안녕하세요

우선 cpu 성능의 척도가 되는 Bogomips 를 설명하기 전에 간략하게 PIT(Programmable Interval Timer, 프로그래밍 가능한 구간 타이머) 를 알아보겠습니다.

PIT 는 0x40 - 0x43 포트로 프로그래밍이 가능합니다. 즉 100Hz 로 프로그래밍 해놓으면 10ms 마다 한번씩 IRQ에 타이머 인터럽트가 발생하게 됩니다.

8254 칩의 내부 발진기 주파수는 1.193180  인가 뭐 그럴겁니다 ㅡ.ㅡ; amd 의 것은 다르다고 하지만 어쨌든.. 리눅스에선

#define CLOCK_TICK_RATE 1193180 입니다

즉 1초에 1193180번 왔다갔다 할겁니다.

그래서 0x40 포트에 1193180 을 넣어버리면 1초마다 IRQ0이 발생하게 됩니다

근데 이렇게 1초마다 인터럽트가 발생하게 두면 너무 느리겠죠? 아마 이렇게 리눅스를 프로그래밍하면 context-switching 하는게 눈에 보일거고 사실 그전에 돌아가지도 않겠죠...

그래서 여기에 100을 나눈 숫자를 포트에 집어넣으면

1/100 초마다 IRQ0 이 발생됩니다.

리눅스에선 IRQ0 이 발생하면 do_timer() 가 실행되는데 간단하게 소스를 보면

volatile long jiffies;

do_timer()
{
   jiffies++;
}

여기서 알수있듯이 인터럽트가 발생할때마다
jiffies 값이 올라갑니다. 이 jiffies 값을 가지고 알람이나 스케쥴링 등등 많은것을 합니다. 매우 중요한 변수지요

이제 bogomips 를 재는 소스를 분석하기 전에
보고밉스가 무엇인가 부터 알고 넘어가도록 합시다

보고밉스는 linus 가 생각해낸 cpu 성능의 척도 입니다. 즉
1 tick(즉 jiffies 가 1 늘어난 동안) 동안 수행 가능한 명령어수 정도로 알고 계시면 됩니다.

아마 리눅스 부팅할때 Calibrating delay loop... 450.210 BogoMIPS! 이렇게 나온걸 보신적 있으실 겁니다. 이 수치가 높을수록 (절대적인 수치는 아니지만) 좋습니다.

커널이 시작해서 여러 초기화를 한후에
sti 명령어를 주어서 인터럽트가 발생하게끔 합니다. 그러면 타이머 인터럽트가 마구 발생하겠죠? 그리고 나서 calibrate_delay 라는 함수를 호출합니다.

이제 소스를 보도록 하죠

#define LPS_PREC 8

void __init calibrate_delay(void)
{
       unsigned long ticks, loopbit;
       int lps_precision = LPS_PREC;

       loops_per_jiffy = (1<<12);

       printk("Calibrating delay loop... ");
       while (loops_per_jiffy <<= 1) {

               /* wait for "start of" clock tick */
               ticks = jiffies; 현재 틱 수를 저장합니다.
               __delay(loops_per_jiffy); 처음에는 4096(1<<12) 번 무한루프를 돕니다.
               ticks = jiffies - ticks; 방금 저장해둔 틱에서 뺍니다.
               근데 이 코드가 의외로 쉬울수도 있고 어려울수도 있습니다.
               그냥 일반 어플리케이션 개발하던 사람이 보면 당연히 0이 나와야 한다고 생각할지도 모르지만
               10ms 마다 jiffies 가 알게모르게 1씩 올라갑니다.

               여기서 하는짓은 처음 4096번 무한루프를 돌고 한틱이 지나갔나 알아보는 것입니다. 만약 4096번 돌고도
               아직 jiffies - ticks 가 0이면 ( 루프가 도는동안 IRQ0 가 발생하지 않았으면) 2배로 곱해서 다음엔 8192 번 돌고
               또 아니라면 반복합니다.                
               if (ticks)
                       break;
       }


아래 코드는 더욱 정확히 재기 위한것입니다. 리눅스 커널 1.0 에선 이런 코드가 없었는데
       2.4 를 보니 있군요. 이에는 숫자를 2배씩 곱하기 때문에 정확한 숫자를 구할수가 없습니다
       예를들어 6000번이 한계이면 8192 라는 값으로 잡히게 되어 있습니다. 이것을 방지하기 위해서
       
       loops_per_jiffy >>= 1 세밀하게 다시 2로 나누고 (4096 이라고 저장하고..)
       loopbit = loops_per_jiffy;
       while ( lps_precision-- && (loopbit >>= 1) ) {
       4096 + 2048 해서 한틱이 지나갔으면 값을 프린트 하고 아니면
       다시 거기다가 1024 를 더하고 또 아니라면 512 를 더하고 이런식으로 해서 거의 정확하게 구하는 것입니다.
               loops_per_jiffy |= loopbit;
               ticks = jiffies;
               __delay(loops_per_jiffy);
               if (jiffies != ticks)        /* longer than 1 tick */
                       loops_per_jiffy &= ~loopbit;
       }

/* Round the value and print it */        
       printk("%lu.%02lu BogoMIPS\n",
               loops_per_jiffy/(500000/HZ),
               (loops_per_jiffy/(5000/HZ)) % 100);

       여긴 프린트 ㅡ.ㅡ;
}




PC에 내장되어 있는 CPU는 8259에서 IRQ를 받습니다.. 그리고 8259는 8253이라는 클럭 디바이스에 연결되어 있지요..
CLOCK_TICK_RATE가 1193180으로 정의되어 있는 이유는 기본적으로 8253의 칩 클럭이 1.19318MHz이기 때문입니다.
그것을 i386 아키텍쳐의 리눅스는 100Hz Timer로 쓰고 있기 때문에 LATCH를 시키려고 100Hz로 나누어 downcounting을 하는 것을 볼 수 있죠.. 그럼 Latch값이 11931.8가 되고 8253의 frequency는 1193180 / 11931.8 = 100 Hz가 되어 주기가 10ms 가 되는 것을 볼 수 있죠..
따라서 리눅스 커널을 클럭 디바이스로 8253을 쓰지 않는 플랫폼에 포팅한다고 하면 저 수치는 칩 데이타에 근거하여 바뀌어져야 합니다.


출처 : http://www.asmlove.co.kr/Board/Lecture02/44094/page/3

'Linux' 카테고리의 다른 글

리눅스 원격접속 VNC 서버 사용하기  (0) 2010.07.15
리눅스에서 시디이미지 만들기  (0) 2010.07.08
CSCOPE settings for vim  (0) 2010.04.02
스크립트(awk,sed,vi,gcc..)  (0) 2009.10.24
고급 타이머  (0) 2009.07.30

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

,

스크립트


기본적인 특수 문자에 대한 설명

  . (dot) 
      개행문자(\n, newline)를 제외한 모든 문자와 매치된다.
      단, awk에서는 라인을 (\n)만 지정하지 않으므로, 개행문자(\n)와도 매치가 된다.

 * (asterisk)
      매치되는 일반문자, 정규식문자의 숫자에 제한을 가지지 않는다( 0 ~ 무한대 ).

 [...] (brackets)
     괄호안에 있는 모든 문자와 일치하는 문자를 찾는다. 예를 들어 [aE] 라고 하면, a 또는 E 문자를 찾는다.
     - (hyphen)을 사용하게 되면, 특정 문자에서 특정 사이의 모든 문자를 가리키게 된다.
     이 괄호에서는 모든 메타문자들이 자신의 기능을 상실하게 된다. 다만 ^ 와 같은 예외의 기능을 가지는 문자들도 있다.
     ^(caret) 는 괄호 내부에서 !(not)의 기능을 가지고 있다. 예를 들어 [^ ] 는 ' '공백문자가 아닌 모든 문자를 가리키게 된다.

 ^ (caret)
     각 라인의 처음을 가리킨다. 이것은 첫번째 문자가 아니라는 걸 명심해야 한다.
     단지 각 라인의 처음이라는 것이다. awk에서는 개행문자의 다음이 아니다.

 $ (dollar)
     각 라인의 끝을 가리킨다. ^(caret)과 마찬가지로 awk에서는 개행문자 이전이 아니다.

 \{n,m\} (middle brackets)
   
  단일문자, 메타문자가 매치되는 갯수의 영역을 명시한다.
     \{n\} 은 정확하게 n개가 되는 것을 말하며,
     \{n,\} 은 n개 이상,
     \{,m\} 은 m개 이하를 말하며,
     \{n,m\} 은 n개 이상, m개 이하를 말한다.

 \ (back slash)
     다음에 오는 문자가 특수문자(Special Character)임을 알린다.


< Vi >

Replace

: %s/boy/girl/           // edit mode에서 파일 전체행의 boy라는 글짜를 girl이라는 글짜로 바꾸는 작업.

: 5,10s/boy/girl/g      // 5행에서 10행까지 글짜 바꾸는데, 한줄에 두 번 이상 나와도 바꾸는 옵션 g.

: .,20s/boy/girl/         // 현재행 .에서 20행 까지.

: .,$s/boy/girl/           // 현재행 .에서 마지막 행까지.

: .,/man/s/boy/girl/     // 현재행 .에서 다음의 man 이라는 글짜를 포함하는 행까지.


<  SED - a Stream EDitor     >

  sed   [-e script][-f script-file][file...]

기본적인 기능은 ed에서 따 왔으며, 이 기능들은 모두 sed에 적용이 된다. 다만 ed는 대화형 편집기이며,
sed는 스트리밍 편집기이다.
대화형 편집기와 스트리밍 편집기의 차이점은 대화형 편집기는 입력 및 출력이
하나로 이루어지며, 스트리밍 편집기는 하나의 입력이 하나의 출력
을 낸다는 것이다.
\n 을 개행문자로 사용하는 스트리밍 에디터이다.  

 찾기(search), 출력(print),
   
 sed -n '/abd/p' list.txt   :  list.txt 파일을 한줄씩 읽으면서(-n : 읽은 것을 출력하지 않음)  abd 문자를 찾으면 그 줄을 출력(p)한다.

 치환(substitute),
    
sed 's/addrass/address/' list.txt   :  addrass를 address로 바꾼다. 단, 원본파일을 바꾸지 않고 출력을 바꿔서 한다.
     sed 's/addrass/address/' list.txt > list2.txt
     sed 's/\t/\ /'  list.txt            : 탭문자를 엔터로 변환
    sed 's/□□*/□/'  list.txt       : ( *표시: □ 는 공백 문자를 표시한다. ) 위의 구문은 한개이상의 공백문자열을 하나의 공백으로 바꾼다.

 추가(insert)
     scriptfile  -  s/</\ /g
     sed -f scriptfile 1.html  :  < 문자부분에  \n 개행문자를 넣어서 다음줄에 출력하도록 한다.

 삭제(delete)
    
sed '/TD/d' 1.html   :  TD 문자가 포함된 줄을 삭제하여 출력한다.
     sed '/Src/!d' 1.html :  Src 문자가 있는 줄만 지우지 않는다.
     sed '1,2d' 1.html    :  처음 1줄, 2줄을 지운다.
    sed '/^$/d   1.html    : 공백라인을 삭제하는 명령이다

  파일 이름만을 뽑아내는 정규식
     s/^.*\/\([a-zA-Z0-9.]*\)".*$/\1/   : ^는 라인의 맨 처음, .* 아무문자열, \(, \)은 정규표현식을 그룹화, $ 는 라인의 맨 끝.
    ( s;^.*\/\([a-zA-Z0-9.]*\)".*$;\1;)    \1는 그룹화된 첫번째 요소말한다.
                                                                        [a-zA-Z0-9.] 는 알파벳과 숫자 및 .(콤마)를 표현하는 문자(character)를 말한다.
       즉 GF02.jpg와 같은 문자열을 첫번째 그룹화하고 난 다음 라인 전체를 그룹화된 내용으로 바꾸는 것이다. 

  /g     : global을 의미  한줄에 대상문자가 여러개일 때도 처리하도록 한다.

 who | sed -e 's; .*$;;'    :  각 라인의 첫 번째 공백에서부터 마지막까지 삭제하라.

 who | sed -e 's;^.* ;;'     :  각 라인의 처음부터 맨 마지막 공백까지 삭제하라.

 who | sed -e 's;^.*:;;'     :  각 라인의 처음부터  : 문자가 있는 곳(:문자포함)까지 삭제하라.  

 -n 옵션
 sed는 항상 표준 출력에서 입력 받은 각 라인을 나타낸다는 것을 알아냈다. 그러나 때때로 한 파일로부터 몇 개의 라인들을 추출해 내기 위해 sed를 사용하기를 원할 때도 있다. 이러한 경우에 -n옵션을 사용한다. 이 옵션은 사용자가 만약 해야 할 일을 정확히 말해 주지 않는다면 임의의 라인을 프린트하는 것을 원하지 않는다고 sed에게 말한다. 따라서 p명령이 같이 쓰인다. 라인 번호와 라인 번호의 범위를 나타냄으로써 sed를 사용하여 텍스트의 라인들을 선택적으로 프린트할 수 있게 한다. 다음에서 볼 수 있는 바와 같이, 한 파일로부터 첫 번째 두 라인이 프린트되었다.

 $ sed  -n  '1,2p'  intro                 Just print the first 2 lines from intro file.

만약 라인 번호 대신에 슬래시로 에워 싸인 문자열과 함께 p명령이 쓰인다면 sed는 이들 문자들이 포함하고 있는 표준 입력을 통해서 라인들을 프린트하게 된다. 따라서 하나의 파일로부터 처음의 두 라인을 프린트하기 위하여 다음과 같이 사용될 수 있다.

 $ sed  -n  '/UNIX/p'  intro              Just print lines containing UNIX

   sed  '5d'                                   : 라인 5를 삭제
   sed  '/[Tt]est/d'                        : Test 또는 test를 포함하는 모든 라인들을 삭제
   sed  -n  '20,25p'  text                : text로부터 20에서 25까지의 라인들만 프린트
   sed  '1,10s/unix/UNIX/g'  intro    : intro의 처음 10개의 라인들의 unix를 UNIX로 변경
   sed  '/jan/s/-1/-5'                     : jan을 포함하는 모든 라인들 위의 첫 번째 -1을 -5로 변경
   sed  's/...//'  data                      : 각 data라인으로부터 처음 세 개의 문자들을 삭제
   sed  's/...$//'  data                    : 각 데이터 라인으로부터 마지막 3문자들을 삭제
   sed  -n  '1'  text                        : 비 프린트 문자들을 \nn으로 (여기서 nn은 그 문자의 8진수 값임),
                                                    그리고 탭 문자들을 > 로 나타내는 각 텍스트로부터의 모든 라인들을 프린트

awk 명령어


 awk '/west/'  datafile            :  west 라는 글이 있는 줄 출력

 awk '/^north/' datafile           :  north로 시작하는 줄 출력

 awk '/^(no | so)/' datafile     :  no 또는 so 로 시작하는 줄 출력

 awk '{ print  $3, $2 }'  datafile  : datafile 리스트의 세 번째 와 두 번째 필드를 스페이스로 띄어서 출력

 awk '{ print  $3  $2 }'  datafile  : datafile 리스트의 세 번째 와 두 번째 필드를 그냥 붙여서 출력 

 awk '{ print  "Number of fields : " NF} '  datafile  : datafile의 각 줄마다의 필드수를 리턴한다.

 awk '$5 ~ /\.[7-9]+/'  datafile   :  다섯 번째 필드가 마침표 다음엣 7과 9사이 숫자가 하나 이상 나오는 레코드 출력

 awk '$2 !~ /E/ { print $1, $2 }'  datafile  : 두 번째 필드에 E 패턴이 없는 레코드의 첫 번째와 두 번째 필드 출력

 awk '$3 ~  /^Joel/{ print $3 " is a nice guy."} ' datafile  : 세 번째 필드가 Joel로 시작하면 " is a nice guy"와 함께 출력

 awk '$8 ~ /[0-9][0-9]$/ { print $8 }' datafile  : 여덟 번째 필드가 두 개의 숫자이면 그 필드가 출력

 awk '$4 ~ /Chin$/ { print "The price is $" $8 "." }' datafile   : 네 번째 필드가 Chine으로 끝나면 "The price is $" 8번 필드 및 마침표가 출력

 awk -F:  '{ print  $1 } '  datafile     : -F 옵션은 입력 필드를 ':'로 구별.

 awk -F"[ :]" '{ print  $1, $2 } ' datafile   : 입력 필드로  스페이스와 ':'를 필드 구별자로 사용

 awk -f  awk_script.file datafile     :  -f 옵션은 awk 스크립트 파일 사용할 때 씀.

 

 awk '$7 == 5'  datafile        : 7번 필드가 5와 같다면  출력

 awk '$2 == "CT" { print $1, $2 }' datafile    : 2번 필드가 "CT" 문자와 같으면 1, 2 번 필드 출력

 awk '$7 <  5  { print $4, $7}' datafile         : 7번 필드가 5보다 작다면  4번, 7번 필드 출력

 awk '$6 > .9 { print $1, $6}' datafile          : 6번 필드가 .9 보다 크다면  1번, 6번 출력

 awk '$8 > 10 && $8  < 17 ' datafile    

 awk '$2 == "NW" || $1 ~ /south/ { print $1, $2 }' datafile

 

 

 

 

 재지향 출력/입력

     파일 기술자 0 : 표준 입력
     파일 기술자 1 : 표준 출력
     파일 기술자 2 : 표준 에러 출력
     $ kill -1 1234 > killout.txt  2>&1     :  killout.txt에  표준 출력과 에러출력을 같이 하겠다는 의미.
     $ kill -1 1234  1>&2                      :  표준출력을 에러출력으로 하겠다는 의미.

 

  

gcc 컴파일 ( make 파일 )


1. 의존성

     all  :  myapp myapp.1        // 여러 파일을 만들려면 가짜 대상인 all을 사용한다.

     myapp : main.o 2.o 3.o

     main.o : main.c a.h

     2.o : 2.c a.h b.h

     3.o : 3.c b.h c.h

2. 규칙

     all  :  myapp myapp.1       // 아무런 대상 명시 없이 make를 실행하면 기본행동으로  all에 있는 것을 생성한다.

     myapp : main.o 2.o 3.o

            gcc -o myapp main.o 2.o 3.o

     main.o : main.c a.h

            gcc -c main.c

     2.o : 2.c a.h b.h

            gcc -c 2.c

     3.o : 3.c b.h c.h

            gcc -c 3.c

3. 매크로

    매크로 정의 형식 :   MACRO_NAME=value

    사용 형식           :    $(MACRO_NAME) or  ${MACRO_NAME}

        CC=gcc

        INCLUDE=.

        CFLAGS=-g -Wall -ansi

        all  :  myapp myapp.1       

        myapp : main.o 2.o 3.o

            $(CC) -o myapp main.o 2.o 3.o

        main.o : main.c a.h

            $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

        2.o : 2.c a.h b.h

            $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c

        3.o : 3.c b.h c.h

            $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c

4. Clean & Install

        INSTDIR=/usr/local/bin

        clean  :                             // 어떤 의존성도 없으므로 적을 것이 없다.

             -rm main.o 2.o 3.o       // make 명령은 -로 시작하는 명령의 결과를 무시한다. 그래서 make clean은 무조건 성공했다고 간주한다.

        install  :  myapp                // install을 위한 명령을 수행하기 전에  myapp를 먼저 생성해야 한다는 것을 의미

        @if [ -d $(INSTDIR) ]; \    // @로 시작되는 줄은 표준 출력하지 않는다.

                then \

                cp myapp $(INSTDIR); \

                chmod a+x $(INSTDIR)/myapp; \

                chmod og-w $(INSTDIR)/myapp; \

                echo "Installed in $(INSTDIR)"; \

          else \

                echo "Sorry, $(INSTDIR) does not exist"; \

          fi

5. 내장(Built-in) 규칙

        make는 내부에 기본 규칙을 매크로를 사용하여 가지고 있다.

        OUTPUT_OPTION = -o $@           //  $@  현재 대상의 완전한 이름

        COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c

        .c.o :                     // c로 끝나는 파일로부터 o로 끝나는 파일을 만든다는 규칙.

             $(COMPILE.c) $< $(OUTPUT_OPTION)

6. 접미사 규칙

     형식 : .< 소스접미사>.<결과접미사>:

        all : myapp

        .SUFFIXES : .cpp

        .cpp.o :                 // cpp 파일로부터 오브젝트 파일 만들어내는 규칙. (유닉스는 c++파일에 대해 cpp가 아닌 cc로 끝내는 것이 관례)

             $(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<      // -xc++은 gcc에게 소스파일이 C++ 파일임을 말해줌.

                                                                                    //  $< 는 시작하는 파일명 즉 소스 파일명으로 확장되는데,

                                                                                    //                    의존하고 있는 대상보다 최신의 파일 하나.

                                                                                    //  $?  의존하고 있는 현재 대상 중 최신의 것들

                                                                                    //  $*  접미사를 제외한 대상 파일의 이름.

 

 

 

 

 

 

 

'Linux' 카테고리의 다른 글

리눅스 원격접속 VNC 서버 사용하기  (0) 2010.07.15
리눅스에서 시디이미지 만들기  (0) 2010.07.08
CSCOPE settings for vim  (0) 2010.04.02
BogoMIPS  (0) 2009.12.27
고급 타이머  (0) 2009.07.30

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

,
오늘 우연히 http://kldp.org/node/34326 을 읽었다.

여기에서 이야기 하는 부분은 결국

sleep  계열을 쓸때는

signal(SIGALRM,SIG_IGN); 이라고 하라는 말 아닌가. ( sleep 계열 함수가 SIGALARM 과 관련이 있다는..)

하지만 http://kldp.org/node/65158 를 살펴보면

sleep 을 쓸때는 별 문제가 없었고... usleep 을 쓸떄는 문제가 생겨 SIGALRM 을 처리 해주었다고 한다.

옛날 기억이 난다.... 색인기의 부하를 줄여주기 위해 usleep 을 쓰면 core 파일이 없이 세그먼트 폴트 ㅠ.ㅠ

이런 이유였다...

그런데 말이다 sleep 을 대신해서 select 을 쓸수 있다 요렇게 < http://kldp.org/node/45921 참조 >

---------------------------------------------------------
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
select (NULL, NULL, NULL, &timeout);
----------------------------------------------------------

좀더 연구를 해보고 싶지만 점심시간인 관계로 여기서 마친다....


http://kldp.org/node/34349 를 참조하면 nanosleep 을 써도 된다고 나온다.


출처 : http://blog.daum.net/psyoblade/8303006

'Linux > Programming in UNIX env.' 카테고리의 다른 글

multi-core에서 spinlock 없이 프로그래밍 하기  (0) 2010.07.17
name mangling  (0) 2010.05.18
extern "C"  (0) 2010.05.18
Linux Kernel Linked List Explained  (0) 2010.03.01

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

,