charAt: String에서 한 글자씩 읽기/추출하는 방법


소스 파일명: Example.java
public class Example {
  public static void main(String[] args) {

    // 문자열 정의
    String s = "MP3 플레이어";


    // 한 글자씩 화면에 출력
    for (int i = 0; i < s.length(); i++) {
      System.out.println( s.charAt(i) );
    }

/* 출력 결과:

M
P
3






*/


    // 괄호에 넣어 한 글자씩 출력
    for (int i = 0; i < s.length(); i++) {
      System.out.format("[%c] ", s.charAt(i) );
    }
    // 출력 결과: [M] [P] [3] [ ] [플] [레] [이] [어]




    System.out.println(); // 줄바꿈

  }
}

'Development > Java' 카테고리의 다른 글

[String형 문장을 이을때는...]  (0) 2012.04.28
[아스키코드 출력]  (0) 2012.04.27
[StringBuffer]  (0) 2012.04.19
[static]  (0) 2012.04.19
[배열 복사]  (0) 2012.04.09
Posted by cyj4369
,

startProject하기


1. /usr/local/bin 에다가 startProject명령을 넣어준다.

2. startProject project0

3. startProject project1 ~/바탕화면/geekos/src project0


Posted by cyj4369
,

[StringBuffer]

Development/Java 2012. 4. 19. 18:13

스트링 버퍼는, 바이너리 스트링의 연결 연산자 + 를 구현하기 위해서 compiler 로 사용됩니다. 예를 들어, 다음의 코드

     x = "a" + 4 + "c"
 

(은)는, 이하의 동등한 코드로서 컴파일 됩니다.

     x = new StringBuffer(). append("a"). append(4). append("c")
                           . toString()
 
새로운 스트링 버퍼 (초기 상태에서는 하늘)를 작성해, 각 연산자의 스트링 표현을 스트링 버퍼에 차례로 추가하고 나서, 스트링 버퍼의 내용을 스트링 으로 변환합니다. 이것에 의해, 작성되는 일시적인 스트링의 전체양을 줄일 수가 있습니다.

StringBuffer 의 기본적인 오퍼레이션에는,append 메소드 및 insert 메소드가 있어, 이러한 메소드는 어떤 종류의 데이터도 받는 것이 할 수 있도록 overload 되어 있습니다. 메소드는 각각 주어진 데이터를 효율적으로 스트링 으로 변환해, 스트링중의 문자를 스트링 버퍼에 추가 또는 삽입합니다. append 메소드는 항상, 버퍼의 말미에게 줄 수 있던 문자를 추가해,insert 메소드는 지정된 위치에 문자를 추가합니다.

예를 들어,z 를, 현재 "start" 를 포함한 스트링 버퍼 오브젝트라고 보는 경우,z.append("le") 는 스트링 버퍼의 내용이 "startle" 가 되도록(듯이) 변경하는데 대해,z.insert(4, "le") 라고 하는 메소드 호출은 스트링 버퍼의 내용이 "starlet" 가 되도록(듯이) 작용합니다.

일반적으로, sb 가 StringBuffer 의 인스턴스를 참조하고 있는 경우,sb.append(x) 는 sb.insert(sb.length(),  x) 와 같은 결과가 됩니다.

각 스트링 버퍼에는 capacity가 있습니다. 스트링 버퍼에 포함되는 스트링의 길이가 capacity를 초과 하지 않는 한, 새로운 내부 버퍼 배열을 할당할 필요는 없습니다. 내부 버퍼가 오버플로우 하는 경우, 자동적으로 capacity가 증가합니다.




append (boolean b) 
          boolean 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
append (char c) 
          char 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (char[] str) 
          char 배열 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (char[] str, int offset, int len) 
          char 배열 인수의 부분 배열의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (double d) 
          double 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (float f) 
          float 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (int i) 
          int 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (long l) 
          long 인수의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (Object  obj) 
          인수 Object 의 스트링 표현을 이 스트링 버퍼에 추가합니다.
 append (String  str) 
          이 스트링 버퍼에 스트링을 추가합니다.
delete (int start, int end) 
          이 StringBuffer 의 부분 스트링내의 문자를 모두 삭제합니다.

'Development > Java' 카테고리의 다른 글

