if (window.attachEvent) {

    /*IE and Opera*/

    window.attachEvent("onunload", function() {

        /* ......?!@# */

    });

} else if (document.addEventListener) {

    /*Chrome, FireFox*/

    window.onbeforeunload = function() {

        /* ......?!@# */

    };

    /*IE 6, Mobile Safari, Chrome Mobile*/

    window.addEventListener("unload", function() {

        /* ......?!@# */

    }, false);

} else {

    /*etc*/

    document.addEventListener("unload", function() {

        /* ......?!@# */

    }, false);

}


출 처 : http://plibet.blogspot.kr/2013/09/javascript-unload.html




이 두개의 차이를 처음에는 잘 모르죠. 

아래 자세한 영문 설명이 있습니다. (윽 돌 날라온다 ...) 

한글로 설명을 하자면.... 

view는 한개의 object죠. 

viewDidLoad는 view object가 생성된 후 발생하는 이벤트입니다. 메모리에 view가 올라온 다음이라는 것입니다. 

그리고 viewWillAppear는 view가 안보이다가 다시 보이게 되었을때 발생하는 이벤트입니다. 

뷰가 A => B => C 이렇게 네비게이션이 발생할때 

처음에 A => B => C 갈때는 viewDidLoad와 viewWillAppear가 모두 발생합니다. 
C => B => A로 돌아올때는 viewWillAppear만 발생하는 것입니다. 
그다음에 A=>B=>C로 갈때도 viewWillAppear만 발생하는 것입니다. 

생성시점과 보여주는 시점을 분리한 것입니다. 



1) ViewDidLoad - Whenever I'm adding controls to a view that should appear together with the view, right away, I put it in the ViewDidLoad method. Basically this method is called whenever the view was loaded into memory. So for example, if my view is a form with 3 labels, I would add the labels here; the view will never exist without those forms. 

2) ViewWillAppear: I use ViewWillAppear usually just to update the data on the form. So, for the example above, I would use this to actually load the data from my domain into the form. Creation of UIViews is fairly expensive, and you should avoid as much as possible doing that on the ViewWillAppear method, becuase when this gets called, it means that the iPhone is already ready to show the UIView to the user, and anything heavy you do here will impact performance in a very visible manner (like animations being delayed, etc). 

3) ViewDidAppear: Finally, I use the ViewDidAppear to start off new threads to things that would take a long time to execute, like for example doing a webservice call to get extra data for the form above.The good thing is that because the view already exists and is being displayed to the user, you can show a nice "Waiting" message to the user while you get the data.


출처 : http://kapps.co.kr/bbs/board.php?bo_table=m52&wr_id=33

Objective-C 프로그래밍을 경험해 봤다면 어색하게 느껴지지 않겠지만, 그렇지 않은 사람들에게는 IBOutlet과 IBAction이 무슨 역할을 하며 왜 필요한지에 대해서 의아해 할 것이다. 간단해 보이지만, 아이폰 프로그래밍에 있어서 없어서는 안될 중요한 존재이다. 이것들이 없다면 아무리 멋지게 프로그램을 작성하여도 연결선이 끊어진 조이스틱을 두드리는 것과 마찬가지가 되는 것이다.




아웃렛(IBOutlet)
아웃렛은 IBOutlet 키워드를 사용하여 선언하는 인스턴스 변수들이다. 아웃렛이 하는 역할은 정말로 단순하다. 컨트롤러 헤더 파일에 선언한 객체를 인터페이스 빌더가 알아 볼 수 있도록 해준다. xib파일 안의 객체와 연결을 하고자 하는 모든 인스턴스 변수들은 IBOutlet 키워드로 다음과 같은 형식으로 선언이 되어야 한다.

@property (nonatomic, retain) IBOutlet UILabel *newLabel;

아웃렛으로 선언된 인스턴스 변수는 프로젝트가 빌드되면서 가장 먼저 전처리기에서 번역되고, 그것이 xib 파일과 연결된 객체라는 사실이 컴파일러에게 전달된다. 프로젝트의 실행과 관련된 컴파일에서는 아무런 영향을 미치지 않는것이다. 하지만, 이것이 없다면 인터페이스 빌더는 어떻게 객채들과 연결해야 할지 모른체 빌드를 실행하게 될 것이다. 마치 지금 당장 전화를 걸어야 하는 상황에서 어떤 전화기를 가지고 어디로 전화를 해야 할찌 모르는 상황과 비슷하다고 할 수 있다.


액션(IBAction)
액션은 IBOutlet과 마찬가지로 컨트롤러 헤더파일에서 하나의 메소드 형태로 선언되어 그 역할을 하게 된다. IBAction이 선언되면 이 메소드가 액션 메소드라는 것을 인터페이스 빌더에게 알려주게 되며, 컨트롤러를 통해서 호출이 가능해 진다. 액션 메소드는 헤더파일에서 다음과 같은 형식으로 선언된다.

 - (IBAction)newAction:(id)sender;

