이 블로그 검색

2011년 12월 22일 목요일

리눅스 어셈블리 분석 기초


어셈블리 초보자의 입장에서 간단한 c소스를 역어셈블하고 각각의 의미를 알아본다.

다음은 테스트할 간단한 소스이다.

    void Swap(int *parm_a, int *parm_b)
    {
        int temp = 0;       
        temp = *parm_a;
        *parm_a = *parm_b;
        *parm_b = temp;
    }
     void main()
    {
        int a = 5, b = 7;       
        Swap(&a, &b);
    }

2011년 9월 28일 수요일

const 사용

참조: http://drdobbs.com/cpp/231601151?cid=DDJ_nl_upd_2011-09-27_h

헷갈리는 const사용시,
전통적인 const의 사용법과는 좀 틀리지만 알아보기 쉬운 스타일을 소개한 내용이다.
기존에 사용되는 고전적인 방법과 다음에 소개되는 사용 스타일중 어떤것이 더 이해하기 쉬운지는 각자 판단에 맡긴다...

const 사용 style :
1. const 선언을 위해서는 const가 되어야하는것 오른쪽에 const를 위치한다.

예를 들어 const char에 대한 포인터를 선언하는 경우 다음과 같다.

// pointer to const char 
const char a ='a'; 
const char b ='b'; 

// 고전적인 방법
const char* p0=&a; 
p0=&b; //OK 
*p0 ='c'; //Not OK

// 위의 규칙을 적용, 다음처럼 가능.
char const *p; //좀 이해하기 쉬운가?

p=&b; //OK 
*p ='c'; //Not OK

// 위규칙을 적용한 또다른 경우 (문자열)

// 고전적인 방법
 const char* NAME = "Foo"; // NAME=> const char* "Foo"에 대한 포인터

NAME[0] = 'g'; //Not OK 
NAME = "Bar"; //OK

// 위의 규칙을 적용, 다음처럼 가능.
char const* NAME1 = "Foo"; // NAME1=> const char* "Foo"에 대한 포인터 
NAME1[0] = 'g'; //Not OK 
NAME1 = "Bar"; //OK


2. 이선언을 해석하기 위해서는 오른쪽에서 왼쪽으로 가면서 해석한다.

즉, p는 const char에 대한 포인터이다.

참조
char에 대한 const 포인터를 선언하는 경우 다음과 같다.
// const pointer to char 
char a2 = 'a'; 
char b2 = 'b'; 
char* const p2 = &a2; 
p2=&b2; //Not 
OK *p2='c'; //OK
해석 역시, p는 const 포인터 (char에 대한) 로 해석한다.

const pointer to const char* 경우 :
어떤 개발자가 NAME의 내용을 변경못하게 하기 위해,
const char*를 가르키는 포인터 NAME을 다음처럼 선언했다고 가정한다.

const char* NAME = "Foo"; (혹은 앞서 설명된대로.. char const* NAME = "Foo"; )

그래서, 다음처럼 사용될수 없다. 
NAME[0] = 'g'; 

그런데 다음은 가능하다. 이것은 의도했던 상황은 아니다. 
NAME = "Bar"; //"Bar" 역시 const char* 이기 때문. 
이것을 막기 위해서는 상수 char*를 가르키는 포인터 NAME 역시 const로 지정해야한다.

const char* const NAME = "Foo"; //혹은
char const* const NAME = "Foo";

2011년 9월 21일 수요일

protothread

protothred 는 주로 쓰레드 사용의 장점이 필요하지만, 기존 쓰레드 생성시의 부하,
즉 쓰레드별 스택 생성됨으로 인해 메모리 사용양이 문제가 되는 임베디드 시스템등에서
사용할 목적으로 작성되었다. 쓰레드별 스택이 불필요하므로 매우 경량이며
(쓰레드당 2 바이트 메모리만 필요)소스코드의 복잡도 감소등을 장점으로 내세운다.
C 언어로 작성된 몇개의 헤더파일로만 이루어져 있으며 헤더 파일 내부는
C 매크로와 먼저 설명한 switch 구문의 마법을 활용하고 있다.  
일반적인 쓰레드는 커널에 의해 스케쥴링이 수행되지만, protothread는 프로그램 내부에서
각 쓰레드들을 스케쥴링하는 부분을 필요로 한다.