[아스키코드 출력]  (0) 2012.04.27
[String에서 한 글자씩 읽기/추출하는 방법]  (0) 2012.04.27
[static]  (0) 2012.04.19
[배열 복사]  (0) 2012.04.09
[시간 측정]  (0) 2012.04.08
Posted by cyj4369
,

[static]

Development/Java 2012. 4. 19. 14:36

함수안에 선언된 변수는 그 함수를 빠져 나가면 사라진다. 할당되었던 메모리는 해제되고 저장되어 있던 값도 사라진다. 이러한 특성 때문에 자동변수(auto variable) 이라고 부르기도 한다. 그 함수 내에서만 유효하다고 해서 지역변수라고 부르기도 한다. 따라서 그 함수로 들어올 때 마다 변수가 다시 메모리 할당된다. 

그래서 함수간 또는 파일간에 변수 값을 공유하고자 할 때는 전역변수(global variable) 또는 정적변수(static variable)를 사용하는데 이 두 변수간의 차이를 정확히 이해를 하고 사용해야 한다. 

전역변수와 정적변수는 lifetime은 동일하다. 즉, 해당 프로그램이 죽을 때까지 한번 할당된 변수 메모리와 값은 유지된다는 점이다. 반면에 scope는 서로 다르다.  전역변수는 해당 프로그램(실행파일 기준)의 어느 함수, 어느 파일에서도 접근이 가능한 반면 정적변수는 변수가 선언된 파일이나 함수내에서만 접근이 가능하다. 

scope에 대한 예를 들면  a 파일에 선언되어 있는 static 변수는 a파일에서는 어느곳에서나 접근이 가능하지만 b파일 에서는 읽을 수가 없다. (file scope) 동일 파일 속에서도  a() 함수 속에서 선언된 static 변수는 a()의 어느곳에서도 접근할 수 있지만 b() 함수 에서는 접근할 수 없는 것이다. (function scope)  반면에 global 변수는 프로그램내의  어느 파일, 어느 함수에서도 읽을 수 있다. 

lifetime 에 대하여 고찰해 보면 한번 선언된 전역변수와 정적변수의 값은 프로그램(프로세스)가 죽을 때까지 계속 유지된다. 따라서 미들웨어에 의해 프로세스가 통제되는 프로젝트의 경우 프로세스가 죽지 않고 항상 살아있기 때문에 매번 거래가 시작될 때 반드시 변수를 초기화 해 주어야 한다. 초기화를 빼먹을 경우 이전 거래에서 사용했던 변수 값들이 그대로 보존되어 있기 때문에 잘못된 결과를 초래할 수 있기 때문이다.

특히 국방, 금융권, 원자력과 같은 크리티컬한 사이트의 프로젝트에서는 예기치 않은 대형사고가 발생될 수 있기 때문에 각별히 신경써서 사용해야 할 것이다.  

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

이러한 개념은 extern 함수와 static 함수에도 똑같이 적용된다. static 함수는 해당 파일 내에서만 호출될 수 있다. 그러나 extern 함수는 다른 파일 속에서도 호출될 수 있는 것이다. 보통 extern 함수 앞에는 extern 이란 말을 생략하기도 한다. 따라서 static 이 붙지 않은 것은 extern 함수라고 간주하면 될 것이다.  





---예제---

static 변수: 해당 클래스의 모든 인스턴스들에 의해 공유되는 변수

class Thing {

  static int count;     // static 변수 이 변수는 각 객체들 사이에서 공유된다.

  String name;

  Thing(String name) {

    this.name = name;

    ++count;  }

}

class StaticVariable {

  public static void main(String args[]) {

    Thing t1 = new Thing("Bowling Ball");

    System.out.println(t1.name + " " + t1.count);

    Thing t2 = new Thing("Ping Pong Ball");

    System.out.println(t2.name + " " + t2.count);

    Thing t3 = new Thing("Football");

     System.out.println(t3.name + " " + t3.count);  }

}



static 변수를 사용시 객체를 생성하지 않고 클래스명과 직접 사용 가능(전역 변수 기능)

class StaticBag {

  static boolean flag;

  static int i, j = 2, k = 3, l, m;

  static double array[] = { -3.4, 8.8e100, -9.2e-100 };

  static String s1, s2 = new String("Hello");}

class StaticBagTest {