메소드의 형식을 갖는 IBAction는 void를 리턴 타입으로 가진다. 액션 메소드는 변수값을 리턴하지 않는다는 것이다. 액션 메소드는 하나의 인자값을 갖게 되는데, 이것은 sender라는 이름의 id 타입으로 정의되고, 포인터 값이 전달된다. 이것은 똑같은 액션 메소드를 호출하는데 있어서 어떤 액션을 통해서 메소드를 호출하였는지 구분하는 구분자의 역할을 하게 된다. 만약 버튼이 하나밖에 없는 것 처럼, 액션의 구분이 필요하지 않다면 뒷부분의 '(id)sender' 를 제거하고 작성하면 가능하다. 하지만 버튼이 여러게 있는데 '(id)sender' 를 제거하면 모든 버튼이 동일한 역할만을 하게 될 것이다.


생성자와 변경자를 자동으로 생성해주는 @property를 선언하자!
C++ 나 JAVA같은 객체지향 언어를 다루다 보면, 기능을 모듈화 하고, 데이터를 보호하기 위해 하나의 객체에 생성자와 변경자를 만드는 것을 보게 된다. 생성하는 클래스나 객체가 적을때는 그리 어렵지 않겠지만, 늘어나는 클래스 객체의 getter(생성자 메소드)와 setter(변경자 메소드)를 일일이 생성하는 것은 매우 지루한 일이 될 수 있다. Objective-C에서의 프로퍼티는 이러한 Getter 와 Setter를 자동으로 생성해 주어 개발자의 수고를 조금이나마 덜어준다. 오브젝티브 C의 프로퍼티에는 몇가지 속성들이 있는데 retain, nonatomic 의 두가지의 속성은 아이폰 애플리케이션을 작성하는데 있어서 많이 사용되는 속성이다.

retain : 리테인은 메모리에 할당된 특정 객체를 참조하는 것을 의미한다. 각 객체는 리테인 카운트라는 데이터를 가지게 되는데, 객체가 호출될때마다 카운트 값을 증가시키고, 릴리즈 될때에는 카운트 값을 하나 감소시키면서 그 객체가 사용되고 있는지의 여부를 판단하게 된다. 이것은 메모리를 효율적으로 관리하기 위한 하나의 수단으로 이용된다.

nonatomic : 접근자와 변경자 메소드가 생성되는 방법을 바꾼다. 이 옵션에 대해 디폴트로 설정되어 있는 atomic의 경우 멀티 스레딩이 가능한 코드들을 추가로 생성하게 된다. 하지만 명시적으로 nonatomic을 설정함으로 멀티 스레딩의 가능성을 줄이고, 불필요한 것들로 인한 오버헤드를 상당부분 줄일 수 있다. 어느정도 자원 사용에 있어서 한계가 있는 아이폰 프로그래밍의 경우, 메모리에 사용에 대한 치밀한 접근이 필요하다.



아웃렛, 액션, 프로퍼티는 아이폰 프로그래밍을 하는데 있어서 필수적인 요소들이다. 어렵지 않은 역할을 가지고 있지만, 그 기본을 탄탄히 함으로써 좀더 규모있는 애플리케이션을 다루게 될때 당황하지 않고 적절하게 대응할 수 있는 것이다.


출 처 : http://devist.tistory.com/39

자료형

사용하는 데이터 종류 및 범위

String

문자열

Array<T>

배열, 제너릭으로 사용하기 위해 T(임의의 자료형을 말한다)으로 표기했다.

Dictionary<T,S>

사전형, T형으로 된 키와 S형으로 된 값을 갖고 있다.

Int8

정수형이며, -128 ~ 127까지의 정수값을 저장한다.

Int16

정수형이며, -32,768 ~ 32,767까지의 정수값을 저장한다.

Int32

정수형이며, -2,147,483,648 ~ 2,147,483,647까지의 정수값을 저장한다.

Int64

정수형이며, -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 까지의 정수값을 저장한다. 보통 Int라고 사용하면 Int64를 의미한다. 그 이유는 현재 swift가 실행되는 운영체제가 64bit 운영체제이기 때문이다.

Uint8

정수형이며, 0부터 255까지의 양수 값만을 갖는다.

Uint16

정수형이며, 0부터 65,535까지의 양수 값만을 갖는다.

Uint32

정수형이며, 0부터 4,294,967,295까지의 양수 값만을 갖는다.

Uint64

정수형이며, 0부터 18,446,744,073,709,551,615까지의 양수 값만을 갖는다.

Float32

비트의 부동소수점 데이터를 가지며 Double 형의 정밀도가 필요하지 않는 경우에 주로 사용한다.

Double64

비트의 부동소수점 데이터를 가지며 소수점 이하의 값을 정밀하게 표현하는 경우에 사용한다.

Bool

Truefalse 값 중 하나를 표현하는 경우에 사용한다.


1. 전처리


2. 어휘 분석

 : 전처리 된 파일을 읽어 소스를 문법적으로 의미 있는 가장 최소 단위(토큰)로 나눈다.


3. 구문 분석

 : 파서에 의해 구문 분석이 이루어짐. 구문 분석은 어휘 분석 단계에서 스캐너가 토큰을 인식하고 파서에서 넘겨주면 파서가 문법적 오류가 있는지 검사한 후에 중간 언어 생성을 위한 파서 트리를 만든다.


4. 의미 분석

 : 문법상 오류는 없지만 의미상 오류가 있는 것을 검사한다.
 ex) 선언되지 않은 변수의 사용 / 함수의 인자 개수나 인자형의 불일치 / 자료형의 불일치 등


