트렐로에 정리

운영체제 1장 소개.pdf

운영체제 2장 프로세스와 스레드 관리.pdf

운영체제 3장 기억장치 관리.pdf

운영체제 4장 가상 메모리 관리.pdf


시스템 분석 중간.pdf



I.    시스템 분석 및 설계

1.    시스템 분석 설계란?

2.    시스템이란?

3.    시스템 개발 과정

4.    시스템 개발 방법론

5.    소프트웨어 개발 프로세스 모델

6.    프로젝트 팀 역할

II.   프로젝트 계획

1.    비즈니스 목표 설정

2.    프로젝트 선정

3.    타당성 분석

4.    규모 산정

5.    일정 계획

6.    조직 구성

III. 요구 분석

1.    개요

2.    요구 정의

3.    비즈니스 프로세스 분석

4.    요구 취합 방법

5.    요구 문서화




GetAsyncKeyState() 함수와 GetKeyState() 함수는 키보드의 키가 눌렸는지를 체크하는 함수들이다.

GetAsyncKeyState() 함수는 비 동기(Asynchronism)로 처리한다. 즉, 호출된 시점에서 키 상태를 조사하여, 메시지 큐를 거치지 않고 바로 리턴을 해 주므로 키 입력을 바로 처리해 줄 수가 있다.

GetKeyState() 함수는 호출된 시점에서 메시지 큐를 거치며, 메시지 발생 후의 상태를 리턴하게 되므로, 키보드 메시지 처리 루틴내에서 사용해야 한다. 게임과 같이 키 입력에 바로 반응을 해 줘야 하는 프로그램에서는 GetAsyncKeyState()함수를 사용하는 편이 좋다.

GetAsyncKeyState() 함수의 사용은

[code]if ( GetAsyncKeyState(VK_RETURN) )        // enter 키가 눌렀다면
// 처리            }
[/code]

이렇게 직접 쓰여도 되지만, 아래와 같은 형식으로 많이 사용한다.

[code]if ( GetAsyncKeyState(VK_RETURN) & 0x8000 )         // enter 키가 눌렀다면
// 처리            }
[/code]

왜? 0x8000으로 AND 연산을 통하여 키 눌림을 체크할까?

GetAsyncKeyState() 함수는, 키가 눌려진 시점에서 0x8000 값을 리턴해 준다. 그리고 함수가 호출되었을때 키가 눌려져 있었다고 0x0001 값을 리턴해 준다. 즉, 0x8000 은 현재 키가 눌려진 상태를 뜻하고, 0x0001은 지난번 호출과 이번 호출 사이에 키가 눌려진 적이 있었다 라는 것을 뜻한다.

예를 들어, 아래 방향키가 게임내에서 비행기도 움직이고, 메뉴에서 위아래 선택에도 쓰인다고 가정해 보자. AND 연산없이 그냥 사용하여 비행기를 움직이다가, F1키를 눌러 메뉴를 띄웠을 때 커서가 자동으로 아래쪽으로 내려갈 위험성이 생긴다. 왜냐하면, F1키를 누르는 시점에서 - if ( GetAsyncKeyState(VK_F1) ) - VK_DOWN은 눌리지 않았지만 리턴값이 0x0001이 되어 if ( GetAsyncKeyState(VK_F1) ) 전에 호출된 if ( GetAsyncKeyState(VK_DOWN) ) 이 참이 되기 때문이다. 하지만, 0x8000으로 AND 연산을 하면,

   1000 0000 0000 0000 (키눌림 0x8000)                0000 0000 0000 0001 (눌러진 적이 있음 0x0001)
& 1000 0000 0000 0000 (AND 연산 0x8000)           & 1000 0000 0000 0000 (AND 연산 0x8000)
---------------------------------------          -----------------------------------------------
   1000 0000 0000 0000 (키눌림 0x8000)                0000 0000 0000 0000 (키 안 눌림)

위와 같은 결과가 되어, 정확한 시점에서 키가 눌러진 상태를 체크할 수가 있는 것이다. 즉, 정확한 시점에서 키눌림 상태를 체크하기 위해서 0x8000으로 AND 연산을 해 주는 것이다.

 

GetKeyState() 함수의 사용 예

[code]// 키보드 메시지 처리루틴이 있는 콜백 함수 내에서...
// char szTemp[256];

case WM_KEYDOWN:
        // Ctrl + 왼쪽 화살표키가 눌렸는지 조사
        if ( wParam == VK_LEFT ) && (GetKeyState(VK_CONTROL) < 0) )
        {
                wsprintf(szTemp, "Ctrl키와 왼쪽 화살표 키가 눌림");
        }
        return 0;
