Posts [CS] 드디어 Process & Thread 시뮬레이션 해보기
Post
Cancel

[CS] 드디어 Process & Thread 시뮬레이션 해보기

길 잃은 라쿤이 돌아왔다. 어디까지 갔다온건지 soulU Seminar 다녀온 느낌이랄까… 원래 목표는 한 주를 마치는 금요일날 가까스로 클래스를 통한 시뮬레이션을 마치고, 주말에는 비동기와 woker threads 같은(?)걸 재빨리 공부해서 응당 thread 처럼 작동하게끔 해볼 작정이였는데, 아주 그럴싸한 계획이 되고 말았다.



Process 와 Thread 작동원리 시뮬레이션 해보기


1. Process simulator (Single Thread)



우선 Process 에 대해서 이해한대로 표현해보자면 흔히 내가 사용하는 카카오톡 은 내가 터지해주기 전까지는 Program 에 지나지 않았으나 내가 터지하는 순간 Process 가 된다.

Process 의 코드들은 먼길 떠나서 배웠던 방식으로 메모리에 얹혀지고, 우리의 영특한 CPU 께서 이 코드를 매우 빠르게 처리해준다.

이 과정에서 Processforkchild threads 가 생겨나고, 녀석들이 각자의 일을 처리하고 돌아올 때까지 parentchild 를 기다리게 된다. process memorystack 영역에서 RET 를 생각하니 이해가 조금 더 수월했다.

dont-sigkill

$ the real reason to not use sigkill

이런 짤도 있었는데, sysAdmin 이라거나 sigKill 에 대해서 잘 모르면서도 재미가 있다. 얼핏보기에 프로세스 강제종료 같은거 시키면 자식 스레드는 부모를 못만나고 사망하게 된다는 슬픈이야기 같다. ‘미안하다 그 동안의 스레드들아… 너희들은 이번 기회에 내게 충분히 갚아줬다…’

1. Process 생성

  • 공부하면서 떠오르는 생각으로는 모든 Process 는 하나 이상의 thread 를 가지므로 single thread 로 먼저 구현해보고, multi threads 는 기존의 Processextends 하는 방식으로 해볼까 하는 계획을 세웠다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    class Process {
      constructor(name, second, elapsed = 0, current = status.READY) {
        this.name = name;
        this.second = second;
        this.elapsed = elapsed;
        this.current = current;
      }
    }
    
    • Process class 생성

    • constructor

      • PID 역할을 해줄 name
    • Process 사이즈를 대신할 second
      • 현재 처리 경과를 보여줄 elpased
    • PC 로 상태관리를 해줄 current

2. Queue 생성

  • queue 라는걸 머리 속으로 생각은 이미했지만 실제로 구현하려니 생각보다 쉽지 않았다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    class Queue {
      constructor(processList) {
        this.processList = processList;
        this.queue = null;
        this.count = 0;
        this.keepGoing = true;
      }
    }
    
    • Queue class 생성
    • constructor
      • 실행할 Processlist 로 받아서 queueprocessList 로 담는다.
      • count 로 처리 완료시간을 보여줄 예정
      • keepGoing 은 조금 꼼수처럼 됐지만 마지막에 setTimeOut 으로 exec 할 때, process 를 마쳤음에도 불구하고 계속 실행되는걸 막으려고 boolean 을 달아놨다.

3. Process Life Cycle

process-state