  public static void main(String args[]) {

    System.out.println(StaticBag.flag);

    System.out.println(StaticBag.i);

    System.out.println(StaticBag.j);

    System.out.println(StaticBag.k);

    for(int i = 0; i < StaticBag.array.length; i++)

      System.out.println(StaticBag.array[i]);

    System.out.println(StaticBag.s1);

    System.out.println(StaticBag.s2);  }

}



'Development > Java' 카테고리의 다른 글

[String에서 한 글자씩 읽기/추출하는 방법]  (0) 2012.04.27
[StringBuffer]  (0) 2012.04.19
[배열 복사]  (0) 2012.04.09
[시간 측정]  (0) 2012.04.08
[랜덤함수 사용법]  (0) 2012.04.08
Posted by cyj4369
,


(이미지 출처: refit.sourceforge.net )
(처음)
맥북에어(Macbook Air) 2010에 부트캠프로 윈도우즈7(Windows7)을 설치하기 위해 지난 주말을 홀딱 날려버렸네요.
결국, 성공하지 못했지만, 돌아오는 주중에 다시 시도해보려고 합니다.
그건 나중에 더 이야기하도록 하고..

오늘 소개하는 것은 보통 부트캠프의 문제로 혹은, 편의상.. 등의 이유로 설치했던 'rEFlt' 응용 프로그램을 삭제하는 방법입니다.
구글링을 하면, 블로거분들이나 성실하신 분들께서 자세히 설명해주신 것을 볼 수 있지만..
맥 초보분들을 위해 더 자세히 글을 남기고자 합니다.

여담이지만, 윈도우즈 팁 게시글에는 자세한 글들이 많은데, 맥의 팁 게시글은 양 자체도 적을 뿐더러 리눅스(Linux)를 다루듯이 터미널로 설명한 글들을 많이 접해서 아쉽습니다. :(
그 아쉬움을 덜고자 제가 직접 스크린샷으로 정리하고 있네요. :)

아래 정리하는 글은 rEFlt의 부트 라이브러리 뿐만 아니라 맥에서 깔끔하게 모든 관련 파일을 rEFlt 의 권고대로 삭제하는 방법입니다.


(가운데)
rEFlt를 제거하자.

차근차근 아래 방법에 따라가세요. :)

1. 파인더를 실행합니다.
독(Dock)에서 아래 아이콘을 클릭하면 됩니다. :)


2. 파인더가 실행되면, 아래 스크린샷처럼 검색바(스팟라이트: Spotlight)에 'efl'이라는 키워드로 검색합니다.
그러면, 'efl' 이름의 폴더 하나가 검색됩니다.
이 폴더를 선택하고, 삭제합니다.
폴더를 클릭하고, 보조클릭(마우스 오른쪽 클릭) 후, 등장하는 문맥메뉴에서 [휴지통으로 이동]을 클릭합니다.
또는, 단축키 조합인 "[command] + [delete]"를 사용합니다.

3. 그러면, 아래와 같은 메시지가 등장합니다.
맥의 암호를 입력하고, [승인] 버튼을 클릭합니다.

4. 이제 다른 파일을 제거하러 '유틸리티' 폴더로 이동합니다.
위의 메뉴에서 [이동] - [유틸리티]로 갑니다.

5. 유틸리티 애플리케이션들 중에서 'Partition Inspector'를 찾아서 제거합니다.
위에서 배웠던 방식으로 보조클릭으로 [휴지통으로 이동]을 클릭하거나, 단축키 조합인 "[command] + [delete]"를 이용하세요.

6. 그러면, 역시 위에서 봤던 메시지가 또 등장합니다.
맥의 암호를 입력하고, [승인]!

7. 이제 하나 남았습니다.
조금만 더! :)
[Macintosh HD]로 이동합니다.
왼쪽 사이드바에서 맥의 계정을 클릭한 후, [Macintosh HD]를 클릭하여, 이동하면 됩니다.

8. 그리고 [라이브러리] 폴더로 이동합니다.

9. 여기서 [Startupitems] 폴더를 찾아 들어갑니다.

10. 그러면, [rEFltBlesser] 폴더 하나가 보이는데, 이것을 통째로 제거합니다.

11. 역시 아래와 같이 파인더 변경 경고 메시지를 보여줍니다.
암호를 입력하고, [승인]을 클리합니다.
이제, 재시동(재부팅) 하면, 더 이상 rEFlt 화면을 볼 수도 없고, 깨끗하게 맥에서 제거된 것입니다.


