*참고: http://web.mit.edu/rhel-doc/4/RH-DOCS/rhel-isa-ko-4/s1-memory-concepts.html#S2-MEMORY-VIRT-SIMPLE*

*디맨드 페이징을 쓴다는 가정하에 포스팅*

What?

,실제 주기억장치보다 큰 메모리 영역을 제공하기 위해 운영체제가 제공하는 논리메모리이다. 

보조기억장치에 존재하며, 프로세스의 주소공간 중에서 필요한 부분만 가지고 있다. (이전편 참고)

또한, 캐시의 기능을 하며(논리메모리만 검색하면 됨) 공유메모리를 가능하게 한다.(어떻게?)

 

더보기

이 어플리케이션의 기계 코드 용량은 10000 바이트라고 가정해봅시다. 또한 데이터 저장 및 입/출력 버퍼에 5000 바이트가 필요하다고 가정합니다. 이러한 경우 이 어플리케이션을 실행하기 위해서는 최소한 15000 바이트의 RAM이 필요합니다. 만일 1 바이트라도 부족하다면 이 어플리케이션을 실행할 수가 없습니다.

하지만 메모리 접근은 순차적이고 지역화되어 있다는 내용을 다시 생각해보십시오. 이러한 특성으로 때문에 이 어플리케이션을 실행하는데 필요한 메모리 용량은 15000 바이트 보다 훨씬 적습니다. 단독 기계 명령어를 실행하기 위해 필요한 메모리 접근 방식을 예를 들어 보겠습니다:

  • 메모리에서 명령을 읽어옵니다.

  • 명령어가 필요로하는 데이터를 메모리에서 읽어옵니다.

  • 명령이 완료된 후 결과가 메모리에 다시 기록됩니다.

매번 메모리에 접근하는데 필요한 실제 바이트 수는 CPU 아키텍쳐 및 실제 명령어와 데이터 유형에 따라 달라집니다. 그러나 한 명령어를 실행하는데 각 메모리 접근 유형마다 100 바이트의 메모리가 필요하다고 가정해본다면 이 경우에는 300 바이트가 필요합니다. 즉 어플리케이션의 전체 15000 바이트 주소 공간을 사용하는 것보다 훨씬 적은 메모리 용량을 사용합니다. 따라서 만일 어플리케이션 실행시 필요한 메모리 요건을 기억할 수 있는 방법을 찾을 수가 있다면, 어플리케이션의 주소 공간 보다 적은 메모리를 사용하여 어플리케이션을 실행하는 것이 가능합니다.

어플리케이션의 나머지는 디스크에 남습니다.

Issue?

ex.

필요한 페이지만 실제메모리에 올려두고

아닌 페이지는 디스크(스왑아웃)로 보내두고

페이지 테이블엔 invalid 표시해둔다.(디맨드 페이징이라서 그럼)

 

접근할 때, invalid라면 페이지 폴트 난 것이므로

IO작업(커널도움)으로 디스크에서 가져와야한다.

(이것이 바로 소프트웨어 인터럽트!)

 

IO작업은 굉장히 느리므로

페이지폴트가 나는 횟수가 성능 좌우. 

 

즉,page 교체시 교체할건지 잘 골라야함.

(Optimal algorithm)

 

 

 

 

Solving?

 

 

 

 

 

 

 

 

 

OPT: 가장 먼 미래에 어떤 페이지 참조하는지 알고 있다는 가정하에, 그 페이지를 교체

but 이건 알고 있어야되기 때문에 실제에선 미래보단 과거를 본다

 

FIFO: 가장 일찍 들어온 것을 가장 먼저 내보낸다.

but 프레임 커질수록 폴트 더 발생(왜?..혹시 갯수가 늘어나면 그만큼 확률도 떨어져서 그런가)

 

LRU(Least Recently Used):제일 오래전에 사용한거를 내쫒는다

->FIFO랑 다른 점은 먼저 들어왔어도 재사용 됐으면 안쫓아낸다는 사실이다//이 방법 젤 많이 쓴다

->링크드리스트로 처음 들어오면 밑에 달고, 뺼 때는 젤 위에꺼 뺸다.비교 필요 없다. O(1)

but 이번에 한번만 들어오고 앞으로 안들어올 애를 두고, 꾸준히 많이 들어오는 애를 보낼수도.

 

LFU(Least Frequntly Used): 가장 참조횟수가 적은 페이지 순으로 내쫓는다.

->O(n)...heap으로 구현해서 O(logn)에 가능.

but 이제 많아지려는애를 쫓아낼수도

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

밑에 정리 필요

더보기

캐슁환경;한정된 빠른공간에 요청된 데이터 저장해 뒀다가 후속요청시 캐쉬로부터 직접 서비스하는 방식

페이징의 TLB,캐쉬,버퍼,웹(얘는 단일시스템 아니고 다른 시스템에서 가져옴. 내 컴터에서 저장된걸로 보여줌)

swap)페이징의 캐쉽 적용 환경

file)캐쉬(cpu와 메인메모리 사이)의 적용환경

 

사실 lru,lfu는 버퍼랑 웹캐싱에만 쓰이지 페이징에는 안됨.

폴트 발생해서 새로 올라가면 접근시간 알 수 있어도 이미 있는 경우라면 참조만 해가기 때문에 정보 모름.

lru비슷하게 구현한 알고리즘이 클락 알고리즘임.

 

할당은 어떻게 잘 해두나?  [사진]

ㄴ사실 lru.lfu같은 알고리즘 쓰면 알아서 조절되긴한다. (글로벌 리플레이스 활용)

<->로컬리플레이스(자신에게 할당된 프레임 내에서만 리플레이스..자기몫 나눠주고 알아서 바꿔써라)

 

+뭐가 더 좋을까?

 