2011년 9월 20일 화요일

protothread : Duff's device

protothread 란 것을 보던중에 아래와 같은 소스를 발견.
그동안 몰랐던 이상야릇한 C언어 switch 구문형태이다~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int counter = 0;
int n = 0;

int somefun()
{
    switch(n){
        case 0: 
   while(1){
     n = 12; 
     case 12:
     if(!(counter == 1000))
         return 0;       
     printf("Threshold reached\n");
     counter = 0;
 }
    }
    n = 0;  
    return 2;
}

int _tmain(int argc, _TCHAR* argv[])
{
 somefun(); // case 0
 somefun(); // case 12
 return 0;
}
Pastie #2495563 linked directly from Pastie.


2011년 7월 4일 월요일

Feeling Note App 출시

취미삼아 간단한 노트앱을 만들어보자며 손댄지 아주 오랬만에 오늘 앱스토어에 등록됨. 

Feeling Home Page

2011년 5월 30일 월요일

sizeWithFont오류


UITextView 기존내용을 사용자가 터치한 경우, 편집상태로 만들면서
커서를 보이면서 화면을 보이게끔 스크롤이 필요하다.
처음부터 터치된 곳까지의 문자열을 계산해서, 그 문장이 차지하는 높이를 구하면
UIScrollView의 scrollRectToVisible 를 이용해서 커서위치를 보이게 만들어주면 된다.

그런데 이 위치, 즉 전체화면에서 현재 터치한 부분의 좌표값을 얻어오는데 문제가 있었다.

일단 첫번째 방법은 NSString 의 sizeWithFont:constrainedToSize:lineBreakMode 사용이다.
대부분의 경우에 이것을 사용하면 되지만, 이상하게도 이것은 아주 긴 내용이거나,
폰트를 변경함에 따라서 정확한 값을 얻을수 없다. 아래의 방법을 이용해서 사용하는
예는 많이 찾아 볼수있었지만 직접 테스트 해보면 정확한 값을 가져오지 못해서 커서가
화면 아래나 위로 숨어버리는 문제가 발생했다.

Data provided by Pastebin.com - Download Raw
  1. CGSize maximumTextViewSize = CGSizeMake(320- 30 ,FLT_MAX);
  2. // 게다가 좌우 여백을 줘야 그나마 비슷하게 구해진다!
  3.  
  4. CGSize expectedLabelSize =[strToCursor  sizeWithFont:self.currentTextView.font
  5.                                                         constrainedToSize:maximumTextViewSize
  6.                                                         lineBreakMode:UILineBreakModeWordWrap];
  7.                                                                                
  8. DLog(@"expectedLabelSize height [%f]", expectedLabelSize.height)//sometimes wrong height !!

두번째는...
이건 뭔 API 를 써야 될찌 감도 안와서 궁리하다가 적용한 원시적인 방법으로(방법이라고 할 수도 없을듯...),
UITextView 의 contentSize.height 가 실제로 텍스트를 가지고 있는 UITextView가 차지하는
높이이므로, 이 높이를 가지고 처리하는 방법이다.

그럴려면 먼저 임시 UITextView를 만들고,내용을 채우고, apple 내부에서 무슨 논리로
처리했는지는 모르겠지만 암튼 텍스트에 맞게 적절히 설정된 높이를 얻어와서 그 좌표를 가지고
스크롤 시켜주면, 커서 위치가 키보드 출력이후에도 사용자에게 보이게끔 잘 설정된다.