(끝)
이렇게해서 rEFlt 제거 방법을 정리해보았습니다.
아래에서 소개하겠지만, 다른 분들께 많은 도움을 받았고, 그보다 더 자세히 정리하기 위해 이 글을 시작하였습니다.

아무쪼록 도움이 되셨길 바라며, 물러나겠습니다.
고맙습니다.
행복하세요. :)

 + 이 글은 아래 링크의 도움을 받아 작성되었습니다.
위 링크는 rEFlt 공식 배포 웹사이트인 Sourceforge.net에서 공지하는 rEFlt 제거 방법입니다.

맥에 대한 팁을 많이 올려주시는 페이퍼북님의 블로그에 정리하신 윈도우즈7을 맥북에어 2010에 설치하는 방법을 설명하시면서 마지막으로 rEFlt를 터미널을 이용해 제거하는 방법을 설명하셨습니다.

위 링크인 전자우유 블로그에서는 rEFlt를 소개하고, 부트로더 라이브러리 파일을 제거하는 방법을 소개하고 있네요.

Posted by cyj4369
,

rm -r 폴더이름 : 해당폴더와 그 밑에 하위 폴더 모두 삭제

Posted by cyj4369
,

[배열 복사]

Development/Java 2012. 4. 9. 01:55

package doTest;
public class IDLCTest_new {
        public static void main(String[] args) {
                char [] test1 = {'g','o','o','d'};
                char [] test = new char[16];
                
                test=(char[]) test1.clone();

                System.out.println("length:"+test.length);
                
                System.out.println(test);
                System.out.println("/"+test+"/"); // 멀티플... 형태로 표현하는곳에선 char[] 배열이 아닌 String 타입을 써야한다.
                
                System.out.print("/");
                System.out.print(test);
                System.out.println("/");                
        }
}

'Development > Java' 카테고리의 다른 글

[StringBuffer]  (0) 2012.04.19
[static]  (0) 2012.04.19
[시간 측정]  (0) 2012.04.08
[랜덤함수 사용법]  (0) 2012.04.08
[자바에서 sleep]  (0) 2011.12.27
Posted by cyj4369
,

[시간 측정]

Development/Java 2012. 4. 8. 21:04

// 시작 부분에 아래처럼 현재 시간을 계산하고

long start = System.currentTimeMillis();


// 프로그램 본문


// 끝에 아래와 같이 삽입

long end = System.currentTimeMillis();

System.out.println( "실행 시간 : " + ( end - start )/1000.0 +"초");

'Development > Java' 카테고리의 다른 글

[static]  (0) 2012.04.19
[배열 복사]  (0) 2012.04.09
[랜덤함수 사용법]  (0) 2012.04.08
[자바에서 sleep]  (0) 2011.12.27
[한 소켓에 여러 인풋/아웃풋 스트림]  (0) 2011.12.27
Posted by cyj4369
,

public class Foo {
  public static void main(String[] args) {


    // 0.0 ~ 1.0 사이의 실수 난수 구하기
    for (int i = 1; i <= 20; i++)
      System.out.println(Math.random());

/* 출력 결과:
0.8835488755737285
0.7442235907969202
0.04143887519495115
0.7752670113987891
0.525644276817284
0.9810655979902362
0.9857864655525691
0.5176456441171947
0.9534154184106848
0.5711598917262706
0.9167881479510426
0.9683895991289863
0.5570482364156645
0.640266931881892
0.7521635329694171
0.37944742406283405
0.1930044225804538
0.6713601888567906
0.28656122448550325
0.9697966362643208
*/




    // 1 ~ 10 까지의 정수 난수 구하기
    for (int i = 1; i <= 20; i++) {
      int n = (int) (Math.random() * 10) + 1;
      System.out.println(n);
    }

/* 출력 결과:
8
4
2
2
7
1
3
7
10
3
7
2
10
8
3
5
4
10
4
1
*/


  }
}

'Development > Java' 카테고리의 다른 글

[static]  (0) 2012.04.19
[배열 복사]  (0) 2012.04.09
[시간 측정]  (0) 2012.04.08
[자바에서 sleep]  (0) 2011.12.27
[한 소켓에 여러 인풋/아웃풋 스트림]  (0) 2011.12.27
Posted by cyj4369
,

0. 서문