폴트 너무 자주나면~~>Thrashing: 

 

막는 알고?

워킹셋 알고리즘)

 

 

 

 

 

 

 

 

메모리 관리

컴퓨터에는 여러가지 메모리가 있다. 먼저 요약해서 그 관계를 얘기하자면, Disk(보조기억장치)에서 실행파일을 가상메모리에 올리고 페이지로 나눈다. 탐색시 보조디스크가 아닌 논리메모리만 탐색해서 필요한 페이지를 물리메모리에 적재시키고, 페이지테이블을 사용해서 그 순서를 기억한다.  

 

이 그림에 disk는 나와있지 않다

 

1.물리주소:주기억장치의 실제 저장위치 주소

2.가상메모리:보조기억장치를 주기억장치처럼 주소지정 가능하게 만든 저장공간 방법.

3.논리주소(가상주소):가상 메모리의 특정 위치에 배정된 주소 .각 프로세스 가상주소공간에서 0번지부터 시작. cpu가 보는 주소

4.가상주소공간:특정 프로세스에게 할당된 가상 주소의 영역

5.주소공간:특정 프로세스가 접근할 수 있는 메모리 주소의 영역

3.심볼릭 주소: 프로그래머 편의를 위한 변수

 

심볼릭 주소->컴파일->논리주소->주소바인딩->물리주소 과정을 거쳐서 메모리에 접근할 수 있다.

논리주소를 물리주소로 변환하는 바인딩 종류는 세 가지가 있다. 

 

컴파일타임바인딩:

논리주소에서 정한 주소를 물리주소에서 그대로 사용하기 때문에, 프로그램 쓰면 빈공간을 찾아서 쓰지 않으므로 비효율적이다.

 

로드타임바인딩:

롸?//논리주소에서 계산해서 사용

 

런타임바인딩:

cpu를 뺏겨 메모리에서 나갔다가 들어올때 마다 주소가 바뀐다.(하드웨어 mmu가 지원이 필요하고 현대에 채택함)

 

 

cpu는 논리주소를 사용한다. 즉, cpu가 준 논리주소를 mmu가 물리주소로 변환을 해서 메모리에 접근하는 것이다. 이 과정을 바인딩이라고 한다.

더보기

cpu가 논리주소만 볼 수 밖에 없는 이유??

컴파일을 하고나서 실행파일을 cpu가 읽는데, 파일한 환경과 파일을 실행할 환경이 다른 경우, 실제 메모리값을 미리 알 수 없으므로 당연히 논리주소를 사용하여야 한다.

 

 

mmu는 하드웨어 장치로서, 

접근하는 메모리 실제 주소=

가상주소공간의 0번쨰 논리주소에 해당하는 물리주소+cpu가 준 논리주소.

 

이때, 지정된 범위 벗어나는지 검사하는 소프트웨어 인터럽트가 존재한다.

 

 

 

 

 

 

 

 

 

 

 

 

주메모리에 접근해서 프로세스 주소공간이 배치된다. 

-연속배치(각 프로세스가 메모리에 연속적으로 배치)

-불연속배치(하나의 프로세스가 메모리에 나눠져서 배치 됨,현대):페이징,세그멘테이션

-고정분할(사용자메모리 영역을 미리 파티션으로 나눠둠 ):페이징

-가변분할(미리 나눠두지 않고 들어오는대로 받아들임):세그멘테이션

 

 

 

 

 

 

불연속 방법 종류

 

고정분할방식 가변분할방식
같은 크기든 다른 크기든 메모리를 미리 파티션으로 나눠둠 미리 나눠두지 않고 들어오는대로 배치한다
크기가 맞는 것이 나올때까지 건너뛰므로 외부단편화 발생 먼저 수행을 마친 프로세스가 나가서 중간에 외부단편화 발생
할당된 공간에 비해 필요한 공간이 작으면 내부단편화 발생 처음부터 필요한만큼 크기 할당받으므로 내부단편화는 없다

 

단편화 해결방법:(바인딩을 많이 해야되서 비용이 많이 들고 주소가 계속 바뀌므로 런타임바인딩이 지원되야 함)

 

배치전략

1.first fit:사이즈가 n이상인 것중 처음 발견되는 것

2.best fit:가장 갭이 작은 홀로

3.worst fit: 가장 큰 홀로(장기적으로 안좋다..더 큰 프로세스 왔을때 써야하는데..)

 

 

 

스와핑: 메모리에서 cpu우선순위 낮은 프로세스를 디스크로 쫓아내거나 불러오는 것.(중기스케줄러)

 

더보기

롸?//동적로딩(동적:필요할 때마다 쓴다 ,로딩: 메모리에 올리는 것):

개발자가 방어적인 코드(예외처리코드)처럼 자주 쓰지 않는 코드를 명시적으로 나타내서 필요할 때만 부름.

+어떤거?

 

동적링킹(링킹:내가 사용하는 라이브러리 등의 코드를 실행할 때 코드로 붙여주는 것 동적:필요할 때마다):

static linking-컴파일하면 내 코드에 그 코드들이 추가가 된다.

dynamic linking-파일로 라이브러리에 코드있고 그 주소로 가는 거만 저장해둬서 필요할 때 그 동작만 수행

ㄴ이래서 printf함수 입출력 함수 쓰면 오래 걸린다는거구나..

 

오버레이:

 

 


페이징

불연속 배치-페이징 

 

 

What? 

주메모리에서 사용하기 위해 2차 기억 장치로부터 데이터를 저장하고 검색하는 메모리 관리 기법이다.

 

How?

가상메모리를 모두 같은 크기의 페이지로 나누고 페이지는 같은 크기로 주메모리로부터 프레임을 할당받아서 적재된다.

인덱스로 바로 접근할 수 있는 페이지 테이블을 가지고 있다.

 