5. 중간 언어 생성

 : 작성된 언어에 독립적인 중간 언어 생성

 ex) gcc의 경우 RTL(Register Transfer Language)이라는 중간언어를 생성함


6. 최적화

 : 중간 코드 최적화

 : 목적 코드 최적화


7. 목적 코드 생성

 : 중간 언어를 바탕으로 목적 코드를 생성한다. 목적 코드는 어셈블리 언어로 작성되며 이후 어셈블러에 의해서 인스트럭션으로 변환 된다. 목적 코드를 생성할 때는 되도록 재배치가 가능한 코드를 생성하게 한다.


8. 어셈블리

 : 어셈블리 언어로 작성된 목적 코드를 기계어 코드로 변환한다.


8. 링킹



Extern, Static 변수와 함수

Extern, Static 키워드는 변수와 함수에서 쓰일 때 그 의미가 조금 다르다. 형식과 쓰임새가 차이가 있기 때문이다.

 

 

<Extern 전역변수, 지역변수>

extern 변수는 다른 파일에서 변수를 공유해서 쓰기 위해 있는 키워드인데, 전역변수는 키워드를 생략해도 기본으로 extern선언이 되는 성질이 있다.

아래 코드를 보면,

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

/*main.c*/

#include <stdio.h>

#include "fun1.h"

int a=1; // 변수 선언

void main(){

f();

printf("%d\n",a); // 여기서의 a는 위의 a

}

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

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

int a; // 또 다른 선언문

void f(){

a = 7;

printf("%d\n",a); //여기서의 a는 바로 위의 a

}

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

/*fun1.h*/

void f();

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

결과는: 7

7

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

fun1.c에서 바꾼 a값이 main.c의 a에도 적용되어있다.

따라서 main.c와 fun1.c의 전역변수 a는 동일한 변수이거나 항상 싱크가 되어있다는 것을 알 수 있다.

여기에서 main.c와 fun1.c의 선언문에 extern을 붙여보자.

(main.c : int a=1; -> extern int a=1;)

(fun1.c : int a; -> extern int a;)

한쪽파일에만 extern키워드를 쓰든 양쪽에 다 쓰든, 모든 경우에서 결과는 같다.

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

//간략화한 코드//

/*main.c*/

extern int a=1;

/*fun1.c*/

int a;

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

/*main.c*/

int a=1;

/*fun1.c*/

extern int a;

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

/*main.c*/

extern int a=1;

/*fun1.c*/

extern int a;

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

결과는 항상

7

7

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

이 된다. 즉, 전역변수로 선언할 경우 extern를 넣든 안 넣든 결과는 똑같아 보인다. extern키워드를 생략해도, 전역변수는 기본으로 extern이기 때문일 것이다. 그런데 초기화구문의 extern과 초기화가 없는 extern선언문은 의미에 차이가 있다. 초기화에 쓰이는 extern은 변수를 새로 정의하는 선언문이라는 의미의 extern이고 실제로 메모리를 할당해서 변수를 정의한다. 그런데 초기화가 없는 선언문은 변수를 새로 정의하는 것이 아닌, “다른 파일(혹은 같은 파일 내의 다른 부분)에서 찾아서 있으면 그것을 가져다 쓰겠다”는 ‘참조선언’의 의미를 갖는 extern이다. 그렇다면 어떤 것이 정의선언이고 어떤 것이 참조선언일까? 아래의 경우를 보자. 아래의 경우는 모두 전역변수로 선언된 경우이다.

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

전역변수 선언문 종류

1. extern int a=1 -> extern변수 정의 및 초기화

2. int a =1; -> extern 변수 정의 및 초기화

3. extern int a; -> extern 변수(새로 정의하지 않고)참조선언

4. int a; -> extern 변수 정의(0으로초기화) 또는 참조선언

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

4의 경우는 그 쓰임새가 애매하다. 1이나 2와 쓰일 때는 참조선언이 되고, 3과 쓰일 때는 정의구문이 된다.(컴파일러에서 자동으로 처리함) 애매한만큼 지양해야 할 코드이다.

따라서

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

/*main.c*/

(extern생략가능)int a=1;

/*fun1.c*/

(extern생략가능)int a=2;

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

같은 식으로 여러번 초기화를 해주면 둘 다 정의 선언문이므로 ‘여러번 정의했다’는 에러가 뜨게 된다. 같은 a를 두 번 정의했으니 에러가 뜨는 것이다. 아까처럼

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

/*main.c*/

(extern생략가능)int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

(extern생략가능)int a; //a라는 extern변수를 다른 곳에서 찾아서 쓰겠다는 의미

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

로 코딩을 했을 경우, 에러가 없다. 이 관계는 다음의 예시에서 더욱 명백해진다.

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

/*main2.c*/

int a=1;

int a;

void main(){

int b=1; (1)

int b; (2)

}

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

새로운 main2.c파일에서 a는 전역변수, b는 지역변수로 선언되었다. 처음 int a=1;로 선언과 동시에 초기화되었고, 바로 아래줄에서 int a;는 선언이 아닌, “다른 곳에서 a를 찾아본 뒤에 그것을 쓰겠다”는 표현이 되므로 에러가 나지 않는다.(extern int a;의 생략형이라고 이해하면 쉬울 것이다.) 그러나 지역 변수 b의 경우 “여러번 재정의”에러가 뜬다. 잠깐 지역변수부분만 살펴보면, extern키워드를 통해 초기화하는 것이 아예 불가능하다.

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