현재 Linux가 돌고 있는 시스템 중의 대부분은 Intel IA32 CPU이다. Linux는 Intel 80386부터 시작하여 80486, Pentium 계열의 CPU에서 실행이 된다. 운영체제의 기능을 구현하려면 CPU의 지원을 필요로 하는데, Linux는 80386부터 등장한 32-bit 보호모드(protected mode)의 지원을 이용하여, 메모리 관리, 프로세스 관리 등을 하고 있다. 여기서는 Linux를 구현하기 위해 필요한 보호모드의 기능들을 간단히 살펴보도록 한다. 
  

1. 실제모드 

실제모드는 x86 계열로 처음 등장한 Intel 8086 CPU와 같은 동작 모드를 말하는 것으로, 16 bit CPU인 8086, 80286에서뿐만 아니라, 80386 이후의 모든 32-bit 프로세서에서도 이를 지원한다. x86 계열의 모든 CPU는 처음 시작할 때는 실제모드로 동작한다. 

실제모드에서는 20 bit address bus를 사용하여 총 1MB의 메모리를 사용할 수 있으며, 16 bit register를 사용한다. 레지스터의 크기가 16 bit이기 때문에, 20 bit 주소를 나타내기 위해 segment register라는 것을 도입하였다. 이는 16 bit segment register와 16 bit offset을 중첩시켜서 20 bit의 주소를 만들어내는 것이다. 모든 메모리 접근에는 segment와 offset이 같이 필요하며, 하나의 segment를 사용하면 64K(0x10000)만큼의 메모리를 사용할 수 있다. 실제모드에서는 가상 메모리라는 개념이 존재하지 않으며, segment와 offset으로 만들어지는 주소는 바로 물리적인 메모리 주소이다. 또한 이 모드에서 동작하는 모든 프로그램은 메모리의 어떤 영역이든지 맘대로 접근할 수 있으며, cli (clear interrupt), sti (set interrupt)같은 명령어를 포함하여 실제모드에서 사용할 수 있는 모든 명령어들을 모두 사용할 수 있다. 

IBM-PC는 8086 CPU를 이용하여 만들어졌는데, 이것이 처음 등장하던 당시엔 1MB의 메모리는 상당히 큰 것이었다. 그래서 IBM은 앞의 640KB(0 - 0x9ffff)만을 프로그램이 사용할 수 있게 하고, 나머지 384KB(0xa0000 - 0xfffff)는 BIOS와 ISA 장치용으로 사용하게 하였는데, 이는 실제모드로 동작하는 각종 프로그램들이 640KB의 메모리만을 사용할 수 밖에 없는 제약을 만들었다. 일반적으로 리눅스 부팅을 할 때 사용되는 LILO도 실제모드로 시작하여 리눅스 커널을 로드하기 때문에, 마찬가지로 640KB의 제약을 받게 된다. 

그림 1-1. 실제모드에서의 메모리 변환과정 
 
그림 1-2. 실제모드에서의 메모리 계산방법



2. 보호모드(Protected Mode) 

Intel 80286부터 처음 도입된 보호모드는 80386에 이르러서 완성된 모습을 보여 지금에 이르게 된다. 80286의 보호모드를 간단히 살펴보면, 우선 24 bit address bus를 사용하여 총 16MB(0x1000000)의 메모리를 사용할 수 있게 하였다. 

segment register는 selector라는 명칭으로 바뀌었고, descriptor table이라는 것을 통하여 16 bit segment를 24 bit base address로 바꾸게 하였다. 이 base address에 offset을 더함으로써 모두 16MB의 메모리를 사용할 수 있었지만, 80286 역시 16 bit CPU였기 때문에 각 segment의 크기는 여전히 64KB의 제한을 가지게 되었다. 

이런 제한을 없애고 완전한 32 bit address space와 32 bit register set을 제공하는 80386이 등장하였고, 이후에 운영체제들은 80386에서 제공하는 보호모드의 기능을 활용하게 되었다. 보호모드에서는 CPU가 제공하는 모든 기능과 명령어들을 활용할 수 있다. 보호모드에서는 privilege level이라는 것이 등장하는데, 이는 프로세서를 활용할 수 있는 권한정도를 말한다. 이는 0부터 3까지 있는데, 0이 모든 일을 할 수 있는 모드로 일반적인 운영체제 구현에서 커널모드가 이 상태이며, 3은 응용프로그램처럼 사용자 권한의 실행상태를 나타낸다. 이 privilege level을 통하여 커널모드/사용자모드를 구현하게 된다. 보호모드에서는 32 bit address bus를 통하여 4GB의 메모리를 사용할 수 있으며, 메모리 보호기능과, 페이징(paging) 메커니즘 등을 통해 가상 메모리를 효율적으로 구현할 수 있다. 인터럽트나 예외처리, task switching 등도 모두 보호모드에서 지원하는 기능을 활용한다. 