Process Life Cycle

  • 구현해보고나니 좀 이해가 가는데 처음 이 그림을 보고 있을땐 너무 막막했다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    class Queue {
      constructor(processList) {
        this.processList = processList;
        this.queue = null;
        this.count = 0;
        this.keepGoing = true;
      }
      
      initializing() {
        for (const key of this.processList) {
          key.current = status.WAITING;
        }
        this.queue = this.processList[0];
        this.print();
      }
      
      nextQue() {
        if (this.isTerminated()) {
          this.queue.current = status.TERMINATED;
        }
      
        if (this.queue.current === status.RUNNING) {
          this.queue.current = status.WAITING;
        }
      
        let terminated = this.processList.filter((el) => el.current === status.TERMINATED);
        let waiting = this.processList.filter((el) => el.current === status.WAITING);
        let elapsed = waiting.filter((el) => el.elapsed < this.queue.elapsed);
      
        if (terminated.length === this.processList.length) {
          this.print();
          this.keepGoing = false;
          return this.printExit();
        }
        if (elapsed.length === 0) {
          this.queue = waiting[0];
        } else if (elapsed.length === 0 && waiting.length === 0) {
          this.queue = this.queue;
        } else {
          this.queue = elapsed[0];
        }
      }
      
      processExecutor() {
        this.queue.elapsed++;
        this.queue.current = status.RUNNING;
        this.print();
        this.nextQue();
      }
      
      isTerminated() {
        return this.queue.elapsed === this.queue.second;
      }
    }
    
    • 그림에 따라서 process life cycleQueuemethod 로 구현해봤다.
    • initializing : 생성된 prcoessQueue 에 들어올 때, currentready 에서 waiting 으로 바뀌고, 첫 번째 processqueue 에 담긴다.
    • processExcutor : queue 에 들어온 prcess 를 실행하고, print 해준 뒤, nextQue 로 보낸다.
    • nextQue : 다음 queue 를 잡아준다.
      • 이 부분이 가장 시간을 많이 잡아먹었다.
      • 함수 종료조건과 스트림 함수를 더 연습해야 될 것 같다.
    • nextQue 에서 다음 큐를 잡는 조건
      • isTermintaed : terminatedprocess 가 있으면 queue 에서 제외 시킨다.
      • queuerunnning 인 상태가 있으면 wainting 시켜준다.
      • terminated 인 상태를 filter 로 걸러서 process 의 개수와 같으면 excutor 를 종료시킨다.
      • waiting 인 상태를 filter 로 걸러서 elapsedqueue 보다 작은 process 를 다음 queue 에 담아준다.
        • 사실 이 부분을 waiting 인 상태 중에 elapsed 가 가장 작은 process 를 다음 queue 에 담는 조건으로 수정하고 싶었는데 수정을 못했다.
        • 그래서 process 가 3개 뿐이고, single thread 를 사용할 때는 process 가 차곡차곡 순서대로 실행되지만 thread 의 개수가 달라지면 일정 process 가 먼저 실행되는 현상이 발생한다.
      • 한 개의 process 만 남겨두고 다른 모든 processterminated 되면 running 인 상태의 processqueue 에 담아 작업을 마쳐준다.




2. Process simulator (Multi Threads)



Thread 의 첫인상은 지렁이다. 이유는 모르겠지만 인터넷에 있는 거의 모든 thread 에 대한 설명이 지렁이 모양으로 나와있다. 이 친구의 특징은

  • Process 안에서 태어난다.

  • Process 간의 context switching 은 비용이 비싸다.

    739f35177ddd7985ac7b8e7211f9f3d4

    context switching in the operating system [Explained]

  • 그러니까 이것 또한 내가 이해한대로 표현해보자면 영특한 CPU 라도 core 개수가 한 개라면 한 가지 일 밖에 할 수가 없다. 그래서 process 는 각각의 thread 를 긴밀하게 switching 하게되는데, 얼마전에 register 따라 갔다가 봤던 process memorystack 영역을 제외한 나머지 부분을 thread끼리 공유한채로, process 가 요리조리 thread 를 바꿔가며 실행하게 된다.

  • 따라서 threadprocess 에 비해 context switching 비용이 싸다.

Threads

Threads

  • 장황한 이해가 된 것 같은데, 이번 시뮬레이션의 포인트를
    • processthread 의 전반적인 작동 방식에 대해 이해하고,
    • multi threads 를 통해 process 실행 시간의 단축을 눈으로 확인(?) 해보는 것으로 했다.
    • (인텔의 hyper threading 은 인텔 주가가 다시 회복되면 정리해볼까… 한꺼번에 너무 다양한걸 얇고깊고넓게 학습해서 양이 너무 많다 ㅠㅠ)

