728x90
C++
클래스(Class)
- 키워드 struct를 대신해서 class를 사용하면, 구조체가 아닌 클래스가 된다.
- 클래스는 기본적으로(별도의 선언을 하지 않으면) 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근이 가능하다.
- 클래스는 정의를 하는 과정에서 각각의 변수 및 함수의 접근 허용범위를 별도로 선언해야 한다. 이것이 struct를 이용해서 정의하는 구조체와 class를 이용해서 정의하는 클래스의 차이점이다.접근제어 지시자(접근제어 레이블)
- C++의 접근제어 지시자는 다음과 같이 총 세가지가 존재한다.
- public : 어디서든 접근허용
- protected : 상속관계에 놓여있을 때, 유도 클래스에서의 접근 허용
- private : 클래스 내(클래스 내에 정의된 함수)에서만 접근허용
- 접근제어 지시자가 선언되면, 그 이후에 등장하는 변수나 함수는 지시자에 해당하는 범위 내에서 접근이 가능하다.
- 함수의 정의를 클래스 밖으로 빼도, 이는 클래스의 일부이기 때문에, 함수 내에서는 private으로 선언된 변수에 접근이 가능하다.
- 키워드 struct를 이용해서 정의한 구조체에 선언된 변수와 함수에 별도의 접근제어 지시자를 선언하지 않으면, 모든 변수와 함수는 public으로 선언된다.
- 키워드 class를 이용해서 정의한 클래스에 선언된 변수와 함수에 별도의 접근제어 지시자를 선언하지 않으면, 모든 변수와 함수는 private으로 선언된다.
멤버변수, 멤버함수
- 클래스를 구성하는(클래스 내에 선언된) 변수를 가리켜 멤버변수라고 한다.
- 클래스를 구성하는(클래스 내에 정의된) 함수를 가리켜 멤버함수라고 한다.
C++에서의 파일분할
- C++은 클래스 별로 헤더파일과 소스 파일을 생성해서 클래스의 선언과 정의를 분리하는 경우가 많기 때문에 많은 수의 파일이 만들어진다.
- .h 클래스의 선언을 담는다.
- .cpp 클래스의 정의(멤버함수의 정의)를 담는다.
- 이를 가르켜 클래스의 선언(declaration)이라 한다.
- 클래스와 관련된 문장의 컴파일 정보로 사용되는 클래스의 선언은 헤더파일에 저장을 해서, 필요한 위치에 쉽게 포함될 수 있도록 해야 하며, 클래스의 정의는 소스 파일에 저장해서, 컴파일이 되도록 하면 된다.
객체지향 프로그래밍
객체지향 프로그래밍의 이해
- 객체지향 프로그래밍은 현실에 존재하는 사물과 대상, 그리고 그에 따른 행동을 있는 그대로 실체화 시키는 형태의 프로그래밍이다.
- 객체는 하나 이상의 상태 정보(데이터)와 하나 이상의 행동(기능)으로 구성이 되며, 상태 정보는 변수를 통해서 표현이 되고(변수에 상태 정보를 저장할 수 있으므로), 행동은 함수를 통해서 표현이 된다.
- 클래스의 멤버변수 선언문에서 초기화까지 하는 것을 허용하지 않는다.
클래스 기반의 두 가지 객체생성 방법
- 일반적인 변수의 선언방식
- 동적 할당방식
객체간의 대화 방법
- 하나의 객체가 다른 하나의 객체에게 메시지를 전달하는 방법은(어떠한 행위의 요구를 위한 메시지 전달) 함수호출을 기반으로 한다. 그래서 객체지향에서는 이러한 형태의 함수호출을 가리켜 메시지 전달(Message passing)이라 한다.
정보은닉
- 제한된 방법으로의 접근만 허용을 해서 잘못된 값이 저장되지 않도록 도와야 하고, 또 실수를 했을 때 실수가 쉽게 발견되도록 해야 한다.
- 멤버변수를 private으로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버변수의 접근을 유도하는 것이 바로 정보은닉이다.
- 해당 변수에 접근하는 함수를 엑세스 함수(access function)이라고 하는데, 이들은 멤버변수를 private으로 선언하면서 클래스외부에서의 멤버변수 접근을 목적으로 정의되는 함수들이다.
const 함수
- 이 함수 내에서는 멤버변수에 저장된 값을 변경하지 않겠다는 것을 의미한다. 매개변수도 아니고, 지역변수도 아닌, 멤버변수에 저장된 값을 변경하지 않겠다는 선언이다. const 선언이 추가된 멤버함수 내에서 멤버변수의 값을 변경하는 코드가 삽입되면, 컴파일 에러가 발생한다.
- 이렇게 함수를 const로 선언하면, 실수로 자신의 의도와 다르게 멤버변수의 값을 변경했을 때, 컴파일 에러를 통해서 이를 확인할 수 있다.
- const 함수 내에서는 const가 아닌 함수의 호출이 제한된다. const로 선언되지 않은 함수는 아무리 멤버변수에 저장된 값을 변경하지 않더라도, 변경할 수 있는 능력을 지닌 함수이다. 따라서 이러한 변경이 가능한 함수의 호출을 아예 허용하지 않는다.
생성자
- 클래스의 이름과 함수의 이름이 동일하고 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다면 이러한 유형의 함수를 가리켜 생성자(constructor)라 하며 객체 생성시 딱 한번 호출된다.
- 생성자도 함수의 일종이니 오버로딩이나 매개변수에 디폴트 값을 설정할 수 있다
멤버 이니셜라이저(Member Initializer)
- 멤버 이니셜라이저는 멤버변수로 선언된 객체의 생성자 호출에 활용된다.
- 객체의 생성과정
- 메모리 공간의 할당
- 이니셜라이저를 이용한 멤버변수(객체)의 초기화
- 생성자의 몸체부분 실행
- 멤버 이니셜라이저는 객체가 아닌 멤버의 초기화에도 사용할 수 있다. 따라서 프로그래머는 생성자의 몸체에서 초기화 하는 방법과 이니셜라이저를 이용하는 초기화 방법 중에서 선택이 가능하다. 그러나 일반적으로 멤버변수의 초기화에 있어서는 이니셜라이저를 선호하는 편이다.
- 초기화의 대상을 명확히 인식할 수 있다.
- 성능에 약간의 이점이 있다.
- 이니셜라이저를 이용하면 선언과 동시에 초기화가 이뤄지는 형태로 바이너리 코드가 생성된다. 반면, 생성자의 몸체부분에서 대입연산을 통한 초기화를 진행하면, 선언과 초기화를 각각 별도의 문장에서 진행하는 형태로 바이너리 코드가 생성된다.
- const 멤버변수도 이니셜라이저를 이용하면 초기화가 가능하다.
디폴트 생성자(Default Constructor)
- 메모리 공간의 할당 이후에 생성자의 호출까지 완료되어야 객체라고 할 수 있다. 즉, 객체가 되기 위해서는 반드시 하나의 생성자가 호출되어야 한다.이러한 기준에 예외를 두지 않기 위해서 생성자를 정의하지 않는 클래스에는 C++ 컴파일러에 의해서 디폴트 생성자가 자동으로 삽입된다. 디폴트 생성자는 인자를 받지 않으며, 내부적으로 아무런 일도 하지 않는 생성자이다.
private 생성자
- 클래스 내부에서만 객체의 생성을 허용하려는 목적으로 생성자를 private으로 선언하기도 한다.
- 객체의 생성방법을 제한하고자 하는 경우에는 매우 유용하게 사용된다.
소멸자
- 소멸자는 클래스의 이름 앞에 ~가 붙은 형태의 이름을 갖는다.
- 반환형이 선언되어 있지 않으며, 실제로 반환하지 않는다.
- 매개변수는 void형으로 선언되어야 하기 떄문에 오버로딩도, 디폴트 값 설정도 불가능하다.
- 소멸자는 객체소멸과정에서 자동으로 호출이 된다. 그리고 프로그래머가 직접 소멸자를 정의하지 않으면, 디폴트 생성자와 마찬가지로 아무런 일도 하지 않는 디폴트 소멸자가 자동으로 삽입된다.
- 이러한 소멸자는 대개 생성자에서 할당한 리소스의 소멸에 사용된다.
this 포인터
- 멤버함수 내에서는 this라는 이름의 포인터를 사용할 수 있는데, 이는 객체 자신을 가리키는 용도로 사용되는 포인터이다.
책
이것이 컴퓨터 과학이다.
동기화와 교착 상태
- 프로세스 혹은 스레드가 공유하는 자원을 공유 자원(shared resource)라고 한다. 공유 자원은 메모리나 파일이 될 수도 있고, 전역 변수나 입출력장치가 될 수ㅜ도 있다.
- 다수의 프로세스 혹은 스레드가 동시에 공유 자원에 접근할 경우 실행에 문제가 발생할 수 있다. 공유 자원에 접근하는 코드 중 동시에 실행했을 때 문제가 발생할 수 있는 코드는 임계 구역(critical section)이라고 한다. 동시에 실행되는 프로세스나 스레드가 동시에 임계 구역에 진입하여 실행되면 문제가 발생할 수 있다.
- 프로세스 혹은 스레드가 동시에 임계 구역의 코드를 실행하여 문제가 발생하는 상황을 레이스 컨디션(race condition)이라고 한다. 레이스 컨디션이 발생하면 자원의 일관성이 손상될 수 있기 때문에 2개 이상의 프로세스 혹은 스레드가 임계 영역에 진입하고자 한다면 둘 중 하나는 작업이 끝날 떄 까지 대기해야 한다.
- 레이스 컨디션을 방지하면서 임계 구역을 관리하기 위해서는 프로세스와 스레드가 동기화되어야 한다. 프로세스 혹은 스레드의 동기화(synchronization)란 다음의 2가지 조건을 준수하며 실행하는 것을 의미한다.
- 실행 순서 제어 : 프로세스 및 스레드를 올바른 순서로 실행하기
- 상호 배제 : 동시에 접근해서는 안 되는 자원에 하나의 프로세스 및 스레드만 접근하기
동기화 기법
- 뮤텍스 락(mutex lock)
- 동시에 접근해서는 안 되는 자원에 동시 접근이 불가능하도록 상호 배제를 보장하는 동기화 도구이다.
- 임계 구역에 접근하고자 한다면 반드시 락(lock)을 획득(acquire)해야 하고, 임계 구역에서의 작업이 끝났다면 락을 해제(release)해야 한다.
- 세마포어(semaphore)
- 공유 자원이 여러 개 있는 상황에서도 동기화가 가능하다. 세마포어는 뮤텍스 락과 비슷하게 하나의 변수와 2개의 함수로 구성된다.
- 변수 S : 사용 가능한 공유 자원의 개수를 나타내는 변수
- Wait() 함수 : 임계 구역 진입 전 호출하는 함수
- Signal() 함수 : 임계 구역 진입 후 호출하는 함수
- 이때 사용 가능한 공유 자원의 개수는 임계구역에 진입할 수 있는 프로세스의 개수와 같다.
- wait()는 함수 호출 시 가장 먼저 사용 가능한 공유 자원의 개수를 나타내는 변수 S를 1 감소시키고, 변수 S의 값이 0보다 작은지 여부를 확인한다. S를 1 감소시켰을 때 S가 0 이상이라는 것은 사용 가능한 공유 자원의 개수가 남아 있었음을 의미한다. 이 경우, wait()를 호출한 프로세스 및 스레드는 임계 구역에 진입한다. 반대로 S를 감소시켰을 때 S가 0 미만이라는 것은 사용 가능한 공유 자원의 개수가 남아 있지 않았음을 의미한다. 따라서 변수 S의 값이 0보다 작으면 wait()를 호출한 프로세스 및 스레드는 대기 상태로 전환되어 임계 구역에 진입할 수 없게 된다.
- signal() 함수는 임계 구역에서의 작업이 끝난 프로세스 및 스레드가 호출한다. 호출 시 변수 S를 1 증가시키고, 변수 S의 값이 0 이하인지를 확인한다 S를 1 증가시켰을 때 S가 0보다 크다는 것은 사용 가능한 공유 자원의 개수가 1개 이상 남아 있음을 의미하며, 반대로 S를 1 증가시켰을 때 0 이하라는 것은 임계 구역에 진입하기 위해 대기하는 프로세스가 존재함을 의미한다. 이 경우, 대기 상태로 접어든 프로세스 중 하나를 준비 상태로 전환한다.
- 공유 자원이 여러 개 있는 상황에서도 동기화가 가능하다. 세마포어는 뮤텍스 락과 비슷하게 하나의 변수와 2개의 함수로 구성된다.
- 조건 변수(condition variable)과 모니터(monitor)
- 조건 변수란 실행 순서 제어를 위한 동기화 도구로, 특정 조건 하에 프로세스를 실행/일시 중단함으로써 프로세스나 스레드의 실행 순서를 제어할 수 있다. 조견 변수에 대해 wait()와 signal()함수를 호출할 수 있다.
- 아직 특정 프로세스가 실행될 조건이 되지 않았을 때는 wait()를 통해 실행을 중단한다.
- 특정 프로세스가 실행될 조건이 충족되었을 때는 signal()을 통해 실행을 재개한다.
- 모니터(monitor)는 공유 자원과 그 공유 자원을 다루는 함수(인터페이스)로 구성된 동기화 도구로, 상호배제를 위한 동기화뿐만 아니라 실행 순서 제어를 위한 동기화까지 가능하다. 프로세스 및 스레드는 공유 자원에 접근하기 위해 반드시 정해진 공유 자원 연산(인터페이스)을 통해 모니터로 진입해야 하고, 모니터 안에 집입하여 실행되는 프로세스 및 스레드는 항상 하나여야 한다. 이미 모니터 내로 진입하여 실행 중인 프로세스 및 스레드가 있다면 큐에서 대기해야 한다.
- 조건 변수란 실행 순서 제어를 위한 동기화 도구로, 특정 조건 하에 프로세스를 실행/일시 중단함으로써 프로세스나 스레드의 실행 순서를 제어할 수 있다. 조견 변수에 대해 wait()와 signal()함수를 호출할 수 있다.
- 스레드 안전(thread safety)
- 멀티스레드 환경에서 어떤 변수나 함수, 객체에 동시 접근이 이루어져도 실행에 문제가 없는 상태를 의미한다.
- 레이스 컨디션이 발생했다면 이는 스레드 안전하지 않은 상황이고, 반대로 어떤 함수가 스레드 안전하다면, 이는 여러 스레드에 의해 호출되어도 레이스 컨디션이 발생하지 않는 것을 의미한다.
교착 상태(DeadLock)
- 교착상태란 일어나지 않을 사건을 기다리며 프로세스의 진행이 멈춰버리는 현상을 의미한다.
- 교착 상태의 발생 조건
- 교착 상태가 발생하는 상황에는 4가지 필요조건이 있다. 상호 배제, 점유 대기, 비선점, 원형 대기이다. 4개의 조건이 모두 만족할 때 교착 상태가 발생할 가능성이 생긴다고 보면 된다.
- 상호 배제
- 한 프로세스가 사용하는 자원을 다른 프로세스가 사용할 수 없는 상황
- 점유와 대기
- 한 프로세스가 어떤 자원을 할당받은 상태(점유)에서 다른 자원 할당받기를 기다리는(대기) 상황
- 비선점
- 해당 자원을 이용하는 프로세스의 작업이 끝나야만 비로소 자원을 이용할 수 있는 상황
- 원형 대기
- 프로세스와 프로세스가 요청한 자원이 원을 이루는 형태. 프로세스가 서로 점유한 자원을 할당받기 위해 원의 형태로 대기할 경우 교착 상태가 발생할 수 있다.
- 교착 상태의 해결 방법
- 교착 상태 예방
- 교착 상태를 예방하는 방법은 교착 상태를 발생시키는 4가지 필요 조건 중 하나를 충족하지 못하게 하는 방법이다.
- 교착 상태 회피
- 교착 상태 회피는 교착 상태가 발생하지 않을 정도로만 조심하면서 자원을 할당하는 방법이다. 교착 상태 회피는 기본적으로 교착 상태를 한정된 자원의 무분별한 할당으로 인해 발생하는 문제로 간주한다.
- 교착 상태 예방
CPU 스케줄링
- 운영체제의 CPU 배분 방법이 바로 CPU 스케줄링이다 CPU 스케줄링 알고리즘은 이러한 CPU 스케줄링 절차를 말하며 CPU 스케줄링 알고리즘을 결정하고 수행하는 운영체제의 일부분을 CPU 스케줄러라고 한다.
우선순위
- 운영체제는 프로세스별 우선순위(priority)를 판단하여 PCB에 명시하고, 우선순위가 높은 프로세스에는 CPU의 자원을 더 빨리, 많이 할당한다.
- 운영체제는 CPU 활용률을 높게 유지할 수 있도록 우선순위를 할당한다. 운영체제는 높은 CPU 활용률을 유지하기 위해 기본적으로 입출력 작업이 많은 프로세스의 우선순위를 높게 유지한다.
- CPU 활용률이란 전체 CPU의 가동 시간 중 작업을 처리하는 시간의 비율을 의미한다.
- 대부분의 프로세스들은 CPU와 입출력장치를 모두 사용해 실행과 대기 상태를 오가며 실행된다. 이때 프로세스가 CPU를 이용하는 작업을 CPU 버스트(CPU burst)라고 하고, 입출력장치를 기다리는 작어블 입출력 버스트(I/O burst)라고한다. 입출력 작업이 많은 프로세스를 입출력 집중 프로세스, CPU 작업이 많은 프로세스를 CPU 집중 프로세스라고 한다.
스케줄링 큐(Scheduling Queue)
- CPU를 이용하고 싶은 프로세스의 PCB와 메모리로 적재되고 싶은 프로세스의 PCB, 특정 입출력장치를 이용하고 싶은 프로세스의 PCB를 큐에 삽입하여 줄 세우는 것을 말한다.
- 운영체제가 관리하는 줄인 큐에는 다양한 종류가 있지만, 대표적으로는 준비 큐와 대기 큐가 있다. 준비 큐(ready queue)는 CPU를 이용하고 싶은 프로세스의 PCB가 서는 줄을 의미하고, 대기 큐(waiting queue)는 대기 상태에 접어든 프로세스의 PCB가 서는 줄을 의미한다. 주로 입출력 작업을 수행 중일 경우, 대기 큐에서 대기 상태로 입출력 완료 인터럽트를 기다리게 된다. 준비 상태인 프로세스의 PCB는 준비 큐의 마지막에 삽입되어 CPU를 사용할 차례를 기다린다. 운영체제는 큐에 삽입된 순서대로 실행하되, 우선순위가 높은 프로세스부터 먼저 실행한다. 실행되는 프로세스가 할당받은 시간을 모두 소모할 경우 준비 큐로 다시 이동하고, 실행 도중 입출력 작업을 수행하는 등 대기 상태로 접어들어야 할 경우 대기 큐로 이동하게 된다. 같은 입출력 장치를 요구한 프로세스들은 같은 대기 큐에서 기다린다. 입출력이 완료되어 완료 인터럽트가 발생하면 운영체제는 대기 큐에서 작업이 완료된 PCB를 찾고, 이 PCB를 준비 상태로 변경한 뒤 큐에서 제거된다. 해당 PCB는 준비 큐로 이동한다.
선점형 스케줄링과 비선점형 스케줄링
- 선점형 스케줄링(preemptive scheduling)은 현재 어떤 프로세스가 CPU를 할당받아 사용하고 있더라도 운영체제가 프로세스로부터 CPU 자원을 강제로 빼앗아 다른 프로세스에 할당할 수 있는 스케줄링을 말한다.
- 비선점형 스케줄링(non-preemptive scheduling)은 어떤 프로세스가 CPU를 사용하고 있을 때 그 프로세스가 종료되거나 스스로 대기 상태에 접어들기 전까지는 다른 프로세스가 끼어들 수 없는 스케줄링 방식을 말한다.
CPU 스케줄링 알고리즘
- 선입 선처리 스케줄링(FCFS, First Come First Served)
- 단순히 준비 큐에 삽입된 순서대로 먼저 CPU를 요청한 프로세스부터 CPU를 할당하는 스케줄링 방식이다. 때때로 프로세스들이 기다리는 시간이 매우 길어질 수 있다는 부작용이 있으며, 먼저 삽입된 프로세스의 오랜 실행 시간으로 인해 나중에 삽입된 프로세스의 실행이 지연되는 문제를 호위 효과(convoy effect)라고 한다.
- 최단 작업 우선 스케줄링(SJF, Shortest Job First)
- 준비 큐에 삽입된 프로세스 중 CPU를 이용하는 시간의 길이가 가장 짧은 프로세스부터 먼저 실행하는 스케줄링 방식이다.
- 라운드 로빈 스케줄링(RR, Round Robin)
- 선입 선처리 스케줄링에 타임 슬라이스라는 개념이 더해진 스케줄링 방식이다.
- 타임 슬라이스(time slice)란 프로세스가 CPU를 사용하도록 정해진 시간을 의미한다.
- 라운드 로빈 스케줄링은 큐에 삽입된 프로세스들이 삽입된 순서대로 CPU를 이용하되, 정해진 타임 슬라이스만큼만 CPU를 이용하는 선점형 스케줄링이다. 프로세스가 정해진 시간을 모두 사용하고도 완료되지 않는다면 문맥 교환이 발생해 다시 큐의 맨 뒤에 삽입된다.
- 최소 잔여 시간 우선 스케쥴링(SRT, Shortest Remaining Time)
- 최단 작업 우선 스케줄링과 라운드 로빈 스케줄링을 합친 스케줄링 방식이다.
- 프로세스로 하여금 정해진 타임 슬라이스만큼 CPU를 이용하되, 남아 있는 작업시간이 가장 적은 프로세스를 다음으로 CPU를 이용할 프로세스로 선택한다.
- 우선순위 스케줄링(Priority)
- 프로세스에 우선순위를 부여하고, 가장 높은 우선순위를 가진 프로세스부터 실행하는 스케줄링 방식이다.
- 우선순위가 높은 프로세스를 먼저 처리하는 방식이기 때문에 우선 순위가 낮은 프로세스는 우선순위가 높은 프로세스로 인해 계속해서 실행이 연기될 수 있다. 이를 아사,기아(starvation)이라고 하는데, 이를 방지하기 위해서 에이징(aging)기법이 있다. 에이징은 오랫동안 대기한 프로세스의 우선순이를 점차 높이는 방식이다.
- 다단계 큐 스케줄링(multilevel queue)
- 우선순위 스케줄링의 발전된 형태로, 우선순위별로 여러 개의 준비 큐를 사용하는 스케줄링 방식이다. 우선순위가 가장 높은 큐에 있는 프로세스를 먼저 처리하고, 우선순위가 가장 높은 큐가 비어 있게 되면, 그 다음으로 우선순위가 높은 큐에 있는 프로세스를 처리한다.
- 다단계 큐 스케줄링에서는 프로세스들이 큐 사이를 이동할 수 없기 때문에 우선순위가 낮은 프로세스의 작업이 계속해서 연기될 수 있다는 단점이 있다.
- 다단계 피드백 큐 스케줄링(multilevel feedback queue)
- 다단계 큐 스케줄링과 비슷하게 동작하지만, 프로세스들이 큐 사이를 이동할 수 있다는 점에서 차이가 있다. 다단계 피드백 큐 스케줄링에 새롭게 진입하는 프로세스는 먼저 우선순위가 가장 높은 우선순위 큐에 삽입되고, 타임 슬라이스 동안 실행된다. 만약 해당 큐에서 프로세스의 실행이 끝나지 않으면 다음 우선순위 큐에 삽입되어 실행된다. 또한 아사 현상을 예방하기 위해 낮은 우선순위 큐에서 오래 기다리고 있는 프로세스들을 높은 우선순위 큐로 이동시키는 에이징 기법을 적용할 수도 있다.
728x90
'Study > TIL(Today I Learned)' 카테고리의 다른 글
24.11.28 면접 후기 (1) | 2024.11.28 |
---|---|
24.11.27 서버, AWS (0) | 2024.11.27 |
24.11.25 CS, C++ (0) | 2024.11.25 |
24.11.24 C++ (0) | 2024.11.24 |
24.11.23 C++ (0) | 2024.11.23 |