void main(){

extern int b=1;

}

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

처럼 코딩을 하면, “블록 범위를 사용하여 extern변수를 초기화할 수 없습니다.”라는 에러메세지가 뜬다. 블록 범위, 즉 로컬변수선언시엔 무조건 그 블록 범위 내에서만 사용하게 되어있으므로, 다른 곳에서 이 변수를 사용할 수 있도록 하는 extern초기화 구문을 사용 못하게 막아놓은 것이다. 로컬변수를 다른 곳에서 사용하려면, 함수 결과값으로 끄집어내서 사용하는 수 밖에는 없다. 대신 초기화가 아닌 extern참조선언문은 함수 내에서도 전역에서 쓰듯이 사용이 가능하다.

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

// 간략화한 코드 //

/*main.c*/

(extern생략가능)int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int a; // 다른 곳의 extern변수 a를 찾아 쓰겠다는 의미

}

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

그런데 지역변수에서 extern변수를 참조선언해서 가져다 쓸 때 주의할 점이 있다. 함수 밖에서 참조선언을 할 때는 참조에 실패하면 컴파일 에러가 나지만, 함수 내에서 참조선언을 하면 참조할 것이 없어도 컴파일 상의 에러가 없다.(헉쓰...) 아래의 코드를 보자.

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

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

extern int b; //extern변수 b참조선언.

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

결과: “b의 외부기호를 확인할 수 없다”는 에러.

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

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int b; //extern변수 b참조선언.

}

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

결과: no error

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

아래의 경우처럼 에러가 없으면 나중에 있지도 않은 b변수를 사용할 때 에러가 난다.

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

/*main.c*/

int a=1; //extern변수 정의 및 초기화

/*fun1.c*/

void fun1(){

extern int b; //extern변수 b참조선언.

printf("%d",b); //변수 b 사용

}

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

결과: “b의 외부기호를 확인할 수 없다”는 에러.

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

따라서 결국 b를 안전하게 사용하고 싶으면 전역변수로 참조선언 한 뒤에 함수에서 쓰는 것이 좋은 코딩이라는 이야기다. 아래의 코드가 그렇다.

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

/*main.c*/

int a=1; //extern변수 정의 및 초기화

int b=2; //extern변수 b를 추가해보았다.

/*fun1.c*/

extern int b; //extern변수 b참조선언은 밖에

void fun1(){

printf("%d",b); //변수 b 사용

}

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

결과: 2

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

정리하자면, 전역변수는 초기화인 경우 항상 쓰지 않아도 extern으로 정의가 되며, (즉 필연적으로 모든 전역으로 정의되는 변수는 이름 하나당 변수 하나가 최대다. 단, static변수로 만들지 않을 경우에 한해서) 초기화구문이 아닌 경우엔 extern키워드를 쓰면 확실히 참조선언문이 되지만 extern을 생략하면 경우에 따라 참조선언도 되지만 정의가 되어있지 않았다면 정의구문이 되어버린다. 따라서 extern은 기본적으로 생략 가능하지만 헷갈림을 방지하고 코딩의 가독성을 위해서 아래와 같이 여러 파일에서 사용할 때는 extern키워드를 명시해주는 것이 좋다.

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

/*main.c*/

extern int a=1; // extern변수를 선언하겠다는 의미. 다른 파일에서도 사용하겠다는 뜻.

생략해도 크게 상관은 없다.(외부에서 쓸 일이 있을 땐 생략 삼가)

/*fun1.c*/

extern int a; //다른 곳에 있는 extern변수를 가져다 쓰겠다는 의미.

생략하면 위의 경우와 구분이 어렵다.(쓰는 것을 필수)

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

덧붙이자면, 아래의 경우는 어떨까?

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

/*main.c*/

extern int a;

/*fun1.c*/

extern int a;

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

둘 모두 참조선언문이라 어디에서도 정의가 되어있지 않기 때문에, 외부 기호를 확인할 수 없다는 에러가 뜨는 것은 당연하다.(전문용어로 찾다가 GG) 그럼 아래의 경우는 또 어떨까?

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

/*main.c*/

int a;

/*fun1.c*/

int a;

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

둘 중 하나의 구문의 정의 선언문이 되고 나머지가 참조선언문이 되는 것은 확실하다. 그러나 그게 어느것인지는 나도 모르겠다.

 

 

<Static 전역, 지역변수>

아까 전역변수의 경우에는 extern키워드를 생략해도 자동으로 extern선언이 된다고 했다. 그렇다면 한 파일 내에서 전역변수로 선언해서 쓰고는 싶은데 다른 파일에서 참조하는 것은 막고 싶을 경우(전역변수지만 extern은 아닌)는 어떻게 할까? 그럴 땐 static선언을 하면 된다.

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

/*main.c*/

static int a=1; //static은 생략하면 안 된다.

/*fun1.c*/

int a; // int a=2, 혹은 extern int a=2도 가능

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

위의 경우, static변수로 선언한 main.c의 a는 main.c파일 안에서만 사용 가능하고 다른 파일에서는 extern키워드로 참조할 수 없다. fun1.c에서 선언한 a는 main.c의 a와는 다른 메모리에 있는 a이다. 이 경우는 fun1.c의 a가 extern변수이고 main.c의 a는 extern변수가 아닌 mian.c에 속박된 static변수가 되는 것이다. 따라서 아래와 같이 쓰면 에러가 난다.

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