그림 2-1. 보호모드 레지스터와 자료구조


3. Intel 80386 Registers
 

  • 일반 목적의 data register (32 bits) :
    • EAX, EBX, ECX, EDX
    • ESI (source pointer), EDI (destination pointer)
    • ESP (stack pointer), EBP (base pointer)
  • Segment Register / Selector (16 bits) :
    • CS (code segment), DS (data segment), SS (stack segment)
    • ES, FS, GS
  • Flag Register (32 bits) : EFLAGS
  • Instruction pointer : EIP
  • Control Register : 시스템의 동작들을 제어하기 위한 레지스터
    • CR0 : 프로세서의 상태와 동작모드를 제어하는 여러가지 제어 flag를 가지고 있다. 대표적인 flag로는 PG (Paging. paging 사용여부 설정), PE (Protection Enable, 보호모드를 사용하는지 여부)가 있다.
    • CR1 : reserved
    • CR2 : page fault가 발생하였을 때 이것이 발생한 linear address를 가지고 있다.
    • CR3 : page directory가 시작하는 physical address를 가지고 있다.
    • CR4 : 아키텍쳐별로 확장한 여러가지 flag들을 가지고 있다.
  • Descriptor Table Register :
    • GDTR (Global Descriptor Table Register) : GDT의 위치를 가리키는 register. LGDT (Load GDT), SGDT (Store GDT) 명령으로 참조하고 설정한다.
    • LDTR (Local Descriptor Table Register) : GDT안에 LDT descriptor를 가리키는 selector. LLDT (Load LDT), SLDT (Store LDT) 명령으로 참조하고 설정한다. 내부적으로 segment의 base address와 segment limit도 가지고 있지만 외부에서는 selector 값만을 참조할 수 있다.
    • IDTR (Interrupt Descriptor Table Register) : IDT의 위치를 가리키는 register LIDT (Load IDT), SIDT (Store IDT) 명령으로 참조하고 설정한다.
  • Task Register : TR
      TSS (Task State Segment)가 있는 위치를 가리키는 GDT 내의 TSS descriptor를 가리키는 selector. 내부적으로 segment의 base address와 segment limit도 가지고 있지만 외부에서는 selector 값만을 참조할 수 있다. LTR (Load TR), STR (Save TR) 명령으로 참조하고 설정한다.
  • Test Register :
    • TR1 : test parity check
    • TR2, TR3, TR4, TR5 : cache test register
    • TR6, TR7 : TLB (Translation Look-aside Buffers) test registers
    • TR9, TR10, TR11 : BTB (Branch Target Buffers) test registers
    • TR12 : new feature control
  • Debug Register :
    • DR0, DR1, DR2, DR3 : debug address register. breakpoint의 linear address를 가지고 있다.
    • DR4, DR5 : reserved
    • DR6 : debug status register
    • DR7 : debug control register
  • Model Specific Registers (MSRs) : 

     

4. 보호모드에서의 메모리 관리 

보호모드에서는 segmentation과 paging 메커니즘을 이용하여 메모리를 관리한다. segmentation은 4GB의 메모리를 segment라는 단위로 쪼개는 것을 말한다. 여기서는 16 bit의 selector와 32 bit의 offset을 이용하여 4GB 범위안에 있는 32 bit의 선형주소(linear address)를 만드는 일을 한다. 이렇게 만들어진 선형주소가 물리주소(physical address)가 되는 것이 아니라 메모리를 4KB 단위로 쪼개서 관리하는 paging mechanism을 거쳐서 물리주소로 변환된다. (control register CR0의 PG flag를 수정함으로써 paging 메커니즘을 사용하지 않을 수도 있다) 

그림 4-1. 보호모드에서의 메모리 변환과정 
 
segment는 segment descriptor라는 것으로 정의가 된다. 