[/code]

GetKeyState() 함수의 리턴값이 음수일 경우는 해당키가 눌린 상태이고, 음수가 아닐 경우는 해당키가 눌리지 않은 상태이다.

GetAsyncKeyState와 GetKeyState는 둘 다 키의 상태값을 알아내는 함수이다. 하지만 이 둘 사이에는 차이점이 있는데 다음과 같은 것들이다.

GetAsyncKeyState는 "키가 눌렸는가?"와 "언제부터 눌렸는가?"를 알아낼 때 사용한다. 키가 눌렸을 때 GetAsyncKeyState는 0x8000 bit가 1이된다. 그리고, 이전에 GetAsyncKeyState가 호출되었을 때부터 이번에 GetAsyncKeyState가 호출될 때까지 중간에 끊기지 않고 계속 눌려있는 상태라면 0x0001 bit는 0이 되고, 그렇지 않은 경우는 1이 된다.

1) CTRL 키가 눌린 상태이다.
2) GetAsyncKeyState(VK_CONTROL)를 호출하면 0x8001을 리턴한다.
3) GetAsycnKeyState(VK_CONTROL)을 한번 더 호출하면 0x8000을 리턴한다.

1) CTRL 키가 눌린 상태이다.
2) GetAsyncKeyState(VK_CONTROL)를 호출하면 0x8001을 리턴한다.
3) CTRL 키를 뗬다가 다시 눌렸다.
4) GetAsycnKeyState(VK_CONTROL)을 한번 더 호출해도 0x8001을 리턴한다.

키가 눌리지 않았을 때, GetAsyncKeyState는 항상 0x0000을 리턴한다.


GetKeyState는 "키가 눌렸는가?"와 "키의 토글상태가 무엇인가?"를 알아낼 때 사용한다. 키의 토글 상태란 Caps Lock, Num Lock의 키가 한번 누르면 불이 켜지고 다시 한번 누르면 불이 꺼지는 것을 생각하면 이해가 빠를것 같다.
키보드 상에는 위에서 말한 키만 불이 켜졌다 꺼졌다하면서 토글상태를 알려주고 있지만 다른 키들도 토글 상태를 갖고 있으며 단지 우리 눈에 보이지 않을 뿐이다. 이 토글 상태를 GetKeyState 함수로 알아낼 수 있다.

GetKeyState의 리턴값은 SHORT가 아닌 CHAR로 생각된다.
리턴값을 살펴보면 0xffffff81, 0xffffff80, 0x00000001, 0x00000000이 되는 것을 볼 수 있기 때문이다.
참고로 같은 SHORT형을 리턴하는 GetAsyncKeyState의 리턴값은 다음과 같다.
0xffff8001, 0xffff8000, 0x00000000 셋 중 하나

** MSDN에는 현재 하드웨어(키보드)의 상태값을 알아내길 원한다면 GetAsyncKeyState를 사용하라고 나와있다. GetKeyState는 메시지큐에 저장된 메시지에 따라 값이 변하기 때문이다.
메시지큐에 처리되지 않은 CTRL키가 눌렸다는 메시지가 쌓여있을 때,사용자가 CTRL키를 더이상 누르지 않고 있다면. GetAsyncKeyState는 "키가 눌리지 않았음"을, GetKeyState는 "키가 눌렸음"을 리턴할 것이다.

컬렉션 프레임 워크의 등장배경

 - 배열은 쉽게 생성하고 사용할 수 있지만, 저장할 수 있는 객체 수가 배열을 생성할 때 결정되기 때문에 불특정 다수의 객체를 저장하기에 문제가 있다.

 - 배열의 또 다른 문제점은 객체를 삭제했을 때 해당 인덱스가 비게 될 수 있다.

 - 배열의 문제점을 해결하고, 널리 알려져 있는 자료구조(Data Structure)를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록
java.util 패키지에 컬렉션과 관련된 인터페이스와 클래스들을 포함시켜 놓았다.


자바 컬렉션이란 ?

 - 객체를 수집해서 저장하는 역할을 한다.

 - 프레임워크란 사용 방법을 미리 정해놓은 라이브러리를 말한다.

 - 컬렉션 프레임워크의 주요 인터페이스로는 List, Set, Map이 있다.



List 인터페이스의 메소드


기능

메소드

설명

객체 추가

boolean add(E e)

주어진 객체를 맨 끝에 추가

void add(int index, E element)

주어진 인덱스에 객체를 추가

set(int index, E element)

주어진 인덱스에 저장된 객체를 주어진 객체로 바꿈