Data provided by Pastebin.com - Download Raw
  1. // kinda primitive way - - ;;;
  2. // 전역으로 UITextView변수를 만들고
  3. // gTextViewforHeightCalculate에 텍스트를 붙여넣고, 실제 높이를 구한다.
  4. // API이용해서 정확하게 가져올수만 있다면 이짓을 할필요는 없을건데...
  5.  
  6. CGRect viewFrame = [gTextViewforHeightCalculate frame];    
  7. viewFrame.size.height = CGFLOAT_MAX  ;
  8. gTextViewforHeightCalculate.frame = viewFrame;
  9. gTextViewforHeightCalculate.minNumberOfLines = 1;        
  10. gTextViewforHeightCalculate.returnKeyType = UIReturnKeyDefault;
  11. gTextViewforHeightCalculate.font =[UIFont fontWithName:self.cur_font size: [self.cur_font_size intValue]];
  12.        
  13. //커서 이전까지의 문자열
  14. NSRange tmpRange;      
  15. tmpRange.location= 0;
  16. tmpRange.length =  self.currentTextView.selectedRange.location ;
  17. NSString* strToCursor =  [self.currentTextView.text substringWithRange: tmpRange] ;                    
  18. gTextViewforHeightCalculate.text = strToCursor ;
  19.  
  20. float nHeight = gTextViewforHeightCalculate.contentSize.height;
  21. DLog(@"UITextView height [%f]", nHeight);        
  22.  
  23. // 커서 위치만큼 scroll
  24. CGRect cgView;
  25. cgView.origin.x = 0.0;
  26. cgView.origin.y = self.currentTextView.frame.origin.y + nHeight;
  27. cgView.size.width = 320;
  28. cgView.size.height = 40;
  29.        
  30. [self.scrollView scrollRectToVisible:cgView animated:TRUE];

2011년 3월 25일 금요일

Intro to Grand Central Dispatch, Part III: Dispatch Sources



Dispatch Sources

이벤트를 모니터 하기 위한 객체.
이벤트 발생시, 자동으로 dispatch queue상의 특정 block을 실행한다.

이벤트 종류는 다음과 같다(GCD in 10.6.0).

1.Mach port send right state changes.
2.Mach port receive right state changes.
3.External process state change.
4.File descriptor ready for read.
5.File descriptor ready for write.
6.Filesystem node event.
7.POSIX signal.
8.Custom timer.
9.Custom event.
1. Custom event
   dispatch_source_merge_data 함수를 호출하여 자신에게 시그널처리하는 경우 발생한다.
    2가지 종류가 존재 한다.
DISPATCH_SOURCE_TYPE_DATA_ADD
DISPATCH_SOURCE_TYPE_DATA_OR

Custom event 처리 예:
어떠한 작업들의 완료로 진행바를 업데이트하는 작업을 예로 들어보자.

- source 객체를 생성하고(dispatch_source_create),

- 각각의 작업 완료시 호출될 이벤트 핸들러(block/function)을  설정한다
  (dispatch_source_set_event_handler).
  완료 이벤트를 받으면 source객체는 등록된 핸들러(block/function)를  실행해서 진행바를
  업데이트 한다. 이 GUI 업데이트 작업은 main queue에서 처리되어야 하므로 source생성시
  dispatch_get_main_queue를 호출했다.

- 프로그레스바 업데이트를 위한 값을 구하는 것은 글로벌 큐에서 수행된다.
   하나의 작업이 완료될때마다 dispatch_source_merge_data를 호출하고 값으로 1을 전달한다.    
   dispatch_source_merge_data 호출을  하면 source객체의 event handler가 호출된다.
   이벤트 핸들러 에서는 dispatch_source_get_data 함수로 값을 가져올수있다
   (호출하면 값은 0으로 reset됨).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_source_t source
  =  dispatch_source_create
  ( DISPATCH_SOURCE_TYPE_DATA_ADD,
   0,
   0,
   dispatch_get_main_queue());
   
 dispatch_source_set_event_handler(source,
     ^{
         [progressIndicator incrementBy: dispatch_source_get_data(source)];
     });      
//dispatch source는 항상 suspended 상태로 시작되기때문에
//생성후 이벤트 처리를 윈하면 resume시켜야한다.
dispatch_resume(source);
    
dispatch_apply([array count], globalQueue,
     ^(size_t index) {         
         // 이부분에서 인덱스로 구한 데이터에 대한 처리 수행
         // .....
         
         //하나의 작업 완료후 이벤트 발생.
         dispatch_source_merge_data(source, 1);
     } );
Pastie #2500389 linked directly from Pastie.
 