여기에는 segment의 base address와 segment limit (크기), 그리고 DPL (descriptor privilege level)을 비롯한 정보가 들어간다. (여기에 보면 segment type이라는 것이 있다. 이것은 이 descriptor가 나타내는 것이 무엇인지를 말하는 것이다. segment는 일반적인 code/data/stack일 수도 있고, 뒤에 나오는 TSS 일수도, gate일 수도 있는데 이 type을 가지고 무엇을 가리키는 descriptor인지 구별할 수 있으며, 이에 따라 descriptor의 구조가 달라진다.) 

segment descriptor table은 이들 segment descriptor를 모아두고 있는 것을 말하는데, 여기에는 시스템 전체에 대한 descriptor table인 GDT (global descriptor table)과 프로세스마다 개별적으로 정의하고 있는 LDT (local descriptor table)이 있다. 이들은 모두 메모리 상에 존재하게 되는데, GDT의 위치는 GDTR (GDT register)가 가리키고 있다. LDT의 위치는 LDTR (LDT register)가 가리키고 있는데 이 LDTR은 원래 GDT에 있는 한 segment descriptor를 가리키고 있는 selector이다. 즉 GDT에는 시스템에 있는 모든 LDT에 대한 segment descriptor가 들어있으며, LDTR은 이 중 현재 프로세스의 LDT에 대한 segment descriptor를 가리키는 selector이다. 

16 bit인 selector에는 이 descriptor table에서의 index 값과, 이것이 이것이 GDT에서의 index인지, LDT에서의 index인지를 나타내는 TI (table indicator), 권한을 나타내는 RPL (requestor privilege level)이 들어있다. 이 TI와 index를 가지고 해당하는 table에서 segment descriptor를 찾아서 base address를 구하게 된다. 이 값에 offset을 합하면 지정한 selector와 offset에 해당하는 linear address가 만들어지게 된다. 


그림 4-2. Selector

그림 4-3. Segment Descriptor

그림 4-4. descriptor table 참조

그림 4-5. 가상주소에서 선형주소로의 변환

이렇게 segmentation을 거쳐 나온 linear address는 paging 메커니즘을 통하여 physical address로 변환이 된다. 

paging은 요즘의 운영체제에서 모두 구현하고 있는 paging을 지원하기 위한 것이다. 이를 이용하여 실제로 하드웨어적으로 있는 메모리보다 많은 메모리를 사용할 수 있으며, 메모리를 효율적으로 사용할 수 있으며, swapping을 쉽게 구현할 수 있게 된다. 메모리는 4KB 단위의 page로 쪼개지며, 이들은 page directory와 page table로 체계화된다. 

각 page들의 시작 위치는 page table entry에 기록이 되며, 각 page table들의 위치는 page directory entry에 들어있다. page directory의 시작 위치는 control register중의 하나인 CR3이 가지고 있다. 앞에서 넘어온 linear address는 page directory에서의 index, page table에서의 index, offset 세가지로 쪼개지며, 이들 table들을 차례로 따라가서 실제 page의 주소를 얻고, 여기에 offset을 더함으로써 실제 physical address가 나오게 된다. 

그림 4-6. 선형주소에서 물리주소로의 변환 
 

이렇게 가상주소가 물리주소로 변환되는 과정을 정리하면 다음과 같다. 


그림 4-7. 가상주소에서 물리주소로의 변환


5. 보호모드에서의 태스크 관리
 

보호모드에서는 각 task별로 TSS (Task State Segment)라는 것을 관리한다. 여기에는 하나의 task의 상태 - 일반 register, segment register, flag, EIP, stack segment selector, stack pointer, LDT selector, page directory base address 등 - 가 저장이 되며, task switching이 일어날 때 이전의 상태를 자동으로 여기에 저장을 하며, 새로운 TSS에 있는 상태가 현재 상태로 복구가 된다. task register인 TR은 현재 task의 TSS를 가리키는 selector이다. 각 TSS는 GDT에서 TSS descriptor로 기술되며, TR은 이 중에서 현재 TSS의 descriptor를 가리키는 것이다. 


그림 5-1. Task Register


task switching을 하는 방법으로 우선 call이나 jmp 명령에 전환할 TSS selector를 지정하는 것이 있다. 이렇게 하면 프로세서는 현재 상태를 현재 TSS에 저장을 하고, 새로운 task의 TSS로 상태를 모두 바꾼후, 새로운 task를 실행한다. 