객체 검색

boolean contains(Object o)

주어진 객체가 저장되어 있는지 여부

E get(int index)

주어진 인덱스에 저장된 객체를 리턴

isEmpty()

컬렉션이 비어 있는지 조사

int size()

저장되어 있는 전체 객체 수를 리턴

객체 삭제

void clear()

저장된 모든 객체를 삭제

E remove(int index)

주어진 인덱스에 저장된 객체를 삭제

boolean remove(Object o)

주어진 객체를 삭제


 - List 컬렉션은 객체를 일렬로 늘어놓은 구조를 가지고 있다.

 - 객체를 인덱스로 관리하기 때문에 객체를 저장하면 자동 인덱스가 부여되고 인덱스로 객체를 검색, 삭제할 수 있는 기능을 제공한다.

 - List는 객체 자체를 저장하는 것이 아니라 객체의 번지를 참조한다.

 - 동일한 객체를 중복 저장할 수 있는데, 이 경우 동일한 번지가 참조된다.

 - null도 저장이 가능한데, 이 경우 해당 인덱스는 객체를 참조하지 않는다.


List 사용법

 1. 객체 추가 - add(int index, E element), add(E e)

   1) 맨 끝 추가

List<Board> list = new Vector<Board>();
list.add(new Board("제목1", "내용1", "글쓴이1"));


   2) 인덱스 추가

list.add(1, new Board("제목5", "내용5", "글쓴이5"));


 2. 객체 검색 - get(int index), contains(Object o)

  - 인덱스로 객체 찾기

String str = list.get(3).toString();


  - 특정 객체가 존재하는지 확인

list.contains(Object o);


 3. 객체 삭제 - E remove(int index), remove(Object o)

  1) 인덱스로 객체 삭제

list.remove(2);


  2) 객체 이름으로 삭제

list.remove(Object o);


 4. 객체 출력(for)

  1) for문

System.out.println("for문");

for(int i = 0 ; i < list.size() ; i++){

System.out.println(list.get(i).toString());

}


  2) each for문

System.out.println("each for문");

for(Board index : list){


System.out.println(index.toString());

}



ArrayList


 - List 인터페이스의 구현 클래스

 - ArrayList에 객체를 추가하면 객체가 인덱스로 관리

 - 배열과 다르게 자동적으로 저장 용량이 늘어난다.

 - 기본 생성자로 ArrayList 객체를 생성하면 내부에 10개의 객체를 저장할 수 있는 초기 용량을 가지게 된다. (처음부터 용량을 크게 잡을 수도 있음)

 - 일반적으로 컬렉션에는 단일 종류의 객체들만 저장된다.


 - ArrayList에 객체 추가하면 인덱스 0부터 차례대로 저장된다.

 - ArrayList에서 특정 인덱스의 객체를 제거하면 바로 뒤 인덱스부터 마지막 인덱스까지 모두 앞으로 1씩 당겨진다.

 - 특정 인덱스에 객체를 삽입하면 해당 인덱스부터 마지막 인덱스까지 모두 1씩 밀려난다.

 - 따라서 빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList를 사용하는 것이 바람직하지 않다.

 - 인덱스 검색이나, 맨 마지막에 객체를 추가하는 경우에 사용하기에 좋다.


ArrayList 사용법

 1. 객체 추가 - add(int index, E element), add(E e)

   1) 맨 끝 추가

   2) 인덱스 추가


 2. 객체 검색 - get(int index), contains(Object o)

  1) 특정 인덱스의 객체 얻기

  2) 특정 객체가 포함되어있는지 확인


 3. 객체 삭제 - E remove(int index), remove(Object o)

  1) 특정 인덱스의 객체 삭제

  2) 특정 객체 삭제


 4. 객체 출력

  1) size()

  2) for

  3) each for


Vector

 - ArrayList와 다르게 Vector는 동기화된(synchronized) 메소드로 구성되어 있기 때문에 멀티 스레드가 동시에 이 메소드들을 실행 할 수 없다.

 - 하나의 스레드가 실행을 완료해야만 다른 스레드를 실행할 수 있다.

 - 멀티 스레드 환경에서 안전하게 객체를 추가, 삭제할 수 있다.


Vector 사용법

 1. 객체 추가 - add(E e)

 2. 객체 검색

 3. 객체 삭제

 4. 객체 출력


왜 제네릭을 사용해야 하는가?

 - Java 5부터 제네릭(Generic) 타입이 새로 추가 되었다.

 - 제네릭 타입을 이용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있게 되었다.

 - 제네릭은 컬렉션, 람다식, 스트림, NIO, API Document에서 많이 쓰이기 때문에 확실하게 이해해 두어야 한다.


 - 제네릭은 클래스와 인터페이스, 그리고 메소드를 정의할 때 타입(type)을 파라미터(Parameter)로 사용할 수 있도록 한다.