페이지 주소와 offset으로 이루어진 키값 테이블.

p,d => p페이지 d번째라는 뜻

(페이지 테이블에서 p에 해당하는 물리주소 f를 찾아서 d만큼 떨어진 실제주소를 찾는다.)

 

 

 

Where?

캐시에 넣기엔 너무 크고,에 넣기엔 빠르게 주소변환 해야하는데 부적절해서 메모리에 넣는다...

즉,

주메모리에 2번 접근한다<-속도 높이기 위해서 TLB(캐시역할하는 테이블,메인메모리와 cpu사이 하드웨어로 존재) 쓴다.

 

 

 

 

 

 

더보기

32bit에선 주소를 2^32까지 표현할 수 있다. 2^30은G이므로 4G.

이때, 주소 하나당 한 페이지인데 한 페이지는 최대 4K이므로

프로그램은 최대 4G/4K=100만개의 페이지로 구성되어 있다. 

페이지테이블의 엔트리가 4바이트이므로 프로세스마다 4메가의 페이지테이블이 필요하게 되어 메모리 공간이 낭비된다. 

 

2단계 페이지 테이블!

속도는 줄어들지 않아도

공간은 줄어든다. 

 

->비효율->2단계로 가자~~

->근데 2단계 페이지테이블까진 몬하게따 힘들다

 

프로세스가 공유하는 코드 있으면 메모리에 하나만 올림.

(shared code=pure code)<-read only여야함. ipc통신의 쉐어드 메모리랑은 다른 것

 

 

 

 

 

 

 

 

 

 


세그멘테이션

 

What? 

주메모리에서 사용하기 위해 2차 기억 장치로부터 데이터를 저장하고 검색하는 메모리 관리 기법이다.

 

How?

가상메모리를 의미적인 세그먼트로 나눈다.

(sharing하거나protection)

 

 

페이징은 크기 고정되어있지만 세그먼트는 가변적이므로 프로세스 초기위치 말고도 limit이 필요하다.limit 연산 시 할당크기 넘는다하면

할당 안함. 

 

 

 

 

페이징 세그멘테이션

엔트리에 권한값을 부여해 페이지마다

read only/write등을 달아줘야한다.

의미단위라 그런 문제는 없고
allocation문제가 발생하지 않는다. allocation문제가 발생할 수 있다.
테이블에 의한 메모리 낭비는 페이징이 더 크다.  
페이징은 갯수 알 수 있는데 갯수 추측 못함.

내부단편화 있고(페이지보다 작으면 무조건 발생)

외부단편화는 불연속이라서 없다

내부단편화는 없지만 외부단편화는 있다 

 

 

+새그맨트~페이지 섞어서 쓰기도..

+프로세스의 메모리 접근시 cpu가 바로 접근 가능. but IO장치 접근할때만 커널도움 받음.

 

What?

여러 프로세스가 같은 자원을 기다리며 blocked된 상태.

롸?//지금 충분하게 있는 자원상태 혹은 자원이랑 뱉어낼 애 있는 상태 합,...<-이 시퀀스가 되면  safe하다고 함.

Why? 

상호배제(Mutual exclusion) : 한번에 여러 프로세스가 쓸 수 없는 자원이다

 

2.점유대기 : 자원을 사용중인 프로세스가 자원을 내놓지 않는다

 

3.비선점 : 다른 프로세스로부터 자원을 빼앗을 수 없다

 

4.순환대기 : 프로세스가 순환적으로 자원을 요구한다. 1번 자원을 사용하며 2번 자원을 기다리고, 2번 자원을 사용하며 3번 자원을 기다리고, 3번 자원을  사용하며 1번 자원을 기다리는 세 개의 프로세스.

Solution?

예방-> 데드락 성립조건 4가지중 하나라도 충족되지 않도록  만든다.

 

1.상호배제-> 상호배제 조건을 제거한다

 

2.보유대기->한 프로세스에게 가용 자원을 모두 할당시키고 결국 스스로 내놓게 만듬

Issue->일어나지도 않은 일을 위해 자원들 몰아서 써서 성능이 낮아진다.

 

3.비선점-> 선점 가능한 프로토콜을 만든다. (ex. 프로세스가 cpu 선점 시 상태를 저장해둠)

 

4.순환대기->사이클이 없도록 순서를 정한다.(ex.1번을 획득해야만 2번획득)

 

 

 

 

회피-> 미리 사용할 자원을 모두 정의해둔 것을 바탕으로 데드락을 발생시킬 가능성이 높은 프로세스에게는 자원 할당을 안해준다. 

 

실선은 자원을 사용중이라는 뜻, 점선은 언젠가 한번은 쓸 자원이라는 뜻일 때,

 

점선이 향하는 자원을 p1에게 주면 사이클이 생겨서 데드락이 발생할 것을 알고 자원을 할당해주지 않는다.

or

가용자원에서 최대를 쓸 수 있는 프로세스에게'만' 자원을 줘서 보유대기예방과 같은 효과를 낸다.(은행원 알고리즘)

 

 

 

 

발견->

 

1)자원할당그래프를 그려서 사이클을 찾아본다

그래프에 사이클이 있으면 데드락'일 수도 있다.'

(인스턴스가 하나면 무조건 데드락인데 인스턴스가 두개면..)

 

왼쪽: 데드락이다.

 

오른쪽: 데드락 아니다. 사이클이 있긴하지만 P4가 여분을 쓰다가 반납하면 쓸 수 있는 상황이기 때문이다. 

 

 

 

 

다양한 그래프 종류가 있다..

롸?// 사이클 찾는 시간복잡도? n^2! 간선 다 따라가보면 된느데

간선이 결국 하나에서 모든 곳에 다 뻗쳐도 n-1개기 때문.

 

 

 

 

 

 

 