/*main.c*/

static int a=1; //static은 생략하면 안 된다.

/*fun1.c*/

extern int a;

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

extern int a;에서 참조선언을 했으므로 다른 곳에서 a를 찾아서 가져다 써야할텐데, 유일한 a는 static으로 묶여있으니 참조를 할 수 없다. 그래서 외부기호 확인 불가능 에러가 뜨는 것이다. 지역변수의 경우엔 extern변수 정의가 불가능하므로 static으로 따로 정의할 필요가 없을 것이다.

<extern, static 함수>

함수의 경우 extern, static개념이 간단하다. 함수는 쓰임새에 따라서 외부함수, 내부함수로 나눠 부르지만 사실 전역, 지역의 개념이 없고 굳이 말하자면 모두 전역이기 때문이다.

기본적으로 함수선언은 생략해도 모두 extern이다.

아래 코드를 보자.

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

/*main.c*/

#include <stdio.h>

void f(); (1)

void main(){

f();

}

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

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

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

/*fun1.h*/

void f();

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

결과: fun1.c

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

main.c에서 헤더파일을 추가하지도 않았는데, fun1.c의 함수를 가져다 쓴 것을 알 수 있다. fun1.c의 f()함수 선언 자체가 extern으로 되어있고, main.c의 (1)에서도 extern으로 선언해서 다른 파일의 함수를 자동으로 검색해서 call했기 때문이다. 이 점은 위 코드 어느 부분에 extern을 넣어도(심지어 함수정의부분에서도) 똑같은 결과가 나온다는 점에서 알 수 있다. 심지어 (1)을 생략해도 경고가 뜨지만 실행은 된다.(하지만 피해야 할 코딩방법) 함수는 기본적으로 extern으로 선언된다는 것을 알 수 있는 부분이다. 아래와 같이, main.c에서도 같은 이름의 함수 f()를 정의하면 어떻게 될까?

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

/*main.c*/

#include <stdio.h>

void f();

void main(){

f();

}

void f(){

printf("main.c\n");

}

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

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

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

/*fun1.h*/

void f();

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

결과: 함수 재정의 오류

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

main.c에서 이미 정의한 f()함수가 자동으로 extern이기 때문에, fun1.c에서 또 정의를 해주면 이것도 자동으로 extern이고, extern함수는 고유해야하기 때문에 같은 함수를 두 번 정의한 것으로 인식해서 에러가 나게 된다. 이것을 막고, 두 함수를 모두 사용하고 싶다면

그 때 static 키워드로 선언을 하면 된다.

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

/*main.c*/

#include <stdio.h>

static void f(); (1)

void main(){

f();

}

void f(){

printf("main.c\n");

}

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

/*fun1.c*/

#include <stdio.h>

#include "fun1.h"

void f(){

printf("fun1.c\n");

}

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

/*fun1.h*/

void f();

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

결과: main.c

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

잘 보면 mian.c의 f()함수는 static으로 선언되었고, fun1.c의 함수는 extern으로 선언되었으므로, main.c에서 실행한 f()함수는 mian.c에서 정의한 함수가 되며 출력도 그렇게 나온다. 반대로 mian.c의 함수를 extern으로, fun1.c의 함수를 static으로 정의해도 결과는 똑같다. 둘 다 static으로 정의해도 마찬가지다. 이유는 생각해보시길...




1. [UP System] 프로세스 컨텍스트 간의 동기화


semaphore, preempt_disable

semaphore는 CS(Critical Section)를 공유하는 프로세스 컨텍스트들을 휴면 시켜서 동기화를 달성한다. preempt_disable은 CS를 공유하는 프로세스 컨텍스트들은 물론이고 다른 모든 프로세스 컨텍스트들 또한 선점을 금지시켜 동기화를 달성한다. CS 구간이 긴 경우 semaphore를 짧은 경우 preempt_disable을 사용한다.


local_irq_disable을 사용할 수도 있겠지만 인터럽트 컨텍스트가 없기 때문에 불필요하다. 프로세스 컨텍스트는 휴먼 가능 하므로 spin_lock은 사용 불가능하다.



2. [UP System] 프로세스 컨텍스트와 인터럽트 컨텍스트 간의 동기화


local_irq_disable

local_irq_disable은 모든 인터럽트를 금지 시키기 때문에(심지어 타이머 인터럽트에 기반한 스케줄러까지) CS를 공유하는 프로세스 컨텍스트와 인터럽트 컨텍스트 간 동기화를 달성한다.


인터럽트는 스케줄링 대상이 아니므로 semaphore, preempt_disable로는 프로세스 컨텍스트와 동기화를 달성할 수 없다. 또한 semaphore는 휴면 가능하므로 인터럽트 컨텍스트에서는 아예 사용이 불가능하다. 인터럽트 컨텍스트는 항상 프로세스 컨텍스트에 우선 하므로 spin_lock은 사용 불가능하다.



3. [UP System] 인터럽트 컨텍스트 간의 동기화


local_irq_diisable

인터럽트 컨텍스트는 스케줄링 대상이 아니므로 local_irq_disable로 동기화를 달성한다.


인터럽트는 우선순위에 따라 중첩이 가능하므로 spin_lock 사용 불가능하다.