dispatch_source_merge_data 호출을 하면 source객체의 event handler가 호출된다.
만약 여러번의 호출이 발생하면 event handler도 여러번 발생할것이다.

그런데 다음 상황을 고려해보자.
GUI 업데이트 작업은 메인 큐에서 수행되는데 만약 메인큐를 처리하는 메인 쓰레드가 다른 작업등으로 인해
바쁜 경우, 메인큐의 작업은 지연되고있을 것이다.
이때 DISPATCH_SOURCE_TYPE_DATA_ADD 타잎의 source 객체인 경우면, 처리되지 못하고
대기중인 여러 이벤트들은 하나로 통합(coalesce) 된다.

그리고 핸들러에서 사용될 값(dispatch_source_merge_data 호출시 넘기는 unsigned long 값)은 dispatch_source_merge_data 호출 할때 마다 계속 더해진다.

이후 메인 쓰레드가 처리가능시점이 되면 event handler가 한번 호출되면서 그동안 누적된 값이
전달된다. dispatch_source_get_data 호출을 하면 값은 다시 0으로 초기화된다.

즉, 이벤트가 바로바로 처리 안되는 경우, 이벤트를 계속 누적시키지 않고 처리가능
시점에 한번만 처리할수 있게 이벤트를 통합하면서 데이터값은 계속 누적시켜 주는 것이다.

하지만 메인 쓰레드가 여유가 있어서 메인 큐에서 바로 바로 가져와 처리 가능하다면,
이벤트 핸들러는 통합처리없이 그때그때마다 수행될것이다.

이벤트 통합을 원하지 않는다면 dispatch source를 사용할 필요는 없다. dispatch_async 호출을 하면 된다.

dispatch_async 대신 dispatch source를 사용하는 오직 하나의 이유라면 바로 이벤트 통합처리 때문이다.

2. Built-In Events  

기본으로 제공되는 이벤트는 어떤것이 있는지 알아본다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DISPATCH_SOURCE_TYPE_READ : file descriptor를 모니터한다.
 
 dispatch_queue_t globalQueue  =  dispatch_get_global_queue
         (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_source_t stdinSource
      = dispatch_source_create  ( DISPATCH_SOURCE_TYPE_READ,
        STDIN_FILENO, //file descriptors
               0,
        globalQueue );
                                
    dispatch_source_set_event_handler ( stdinSource,
     ^{
      char buf[1024];
         int len = read(STDIN_FILENO, buf, sizeof(buf));
         if(len > 0)
             NSLog(@"Got data from stdin: %.*s", len, buf);
     });
     
    dispatch_resume(stdinSource);
Pastie #2500392 linked directly from Pastie.
    
주의할점은 일기 작업이 완료되었다고 해도 dispatch source가 살아있다면 filer 기술자를 close하면 안된다.
만약 다른 source 객체에서 동일한 파일기술자 번호로 생성이 된다면, 그 source 객체는 원하지 않는
읽기가 발생할것이다.

이경우 dispatch_source_set_cancel_handler 함수를 이용한다.
dispatch_source_set_cancel_handler 호출하면서 파일기술자를 닫는 핸들러(block)를 인자로 주고,
그후 dispatch_source_cancel 함수를 호출하여 cancel 핸들러가 호출되게 할수있다.
그럼 파일 기술자가 close될것이다.

좀더 자세한 사항은 apple man 페이지를 참조한다.

DISPATCH_SOURCE_TYPE_TIMER : 주기적으로 이벤트를 발생시킨다.

이 타잎은 좀 특별하다. handle/mask 인자없이 생성되며, 별도의 dispatch_source_set_timer
함수를 이용해서 타이머를 설정한다.

void dispatch_source_set_timer (  dispatch_source_t source,
                              dispatch_time_t start,
                              uint64_t interval,
                                  uint64_t leeway );

leeway:  타이머 정확성을 지정한다. 0 이라면 지정된 interval 을 최대한 정확하게 지켜달라는 의미.
그외 지정되는 숫자는 그 만큼의 interval  증가도 무방하다는 의미이다.
성능향상 및 파워 소비량을 감소 목적.

온전한 내용을 원하신다면 원문을 참조하기 바랍니다...