2)자원할당테이블을 그려서 가용자원들 쌓아서 생각해보고 시퀀스가 가능한지 합쳐나가면서 체크한다.(추천)

 

 

a 7,b 2,c 6개가 있는데 allocation처럼 프로세스들에게 분할이 되어있는 상태이다. 

이 상태에서 p1,p2,p3,p4가 request와 같이 자원을 추가 요청한다.

이때, 가용자원은 없지만 낙관적으로 생각해서, p0같은 경우 반납할거라고 생각한다. 그럼  요청을 받아들이는 시퀀스가 존재하므로 다 만족할 수 있어서 데드락이 아니다 .

 

 

 

 

 

 

하지만 p2가 자원 c를 더 요청한다면 ,

요청한 자원도 만족할 때까지 본인이 가진 자원을 내놓지 않기 때문에 시퀀스가 불가능하고, 데드락이다.

 

 

 

 

 

 

 

 

*데드락은 최대한 낙관적이게 생각해도 데드락인 경우에만 데드락이라 판정한다*

 

 

회복->

1.모두 죽인다.

2.차례로 하나씩 죽여본다.

3.특정 기준으로 하나만 지목하여 죽인다.(Issue:꼭 자원이 적은걸로만 하면 기아현상 될수도..)

4.회복 안하고 무시한다.( 데드락 판별 알고리즘을 코드로 넣는 것 등은 성능을 저하시키므로, 차라리 인간이 직접 처리하는 것이 낫기 때문에 요즘 채택하는 방식이다.)

 

What?

동기화(同期化, synchronization)란, 말그대로 동시에 시스템을 작동시킬 수 있도록 만드는 것이다. 

그러므로 프로세스 동기화란, 멀티프로세스를 가능하게 만드는 것이다. 

이렇게 따로 용어까지 있는 정도면, 프로세스를 동시에 작동하는 것이 어떤 문제를 발생시킬 수 있을 정도로 중요한 문제가 될 수도 있다는 것이다. 바로 자원의 문제이다. 

 

 

먼저, 프로세스를 작동시키는 물리적인 주체는 cpu가 어떻게 프로세스를 작동시키는지,  자원을 어떻게 다루는지 보자..


*cpu 하나는 한번에 프로세스 하나만 처리할 수 있다.*

*프로세스는 ram 위에 작동되는 프로그램을 뜻하고, ram 위에는 프로세스마다 자신의 Code,Data,Stack에 대한 주소공간이 있다*

*cpu가 프로세스를 작동시키는 과정은 다음 그림과 같다*

'cpu'는 '프로세스의 주소공간'과 계속 데이터를 주고 받으며 동작한다. 

 

 

 

 

cpu는 하나의 프로세스 Data(on ram)에서 연산할 data를 가져와 연산을 하고, 연산 결과를 다시 Data(on ram)에 저장한다.

 

 

 

 

 

 

 

 

 

 


Issue?

그렇다. 한 프로세스의 주소공간은 다른 프로세스의 주소공간에 독립적이므로, Data 자원에 대해 문제가 생길 일이 없다. 하지만 프로세스 사이에 공유메모리(이전글)를 사용하는 경우라면, 얘기가 달라진다. 

 

예를 들어, a프로세스가 cnt란 변수를 1만큼 올렸는데, b프로세스가 cpu를 빼앗아서 cnt를 2로 올리고, 다시 a프로세스가 cpu를 빼앗으면 cnt를 1에서 이어서 올리는 것이 아닌, 2에서부터 올리게 되어 원래의 의도와 달라진다. 이것을 cpu를 빼앗기는 타이밍에 의해 Data의 일관성이 깨진다고 한다. 

 

 

Solution?

cpu가 하나인 경우) 프로세스 선택 정책을 선점이 아닌 비선점을 택하면 된다. 위의 경우에서, a프로세스가  cnt를 올리려고 했던만큼 완전히 올리기 전까진 b프로세스가 cpu를 뺏지 못하는 것을 의미한다. 

 

cpu가 여러개인 경우) 프로세서 자체가 구분되어 있어서 자원에 접근하는 것을 막을 방법이 없다. 그래서 커널에서 을 걸어서, 커널이 자원에 한 프로세스만 접근하도록 결정한다. 피터슨 알고리즘, 세마포어, 모니터가 있다. 

 

먼저, 용어에 대해 정리하고 가자. 


(lock 상호배제 or mutex or mutual exclusion):  동기화 메커니즘

 

 

Issue에서 설명한 것과 같이, 공유 자원에 대해 여러 개의 프로세스가 동시에 접근을 시도할 때 접근의 타이밍이나 순서 등이 결과값에 영향을 줄 수 있는 상태경쟁상태라고 말한다. 

 

 

 

 

 

 

공유자원 중에서도, 둘 이상의 스레드 동시에 접근해서는 안되는 코드의 일부크리티컬 섹션,임계구역, 공유변수라고 말한다. 

 


1)피터슨 알고리즘(소프트웨어적 락):

 

자신 프로세스의 turn=0, 다른 프로세스의 turn=1 일 때(각각 다른 프로세서가 맡았을 때) 0을 기준으로 while(turn!=0)은 1의 순서인 동안 루프만 돌며 임계영역에 진입하지 않는다는 뜻이다. 1의 순서가 끝났으면 자신 프로세스는 루프를 지나 critical section에 진입하고, 볼 일을 다 보면  다시 turn=1로 순서를 바꾸어준다. 다른 프로세서의 turn 1 프로세스는 자신의 차례이니 critical section에 진입한다. 

 

 