semaphore, preempt_disable는 인터럽트 컨텍스트 간의 동기화에 아무런 역할을 하지 못하며 semaphore는 휴면 가능하므로 인터럽트에서는 아예 사용이 불가능하다.



4. [SMP System] 프로세스 컨텍스트 간의 동기화


spin_lock

프로세스 컨텍스트가 하나의 프로세서에서 유사 동시성을 가짐과 동시에 다수의 프로세서에서 진정한 동시성을 가지게 된다. CS가 다수의 프로세서에 의해 공유 되므로 spin_lock을 사용해야 다수의 프로세서 간에 동기화가 달성 된다.


preempt_disable

preempt_disable 은 특정 프로세서에서의 선점을 금지 한다. 예를 들어 A, B 두 개의 프로세서가 있다. 그리고 CS를  공유하는 1, 2, 3, 4 네 개의 태스크가 있다. 어떠한 원인인지는 몰라도(로드 밸런싱을 포함한 다수의 경우가 있을 수 있음) 태스크 1, 2는 A 프로세서에서 실행되고 있고, 태스크 3, 4는 B 프로세서에서 실행 된다고 가정한다. 프로세서 A의 태스크 1, 2 든 프로세서 B의 태스크 3, 4든 CS 진입 시에 preempt_disable을 호출하면 프로세서 A의 태스크 1, 2 간의 유사 동시성에 의한 동기화가 달성되고 프로세서 B의 태스크 3, 4 간의 유사 동시성에 의한 동기화도 달성 될 것이다. 하지만 이것만으로는 프로세서 간의 진정한 동시성에 의한 동기화는 달성할 수 없으므로 태스크(1, 2)와 (3, 4)간의 동기화는 spin_lock으로 달성 될 것이다.


preempt_disable은 유사 동시성에서의 동기화를 위해서만 사용되는 것이다.

SMP에서 프로세서 컨텍스트 간의 완벽한 동기화를 위해서는 다음과 같은 코드 모양이 될 것이다.


preempt_disable     --> 유사 동시성 동기화

spin_lock               --> 진정한 동시성 동기화

   .....

   CS

   .....

spin_unlock

preempt_enable


이렇게 코드가 작성 된다면 SMP에서 UP로 갈 경우 spin_lock만 걷어내면 될 것이다.



5. [SMP System] 프로세스 컨텍스트와 인터럽트 컨텍스트 간의 동기화


local_irq_disable + spin_lock

local_irq_disable은 로컨 프로세서의 모든 인터럽트를 금지 하므로 유사 동시성에서의 프로세스 컨텍스트 간의 동기화를 달성함과 동시에 프로세스 컨텍스트와 인터럽트 컨텍스트 간의 동기화를 달성할 수 있다. 하지만 인터럽트는 다른 프로세서에 의해 핸들링 될 수 있으므로 반드시 spin_lock을 더해줘야 한다. 이 조합을 하나의 함수로 처리한 것이 spin_lock_irqsave(), spin_lock_irqstore()이다. UP에서와 마찬가지로 유사 동시성에서의 데드락을 방지하기 위해 반드시 인터럽트를 먼저 금지하고 락을 잡아야 한다.


물론 preempt_disalbe을 사용해 프로세스 컨텍스트 간의 동기화를 해결할 수 있겠지만, local_irq_disable이 이를 해결해주므로 사용할 필요가 없다. 또한 semaphore는 휴면 가능하므로 인터럽트 컨텍스트에서는 아예 사용이 불가능하다.


preempt_disable     --> 프로세스 컨텍스트 간의 동기화 (유사 동시성) => 불필요

locak_irq_disable   --> 프로세스 컨텍스트 간의 동기화 + 프로세스 컨텍스트와 인터럽트 컨텍스트 간 동기화(유사 동시성)

spin_lock              --> 진정한 동시성 동기화

   .....

   CS

   .....

spin_unlock

local_irq_enable

preempt_enable



6. [SMP System] 인터럽트 컨텍스트 간의 동기화


local_irq_disable + spinlock

locak_irq_disable은 유사 동시성에서의 동기화를 위해 사용하는 것이며 (단일 프로세서에서의 인터럽트 컨텍스트 간의 동기화), 진정한 동시성에서의 동기화는 spin_lock을 통해 달성 된다. 또한 유사 동시성에서의 데드락을 방지하기 위해 반드시 인터럽트를 먼저 금지하고 락을 잡아야 한다. semaphore, preempt_disable는 인터럽트 컨텍스트 간의 동기화에 아무런 영할을 하지 못하며, semaphore는 휴면 가능하므로 인터럽트 컨텍스트에서는 아예 사용할 수 없다.



spin_lock

spin_lock은 두 개 이상의 프로세서가 실제로 동일한 시간에 같은 CS 구간에 접근할 때만 사용을 한다. 하나의 프로세서만을 사용하는 시스템에서는 시분할에 의한 유사 동시성만을 가지므로 spin_lock이 필요 없다. SMP 시스템에서는 두 개  이상의 프로세서에 의한 진정한  동시성과 하나의 프로세서에 의한 유사 동시성을 가진다. 즉, SMP 시스템에서는 두가지 종류의 동시성을 모드 가지는 것이다. 이때 spin_lock이 의미가 있는 경우는 두 개 이상의 프로세서에 의한 진정한 동시성을 가질때 이다. 그러므로 spin_lock을 생각할 때 SMP냐 UP냐, 선점이냐 비선점이냐를 생각하며 고민할 필요가 없다.



