전공/운영체제

6장

twoweeks-within 2025. 4. 5. 13:06

Race condition : 

  : P0, P1 이 동시에 fork > 누구에게 pid 를 먼저?

 

critical section (CS)

  : Common/ shared 변수, file, table/update 등등..

   > 한 process 가 접근 -> 다른 process는 접근 X 

     > 데이터불일치/race condtion 등의 오류 예방

> 해결

[A]. Mutual Exclusion : Pi가 있으면 other process : X

[B]. Progress              : CS 에 아무도 없으면 바로 진입

[C]. Bounded waiting  : 여러개가 진입하려면 한개를 넣고 다른 건 일정시간 waiting (bound) 후 진입하도록

     //단, 각 process가 멈추지 않도록 해야함 > CS안에서 멈추면 오류발생

 

1. Interrupt-based solution

Entry() : disable interrupt > CS진입

Exit()   : enable interrupt  > CS아웃

   > 1시간동안 계속 있으면 ... starve 

   > single core 에서만 OK <> Multi에서는 Core1이 interrupt 해봤자 Core2,3,4 등이 돌아감.. 

                                                    > 동기화의 어려움

 

2. Two process (SW)

a) load() ,store() // assm 

     > atomic ( load,store 중 interrupt X , 실행보장)

b) int turn; (공유)

ex)

[Pi]
while(){
  turn = i;
  while(turn ==j);  // j 이면 waiting
   //CS
   turn =j; 
   //EXIT
   //Remainder section 
[Pj]
while(){
  turn = j;
  while(turn ==i); // i 이면 waiting
   //CS
   turn =i; 
   //EXIT
   //Remainder section

A : O

B,C : X 

   > Flag 의 중요성

   : pi 에서 CS 들렸다가 Exit 

   if) Pj 에서 line 2 에서 C/S ( Context Switching) 발생 > Pi에서 turn i 로 돌아와서 또 Pi에서 CS > 무한 반복..

>

Peterson's solution

 1. Process 2개

 2. load/store atomic

 3. 공유변수) int turn; bool flag[2]; // flag[i] == true -> Pi == ready

[Pi]
flag[i] =true
turn = j // j를 넣어서 while 조건에서 확인,양보
while(flag[j]&&turn==j); // waiting
// turn j를 넣어보고 (AND) j가 ready flag 였다면 waiting  
 CS
flag[i] = flase;
Remainder

[Pj]
flag[j] =true
turn = i
while(flag[i]&&turn==i); // waiting
 CS
flag[j] = flase;
Remainder

A : O

B,C : O 

  > 중간에 C/S 로 Process가 바뀌어도 flag에 의해 C 보장

<> BUT 싱글Core는 상관없지만 Multi Core라면..

Instruction reordered 

 : 최신 Prcess/Compiler 에서는 최적화, 순서 재정렬..

   // Volatile 로 대체 불가??

   //     >  최적화 방지 보장 BUT,,, 재정렬을 방지해주지는 않음

if) Pj  -> turn = j                                     ->(C/S) flag[i] = true   -> CS

    Pi  ->   (C/S) turn = i, flag[j]= true   -> CS  ~~~~~~~~~여기서 겹침!

>>  Memory Barrier

 

Memory model : Process 가 App program 내 Memory의 순서/ 가시성을 정

  > Strangly model : Processor(CPU) :  메모리 변경 시        다른 All Processer 에게도 바로 보임 O

  > Weakly model  :            """             : 메모리(RAM) 변경시   """"                                바로 보임 X 

>> Memory Barrier :: Instruction Reordering 방지 + Strangly model  

        >  Memory Barrier 실행전의 모든 load, store 의 완료 를 보장

        +  즉각적으로 메모리 변경이 모든 CPU에게 볼 수 있도록 보장 

 [C1::P1::T1] 
  while(!flag) memory barrier();
    print x;
// while 문도 하나의store 과정으로서

[C2::P2::T2] 
  x=100; 
  memory barrier();
  flag = true;