Issue->cpu는 instruction 단위로 빼앗길 수도 있어서, critical section 중도에 뺏기면  turn을 돌려놓지 않고 빼앗기는 것이기 때문에 다른 프로세스에서는  turn의 값이 바뀔때까지 기다리고, 자신 프로세스 또한 cpu를 뺏긴 상태기 때문에  <progress,진행 문제>와 <스핀락> 현상이 일어날 수도 있다.

 

progress,진행 문제:  아무도 critical section에 들어가지 않는 상태

스핀락,바쁜대기: critical section에 진입이 가능할 때까지 루프를 돌면서 재시도하는 상태(cpu와 메모리를 계속 써서 낭비함)

더보기

스핀락은 운영 체제 스케줄링 지원을 받지 않기 때문에, 해당 스레드에 대한 문맥 교환이 일어나지 않는다. 따라서 스핀락은 임계 구역에 짧은 시간 안에 진입할 수 있는 경우에 문맥 교환을 제거할 수 있어 효율적이다. 하지만 만약 스핀락이 오랜 시간을 소요한다면 다른 스레드를 실행하지 못하고 대기하게 되며, 이 경우 비효율적인 결과를 가져온다

 

 

2)Test and Modify(하드웨어적 락):

 

 

 instruction 단위로 cpu 뺏길수도 있어서 생기는 위의 문제를 

하드웨어적으로 instruction이 끝날때마다 flag 바꿔주는 것을 지원하는 방법이다. 

 

Issue->여전히 프로그래머의 실수가 영향을 미친다는 단점을 가지고 있다.

 

 

 

 

3)세마포어: 

 

 

위의 방식들을 추상화시킨 방법이다. 

세마포어 S는 정수값을 가지는 변수이고, P와 V라는 명령으로만 접근할 수 있다. 

즉, 한 프로세스(또는 스레드)에서 세마포어 값을 변경하는 동안 다른 프로세스가 동시에 이 값을 변경해서는 안 된다.

 

 

방법1...스핀락 사용