출 처 : http://blog.naver.com/redcultuer?Redirect=Log&logNo=130109621698









링커가 모든 심볼을 해석하고 나면, 심볼 참조는 오직 하나의 심볼 정의만을 가지게 된다. 그 때, 링커는 아래 두 스텝으로 구성된 재배치 작업을 하게 된다.

 - 섹션과 심볼정의들을 재배치 한다. 링커는 같은 타입의 모든 섹션들을 새로운 하나의 섹션으로 통합한다. 예로 들면, 링커는 입력으로 받은 모든 재배치 가능한 오브젝트 파일들의 .data 섹션을 합쳐 하나의 .data 섹션을 만든다. 같은 과정이 .code에 대해서도 행해진다. 그런 후에 링커는 병합된 새로운 섹션과, 병합된 새로운 색션내의 각 색션, 그리고 모든 심볼들에 대해 런-타임 메모리 주소를 할당한다. 이러한 작업 후에는 프로그램의 모든 코드와 전역 변수들은 고유한 로드-타임 주소를 가지게 된다.

 - 섹션들안에 있는 심볼의 참조를 재배치 한다. 이 과정에서,  링커는 코드와 데이터 섹션에 있는 모든 심볼 참조를 수정하여, 그것들이 올바른 로드-타임 주소를 가지게 한다.


 어셈블러가 해석 안 된 심볼들을 만날 때마다, 어셈블러는 오브젝트 파일의 .rel.text / .rel.data 섹션에 해석 안 된 심볼들을 위한 재배치 항목을 생성한다. 이러한 재배치 항목은 해석 안된 심볼들이 어떻게 해석되어야 하는지에 대한 정보들을 담고 있다. 전형적인 ELF 재배치 항목은 다음과 같은 멤버들로 구성된다.

 -  옵셋, 재배치 되어질 필요가 있는 심볼 참조의 섹션내에서의 옵셋을 나타내며, 혹은 디스크의 저장공간이 오브젝트 파일 내에서 재배치 되어질 필요가 있을 시, 이 값은 재배치 될 필요가 있는 디스크의 섹션의 처음부터 바이트 단위로 얼마만큼 떨어져 있는가를 나타낸다.

 -  심볼, 이것은 심볼 테이블에서의 인덱스로서, 아직 해석 안 된 심볼이 심볼 테이블에서 몇 번째 위치에 있는가를 나타낸다.

 -  타입, 재배치 타입, 일반적으로 R_386_PC32는 S+A-P로 계산하며, R_386_32는 S+A로 계산한다. 이 계산에서, S는 재배치 항목의 심볼 항목에 들어있는 값을 가리키며, P는 섹션 옵셋 혹은 재배치 되는 저장장치의 주소를 나타낸다. 그리고 A는 재배치 가능한 필드를 계산하는데 필요한 주소이다.

출 처 :  http://wiki.kldp.org/wiki.php/DocbookSgml/LinkerLoader-TRANS 



모든 재배치 가능한 오브젝트 파일들은 심볼 테이블과 그와 관련된 심볼들을 가지고 있다. 링커의 관점에서 볼 때 심볼들은 다음과 같이 분류할 수 있다.

 - 현재의 파일에서 정의되고, 다른 파일들에서 참조되는 전역 심볼. 모든 non-static 함수들과 전역 변수들이 이 분류에 해당한다.

 - 현재의 파일에서 참조는 되나, 다른 곳에서 정의된 전역 심볼. extern으로 정의된 모든 함수들과 변수들이 이 분류에 해당한다.

 - 현재의 파일에서만 정의되고 참조되는 지역 심볼. 모든 static 함수들과 변수들이 이 분류에 해당한다.

링커는 심볼의 참조를 해석할 때, 입력으로 주어지는 재배치 가능한 오브젝트 파일의 심볼 테이블로부터 꼭 하나만 존재하는 심볼의 정의를 참조하여 심볼 참조를 해석한다. 지역 심볼은 그에 대한 다중 정의를 심볼 테이블이 가질 수 없으므로 쉽게 해석 된다. 그러나 전역 심볼의 해석은 약간의 트릭이 요구된다. 컴파일 타임 때, 컴파일러는 전역 심볼들을 strong 혹은 weak 한 것으로 만드는데, 함수들과 초기화 된 전역 변수들은 strong 하게, 초기화 되지 않은 변수들은 weak하게 만든다. 그러면 링커는 아래의 룰을 적용하여 심볼들을 해석하게 된다. 

1. 다중 strong 심볼들은 허가되지 않는다.

2. 하나의 strong 심볼과 여러 개의 weak 심볼들이 있으면, strong 심볼을 선택한다.

3. 여러 개의 weak 심볼들이 있으면, 그것들 중 아무거나 선택한다.

출 처 : http://wiki.kldp.org/wiki.php/DocbookSgml/LinkerLoader-TRANS 



1. 초기화 된 전역 변수는 strong 하게, 초기화 되지 않은 변수들은 weak 하게 만든다..

- funcA.c -
1
2
3
4
5
6
7
8
#include <stdio.h>

int a = 6;          // Strong Symbol