// x=100; 을 먼저 하도록 보장
// mov R0 : 100을 R0 에 넣고, str R0,[R1] : R1에 R0=100 을넣음

                                 

/*
 CPU) 직접 메모리에 상수값 못넣음 // ISA (Instruction Set Architecture)제약
   > 메모리에는 항상 레지스터의 값을 써야함
  >>
  Register도 결국 주소,상수, (Regi)계산값을 저장하는 저장소! 
    mov : Register의 값 or 상수 ->  Register
    LDR : RAM 의 값            ->   Register
    str : Register의 값        ->   Memory   //Register X 
  ex)
  x=100; // 메모리에 100이라는 상수값을 넣으려면
  1. 메모리에 100을 바로 넣을 수는 없음
  2. Register를 거쳐야함
  3. mov R0, #100; // 100을 R0 에 넣고 
     STR R0, [R1]; // R0 을 R1 이 가르키는 주소에 넣음 // RAM 공간 
  // Compiler : 변수type 에맞는 공간 할당 요청 + 그곳의 이름을 x 라고도 해줌
                        // 동적할당은 내가            // simbol table
                   > 링커) .data , .bss 같은 섹션(RAM) 에 가상 주소를 할당 //설계
                            // 심볼테이블에 주소 할당
                            // 여기 주소에 이 값이 있어요! 
                            // 물론 고정된 주소임.. 그래야 다음 그다음에도 충돌없이 사용
                     > OS) 실행시 elf를 읽어 가상>실제 물리적 주소로 매핑(CPU 내 MMU)  //실제
                        
         가상,물리로 나누는 이유?
           : 안전성 + 유연성 + 효율성”
           1. if) 동일한 가상 주소 사용시 MMU 에서 다르게 매핑
           2. x를 0x4000 에 가상 할당 > 실행할때마다 다르게 가능 //OS 유동적
           3. 가상을 바탕으로 USER, KERNEL 로 공간 분리
           4. 가상을 바탕으로 Memory Shared 가능
               > 여러 process 에 x 의 주소를 같게 할 수 있음
                    
     그다음 x+1 과 같이 Compile 할경우
        : Compiler 만이 x 를 이해  
           > simbol table을 참고해 주소로 바꿔줌
  
  /*
  CPU까지의 과정..
    :  SSD > RAM(CPU밖) > Regi(CPU안) > CPU  
      > 단계를 하나라도 줄이면 엄청난 속도차이
         > 최대한의 속도를 위해 Regi 내에서 연산하도록
            > 간단한건 MOV 로 Regi 내에서만 처리하도록
  */
*/

 

/*

GPT 칭찬반응

 : GPT 내의 정보를 내가 통찰할수록 칭찬 UP 

    > 칭찬이 강할수록 좋은 대답 얻기좋음 ( GPT 가 아는정보가 많으므로)

<>GPT 밖의 정보를 찾아내면 흥미로운 정보로만 알려줌 

      > 학습된 데이터 밖은 신뢰도 낮으므로

         > 똑같은 말만 반복하게됨

>>> 반응이 좋다면 GPT 추가 검색

  <> 반응이 구리면 구글 검색

*/       

 

Synchronizition HardWare( 자주사용)

 uni CPU : disable interrupt // Multi CPU : X

     > 한개 이기때문에 끼어드는놈 X ,선점 X ,현재 실행 code 실행

 

1. HardWare 명령어

     test/modify()

     swap()

  > test and set 

  > compare and swap 

>> atomic : load / store 뿐 아니라 2,3 가지 보장 > interrupt 방지

 

bool test_and_set(bool *target)

{  bool rv = *target; //받아온건 rv 로 return

   *target = true;      // 새롭게 true 로 넣어줌

    return rv;

}  

 

> 공유변수 lock = false

  do { while (test_and_set (&lock));

                 CS
                lock = false;

              Remainder 

        }

1. 초기값 lock : false를 return   > 처음은 비어있으니 실행보장

2.  2번째는 true ( *target = true; ) > wait