P(S) { while S <=0; // 아무것도 하지 않음 (반복문)  S--; } 자원 얻음

V(S) { S++; }  자원 반납

 

방법2...슬립락 사용(바쁜대기를 보완하여, cpu와 메모리를 쓰지않는동안 넘겨준다 )

P(S) { S--; if S < 0 // 이 프로세스를 재움 큐에 추가 (block) }  가용자원 없으니 block하는거다. 1편 참고

V(S) { S++; if S <= 0 // 재움 큐로부터 프로세스를 제거 (wakeup) } 되돌려줬는데도 음수란건 누군가가 음수로 만들고 block되있는 상태라는걸 뜻한다. block된  프로세스를 깨운다.

 

Issue->여전히 프로그래머의 실수가 영향을 미친다는 단점을 가지고 있다.

 

 

 

 

 

 

 

 

 

종류: 이진 세마포어(0또는 1만 가질 수 있고 mutex구현을 위해 사용한다), 계수 세마포어(그 외. 다른 프로세스가 접근 못한다는 특성을 이용해서 자원카운팅만 할 때 사용한다)

 

 

 

4)모니터(최종보스)

 

고급 언어(Java)에서 이 기능을 지원하며, 한번에 하나의 프로세스만 모니터에서 활동하도록 보장해준다. 모니터는 안에 공유변수 넣어두고, 공유변수 접근하는 함수들을 내부에 모아둔다.그리고 자체적으로 활동 가능한 함수는 하나만 되도록 만듬.프로세스가 모니터에 들어가고자 할 때 다른 프로세스가 모니터 내부에 있다면 입장 큐에서 기다려야 한다.

 

 

 

Difference?

세마포어는 스핀락이라 자원낭비가 생기거나, 보완된 방식을 사용해도 block 때문에 문맥교환 비용이 증가하는 반면

모니터는 입장 큐에 대기만 하면 되기 때문에 그 비용이 발생하지 않는다. 또한, 락을 거는 것이 아니기 때문에 자원을 더 쓸 수 있어서 효율적이다

 

 

 

 

위에서 나왔던 모든 Issue를 처리한 상태로 동기화한 것을 thread safe라고 한다.

 

 

//자바의 String 관련 동기,비동기도 알아보자

//생산자,공급자 ..배고픈 철학자 등 건넜음

//세마포어,뮤텍스 구현 한번 해보면 좋을듯

 

(1)프로세스 생성

 

What? 프로세스를 생성하는 방법은 부모프로세스가 자식프로세스를 낳는 것이다.

즉, 프로세스를 복제시키는 것이다. 

 

How? 리눅스에서는 fork()함수를 호출해서 생성한다

code,data,stack+pc까지 복제해서 생성된다.

다음에 exec()함수를 호출해 새로운 프로그램을 올린다.

main부터 코드 실행하다가 fork (시스템콜,운영체제 필요)만나면 프로세스 하나 만들고 아무 일 없듯이 다시 아래 코드 실행한다.자식은 문맥을 복사했기 때문에 부모가 pc가 어디에 멈춰있는지 알아서 그 다음부터 실행한다.

즉, 자식은 if부터 시작한다는 말!(물론 fork반환은 각각 다르게 리턴되서 각자 실행하는 코드분기 다르다)

 

+cpu는 한번에 하나 프로세스인데 동시에 실행된다고? 스레드가 아닌데?

...더보기

->그게 바로 멀티 프로세싱! 

엄밀히 말해 한 개의 CPU를 가진 개인용 컴퓨터가 특정 순간에 수행할 수 있는 태스크의 개수는 하나뿐이다. 따라서 멀티태스킹은 스케줄링이라는 방식을 사용하여 컴퓨터 사용자에게 병렬 연산이 이루어지는 것과 같은 환경을 제공한다. 스케줄링 방식은 CPU 사용시간을 일정한 기준에 따라 나누어 각 태스크가 사용할 수 있도록 분배한다. 

ㄴ출처:위키백과

 

 

Why?

1.fork 전의 리소스(간단히 variable이라고 함)를 자식프로세스가 복사해 가지는 것인데, 리소스자체가 많으면 무거워지는 단점이 있지만(그래서 쓰레드가 나온걸로 알고 있습니다), 자식프로세스와 부모프로세스간 또는 자식프로세스간의 통신을 위해서 IPC의 동일한 키값을 갖기 위해서는 이것만큼 쉬운 방법이 없다. 직접 멀티 프로세스 프로그램을 해 보시면 느낄 수 있다고 한다.

 

2.동일한 프로세스의 여러 인스턴스를 매우 효율적으로 만들 수 있다. 예를 들어 웹 서버에 여러 클라이언트가 동일한 프로세스를 여러 개 제공 할 수 있습니다. 다른 프로세스를 생성하는 비용이 현재 프로세스를 복제하는 비용보다 훨씬 큰 경우는 스레드를 사용하지만 공유자원때문에 오류가 생길 확률이 높아진다.

 

+exec라는 시스템콜도 있지만 걔는 완전히 새로운 프로세스를 실행하는거다.

exec()라는 함수 호출 뒤의 코드는 실행불가능하다.

 

+wait()은 자식프세가 끝날때까지 기다려줌. sleep으로 잠들어있는다.기존과 다르게 부모-자식이 원래 프로세스 사이의 모양답게 경쟁이었는데 잠시 협력으로 모양이 변함.

 

+exit()

자발적 종료:컴파일러가 위치를 잡아줌. 스스로 종료

비자발적: 프세법칙이 부모보다 자식이 먼저 죽는거랬다. 부모가 kill같은 사용자로부터 멈춰짐을 당하면 함수 타고 내려가서 자식들 다 죽이고 자신도 죽음->근데 독립적인데 어케 말함?아, 트리형태로 저장이랬지

혹은 부모가 자식이 자원 너무 많이 써서 죽이는 경우. 프세 우선순위가 부모인갑다..

 

프로세스 원칙적으로 독립적(지 변수 지만 봄.자신만의 주소공간 가짐).하지만 가끔 협력해야하는 경우 두가지 방법 씀.

1.쉐어드 메모리(면접쓰):물리적으로 변수 공유(커널한테 미리 말해야됨)

2.메시지 통신:필요한 것 커널통해서 메시지로 서로 전달

 

하나의 프로그램 안에서 통신을 위해 메모리를 사용하는 일은 일반적으로 공유 메모리로 부르지 않는다.

 

 

물리적으론 램에 있다

 

 

~스레드간의 협력도 기대해라~

 

프로세스들 보면 cpu만 주루룩(cpu burst) 쓰는게 많은 프로세스가 있고

IO만 주루룩(IOburst) 쓰는게 더 많은 프로세스가 있는데 이 둘이 교차 됨.

근데 사용자가 더 체감크니 IO프로세스에 좀 더 자원 줌.

 

그래서 CPU 스케줄러&디스패쳐 필요!

스케줄러는 뭐로 되있니?하드?소프? -> 운체 안에 코드로 있다

스케줄러가 결정을 내리면 디스패쳐가 실질적으로 문맥교환 도와주는거임

 

그래서 그게 언제 어떻게 적용되는데??

 

 

 

1은 나 cpu더 못써~io로 간다 해서 일어나는거

2는 한 애가 cpu 너무 오래 못쓰게 방지하려고 일어나는거

..

+선점형(뺏아서 바꿈),비선점형 있음(자진반납으로 바꿈)

 

 


(2)CPU스케줄링

 

2가지 이슈로 나뉜다.

 

1.다음에 누구에게 줄건지 2.강제로 할건지 끝나면 할건지

->현대는 선점형!

 

5가지로 성능을 평가할 수 있다.

 

1. cpu사용량:주방장이 놀지않고 일하는 비율

2.대기시간:밥먹는 시간말고 기다리는 시간

3.소요시간:들어왔다가 나가기까지

4.처리한 양:손님 얼마나 받았냐

5.응답시간: 첫 밥 먹기까지 걸린시간.중요. 초반에 단무지라도 주며 기다려야함.

 

그래서, 이런 종류들이 있다.(어디에 쓰면 좋을지랑 예제 하나 풀어보며 해보자)

 

1.FCFS: 온 순서대로. 비선점

앞에 긴거오면 다른 프로세스들 입장에서 겪는 대기시간

길어짐 (waiting시간 증가)

고민해보자..공평성이 중요할 때 좋을듯!티켓팅

 

그럼 짧은거 먼저 앞으로 땡겨올까?

2.SJF:짧은거 먼저

nonpre,pre 다 있는데 

pre는 스케줄 되있다가도 중간에 더 짧은애 오면 뺏음(참고로 계속 기준은 앞으로 더 써야하는 시간, 즉

남은 시간이다). 뺏는거라 대기 시간은 더 짧아짐

화장실생각! 대기가 치명적일떄!

+근데 더 짧은애 와도 강제없이 기다리면 그게 그냥 fcfs아닌가?

ㄴㄴ!계산해보면 뭔 차인지 안다. 먼저 온 애가 길어서 일단 실행중일때

큐에 쌓여있으면 그 중에서 스케줄링 하는거임.

 

 

 

 

->하지만 기아현상 발생!긴거는 영원히 못먹을수도..

꼭 대기중에 자기보다 짧은애만 들어오는 경우!

->실제cpu얼마나 잡을지 예측을 못하지만 예측공식(예전거 토대로) 있긴함

ㄴ왜하는거? 이미 계산되있는거 아녔나?->예측하고 들어왔는데 인터럽트 등으로 더 길어졌을때

본래 의도대로 가장 먼저 있는게 아니게 되니..예측을 사용하는 거임 

 

3.우선순위큐:우선순위 젤 높은 애 한테 줄래. 우선순위는 다양하게 정의.

pre,nonpre.

사실 sjf도 결국 cpu타임이 기준인 우선순위큐임.

->그래서 마찬가지로 aging이라는 현상발생..

오래기다리면 순위높여주는걸로!

 

4.라운드로빈:가장 현대적!할당시간 세팅해서 줬다가 뺏고 다시 주고...

->공평해서 기아현상 ㄴㄴ!쓸 시간 예측도 불필요!

프세 길면 퀀텀도 길게 줌. 하긴 안그러면 오버헤드 많이 붙어서 더 안좋을듯.

지나치게 퀀텀 짧을때의 예시기도 함.

 

+스케줄링으로도 모자라서 큐를 여러층으로 분리해서 더 최적화를 했다!

 

위의 큐일수록 우선순위 주고 아래일수록 안주는데 그렇게하면

starvation 생길 수 있으니 일단 위의 큐에 담기되 퀀텀내에 안끝나면

다음 순위 큐(퀀텀 김)로 밀려남.RR에서도 긴거일수록 퀀텀 길게 줬으니까.

큐마다 적용하는 스케줄링 다르다! 윗큐에 있다는건 빠르게 진행된단거니까

IO버스트가 짧게 자주 있거든, 그래서 거기엔 RR..

 

 

 

 

 

 

+Realtime스케줄러:데드라인 있어서 그 데드라인까지 각각 끝낼 수 있도록 먼저 스케줄링해두는 방식

하드-반드시 지켜야함

소프트-우선순위를 둠

 

+쓰레드 스케줄러:

로컬-운체가 일단 cpu를 프로세스에게 넘기면 그 뒤로 사용하는건 알아서 하도록 하는거

글로벌-운체가 쓰레드까지 스케줄링에 관여하는 것

 

  

지금까지 cpu하나 얘기였는데 여러개면 더 복잡~~

그리고 여기서 내가 더 주목할건

멀티프로세서랑 멀티프로세싱(앞에 위키백과 올린거)이 다르다는거!

멀티프로세싱은 cpu가 하나일수도 있지만 프로세서는 아니다.

 

 

 

 

 

 

 

성능비교 위해서 실제로 시뮬한다.(과제 있는듯)

참고자료)이화여대 반효경 선생님 KOCW 강의


 

 

프로세스: 실행중인 프로그램이다.(윈도우 작업관리자 열면 볼 수 있는 것들!)

 

 

 

 

 우리가 C하드(보조기억장치-하드,시디)에 저장하듯 하드디스크에 저장되어 있고,

그걸 실행하는 작업대가 ram(주기억장치-램,롬)이다. 컴퓨터의 메모리라고 하면 일반적으로 램을 말한다.

바로 프로그램이 램에서 작업이 되는건 아니고 커널에 의해 레디큐로 갈지 말지 결정해서 레디큐로 갔다가,(이 과정은 커널의 데이터영역에서 이루어진다.그리고 이 커널의 주소공간 또한 램에 있다! https://whereisusb.tistory.com/10 참고, 헷갈렸던거 제대로 나와있음) 디스패쳐를 통해 램에 올라가 cpu에게 처리받는다. 

 

운영체제가 여러 프로그램을 실행하기 위해 자원(cpu,메모리,네트워크)을 분배한다.  

 

 

How??일단 기준보다 방법을 먼저 보자!

초록점이 프로세스 하나다.

CPU가 빠른 속도로 프로세스를 처리하고 있을 때 (해당프로세스상태:running)

정해진 시간이 끝나면(라운드로빈!) 큐의 뒤로 빠지고(해당프로세스상태:ready) 큐의 앞에 있던 프로세스가 시작된다.

혹은, 실행중인 프로세스가 IO처리가 필요하면 블록큐로 넘어가 끝날때까지 대기하게 된다.(해당프로세스상태:blocked)

IO에서 완료되면 cpu에 IO인터럽트를 해서 끝났다고 알려주고 CPU커널(깜빡깜빡!)을 통해 운영체제에게 아까 프로세스의 상태를 ready으로 바꿔달라고 요청한다. 우선적으로 처리할 수 있도록!

 

+프로세스의 상태(프로세스 스케줄링)

더보기

-running: CPU를 잡고 instruction을 수행중인 상태 

-ready: (물리적으로 코드가 메모리에 올려져있는 둥 모든 조건 만족하고)CPU를 기다리는 상태

-blocked: CPU를 주어도 당장 instruction을 수행할 수 없는 상태(디스크에서 file을 읽어와야하는 경우)

 ex) 어떤 프로그램이 사용자 입력을 읽고 처리하는 내용이면 키보드 입력 받기위해 프로세스를 키보드큐에 보내주고, 블락처리한다. 그동안 다른걸 수행하다가 키보드에 있던 애가 끝나면 키보드 컨트롤러가 cpu에게 인터럽트를 걸어서 알리고,cpu는 운영체제에게 프로세스상태를 ready로 바꾸도록해서 자신이 곧 실행할 수 있게 한다.

 

+공유데이터(면접질문!): IO 디바이스 서비스 기다리느라 block되는 경우 말고 소프트웨어 서비스 기다리느라 block되는 경우도 있다.

ex) 한 프로세스가 접근하는 도중에 cpu를 뺏겨서 다른 프로세스가 접근하면 일관성이 깨지므로 데이터를 막아둔 경우.

 

 

 

 