제네릭의 강점

 - 컴파일 시 강한 타입 체크를 할 수 있다.

  : 자바 컴파일러가 코드에서 잘못 사용된 타입 때문에 발생하는 문제점을 제거하기 위해 제네릭 코드에 대해 강한 타입 체크를 하여 

  : 실행 시 타입 에러가 나는 것보다는 컴파일 시미리 타입을 강하게 체크해서 에러를 사전에 방지하도록 한다.


 - 타입 변환(casting)을 제거한다.

  : 비제네릭 코드는 불필요한 타입 변환을 하기 때문에 프로그램 성능에 악영향을 미친다.

  : 비제네릭 코드와 제네릭 코드의 차이


 > 비 제네릭 코드


void defaultCasting() {

List list = new ArrayList(); // 비제네릭 코드

list.add("Hello");

String str = (String) list.get(0); // 형변환

System.out.println(str);

}


 > 제네릭 코드 


void genericCasting() {

List<String> list = new ArrayList<>(); // 제네릭 코드

list.add("Hello");

String str = list.get(0);


}



Thread

프로세스

 : 프로세스프로그램에 대한 각각의 인스턴스를 의미하므로, 같은 프로그램을 여러 개 띄웠다고 해서 하나의 프로세스를 공유하는 것은 아니다.

 : 프로세스는 운영체제로부터 주소공간, 파일, 메모리 등을 할당받는다.

 : 간단히 말하면 실행중인 프로그램

 : 프로그램을 수행하는데 필요한 데이터, 메모리, 자원, 쓰레드로 구성되어 있다.

쓰레드

 : 프로세스의 자원으로 실제 작업을 실행하는 것이 쓰레드이다.

 : 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소 공간이나 자원들을 대부분 공유하면서 실행된다.


프로세스와 쓰레드

 > 프로세스는 자신만의 고유 공간과 자원을 할당 받아 사용하는데 비해 스레드는 다른 스레드와 공간과 자원을 공유하여 사용한다.


Thread 사용의 장점

 : 스레드는 프로세스내에서 각각의 스택 공간을 제외한 나머지 공간과 시스템 자원을 공유한다.
 : 그러므로 프로세스를 이용하여 동시에 처리하던 일을 스레드로 구현할 경우 메모리 공간은 물론 시스템 자원 소모도 현격히 줄어든다.

 : 스레드 간의 통신이 필요할 경우 별도의 자원을 이용하는 것이 아니라 전역 변수의 공간을 이용하여 데이터를 주고 받을 수 있다.


 - 시스템의 처리량이 향상된다.

 - 시스템의 자원 소모가 줄어든다.

 - 프로그램의 응답 시간이 단축된다.

 - 프로세스 간 통신 방법에 비해 스레드 간의 통신 방법이 훨씬 간단하다.

 ( 스레드 간의 통신 시 데이터를 주고받는 방법은 메모리 공간을 공유하므로 데이터 세그먼트, 즉 전역 변수를 이용하여 구현한다. )

 (공유하는 전역 변수를 여러 스레드가 함께 사용하기 위해서는 충돌 문제가 발생할 수 있는데 이런 문제가 발생하지 않도록 동기화 문제를 해결해야 한다.)


Thread 사용의 단점

 : 여러 개의 스레드를 이용하는 프로그램을 작성하는 경우 주의 깊게 설계해야 한다. 

 : 미묘한 시간 차나 잘못된 변수를 공유함으로써 오류가 발생할 수 있다.



자바에서의 Thread

 1. 쓰레드 클래스 상속하기 (다른 클래스를 상속받을 수 없다.)


public class ThreadEx extends Thread {


@Override

public void run() { // Thread 코드


}


public static void main(String[] args) {

new ThreadEx().start(); // 쓰레드 시작 .start();


}

}


 2. Runnable 인터페이스 구현하기 (재사용성이 높으며, 일관성 유지가능)


public class ThreadEx implements Runnable {


@Override

public void run() { // Thread 코드


}


public static void main(String[] args) {

ThreadEx tEx = new ThreadEx();

Thread t = new Thread(tEx); // 쓰레드 구현

t.start();

}

}


 3. 쓰레드의 특징

  - start() vs run()

  - start()

 : 새로운 스레드가 작업을 실행하는데 필요한 호출스택을 생성한 후 run()을 호출 해 새로 생성한 호출스택 첫번째로 run()을 저장한다.

  - run()

 : 스레드를 실행시키는게 아니라 클래스에 속한 메서드 하나를 호출하는 것


 ※ 모든 쓰레드는 독립적인 작업 수행을 위해 자신만의 호출 스택을 필요로 하기 때문에 