task gate는 task switching을 일으키는 특별한 descriptor로서 LDT나 IDT (Interrupt Descriptor Table)에 들어가는 descriptor이다. 여기에는 TSS descriptor에 대한 selector가 들어있다. task gate는 interrupt나 trap이 발생하였을 때 task switching이 일어나게 하는 것처럼 간접적으로 task switching이 일어나게 하거나, 특정한 task들이 TSS에 접근할 수 있도록 하기 위해서 사용한다. 앞의 경우에서처럼 call이나 jmp에 task gate의 selector를 지정을 해주면 여기서 가리키는 TSS로 task switching이 일어난다. 또한 task gate가 IDT에 있을 때 해당하는 interrupt가 발생하면 이 task gate를 통하여 해당하는 TSS로 task switching이 일어나게 된다. 


그림 5-1. Task Gate


6. 보호모드에서의 인터럽트/예외 처리
 

인터럽트나 예외가 발생하면 어떤 것이 발생하였는지 식별하는 번호가 나오게 된다. (인터럽트는 CPU의 인터럽트 핀에 의하여 발생하며, 예외는 프로세서가 작업을 하는 중에 잘못된 일을 발견하거나 - 여기에는 fault, trap, abort가 있다 -, 프로그래밍으로 들어가 있는 코드에 의하여 - INT 3, INT n, BOUND 등 - 에 의하여 발생하는 것이다) 이 번호를 vector라고 부르는데 이를 index 삼아, IDT (interrupt descriptor table)에 있는 descriptor를 찾아서 이를 처리하게 된다. IDT는 interrupt 처리에 관련된 descriptor들을 모아둔 것으로 이의 위치는 IDTR (IDT register)가 가지고 있다. 

여기에 들어가는 descriptor로는 task gate, interrupt gate, trap gate가 있다. task gate는 앞에서 이야기한 것처럼 TSS selector를 가지고 있는 gate로서, 해당하는 TSS로 task switching이 일어나게 된다. interrupt gate와 trap gate는 GDT/LDT에 있는 segment descriptor에 대한 selector와 offset을 가지고 있는 gate로서, selector를 가지고 segment의 시작주소를 얻고, 여기에 offset을 더하여 이를 처리할 handler의 주소를 얻게 된다. 그리고 이 handler의 위치로 건너가서 해당하는 처리를 한 후 이를 마친후에 복귀하게 된다. 이 과정에서 privilege level이 바뀔 수도 있으며, 처리 중에는 현재 TSS에 있는 stack을 그대로 사용한다. interrupt gate와 trap gate를 통하는 경우의 차이는, interrupt gate를 통하는 경우 처리하는 동안 interrupt를 금지하고 (flag의 IF를 clear) 처리를 마치고 복귀할 때 다시 이를 복구하지만, trap gate를 통하는 경우에는 interrupt를 금지하지 않는다. 


그림 6-1. Interrupt Gate와 Trap Gate

그림 6-2. Interrupt 발생시 Task Gate


7. 보호모드에서의 시스템콜
 

시스템콜은 사용자모드에서 커널모드에서 실행되는 코드를 부르기 위한 방법이다. 이를 위해서 위해서 보호모드에서는 call gate라는 것을 제공한다. call gate는 다른 privilege level 사이로 제어권을 넘기는 방법으로, segment selector와 offset을 가지고 있다. 이 selector는 GDT/LDT에 있는 segment descriptor를 가리키고, 여기서 얻어지는 base address에 offset을 더하여 실행할 함수의 위치를 얻게 된다. call이나 jmp 명령에 목적하는 call gate의 segment selector를 지정함으로써 call gate를 통하여 커널모드로 진입하게 된다. 

이 때 CPL (current privilege level)과 call gate descriptor의 DPL, call gate selector의 RPL을 비교하여 권한을 검사한다. 시스템콜에 진입하였을 때에는 현재 task의 TSS에 있는 stack을 사용하며, ret 명령을 통해서 결과값과 함께 시스템콜을 부르기 이전의 상태로 되돌아가게 된다.

그림 7-1. Call Gate



'Embedded Lab > linux, x86' 카테고리의 다른 글

[vi편집기 자동정렬]  (0) 2012.05.01
[rm, rndir 옵션]  (0) 2012.04.11
[TSS(Task State Segment)]  (0) 2012.04.04
[extern 키워드]  (0) 2012.04.04
[cp 명령어옵션]  (0) 2012.04.03
Posted by cyj4369
,