cpu가 프로세스 처리하는 과정

 

프로세스 실행) code가 stack에 쌓인다. 그 과정에서 data를 참조한다.

+JRE로 따지면 code,data영역이 코드와 static저장하는 method 영역 역할이고

stack은 jvm stack 역할이고 나와있진 않지만 heap은 그대로 heap역할을 하는듯하다.

 

인터럽트 등의 이유로 다른 프로세스로 문맥교환) 커널에게 요청(시스템콜)을 한다. pc의 주소는 프로세스주소공간이 아닌 커널 주소공간을 가리키게 된다.

 

운영체제-커널 동작) 프로세스 실행과정과 마찬가지이다. 다만, 커널은 자신의 data영역에 큐 자료구조를 만들어서 프로세스 상태를 변화시키며 순서를 바꾼다.

 

 

커널주소공간에서 프로세스들을 관리하는 모습

이때!!! 커널은 모든 프로세스가 접근할 수 있기 때문에 동시에 접근하는 경우를 막기 위해서

커널스택을 프로세스가 나눠가진다. 

 

+그리고 위의 큐들이 모두 커널의 주소공간-데이터 영역에 있기 때문에

운영체제가 프로세스 스케줄링이 가능한 것

 

 

커널이 한 프로세스에서 다른 프로세스에게 cpu 넘겨줄 때 context switch가 일어난다