1. Thread 생성

  • 시뮬레이션도 시뮬레이션이지만 class 자체도 생소하고, extends 를 써봐야겠다는 생각에 좀 재미있었다.

  • 이번에는 super 를 사용하는 것도 성공했다구.

  • multi threads 구현에는 처리 시간의 비교를 위한 약간의 조건이 들어갔는데,

  • process 총 처리 시간을 2로 나눴을 때의 몫이 thread 의 개수가 된다.

  • process 는 기본적으로 하나 이상의 thread 를 가지니까 생성된 thraed 의 개수에 한 개를 더해야 하는가에 대한 조정이 필요한데 일단 시뮬레이션이 목적이니 생성된 thread 의 개수를 process 가 가지는 thread 의 개수로 하기로 했다.

    1
    2
    3
    4
    5
    6
    
    class Thread extends Process {
      constructor(name, second) {
        super(name, second);
        this.thread = 1;
      }
    }
    
    • 일단 하고싶었던대로 Process 를 상속해서 Thread 를 생성했다.
      • 사실 이 녀석은 multi threads 를 가진 process 니까 이름을 달리해줬어야 할 것 같은 느낌이 지금에서야 든다.
    • contructor 에 기본적으로 thread 를 하나 가질 수 있도록 설정은 해놓았지만 의미는 없다.

2. ThreadQue 생성

1
2
3
4
5
6
7
8
9
10
11
   class ThreadQueue extends Queue {
     threadExecutor() {
       this.queue.elapsed += this.queue.thread;
       if (this.queue.elapsed > this.queue.second) {
         this.queue.elapsed = this.queue.second;
       }
       this.queue.current = status.RUNNING;
       this.print();
       this.nextQue();
     }
   }
  • Queueextends 해서 ThreadQueue 를 생성했다. 단지 method 를 바꿔줘야 했다.
  • threadExcutor 라는 method 를 작성해서 elapsed 를 각 Process 가 가진 thread 의 개수만큼 올라갈 수 있도록 바꿔줬다.


3. Exec Process

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ================= create process ================
const currentProcessList = Random.createList(4);
const currentExecProcessList = Random.execList(currentProcessList);

// =================== print =======================
Print.currentInfo(currentProcessList);
Print.execListInfo(currentExecProcessList);

// ===================== Que =======================
const queue = new Queue(currentExecProcessList);

function run() {
  function exec() {
    queue.processExecutor();
    if (queue.keepGoing) {
      setTimeout(exec, 1000);
    }
  }

  queue.initializing();

  exec();
}

run();
  • 기본적으로 Single Thread 에서는 4개의 Process 를 생성하고, 그 중에 3개를 랜덤하게 뽑아서 실행시킨다.
  • run 에서는 exec 함수를 작성해서 processExcutorsetTimeOut 을 통해 recursion 으로 작성해봤다.
    • 사실 setTimeOut 에 대한 공포감이 있었는데, 이게 성공되서 너무 기뻤다.
    • setInterval 도 재밌는 친구였다.
    • 빨리 비동기에 대해서 공부 할 것 (체크)…

4.Compare result

같은 Process 를 3개씩 생성하고, single threadmulti threads 로 비교해봤다.

1. Single Thread

singlethread

  • 24초 걸렸다.

2. Multi Threads

multithread

  • 8초 걸렸다.

5. 정리

  • 좀 많이 돌아오느라 node module 에서 구현해보지 못한게 아쉽다.
  • threadcontext switching 하는 부분까지 자세하게 할 수 있는 방법이 없을까 고민해봤는데, 여기까지 하는걸로도 주말이 홀랑 지나갔다.
  • 마음을 좀 가다듬어야 할 것 같다. ㅠㅠ


참조

This post is licensed under CC BY 4.0 by the author.

[CS] 프로세스 메모리 구조(Memory Model), 그리고 생애 첫 어셈블리어(Assembly Language)

[Algorithm] Hacker Rank 와의 첫만남, 그리고...(feat.CodeForce)

Comments powered by Disqus.