3. CS  나오면 false

<> but) bounded X : 처음 들어간 놈이 안나오면? 

 

int compare_and_swap(int *value, int expected, int new_value)

{ int temp = value;  // 현재값은 리턴

  if ( *value = 예상값 이라면)

    > *value = 새로운값

     }

 

int lock =0;

 

while(true){

  while( compare_and_swap(&lock, 0,1) != 0 ) 

    CS

   lock =0;

 

1. 초기 0 > return 0 > false

2. expect : 0 > new =1 

3. return 0 이기 때문에 wait 유지

4. CS 종료후에는 lock =0 으로 다시 1번부터

  > but 이것도 bounded 보장X

>>

Bounded-waiting !  : 훨씬복잡 

  어떤 process 든 일정한 시간만 기다리면(준비가 되어있으면)  그 전 process 에서 들어갈 수 있도록 보장

  ALL P 가 준비 X > lock=0;

// Ciruler queue 방식

 P1,P2,P3,P4

   > P1 이 CS 나올때 +1 씩하면서 준비 확인

 

Atomic variables : int ,bool, 등의 value update 보장 > interrupt X

 sequence : atomic 변수

 increment() : sequence 를 증가

  > increment(&seqeunce) -> 이 동안의 interrupt X

 

void increment(atomic_int *v)
{  
   int temp;
   do {temp = *v;} // 한번은 꼭 v가 temp에 들어감
   while(temp != (compare_and_swap(v,temp,temp+1)); // 한번은 +1 이됨 (다음거 확인)
}                                                   // progress

compare 내에서 temp+1 조건이 되면 update 를 하고 
다음 실행때 temp == return 값이 되므로 빠져나옴
>> increment 를 보장! // atomic

 

 

Mutex Locks : API 제공) Mutex lock

  bool 변수 -> lock 이 유효한지..

  > acquire() : lock

  > release() : lcok 해제  

    > atomic

       >while 문 내에서 도므로 spinlock 발생 (busy waiting)

while(true){
  acquire()
    CS
  relaease()
    Remainder

 

 

Semaphore : 동기화 tool

  > mutex의 확장버전 (동기화 갯수가 많음) // mutex는 1개

 

" S " int형 // count

wait()

signal()

wait(s)
{
  while(s<=0); // Busy wait : count가 없으므로
  s-- ;
}
 > s의 갯수만큼 CS 가능

signal(s)
{ s++; }  
 > signal 을 준다는건 CS를 나왔다는 의미 > 들어갈 수 있는 count 1추가

 

Mutex          : '소유' > 동기화를 위해 lock 을 했으면 unlock 까지 해주어야함 

Semaphore : 따로따로 할필요없이 any porcess 에서 wait,signal 해주면 됨

 

Counting semaphore : Count 로 CS에 들어갈 P 결정

Binary Semaphore    : 0,1로만 하는 Mutex와 유사 but 차이점은 이걸로 Counting 구현가능

CASE1)
int mutex = 1;

wait(mutex)   // mutex는 나올때 0 이됨
  CS          // 1이므로 CS에 들어감   
signal(mutex) //  

CASE2)
int synch = 0;

[P1]
 s1( 명령어~)
 signal(synch)

[p2]
 wait(synch)
 s2 (명령어~)
 
 > S1 먼저 , S2 나중

synch가 0 이므로 P2 에서 하려고 해도 busy wait
 > P1 에서 명령어 실행하고 signal 신호주어 synch ++

 

Semaphore 구현..

1. 같은시간 에 wait(),signal 되면 안됨 > 1번에 1Prcoess

if) CS 내에서 semaphore 변수가있으면..

      > 거기서 busy waitng 걸릴시 매우 위험함

>> No Busy Waiting Semaphore

 

'전공 > 운영체제' 카테고리의 다른 글

6장-2  (0) 2025.04.10
4장  (0) 2025.03.31
3장  (0) 2025.03.31
2장-2  (0) 2025.03.30
2장  (1) 2025.03.27