프로세스가 cpu를 뺏겼다가 다시 cpu를 되찾을 때 처음부터 다시 하는게 아니라 하던 곳에서 하려면 그 문맥 (레지스터 상황,코드 위치)을 기억해둬야 한다. 즉, cpu를 뺏기기 전에 해당 프로세스의 정보들을 자신의 PCB에 저장을 해두고 나중에 되찾을 때 cpu하드웨어에 그때의 상황을 복구를 하는 작업을 운영체제가 해준다.

 

+반드시 인터럽트나 시스템콜이 발생한다고 context switch가 발생하는건 아니다. 예를 들어서, 프로세스 A->인터럽트 발생(프로세스A를 위한)->프로세스A 의 상황이라면 프로세스가 바뀌는게 아니기 때문에 context switch가 발생하지 않는다.  문맥교환프로세스가 바뀔때 정보를 저장하는게 목적임을 명심하자!

 

 

큐에 담기는 프로세스가 선입선출되는 다양한 기준

 

프로그램 시작전 / Job 스케줄러 / 장기)

메모리에 프로세스 올라가서 ready되기 직전에 올라와도 될지 안될지를 결정.

메모리에 몇 개 올려놓을건지를 기준으로 판단한다. degree of Muliprocess와 관련있다.

 

시작후 / Swaper / 중기)

메모리에 프로세스가 너무 많이 올라가있으면 몇 개를 쫓아냄.

 

시작후/ CPU스케줄러 / 단기)

굉장히 빨라야하고, 다음에 어떤 프로세스를 running할지 결정.

 

 

+중기 스케줄러가 확실히 성능을 개선시켜준다. 메모리를 뺏는거니까!

+Suspend상태는 사용자가 프로그램을 직접 중지시킨 경우고, 이땐 메모리에서 나와 디스크로 swap out된다.

 

 

동기식 입출력 비동기식 입출력

프로세스가 IO에 요청을 하고 완료될 때까지 자신의 일 못하는 것

scanf

프로세스가 IO에 요청해두면 자신의 일 할 수 있는 것

timeout

 

+프로세스를 너무 적게 올려놔도 안좋은 이유? 한 프로세스 IO로 블락된 동안 cpu놀고 있게 되니까.

 

 


스레드: 코드를 나눠서 처리할 수 있는 부분은 병렬적으로 따로 처리하는 것

 

Why?장점이 뭐길래?

 

->웹화면 객체 띄워지는걸 다 받아서 출력하는거보다 따로 처리해서 오는대로 보여주면 응답성 향상!

->0*10이 cpu하나면 하나하나 해야하지만 각각 나눠서 계산하고 나중에 합하는 방식도 가능

 

->일부 변수를 공유하기 때문에 프로세스 여러개 있는 것보다 단일 프로세스에 다중스레드 방식이 메모리 절약 됨.

 

어떻게 메모리를 절약하냐?

프로세스 하나->PCB 하나->프로세스상태값, 프로세스id, 메모리제한, pc, 레지스터 위치정보.

ㄴPCB는 운영체제(커널) 프로세스들을 관리하기 위해 자신만의 메모리를 가진 것이다.

 

'PC와 레지스터만'  스레드들이 각각 나눠저장하고 (코드를 쪼개서 따로 풀거니까 그 코드 위치도 따로 알고 있어야 됨)

나머지는 공유한다~~아래 그림을 참고하자

 

 

왼쪽과 오른쪽 그림을 잘 보자. 프로세스마다 주소공간을 가지고 있다.(그걸 읽으면서 동작하니까)

각 프로세스는 커널스택과 PCB 또한 나누어가진다.(정확히는 운영체제가 관리하기 위해서 프로세스별로 있는거임)

 

왜냐하면, 다중스레드 처리를 위해서 프로세스 PCB의 PC,레지스터를 스레드별로 구분해서 가져야 하기 때문이다.

실행할 코드를 push pop할 공간을 위해 프로세스 주소공간의 스택영역도 스레드별로 구분되어 있다.

결국 프로세스와 스레드가 공유하는 것은 data영역과 code영역이라는 것이다.(커널주소공간은 os를 위한 것이니 프로세스 관점으로 보자면)

 

 


공부한걸 바탕으로 아래 그림을 이해해보자.

 

 

 

 

공룡책 문제 빨리 풀어보자~~

 

 


멀티프로세스 멀티스레드
프로세스를 여러개 작동하는 것 한 프로세스를 이루는 스레드를 여러개 작동하는 것
cpu를 넘겨줄 때 문맥교환 일어남(비쌈) 문맥교환이 일어나지 않고 메모리 절약도 된다
ipc를 제외하곤 메모리 공유를 하지 않는다(전면마비 안옴) 힙을 공유하기 때문에 전면마비가 올 수 있다

 

+ Recent posts