새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 호출 스택은 소멸된다.


run() 메소드 호출 시 Main() 위에 Run() 메소드를 하나 호출하는 것 

start()할 때 독립적인 작업 수행을 위해

쓰레드마다 각자의 콜 스택을 생성

run() 메소드를 실행한다.


※ 4개의 호출스택(Thread)가 번갈아 가며 실행하는

 멀티쓰레드(다중처리처럼 보이는 것)


 4. Thread의 동기화(synchronized)

 : 싱글스레드의 경우 프로세스 내 단 하나의 스레드만 작업하기 때문에 자원을 이용하는 데에 문제가 없지만 멀티 스레드일 경우 같은 자원을 공유할 때 문제가 생길 수 있다.

 : 그렇기 때문에 현재 작업 시 lock을 걸어 다른 스레드가 접근하지 못하게 하는 작업이 필요하다. 이 작업을 '동기화(synchronized)'라고 한다.


 - 하나의 객체에 다수의 Thread가 접근 시

  Synchronized(객체 참조변수) { }

 - 하나의 메서드 내에서 객체 참조를 하는 경우

  public Synchronized void 메소드명( ) { }



MultiThread Example Code




public class Multithread {


SharedArea area; // 공유 객체 선언


public Multithread() {

area = new SharedArea(); // 공유 객체 생성

area.account1 = new Account("111-111-1111", "이몽룡", 20000000);

area.account2 = new Account("222-222-2222", "성춘향", 10000000);

TransferThread thread1 = new TransferThread(area); // 입/출금 Thread 객체

thread1.start();

PrintThread thread2 = new PrintThread(area); // 결과 출력 Thread 객체

thread2.start();

}


public static void main(String[] args) {

new Multithread();

}

}


/**

 *

 * @author SeokRae

 * @Description 출력 Thread

 */

class PrintThread extends Thread {

SharedArea sharedArea;


public PrintThread(SharedArea area) {

sharedArea = area;

}


@Override

public void run() {

for (int cnt = 0; cnt < 12; cnt++) {

int sum = sharedArea.getTotal();

System.out.println("계좌 잔액 합계 : " + sum);

try {

Thread.sleep(1); // 두 Thread의 실행 시간을 맞추기 위해 1/1000 초간 시간을 지연시킨다.

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}


/**

 * 

 * @author SeokRae

 * @description 입/출금

 */

class TransferThread extends Thread {

SharedArea sharedArea; // 공유 객체 선언


public TransferThread(SharedArea area) {

this.sharedArea = area;

}


@Override

public void run() {

for (int cnt = 0; cnt < 12; cnt++) {

sharedArea.transfer(100); // 계좌 이체 메소드 호출

try {

Thread.sleep(1); // 두 Thread의 실행 시간을 맞추기 위해 1/1000 초간 시간을 지연시킨다.

} catch (InterruptedException e) {

e.printStackTrace();

}

}


}

}


/**

 * 

 * @author SeokRae

 * @description 계좌정보

 */

class Account {

String accountNo; // 계좌번호

String ownerName; // 예금주 이름

int balance; // 잔액


// shift + art + s > o

// 필드 값을 모두 사용하는 생성자


public Account(String accountNo, String ownerName, int balance) {

this.accountNo = accountNo;

this.ownerName = ownerName;

this.balance = balance;

}


void deposit(int amount) { // 입금

balance += amount; // 예금액을 잔액에 추가

}


int withdraw(int amount) { // 출금

if (balance < amount) { // 잔액값보다 출금액이 클 경우

return 0;

}

balance -= amount; // 잔액에서 출금액을 뺀다.

return amount;

}

}


/**

 * 

 * @author SeokRae

 * @description 공유 객체

 * @function 계좌 입/출력, 합계

 */

class SharedArea {

Account account1;

Account account2;


synchronized void transfer(int amount) {

// synchronized(this){

account1.withdraw(amount * 10000);

System.out.print("이몽룡 계좌 : " + amount + "만원 인출 ,");

account2.deposit(amount * 10000);

System.out.println("성춘향 계좌 : " + amount + "만원 입금");

// }

}


synchronized int getTotal() { // 잔액의 합계를 구한다.

return account1.balance + account2.balance;

}


}




네트워크 중간고사 정리.docx


+ Recent posts