void funcA()
{
    printf("funcA()\n");
}

- main.c -
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int a;                  // Weak Symbol

int main()
{
    printf("a = %d\n", a);
    funcA();

    return 0;
}

- 결과 화면 -




2. 2개의 초기화 된 전역 변수 (Strong Symbol) 일 때

- funcA.c -
1
2
3
4
5
6
7
8
#include <stdio.h>

int a = 6;          // Strong Symbol

void funcA()
{
    printf("funcA()\n");
}

- main.c -
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int a = 10;         // Strong Symbol

int main()
{
    printf("a = %d\n", a);
    funcA();

    return 0;
}

- 결과 화면 -


컴파일 시 에러 발생...




3. 초기화 된 전역 변수 하나를 __weak 속성을 부여 했을 때

- funcA.c -
1
2
3
4
5
6
7
8
#include <stdio.h>

int a = 6;          // Strong Symbol

void funcA()
{
    printf("funcA()\n");
}

 - main.c -
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>

int __attribute__((weak)) a = 10;   // Weak Symbol

int main()
{
    printf("a = %d\n", a);
    funcA();

    return 0;
}
 
- 결과 화면 -
 

초기화 된 전역 변수 2개를 선언했더라도, 2번 경우와 다르게 하나의 심볼을 weak로 지정해서 에러없이 컴파일 되며, 실행도 잘 되었다.


 
리눅스상에서 콘솔을 통해 키를 입력받을때, Return키를 입력받지 않고 키 값에 반응해 실시간(?)으로 처리되는 작업이 필요할때가 있다. 헌데 리눅스에서는 윈도우처럼 getch함수가 존재하지 않기 때문에 이를 따로 구현해줘야 하는데, 아래는 리눅스 상에서 getch() 함수를 구현한 예이다.

  1. #include <stdio.h>  
  2. #include <term.h>  
  3. #include <termios.h>  
  4. #include <unistd.h>  
  5.   
  6. int getch(void)  
  7. {  
  8.   int ch;  
  9.   struct termios buf;  
  10.   struct termios save;  
  11.   
  12.    tcgetattr(0, &save);  
  13.    buf = save;  
  14.    buf.c_lflag &= ~(ICANON|ECHO);  
  15.    buf.c_cc[VMIN] = 1;  
  16.    buf.c_cc[VTIME] = 0;  
  17.    tcsetattr(0, TCSAFLUSH, &buf);  
  18.    ch = getchar();  
  19.    tcsetattr(0, TCSAFLUSH, &save);  
  20.    return ch;  
  21. }  
  22.   
  23. int main(void)  
  24. {  
  25.     int ch;  
  26.   
  27.     for(; !(ch=='\n');){  
  28.   
  29.         ch = getch();  
  30.         printf("%d \n", ch);  
  31.     }  
  32.   
  33.     return 0;  
  34. }  

터미널로부터 리턴키가 입력될때까지 자료를 입력받는 예제

위의 예제를 보다 발전(?)시키기 위해서 좀더 많은 자료가 필요하다면, 아래의 링크를 참조하기 바란다. 터미널을 제어하는 방법에 대한 내용이 자세하게 정리되어 있는 페이지이다.
링크 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/termios


◆ 추가로 터미널 입력시 특수문자를 요약한 Table

Figure 18.9. Summary of special terminal input characters

Character

Description

c_ccsubscript

Enabled by

Typical value

POSIX.1

FreeBSD 5.2.1

Linux 2.4.22

Mac OS X 10.3

Solaris 9

   

field

flag

      

CR

carriage return

(can't change)

c_lflag

ICANON

\r

DISCARD

discard output

VDISCARD

c_lflag

IEXTEN

^O

 

DSUSP

delayed suspend (SIGTSTP)

VDSUSP

c_lflag

ISIG

^Y

 

 

EOF

end of file

VEOF

c_lflag

ICANON

^D

EOL

end of line

VEOL

c_lflag

ICANON

 

EOL2

alternate end of line

VEOL2

c_lflag

ICANON

   

ERASE

backspace one character

VERASE

c_lflag

ICANON

^H, ^?

ERASE2

alternate backspace character

VERASE2

c_lflag

ICANON

^H, ^?

 

     

INTR

interrupt signal (SIGINT)

VINTR

c_lflag

ISIG

^?, ^C

KILL

erase line

VKILL

c_lflag

ICANON

^U

LNEXT

literal next

VLNEXT

c_lflag

IEXTEN

^V

 

NL

line feed (newline)

(can't change)

c_lflag

ICANON

\n

QUIT

quit signal (SIGQUIT)

VQUIT

c_lflag

ISIG

^\

REPRINT

reprint all input

VREPRINT

c_lflag

ICANON

^R

 

START

resume output

VSTART

c_iflag

IXON/IXOFF

^Q

STATUS

status request

VSTATUS

c_lflag

ICANON

^T

 

 

 

STOP

stop output

VSTOP

c_iflag

IXON/IXOFF

^S

SUSP

suspend signal (SIGTSTP)

VSUSP

c_lflag

ISIG

^Z

WERASE

backspace one word

VWERASE

c_lflag

ICANON

^W

 

출처 : http://codeidol.com/%5B~MODULE~%5D/advanced-programming-in-unix/Terminal-I-O/-18.3.-Special-Input-Characters/

+ Recent posts