<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Dev.log</title>
    <link>https://newdevlog.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 4 Aug 2026 19:20:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kseungwoo</managingEditor>
    <item>
      <title>[Operating System] Condition Variables</title>
      <link>https://newdevlog.tistory.com/23</link>
      <description>&lt;p&gt;지금까지 lock의 개념을 살펴보았고, hardware와 OS의 적절한 조합으로 어떻게 구현되는지 살펴보았다. 하지만, concurrent programs을 빌드하기 위한 기본 요소로 lock만이 있는 건 아니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;특히, 스레드는 종종 실행을 계속하기 전에 condition이 true인지 확인하고 싶어한다. 예를 들어, 상위 스레드는 계속하기 전에 하위 스레드가 완료되었는지 확인하고 싶을 수 있다. 이를 join()이라고도 부른다. 이러한 기다림(wait)은 어떻게 구현할 수 있을 지 살펴보자.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Spin-based Aproach - inefficient&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;693&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zn89A/btq4jtL8f8C/8CfOklsIXrIPBrqSCKNK3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zn89A/btq4jtL8f8C/8CfOklsIXrIPBrqSCKNK3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zn89A/btq4jtL8f8C/8CfOklsIXrIPBrqSCKNK3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzn89A%2Fbtq4jtL8f8C%2F8CfOklsIXrIPBrqSCKNK3k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;693&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Shared Variable(공유 변수)를 사용해 볼 수 있다. 이러한 Spin-based Approach라 칭한 이 솔루션은 일반적으로 작동하지만 상위 항목(parent)이 CPU 시간을 낭비(spin and waste)하고 있기 때문에 매우 비효율적이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대신에 우리가 원하는 것은 child의 실행이 끝날 때까지, 즉 우리가 기다리고 있는 condition이 실현될 때까지&amp;nbsp; parent를 sleep하는 것이다.(CPU를 낭비하지 않고).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;멀티 스레드 프로그램에서, 스레드를 계속하기 전에 어떤 조건이 참일 때까지 기다리도록 하는 것이 종종 유용하다. condition이 true가 될 때까지 회전(spin)하는 간단한 방법은 매우 비효율적이며 CPU Cycles를 낭비하며, 경우에 따라서는 부정확할 수 있다. 그렇다면, 어떻게 thread를 wait하는 것이 좋을까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Definition and Routines&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;조건이 참일 때까지(until the condition becomes true) 기다리기 위해서, 스레드는 조건 변수(conditional variable)라고 하는 것을 사용할 수 있다. 조건 변수는 condition에 따라 실행이 기다려지는 상황에서 적용할 수 있는 명시적 대기열(explicit queue)이다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른 스레드는 상태(state)가 변하면 대기 중인 하나 이상의 스레드를 깨울 수 있으며 따라서 스레드가 계속 진행될 수 있도록 허용한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이러한 Conditional Variable(조건 변수)는 크게 wait()와 signal() 두 가지 연산과 연관되어 있다. wait() 호출은 스레드가 절전 모드(sleep)로 전환할 때 실행되며, signal() 호출은 스레드가 프로그램에서 무언가를 변경했기 때문에 해당 조건에 대해서 대기 중인 절전 스레드를 깨우려고 할 때 실행된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기서 done은 State Variable(상태 변수)다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Conditional Variable (조건 변수)란,&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Condition Variable은 특정 조건을 만족하기를 기다리는 변수이며, &lt;/span&gt;thread간의 신호 전달을 위해 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ju-hy.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;출처 : ju-hy.tistory.com/39&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래는 Conditional Variable(조건 변수)와 State Variable(상태 변수)를 사용하여 thread_join과 thread_exit을 구현한 코드 예제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;615&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bglzil/btq4kUI8w0p/BXL7VJAfomPydhwQepZpQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bglzil/btq4kUI8w0p/BXL7VJAfomPydhwQepZpQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bglzil/btq4kUI8w0p/BXL7VJAfomPydhwQepZpQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbglzil%2Fbtq4kUI8w0p%2FBXL7VJAfomPydhwQepZpQK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;615&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 코드의 두 가지 케이스를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;# Case 1.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;부모&lt;span style=&quot;color: #000000;&quot;&gt;는 자식 스레드를 만들지만 스스로 실행을 계속한다(우리는 하나의 프로세서만 가지고 있다). 따라서 자식 스레드가 완료될 때까지 기다리기 위해 즉시 thr__join()으로 호출한다. 이 경우 lock을 획득하고, child가 완료되었는지(아닌지) 확인한 후 wait()를 호출해 스스로 sleep한다.(따라서 unlock된다). &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;The&amp;nbsp;child&amp;nbsp;will&amp;nbsp;eventually&amp;nbsp;run,&amp;nbsp;print&amp;nbsp;the&amp;nbsp;message&amp;nbsp;&amp;ldquo;child&amp;rdquo;,&amp;nbsp;and&amp;nbsp;call&amp;nbsp;thr&amp;nbsp;exit()&amp;nbsp;to&amp;nbsp;wake&amp;nbsp;the&amp;nbsp;parent&amp;nbsp;thread&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하위 스레드가 마침내 실행되며, child메시지를 보내고, thr_exit()를 호출하여 부모에게 신호를 보낸다. 이 과정에서 lock을 걸고, 완료 상태(done=1)를 설정하며, 부모에게 신호를 보내어 깨운다. 마지막으로, 상위 스레드가 실행되며 잠금이 설정된 상태에서 wait()로부터 돌아오고, 잠금을 해제하고, &quot;parent: end&quot; 메시지를 출력한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;# Case 2.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;자식 스레드가 생성 즉시 실행되고 done을 1로 설정하며 signal을 호출하여 절전중(sleeping)인 스레드를 깨우고 완료된다. 그런 다음 상위 스레드가 실행되고&amp;nbsp; thr_join()을 call하며 done이 1인것을 확인하고 wait()하지 않고 return한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;* done이라는 상태 변수(state variable)가 꼭 필요한 지 의문이 들 수 있다. 만약 상태 변수가 없다면 어떻게 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;잘못된 접근 방식이다. Case 2의 상황에 대처할 수 없게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;자식 스레드가 즉시 실행되서 thr_exit()를 호출하는 경우 자식 스레드는 signal을 보내지만 해당 condition에 대해서 asleep상태의 스레드가 없다. 부모 스레드가 실행되면, wait를 호출할 것이고 asleep 상태에 갇히게(stuck) 될 것이다. 어떠한 스레드도 해당 스레드를 깨우지 않을 것이다. 해당 예시로부터, 상태 변수 done의 중요성을 깨달을 수 있다. done이라는 상태 변수는 스레드가 알고자 하는 값을 기록한다. sleeping, waking, locking은 해당 변수를 둘러싸고 build된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;* 만약 lock 기능이 없다면 어떻게 될까?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;race condition이 발생한다. 만약 부모가 thr_join()을 호출한 다음 done의 값을 확인하면, 그것은 0일 것이고 wait()을 호출하여 sleep 상태빠지려 할 것이다. 이 때 lock 기능의 부재로 인해 parent가 interrupt되며 child가 실행된다면 child는 state variable인 done을 1로 변경하고 signal을 보낼 것이다. 하지만 waiting상태의 잠든 스레드가 없을 것이며, 다음으로 parent가 다시 실행될 때 영원히 sleep상태에 빠진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 join 예제를 통해서 조건 변수를 올바르게 사용하기 위한 몇 가지 기본 요소를 확인할 수 있었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;signal()이나 wait() 호출 시 잠금을 유지하라&lt;/b&gt; -&lt;i&gt;Hold the lock when calling signal or wait.&lt;/i&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;wait()호출 시 lock을 유지하는 것은 선택사항이 아니라, wait() 의미론적으로 보았을 때 강제되어진다(필수사항이다).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜냐하면,&lt;/p&gt;
&lt;p&gt;(a) wait()를 호출할 때 lock이 유지된다고 가정하고,&lt;/p&gt;
&lt;p&gt;(b) 호출자를 sleep 상태로 전환할 때 lock을 해제하며,&lt;/p&gt;
&lt;p&gt;(c) sleep 상태로부터 돌아오기 직전에 다시 lock을 획득하기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 signal() 또는 wait()를 호출할 때 lock을 유지하자.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;T&lt;span&gt;h&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;/&lt;span&gt;Co&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;b&lt;/span&gt;&lt;span&gt;l&lt;span&gt;e&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;이번에 다룰 동기화 문제는 Producer/Consumer Problem(생산자/소비자 문제)로 알려져있고, dijkstra에 의해 처음 제기되었다. 이 문제로부터 잠금 또는 조건 변수로 사용될 수 있는 일반화된 semaphore가 등장하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;하나 이상의 생산자 스레드와, 하나 이상의 소비자 스레드가 있다고 상상해보자. 생산자는 데이터 항복을 생성하여 버퍼에 배치하고, 소비자는 버퍼에서 지정된 항목을 가져와 어떠한 방식으로 소비한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이러한 방식은 실제 많은 시스템에서 사용되는 방식이다. 예를 들어, 멀티 스레드 웹 서버에서, 생산자는 HTTP 요청을 작업 대기열(the bounded buffer)에 넣고, 소비자 스레드는 이 대기열에서 요청을 꺼내서 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Bounded buffer는 하나의 프로그램의 출력을 다른 곳으로 파이프(pipe)할 때도 쓰인다. 예를 들어, &quot;grep foo file.txt | wc -l&quot;이라는 커맨드를 입력하면, 두 프로세스를 동시에 실행한다. 프로세스 grep은 file.txt로부터 standard output에 한줄 한줄 출력하고, 이는 파이프를 통해 리디렉션되어&amp;nbsp; 프로세스 wc의 표준 입력으로 들어온다. wc는 input stream의 라인 개수를 결과로 출력하는 프로세스이다. 그러므로, grep프로새스는 생산자이고, wc 프로세스는 소비자이다. 이들 사이에는 in-kernel bounded buffer가 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;bounded buffer는 공유 리소스이기 때문에 race condition을 막기 위해 동기화된 접근(synchronized access)를 필요로 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Producer/Consumer with If statement&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;505&quot; height=&quot;467&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/481nO/btq4t5qLDUi/ITVimbDyKgyZYdUZKGqy5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/481nO/btq4t5qLDUi/ITVimbDyKgyZYdUZKGqy5k/img.png&quot; data-alt=&quot;if문을 이용한 Producer/Consumer 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/481nO/btq4t5qLDUi/ITVimbDyKgyZYdUZKGqy5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F481nO%2Fbtq4t5qLDUi%2FITVimbDyKgyZYdUZKGqy5k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;505&quot; height=&quot;467&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;if문을 이용한 Producer/Consumer 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위 코드는&amp;nbsp; if를 사용한 producer/consumer 구현 코드이다. 이는 생산자와 소비자 스레드가 각각 하나일 때는 문제가 없지만, 여러개일 때 문제가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어, 생산자 스레드 1개, 소비자 스레드 2개가 있다고 생각해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;604&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfd8SF/btq4DiPHIjH/00LUWLS2CQfd7I3hHS4JI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfd8SF/btq4DiPHIjH/00LUWLS2CQfd7I3hHS4JI0/img.png&quot; data-alt=&quot;if문을 이용한 Producer/Consumer 구현에서 발생할 수 있는 문제 상황 (Broken Solution)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfd8SF/btq4DiPHIjH/00LUWLS2CQfd7I3hHS4JI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfd8SF%2Fbtq4DiPHIjH%2F00LUWLS2CQfd7I3hHS4JI0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;604&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;if문을 이용한 Producer/Consumer 구현에서 발생할 수 있는 문제 상황 (Broken Solution)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;1. Tc1(첫번째 소비자 스레드)가 먼저 실행되고, c2 if문에서 count==0이므로(버퍼에 데이터가 존재하지 않으므로) sleep상태가 된다.&lt;/p&gt;
&lt;p&gt;3. Tp1 스레드가 실행되며 count==0이므로 버퍼에 데이터를 넣고, Tc1에 신호를 보내어 sleep상태의 Tc1을 다시 스케줄링한다.&lt;/p&gt;
&lt;p&gt;4. 먼저 스케줄링되어있던 Tc2가 버퍼에 있는 데이터를 소비한다.&lt;/p&gt;
&lt;p&gt;5. 다음으로 sleep상태에서 깨어난 Tc1가 실행되지만, buffer가 비어있는 문제가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 여기서 볼 수 있는 문제는 Tp1(생산자가) Tc1(소비자)를 깨운 후에, Tc1이 다시 실행되기 전에 bounded buffer의 상태가 변경되었다(Tc2에 의해)는 점이다. 즉, 깨어난 스레드가 실행될 때 원하는 대로 상태(state)가 존재할 것이라는 보장이 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 문제를 해결하기 위해서는 아래처럼 while문을 이용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer/Consumer with While statement (with one condition variable)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;하지만, while 문을 사용하더라도 해결하지 못하는 문제 하나가 더 있다. 바로 조건 변수가 하나라는 점이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;610&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zWth6/btq4BxUjRJw/KGrMxNYH6qTqz4foFfUk2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zWth6/btq4BxUjRJw/KGrMxNYH6qTqz4foFfUk2K/img.png&quot; data-alt=&quot;While문을 이용한 Producer/Consumer 코드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zWth6/btq4BxUjRJw/KGrMxNYH6qTqz4foFfUk2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzWth6%2Fbtq4BxUjRJw%2FKGrMxNYH6qTqz4foFfUk2K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;610&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;While문을 이용한 Producer/Consumer 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이전과 같이 예를 들어, &lt;span style=&quot;color: #333333;&quot;&gt;생산자 스레드 1개, 소비자 스레드 2개가 있다고 생각해보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;660&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgMEj0/btq4CrsMlRm/uqqNCbd4bK1wTytMkquwI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgMEj0/btq4CrsMlRm/uqqNCbd4bK1wTytMkquwI1/img.png&quot; data-alt=&quot;While문을 이용한 Producer/Consumer 구현에서 발생할 수 있는 문제 상황 (Broken Solution)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgMEj0/btq4CrsMlRm/uqqNCbd4bK1wTytMkquwI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgMEj0%2Fbtq4CrsMlRm%2FuqqNCbd4bK1wTytMkquwI1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;660&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;While문을 이용한 Producer/Consumer 구현에서 발생할 수 있는 문제 상황 (Broken Solution)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1.Tc1, Tc2가 순서대로 실행되며, 소비할 데이터가 존재하지 않으므로 sleep상태가 된다.&lt;/p&gt;
&lt;p&gt;2. Tp1이 데이터를 생산하여 bounded buffer에 데이터를 넣고, Tc1을 깨운다.&lt;/p&gt;
&lt;p&gt;3. Tc1이 데이터를 소비하고, Tc2를 깨운 후 sleep 상태에 빠진다. (조건 변수가 하나이므로 다음 대기열에 있는 Tc2를 깨우는 것이다.)&lt;/p&gt;
&lt;p&gt;4. Tc2가 실행되지만 buffer에 데이터가 없으므로 sleep상태에 빠진다.&lt;/p&gt;
&lt;p&gt;5. Tp1, Tc1, Tc2 모두가 sleep 상태에 빠지는 문제가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제를 해결할 수 있는 단순한 방법은 조건 변수를 2개 사용해서 생산자는 소비자만 깨우도록, 그리고 소비자는 생산자만 깨우도록 만드는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer/Consumer with While statement (with two condition variable)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;614&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zjkvS/btq4wvJLgvu/AfgK2vQVfJAU9kZd7nU80k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zjkvS/btq4wvJLgvu/AfgK2vQVfJAU9kZd7nU80k/img.png&quot; data-alt=&quot;While문을 이용한 Producer/Consumer 코드 (조건 변수 2개)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zjkvS/btq4wvJLgvu/AfgK2vQVfJAU9kZd7nU80k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzjkvS%2Fbtq4wvJLgvu%2FAfgK2vQVfJAU9kZd7nU80k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;614&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;While문을 이용한 Producer/Consumer 코드 (조건 변수 2개)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위처럼 조건 변수 2개를 사용하여 생산자는 소비자만 깨우고, 소비자는 생산자만 깨우도록 바꾸어 문제를 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Producer/Consumer with While statement (with two condition variable and multiple buffer size)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이번엔, buffer size가 1 이상 일때의 구현을 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;589&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3hvye/btq4DhQPblA/k1o3o3uOyKZVzUCElqAEuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3hvye/btq4DhQPblA/k1o3o3uOyKZVzUCElqAEuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3hvye/btq4DhQPblA/k1o3o3uOyKZVzUCElqAEuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3hvye%2Fbtq4DhQPblA%2Fk1o3o3uOyKZVzUCElqAEuK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;589&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이전과 달라진 점은 buffer의 크기가 1이 아닌 다수라는 점이다. 이에 따라 put()과 get()코드가 수정되고, 생산자는 버퍼가 가득찬 경우에만 wait()을 수행하며, 소비자는 버퍼가 비어있을 경우에만 wait()를 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Covering Conditions&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;이번엔 다른 문제 상황을 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;558&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd2gZl/btq4CsrLWot/T5xyDr5ha2lF01u4Kovkt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd2gZl/btq4CsrLWot/T5xyDr5ha2lF01u4Kovkt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd2gZl/btq4CsrLWot/T5xyDr5ha2lF01u4Kovkt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd2gZl%2Fbtq4CsrLWot%2FT5xyDr5ha2lF01u4Kovkt1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;558&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위 코드는 멀티 스레드에서 메모리를 할당하고 해제하는 일부 라이브러리의 코드이다. 메모리를 할당받는 allocate() 코드는 소비자(메모리 할당받는) 스레드가 원하는 size만큼의 메모리를 할당받을 수 있을 때까지 wait하고, 메모리를 해제하는 스레드는 사용중인 메모리를 할당 해제한 후 신호를 보내 메모리를 할당받으려하는 스레드를 깨운다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 문제는 어떠한 스레드에 신호를 보내야 하는 가이다. 예를 들어, free()를 통해서 여유 메모리가 100이 되었는데, 메모리를 할당받으려하는 두 스레드(첫번째 스레드는 150만큼의 메모리를, 두번째 스레드는 50만큼의 메모리를 필요로한다.) 중 첫번째 스레드를 깨운다면 여유 메모리가 부족하여 할당받을 수 없으므로 다시 sleep상태가 되고 할당받을 수 있던 두번째 스레드에게는 기회가 가지 않는 문제가 발생한다. 즉, 수행할 수 있는 스레드가 있음에도 아무것도 수행하지 않는 상태가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 문제를 해결하기 위해서 signal() 대신 broadcast()를 사용하여 sleep상태의 모든 스레드를 깨우는 방법이 있다. 물론 성능 면에서 부정적인 영향을 줄 수는 있지만, 위와 같은 이슈를 해결할 수 있다. 이러한 trade-off를 잘 고려해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pages.cs.wisc.edu/~remzi/OSTEP/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620646494352&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operating Systems: Three Easy Pieces&quot; data-og-description=&quot;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome &quot; data-og-host=&quot;pages.cs.wisc.edu&quot; data-og-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Operating Systems: Three Easy Pieces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;pages.cs.wisc.edu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;+) 참고한 블로그 : &lt;a href=&quot;https://icksw.tistory.com/164?category=878876&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;icksw.tistory.com/164?category=878876&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620646505392&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[OS] Synchronization(동기화)를 위한 condition variables(조건 변수) - OS 공부 21&quot; data-og-description=&quot;안녕하세요! Pingu입니다. 오늘도 열심히 OS에 대해 알아보겠습니다! 지난 글에서는 일반적인 자료구조에 Lock을 상호 배제 구현하여 thread safety 하게 만드는 방법에 대해 알아봤었습니다. 여러 가&quot; data-og-host=&quot;icksw.tistory.com&quot; data-og-source-url=&quot;https://icksw.tistory.com/164?category=878876&quot; data-og-url=&quot;https://icksw.tistory.com/164&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dcnCex/hyJ9IzZ0fy/5IYyRrx60wuVp2v5UKsRhk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/l0mca/hyJ9LcpIgM/mmILXrK3uSEprtQJhPOyg0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jphfR/hyJ9YpiXHr/r1xdkHXTUuWC3LXTfuvDH1/img.jpg?width=1039&amp;amp;height=985&amp;amp;face=0_0_1039_985&quot;&gt;&lt;a href=&quot;https://icksw.tistory.com/164?category=878876&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://icksw.tistory.com/164?category=878876&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dcnCex/hyJ9IzZ0fy/5IYyRrx60wuVp2v5UKsRhk/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/l0mca/hyJ9LcpIgM/mmILXrK3uSEprtQJhPOyg0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/jphfR/hyJ9YpiXHr/r1xdkHXTUuWC3LXTfuvDH1/img.jpg?width=1039&amp;amp;height=985&amp;amp;face=0_0_1039_985');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[OS] Synchronization(동기화)를 위한 condition variables(조건 변수) - OS 공부 21&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;안녕하세요! Pingu입니다. 오늘도 열심히 OS에 대해 알아보겠습니다! 지난 글에서는 일반적인 자료구조에 Lock을 상호 배제 구현하여 thread safety 하게 만드는 방법에 대해 알아봤었습니다. 여러 가&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;icksw.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <category>Operating System</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/23</guid>
      <comments>https://newdevlog.tistory.com/23#entry23comment</comments>
      <pubDate>Mon, 10 May 2021 20:35:33 +0900</pubDate>
    </item>
    <item>
      <title>[Software Engineering] Design Pattern(디자인 패턴)이란?</title>
      <link>https://newdevlog.tistory.com/22</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Design Pattern?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;소프트웨어 공학론에서, 좋은 코드를 설계하기 위한 일종의 설계 디자인 방법론이다.&lt;/p&gt;
&lt;p&gt;실무적으로 프로그래머들 사이에서 대중적으로 인정받는 일반화된 효율적인 설계 방식이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;좋은 코드&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;좋은 코드란, 프로그램 개발 시에 맞닥뜨리는 여러 문제나 애로 사항들을 해결하고 만족할 수 있는 적절하게 짜여진 코드라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;확장과 수정에 용이하며, 설계 이후 추가적인 유지 보수에 비용이 적게 들어가며&lt;/p&gt;
&lt;p&gt;코드가 명확하고 단순하며&lt;/p&gt;
&lt;p&gt;재사용성이 높고&lt;/p&gt;
&lt;p&gt;리소스의 낭비가 없는 효율적인.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;객체지향적 관점에서 이러한 부분을 구체적으로 명시하여 5가지 원칙으로 제시한 것이 &lt;b&gt;SOLID&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SOLID&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;SOLID 원칙은 워낙에 유명하고, 알고 있으면 처음엔 와닿지 않더라도 개발하다보면 체감이 확 되는 내용이라 객체지향적 개발을 하는 프로그래머라면 두고두고 봐야하는 내용같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;S&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Single Responsibility Principle (SRP) : 단일 책임 원칙. 하나의 클래스는 하나의 책임만 가져야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;O&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Open/Closed PRinciple (OCP) : 개방 폐쇄 원칙. 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;L&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Liskov Substitution Principle(LSP) : 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;I&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Interface segregation Principle(ISP) : 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;D&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Dependency Inversion Principle(DIP) : 프로그래머는 추상화에 의존하며 구체화에 의존하면 안된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GoF 디자인 패턴&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Gang of Fout는 유명한 개발자 4명인데, 디자인 패턴을 체계화한 사람들이다.&lt;/p&gt;
&lt;p&gt;GoF 디자인 패턴은 생성(Creational) 패턴, 구조(Structural) 패턴, 행위(Behavioral) 패턴으로 구분된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;SOLID 철학이 녹아있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생성 패턴&lt;/h4&gt;
&lt;p&gt;객체 생성과 관련한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;추상 팩토리, 빌더, 팩토리 메소드, 프로토타입, 싱글턴&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;구조 패턴&lt;/h4&gt;
&lt;p&gt;클래스 및 객체를 조합해 더 큰 구조를 만드는 것과 관련한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;어댑터, 브리지, 컴포지트, 데코레이터, 퍼사드, 플라이웨이트, 프록시&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;행위 패턴&lt;/h4&gt;
&lt;p&gt;클래스 및 객체 간의 알고리즘이나 책임 분배와 관련한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;책임 연쇄, 커맨드, 인터프리터, 반복자, 메멘토, 옵서버, 상태, 전략, 템플릿 메소드, 비지터&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <category>Design Pattern</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/22</guid>
      <comments>https://newdevlog.tistory.com/22#entry22comment</comments>
      <pubDate>Thu, 6 May 2021 13:55:24 +0900</pubDate>
    </item>
    <item>
      <title>[Java] static 메모리의 생명주기</title>
      <link>https://newdevlog.tistory.com/21</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;static 변수 및 메소드는 언제 메모리에 할당되고 해제될까?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;본 궁금증을 해결하기 위해 static 메모리 영역에 좀 더 관심을 갖고 공부해보자!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;메모리 영역 살펴보기&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;694&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4yLN2/btq3YVvMOKs/9RHnmJf8RkHhGV1vxXKhh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4yLN2/btq3YVvMOKs/9RHnmJf8RkHhGV1vxXKhh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4yLN2/btq3YVvMOKs/9RHnmJf8RkHhGV1vxXKhh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4yLN2%2Fbtq3YVvMOKs%2F9RHnmJf8RkHhGV1vxXKhh1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;694&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Static 메모리에는 클래스, static 변수 및 메소드가 존재한다. Static 변수 및 메소드는 객체(인스턴스)를 생성하지 않고도 접근이 가능하며, 여러 객체(인스턴스)들이 공유한다. JVM이 프로그램을 시작할 때 할당되고, 프로그램을 종료할 때 할당 해제된다. &lt;span style=&quot;color: #333333;&quot;&gt;GC에 의해 관리되지 않으며&lt;span&gt; 생명주기가 프로그램의 시작부터 종료까지이므로 프로그램 실행 내내 메모리에 할당된 채 존재한다. 따라서 과용하게 되면 시스템의 성능에 악영향을 미칠 수 있다는 점을 생각해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Heap 메모리에는 new 연산자에 의해서 생성된 객체(인스턴스)들이 존재하는 영역이다. GC에 의해 관리된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Static 변수 및 메소드의 특별함&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;일반적인(non-static) 클래스의 변수 및 메소드는 해당 클래스가 인스턴스화(new 연산자를 통한 생성)되기 전까지는 사용할 수 없다. 하지만 static 변수나 메소드는 인스턴스의 생성과 상관없이(즉, 객체를 생성하지 않고도) 접근하여 사용할 수 있다. 즉, 프로그램 시작 시 메모리에 고정적으로 할당되며 프로그램이 종료될 때 해제된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;그래서, Static 변수와 메소드는&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;JVM의 실행과정에서 필요한 클래스의 정보를 메모리에 로딩한다. 이 로딩 시점에서 static 변수가 초기화된다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;따라서, 프로그램 시작 시 static 메모리에 할당되며 프로그램이 종료될 때 해제된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;[출처]&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;a href=&quot;https://kim-daeyong.github.io/2019-07-09-static/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;kim-daeyong.github.io/2019-07-09-static/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620045709357&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;static에 대해&quot; data-og-description=&quot;오늘 면접에서 static의 생명주기에 대한 질문을 들었다. static의 용도나 메모리에 한번 올라가고~ 이런건 알았는데 static이 언제 생성되고 언제 소멸하는지는 대충은 생각하고 있었지만 정확한 때&quot; data-og-host=&quot;kim-daeyong.github.io&quot; data-og-source-url=&quot;https://kim-daeyong.github.io/2019-07-09-static/&quot; data-og-url=&quot;https://kim-daeyong.github.io/2019-07-09-static/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://kim-daeyong.github.io/2019-07-09-static/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kim-daeyong.github.io/2019-07-09-static/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;static에 대해&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;오늘 면접에서 static의 생명주기에 대한 질문을 들었다. static의 용도나 메모리에 한번 올라가고~ 이런건 알았는데 static이 언제 생성되고 언제 소멸하는지는 대충은 생각하고 있었지만 정확한 때&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;kim-daeyong.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/m/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mangkyu.tistory.com/m/47&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620045728917&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Java] static변수와 static 메소드&quot; data-og-description=&quot;1. Static 정리 Java에서 Static 키워드를 사용한다는 것은 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미합니다. 이를 정확히 이해하기 위해서는 메모리 영역에 대한 이해가 필&quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/m/47&quot; data-og-url=&quot;https://mangkyu.tistory.com/47&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/B3rEx/hyJ4Wk5eI9/IuVowICnyNdtQKEShhj4z0/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400,https://scrap.kakaocdn.net/dn/k8aeQ/hyJ47z6s7m/dciX63VPjUKndALCPakVvk/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400,https://scrap.kakaocdn.net/dn/crSpej/hyJ41mnve1/TdwTusKuXkWMcHEiJih7K0/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/m/47&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/m/47&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/B3rEx/hyJ4Wk5eI9/IuVowICnyNdtQKEShhj4z0/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400,https://scrap.kakaocdn.net/dn/k8aeQ/hyJ47z6s7m/dciX63VPjUKndALCPakVvk/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400,https://scrap.kakaocdn.net/dn/crSpej/hyJ41mnve1/TdwTusKuXkWMcHEiJih7K0/img.jpg?width=642&amp;amp;height=400&amp;amp;face=0_0_642_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[Java] static변수와 static 메소드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;1. Static 정리 Java에서 Static 키워드를 사용한다는 것은 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미합니다. 이를 정확히 이해하기 위해서는 메모리 영역에 대한 이해가 필&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;mangkyu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <category>static</category>
      <category>메모리</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/21</guid>
      <comments>https://newdevlog.tistory.com/21#entry21comment</comments>
      <pubDate>Mon, 3 May 2021 21:43:08 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] Stack 2개로 Queue 만들기</title>
      <link>https://newdevlog.tistory.com/20</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stack 2개로 Queue 만들기&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;개발 관련 정보들을 구글링하던 도중 면접에서 Stack 2개로 Queue 1개를 만들어보라는 질문을 받았다는 글을 보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Stack 2개로 Queue를 만든다라는 아이디어를 보고 어떻게 구현하면 좋을지 구글링해보았고, 그러한 정보를 이 글에 담아보려 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Stack(스택)은&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Stack은 LIFO(Last-In First-Out) 구조다. 나중에 들어온 것이 먼저 나간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Queue(큐)는&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Queue는 FIFO(First-In First-Out) 구조다. 먼저 들어온 것이 먼저 나간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Stack 2개로 Queue 1개 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;아래와 같이 Stack 2개를 둔다. 하나의 스택은 Inbox, 그리고 나머지 한 스택은 Outbox다.&lt;/p&gt;
&lt;p&gt;Inbox로 원소가 들어오고, Outbox로부터 원소가 나간다. 즉, 사용자는 Inbox로 데이터를 push()하고, Outbox로부터 데이터를 pop() 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;560&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEqhsV/btq38fyZz9F/SN2X1Faq9h6TiYdH39DRu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEqhsV/btq38fyZz9F/SN2X1Faq9h6TiYdH39DRu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEqhsV/btq38fyZz9F/SN2X1Faq9h6TiYdH39DRu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEqhsV%2Fbtq38fyZz9F%2FSN2X1Faq9h6TiYdH39DRu1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;560&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한번 데이터를 넣어보자. 1이라는 데이터를 push하고 2라는 데이터를 순차적으로 push한다. 사용자가 데이터를 push()할 경우 Inbox로 들어온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;695&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkyYrU/btq37BChLXI/fK5raC7rYaLGzwR17jSquK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkyYrU/btq37BChLXI/fK5raC7rYaLGzwR17jSquK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkyYrU/btq37BChLXI/fK5raC7rYaLGzwR17jSquK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkyYrU%2Fbtq37BChLXI%2FfK5raC7rYaLGzwR17jSquK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;695&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번엔, pop()을 해보자. pop()을 할 경우, Outbox로부터 pop()을 하는데 여기서 핵심적인 부분이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;** Outbox로부터 pop을 할 때는 두 가지 케이스에 대해서 다른 코드를 수행한다. **&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;만약, Outbox Stack이 empty()==true일 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;1. Inbox로부터 모든 데이터를 순차적으로 pop하여&amp;nbsp; Outbox로 push한다&lt;/p&gt;
&lt;p&gt;2. Outbox로부터 데이터를 pop한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;만약, Outbox Stack이 empty()==false일 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;1. Outbox로부터 데이터를 pop한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재 Outbox가 empty하므로 아래와 같이 수행된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;429&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N67lU/btq309mIAyY/EN8n3k8qHKYZ3oPyxW7Q91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N67lU/btq309mIAyY/EN8n3k8qHKYZ3oPyxW7Q91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N67lU/btq309mIAyY/EN8n3k8qHKYZ3oPyxW7Q91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN67lU%2Fbtq309mIAyY%2FEN8n3k8qHKYZ3oPyxW7Q91%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;429&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;543&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RtWWM/btq35EsYo3K/DtYGcLgb7yChodg6azK9IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RtWWM/btq35EsYo3K/DtYGcLgb7yChodg6azK9IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RtWWM/btq35EsYo3K/DtYGcLgb7yChodg6azK9IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRtWWM%2Fbtq35EsYo3K%2FDtYGcLgb7yChodg6azK9IK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;543&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 이렇게 해야만 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;Outbox에 원소가 남아있는 상태에서 Inbox로부터 pop and push를 수행하면 원소의 순서가 꼬인다.&lt;/p&gt;
&lt;p&gt;Outbox에 남아있는 원소가 Inbox에 있는 원소보다 먼저 사용자로부터 push되었으므로, 순서 상 먼저 사용자로부터 pop되어야하는데, Outbox에 아직 pop되어야할 데이터가 남아있는 상태에서 Inbox로부터 pop and push를 진행할 경우 Outbox의 기존 원소 위에 쌓이므로 스택의 LIFO Rule에 의해서 순서가 꼬이게 된다. 즉, FIFO도, LIFO도 아닌 엉망인 자료구조가 만들어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 위의 룰을 지키도록 하자!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조</category>
      <category>Queue</category>
      <category>Stack</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/20</guid>
      <comments>https://newdevlog.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 3 May 2021 21:04:13 +0900</pubDate>
    </item>
    <item>
      <title>[운영체제] 스레드(thread)는 왜 쓸까? - 멀티 스레드의 장점</title>
      <link>https://newdevlog.tistory.com/19</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Classic vs Multi-threaded&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;Classic View - Single Point of Execution within a program, 하나의&amp;nbsp;&lt;/span&gt;&lt;span&gt;프로그램에는&amp;nbsp;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;단일&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;포인트.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&amp;gt; &lt;/span&gt;&lt;span&gt;즉&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;하나의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로그램&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;안에는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;명령어를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가져오고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Single Program Counter(&lt;/span&gt;&lt;span&gt;단일&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PC)&lt;/span&gt;&lt;span&gt;만이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;존재했다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Multi-threaded program - &lt;/span&gt;&lt;span&gt;멀티&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로그램은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;하나&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;이상의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;포인트가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;존재한다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;스레드들은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;같은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;주소공간을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유한다&lt;/span&gt;&lt;span&gt;. (same Page Table) -&amp;gt; &lt;/span&gt;&lt;span&gt;같은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;데이터에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;접근할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;하지만&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;각각의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;own private set of registers(including PC) &lt;/span&gt;&lt;span&gt;레지스터&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt;을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드별로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;각자&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가지고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;스레드를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;T1&lt;/span&gt;&lt;span&gt;에서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;T2&lt;/span&gt;&lt;span&gt;로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;때&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TCB(Thread Control block)&lt;/span&gt;&lt;span&gt;을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;switch&lt;/span&gt;&lt;span&gt;하지만&lt;/span&gt;&lt;span&gt;, address space&lt;/span&gt;&lt;span&gt;는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;그대로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;남는다&lt;/span&gt;&lt;span&gt;.(&lt;/span&gt;&lt;span&gt;즉&lt;/span&gt;&lt;span&gt;, page table&lt;/span&gt;&lt;span&gt;을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;필요가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;없다&lt;/span&gt;&lt;span&gt;.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;578&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x5LvX/btq3TgLAF7v/dKJpJlyRRle61xTMkp3531/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x5LvX/btq3TgLAF7v/dKJpJlyRRle61xTMkp3531/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x5LvX/btq3TgLAF7v/dKJpJlyRRle61xTMkp3531/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx5LvX%2Fbtq3TgLAF7v%2FdKJpJlyRRle61xTMkp3531%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;578&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;577&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buYPlC/btq3KxB5nqE/LZG5S93GxctI8DKxFFOYDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buYPlC/btq3KxB5nqE/LZG5S93GxctI8DKxFFOYDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buYPlC/btq3KxB5nqE/LZG5S93GxctI8DKxFFOYDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuYPlC%2Fbtq3KxB5nqE%2FLZG5S93GxctI8DKxFFOYDK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;577&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;스레드를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;왜&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;쓸까&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;1. parallelism : &lt;/span&gt;&lt;span&gt;병렬성&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;때문에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;쓴다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;병렬적&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가능해진다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;예를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;들어&lt;/span&gt;&lt;span&gt;, CPU &lt;/span&gt;&lt;span&gt;하나당&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;하나의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;갖고&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;멀티&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;시스템에서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;여러&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세서가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;동시에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로그램을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행한다면&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로그램의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;속도가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;더&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;향상될&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것이다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;2. I/O overlapping : &lt;/span&gt;&lt;span&gt;느린&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I/O &lt;/span&gt;&lt;span&gt;때문에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로그램의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;진행이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;되는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;피할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;While one thread in your program waits (blocked waiting for I/O). &lt;/span&gt;&lt;span&gt;즉&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;프로그램에서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;하나의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;I/O&lt;/span&gt;&lt;span&gt;를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;기다리며&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blocked&lt;/span&gt;&lt;span&gt;상태인&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;동안&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CPU &lt;/span&gt;&lt;span&gt;스케줄러는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;다른&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환하여&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;그동안&lt;/span&gt;&lt;span&gt;&amp;nbsp;프로그램에서&amp;nbsp;&lt;/span&gt;&lt;span&gt;무언가를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;3. &lt;/span&gt;&lt;span&gt;스레드&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;대신에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;멀티&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;사용할&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수도&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있지만&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;스레드들은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;address space&lt;/span&gt;&lt;span&gt;를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하므로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;데이터를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하기가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;쉽다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;다만&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;논리적으로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;분리된&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;task&lt;/span&gt;&lt;span&gt;에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;관해서는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;multi process&lt;/span&gt;&lt;span&gt;를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;사용하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;더&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;나은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;선택이다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;오늘날 멀티 스레드 프로그램의 예시&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;오늘날&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;대부분의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;어플리케이션은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;멀티스레드이다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;스레드들은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;어플리케이션&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;내에서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행된다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;어플리케이션&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;내의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;여러&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;개의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tasks(&lt;/span&gt;&lt;span&gt;작업들&lt;/span&gt;&lt;span&gt;)은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;분리된&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;스레드들에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;의해서&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;구현될&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;디스플레이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;업데이트&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;데이터&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가져오기&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;맞춤법&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;체크&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;네트워크&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;요청에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;응답&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;등등&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;프로세스&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;생성보다&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;스레드를&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;생성하는&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;것이&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;더&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;가볍다&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;(&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;비용이&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;적게&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt; &lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;든다&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;.)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;코드를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;단순화해&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;효율을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;높일&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;수&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;커널은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;일반적으로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;멀티스레드화되어있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;멀티 스레드(multi-threaded)의 장점 4가지&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;뛰어난&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;반응성&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;응답성&lt;/span&gt;&lt;span&gt;(Responsiveness) &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;프로세스의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;일부가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;blocked&lt;/span&gt;&lt;span&gt;상태여도&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;계속하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가능하다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;이는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;특히&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;유저&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;인터페이스에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있어&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;중요하다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;자원&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유&lt;/span&gt;&lt;span&gt;(Resource Sharing) &lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;스레드들은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;리소스들을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하므로&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;이는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;메시지를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전달하거나&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;메모리를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것보다&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;쉽다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;경제성(Economy)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;스레드를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;생성하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;생성하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것보다&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;비용이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;적게&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;들며&lt;/span&gt;&lt;span&gt;, thread switching(&lt;/span&gt;&lt;span&gt;스레드&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context switching(&lt;/span&gt;&lt;span&gt;컨텍스트&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;즉&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;전환&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;보다&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;오버헤드가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;적다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;확장성(Scalability)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;프로세스가 멀티 프로세서 구조의 이점을 적극적으로 활용할 수 있다. 즉,&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;다중 CPU 구조에서는 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있으므로 병렬성이 증가한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <category>multi-threaded</category>
      <category>Operating System</category>
      <category>멀티 스레드</category>
      <category>운영체제</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/19</guid>
      <comments>https://newdevlog.tistory.com/19#entry19comment</comments>
      <pubDate>Fri, 30 Apr 2021 14:12:28 +0900</pubDate>
    </item>
    <item>
      <title>[Operating System] Scheduling: The Multi-Level Feedback Queue</title>
      <link>https://newdevlog.tistory.com/18</link>
      <description>&lt;p&gt;MLFQ에서 다루고자 하는 근본적인 문제는 두 가지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째는 짧은 작업을 먼저 실행하여 turnaround time을 최적화(optimize)하는 것이다. 문제는, SJF(또는 STCF)와 같은 알고리즘이 필요로하는 정보인 작업의 실행 시간을 모른다는 것이다.&lt;/p&gt;
&lt;p&gt;두 번째로, 시스템이 대화형 사용자(interactive user)에 반응하도록 하여 응답 시간을 최소화하는 것이다. 저번에 배웠던 Round Robin 알고리즘은 response time(응답 시간)은 짧지만 turnaround time이 길다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리가 프로세스에 대해서 아는 정보가 없을 때 어떻게 이러한 목표를 달성하기 위한 스케줄러를 만들 수 있을까? 스케줄러가 시스템이 실행될 때 실행 중인 작업의 특성을 학습하여 더 나은 스케줄링 결정을 내릴 수 있는 방법은 무엇일까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MLFQ : Basic Rules&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;MLFQ에서&amp;nbsp; 각기 다른 Priority Level(우선 순위 레벨)을 할당한 여러 개의 고유한 queue(대기열)을 가지고 있다. 언제든지 실행 가능한 작업은 단일 대기열에 존재한다. MLFQ는 우선 순위를 사용하여 주어진 시간에 실행할 작업을 결정한다. 우선 순위가 높은 작업이 실행되도록 선택된다. 둘 이상의 작업들이 지정된 대기열에 있을 경우 우선 순위가 같고, 이럴 땐 이들은 라운드 로빈 스케줄링을 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 MLFQ의 두 가지 규칙을 말할 수 있다.&lt;/p&gt;
&lt;p&gt;규칙 1. 작업 A, B에 대해서 우선순위가 A &amp;gt; B이면 A가 실행된다.&lt;/p&gt;
&lt;p&gt;규칙 2. 우선순위가 같은 작업들에 대해서는 라운드 로빈 스케줄링을 실행한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;MLFQ 스케줄링의 핵심은 스케줄러가 우선순위를 설정하는 방법인데, MLFQ는 각 작업에 대해 고정된 우선순위를 부여하기보다는 관찰된 동작에 따라 우선순위를 변화시킨다. 예를 들어, 작업이 키보드 입력을 기다리는 동안 CPU를 반복적으로 포기하면 MLFQ는 interactive process의 행동이라고 판단하여 우선순위를 높게 유지시킨다. 대신 작업이 CPU를 장시간 집중적으로 사용하는 경우 MLFQ는 우선순위를 줄인다. 이처럼, MLFQ는 프로세스를 실행하며 프로세스에 대해 배우고, 작업의 이력(history)을 사용하여 작업의 미래 동작을 예측한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;436&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pYtGU/btq3DJ8mV65/XNDbCUN4mkdbJhGJGL0TT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pYtGU/btq3DJ8mV65/XNDbCUN4mkdbJhGJGL0TT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pYtGU/btq3DJ8mV65/XNDbCUN4mkdbJhGJGL0TT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpYtGU%2Fbtq3DJ8mV65%2FXNDbCUN4mkdbJhGJGL0TT0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;436&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그러나, 때로는 우선 순위가 낮은 작업이 우선 순위가 높은 작업들에 의해 아예 실행이 되지 못하는 상황이 생기는 것을 막기 위해 우선순위가 낮은 작업의 순위를 올리는 판단도 필요하다. 이처럼, MLFQ 스케줄러가 작업의 우선순위를 어떻게 변화하는지를 아는 것이 핵심이다. 이에 대해서 배워보자.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;tt&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;span&gt;#1&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;H&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;w&lt;/span&gt; &lt;span&gt;T&lt;/span&gt;&lt;span&gt;o&lt;/span&gt; &lt;span&gt;C&lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;P&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;이제, 작업의 생명주기동안 MLFQ가 우선 순위 레벨을 어떻게 변경할 것인지를 결정해야 한다. 이 때, 짧은 실행(키보드나 마우스에서 사용자 입력을 기다리며 많은 I/O를 수행하며 CPU를 자주 포기하는 경우)을 하는 대화형 작업과, 많은 CPU 시간이 필요하지만 반응 시간이 중요하지 않은 오래 실행하는 CPU-bound 작업이 혼합되어 있다는 점을 명심해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;규칙 3. 시스템에 들어가면 작업은 가장 높은 우선 순위에 배정된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;규칙 4a. 작업을 실행하는 동안 전체 time slice를 모두 사용하는 경우 우선 순위 레벨이 감소한다.(대기열 하나 아래로 이동)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;규칙 4b. 작업이 time slice를 전부 사용하기 전에 CPU를 포기하면 동일한 우선 순위 레벨을 유지한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;P&lt;/span&gt;&lt;span&gt;r&lt;span&gt;o&lt;/span&gt;b&lt;span&gt;l&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;ms&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;W&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;th&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;O&lt;/span&gt;&lt;span&gt;ur&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Curr&lt;span&gt;e&lt;/span&gt;nt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;M&lt;/span&gt;&lt;span&gt;L&lt;span&gt;F&lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;문제점 1. 시스템에 너무 많은 대화형 작업이 있을 경우 그들이 모든 CPU time을 소모하므로 장기간 실행되는 작업은 CPU time을 전혀 할당받지 못하는 Starvation 문제가 발생할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;문제점 2. 사용자가 그들의 프로그램을 재작성하여 공정한 시스템 리소스 점유율 그 이상을 할당받도록 스케줄러를 속일 수 있다. 즉, time slice가 끝나기 전에 I/O 작업을 실행하여 고의적으로 CPU를 포기하며 동일한 대기열에 남아 더 많은 CPU time을 확보할 수 있다. 예를 들어, time slice의 99%만큼 작업을 실행하고 포기하는 방식이 있다. 이를 통해 CPU를 독점할 수 있다는 문제가 생긴다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;문제점 3. 프로그램이 시간이 지남에 따라 동작을 변경할 수 있다. 작업의 동작 Phase가 CPU-bound -&amp;gt; Interactive로 전환되었을 때 현재의 접근 방식에서는 해당 작업은 시스템에서 다른 대화형 작업들처럼 다루어지지 않을 것이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;tt&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;span&gt;#2&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;T&lt;span&gt;h&lt;/span&gt;&lt;span&gt;e&lt;/span&gt; &lt;span&gt;P&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;y&lt;/span&gt; &lt;span&gt;B&lt;/span&gt;&lt;span&gt;oos&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;starvation 문제를 피하기 위해 새로운 규칙을 추가하자. 시스템 내 모든 작업의 우선 순위를 주기적으로 향상시킨다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;규칙 5. 일정 시간이 지나면(일정 주기마다) 시스템의 모든 작업을 대기열의 맨 위로 이동한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;새 규칙은 두 가지 문제를 동시에 해결한다. 첫째, 프로세스가 중단되지 않도록 보장된다다(starvation 해결). 즉, 맨 위 대기열로 이동하여 CPU를 다른 우선 순위가 높은 작업과 라운드 로빈 방식으로 공유하여 결국 서비스를 받게 된다. 둘째, CPU-bound 작업이 대화형 작업이 된 경우 우선 순위 부스트를 받은 후 스케줄러가 해당 작업을 적절하게 처리한다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;tt&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;span&gt;#3&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;B&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;tt&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;r&lt;/span&gt; &lt;span&gt;A&lt;/span&gt;&lt;span&gt;cc&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;고의적으로 time slice가 만료되기 전에 CPU를 포기하여 작업의 우선 순위를 유지하는 프로그램의 스케줄러 악용으로부터의 문제를 해결하기 위해 규칙 4를 수정한다. 이를 위해 스케줄러는 지정된 우선 순위 레벨에서 프로세스가 사용한 시간 할당의 양을 추적한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;규칙 4: CPU를 포기한 횟수와 상관없이 해당 우선 순위 레벨에서 주어진 시간 할당을 모두 사용하면 우선 순위가 감소한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 통해 프로세스의 CPU 부당 점유를 막는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;T&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;g&lt;/span&gt;&lt;span&gt; &lt;span&gt;M&lt;/span&gt;&lt;span&gt;LF&lt;/span&gt;&lt;span&gt;Q&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;d&lt;/span&gt; &lt;span&gt;O&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;r&lt;/span&gt; I&lt;span&gt;ss&lt;/span&gt;&lt;span&gt;u&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;MLFQ 스케줄링에서 아직 몇 가지 다른 이슈가 존재한다. 한 가지 큰 이슈는 위에서 고려한 스케줄러를 어떻게 매개 변수화하느냐이다. 예를 들어 대기열이 몇 개여야 하는지, 대기열당 time slice가 얼마나 커야 하는지, starvation을 피하고 작업의 동작 변화(cpu-bound -&amp;gt; interactive)를 고려하기 위해 우선 순위를 얼마나 자주 올려야 하는지 등이다. 이러한 질문에 쉽게 대답할 수 없기 때문에 workload 기반 경험과 이후의 스케줄러 튜닝만이 만족스러운 균형을 이룰 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;마지막으로, 많은 스케줄러에는 사용자가 사용할 수 있는 몇 가지 다른 기능이 있다. 예를 들어 일부 스케줄러는 운영 체제 작업에 대해 가장 높은 우선 순위 수준을 예약하므로 일반적인 사용자 작업은 시스템에서 가장 높은 우선 순위를 얻을 수 없다. 또한 일부 시스템은 우선 순위를 설정하는 데 사용자의 개입을 허용하기도 한다. 예를 들어 명령줄 인터페이스를 사용하면 작업의 우선 순위를 늘리거나 줄일 수 있으므로 특정 시간에 작업이 실행될 가능성을 늘리거나 줄일 수 있다. 자세한 내용은 man 페이지를 참조하도록 하자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;MLFQ: Summary&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;bull; 규칙 1: 우선 순위(A) &amp;gt; 우선 순위(B)이면 A가 실행된다(B는 실행하지 않는다).&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;bull; 규칙 2: 우선 순위(A) = 우선 순위(B)인 경우, A와 B는 주어진 대기열의 Time slice를 사용하여 Round Robin 방식으로 실행된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;bull; 규칙 3: 작업이 시스템에 진입할 때 가장 높은 우선 순위(맨 위 대기열)에 배치된다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;bull; 규칙 4: 주어진 우선 순위 수준에서 시간 할당을 모두 사용하면(CPU를 포기한 횟수에 관계없이), 우선 순위가 감소한다(즉, 대기열 하나 아래로 이동).&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;bull; 규칙 5: 일정 시간이 지나면 시스템의 모든 작업을 맨 위 대기열로 이동한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;pages.cs.wisc.edu/~remzi/OSTEP/&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1619508161539&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operating Systems: Three Easy Pieces&quot; data-og-description=&quot;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome &quot; data-og-host=&quot;pages.cs.wisc.edu&quot; data-og-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Operating Systems: Three Easy Pieces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;pages.cs.wisc.edu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <category>MLFQ</category>
      <category>OS</category>
      <category>스케줄링</category>
      <category>운영체제</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/18</guid>
      <comments>https://newdevlog.tistory.com/18#entry18comment</comments>
      <pubDate>Tue, 27 Apr 2021 16:22:19 +0900</pubDate>
    </item>
    <item>
      <title>[Operating System] Scheduling: Introduction</title>
      <link>https://newdevlog.tistory.com/17</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Workload Assumptions&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;글을 들어가기 전에, 시스템에서 실행중인 프로세스(작업)에 대해 이와 같은 가정을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가정 1. 각각의 작업은 동일한 시간동안 실행된다.&lt;/p&gt;
&lt;p&gt;가정 2. 모든 작업들은 동시에 도착한다.&lt;/p&gt;
&lt;p&gt;가정 3. 시작하면 각 작업은 완료될 때까지 실행된다.&lt;/p&gt;
&lt;p&gt;가정 4. 모든 작업은 CPU만 사용한다. (I/O를 수행하지 않는다.)&lt;/p&gt;
&lt;p&gt;가정 5. 각 작업의 실행 시간을 알고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Scheduling Metrics&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;540&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTxxDJ/btq3qL6zw0F/09BNkl5ASKTc16EcUAswwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTxxDJ/btq3qL6zw0F/09BNkl5ASKTc16EcUAswwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTxxDJ/btq3qL6zw0F/09BNkl5ASKTc16EcUAswwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTxxDJ%2Fbtq3qL6zw0F%2F09BNkl5ASKTc16EcUAswwk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;540&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Turnaround Time을 위와 같이 단순하게 가정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;First In, First Out (FIFO)&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;469&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKvwkG/btq3osGmLGE/ZVhnbZRRpVk8B7N4tBszJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKvwkG/btq3osGmLGE/ZVhnbZRRpVk8B7N4tBszJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKvwkG/btq3osGmLGE/ZVhnbZRRpVk8B7N4tBszJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKvwkG%2Fbtq3osGmLGE%2FZVhnbZRRpVk8B7N4tBszJ0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;469&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;465&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbBcfs/btq3qNpM2mK/rqKzbemzAAd3Pt4LxR6J01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbBcfs/btq3qNpM2mK/rqKzbemzAAd3Pt4LxR6J01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbBcfs/btq3qNpM2mK/rqKzbemzAAd3Pt4LxR6J01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbBcfs%2Fbtq3qNpM2mK%2FrqKzbemzAAd3Pt4LxR6J01%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;465&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;FCFS라고도 불린다.&lt;/p&gt;
&lt;p&gt;FIFO는 위 두 그림으로 설명이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;First In, First Out은 먼저 온 사람에게 먼저 제공한다는 뜻으로, 먼저 할당 요청을 한 프로세스에게 CPU를 할당한다.&lt;/p&gt;
&lt;p&gt;비선점형 스케줄링이며 효율적이지 않다. 여기서 비선점적이라는 것(Non-Preemptive)은 CPU를 할당받으면 자신의 작업을 모두 마치기 전까지, CPU를 반환하지 않는다는 의미이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FIFO의 치명적인 단점은 &lt;b&gt;&lt;span&gt;&lt;span&gt;Co&lt;/span&gt;nv&lt;span&gt;o&lt;/span&gt;&lt;span&gt;y Ef&lt;/span&gt;&lt;span&gt;f&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;c&lt;span&gt;t&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;이다. 이는 &lt;span style=&quot;color: #000000;&quot;&gt;상대적으로 짧은 다수의 리소스 잠재적 소비자(수행 시간이 짧은)가 헤비급 리소스 소비자(수행 시간이 긴) 뒤에 대기하는 현상이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Shortest Job First (SJF)&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;613&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5TJ1c/btq3qgTcJy8/GXEq9EklLEKU8PlXYZGffk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5TJ1c/btq3qgTcJy8/GXEq9EklLEKU8PlXYZGffk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5TJ1c/btq3qgTcJy8/GXEq9EklLEKU8PlXYZGffk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5TJ1c%2Fbtq3qgTcJy8%2FGXEq9EklLEKU8PlXYZGffk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;613&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이름 그대로 최단 작업을 우선적으로 실행한다. FIFO와 동일하게 비선점형 스케줄링이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모든 작업이 동시에 도착한다는 가정이 있다면 가장 효율적인 알고리즘이라고 볼 수 있다. 그러나, 모든 작업들이 한꺼번에 도착하는 것이 아니라 언제든지 도착할 수 있다고, 위의 가정 2를 제거한다면 SJF 알고리즘 또한 문제가 많다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;523&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfGxCN/btq3r1afRLP/hxWI4I7DfWqLSpgl3Unay1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfGxCN/btq3r1afRLP/hxWI4I7DfWqLSpgl3Unay1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfGxCN/btq3r1afRLP/hxWI4I7DfWqLSpgl3Unay1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfGxCN%2Fbtq3r1afRLP%2FhxWI4I7DfWqLSpgl3Unay1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;523&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;S&lt;/span&gt;&lt;span&gt;h&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;span&gt;T&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;C&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;l&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;n&lt;/span&gt; &lt;span&gt;F&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;S&lt;/span&gt;T&lt;span&gt;C&lt;/span&gt;&lt;span&gt;F)&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span&gt;위에서의 문제를 해결하기 위해, 가정 3을 제거하고 스케줄러에 타이머 인터럽트(timer interrupt)와 컨텍스트 전환(context switching)을 적용한다면 비선점형 스케줄러(non-preemptive scheduler)인 SJF의 문제를 선점형 스케줄러인 STCF로 어느정도 해결할 수 있다. 즉, SJF에 preemption(선점)을 추가한 Shortest Time-to-Completion First (STCF)&amp;nbsp; 또는 Preemptive Shortest Job First (PSJF)이 그것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;새 작업이 시스템에 들어갈 때마다 STCF 스케줄러는 새 작업을 포함하여 남은 작업 중 가장 적은 시간이 남아 있는 작업을 결정하고 이 작업을 예약한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;538&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tqLTU/btq3oR623ye/jhA7W3Zk2EUKzx4We0fEL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tqLTU/btq3oR623ye/jhA7W3Zk2EUKzx4We0fEL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tqLTU/btq3oR623ye/jhA7W3Zk2EUKzx4We0fEL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtqLTU%2Fbtq3oR623ye%2FjhA7W3Zk2EUKzx4We0fEL1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;538&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;SJF가 모든 작업이 동시에 도착한다는 위의 가정하에서 최적의 알고리즘이었다는 것을 기반으로, 해당 가정만을 제거했을 때는 현재까지&amp;nbsp; STCF가 최적의 알고리즘이라는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;A&lt;/span&gt;&lt;span&gt; &lt;span&gt;N&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;w&lt;/span&gt; &lt;span&gt;M&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;t&lt;/span&gt;&lt;span&gt;r&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;c&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;R&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;o&lt;/span&gt;&lt;span&gt;n&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;e&lt;/span&gt; &lt;span&gt;T&lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;m&lt;/span&gt;&lt;span&gt;e&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;따라서, 우리가 모든 작업의 길이를 알고, 그 작업은 오로지 CPU만을 사용하며, 유일한 metric인 turnaround time(소요 시간)만을 고려했다면 STCF는 훌륭한 정책이다. 실제로, 배치 컴퓨팅 시스템(batch computing system)에서는 이러한 방식의 스케줄링 알고리즘이 타당할 수 있다. 하지만 Time-shared machine(시분할 머신)의 등장으로 모든 것을 바꾸어 놓았다. 이제 사용자는 터미널을 사용하며 시스템에 대화형 성능(interactive performance from system)을 필요로 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;685&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djYVm5/btq3mNLvpIP/0Kw8GmvzKRbIIWbhbX3ZfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djYVm5/btq3mNLvpIP/0Kw8GmvzKRbIIWbhbX3ZfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djYVm5/btq3mNLvpIP/0Kw8GmvzKRbIIWbhbX3ZfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjYVm5%2Fbtq3mNLvpIP%2F0Kw8GmvzKRbIIWbhbX3ZfK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;685&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이제 우리는 응답 시간을 작업이 시스템에 도착하는 시간부터 작업이 예약된 첫 번째 시간까지로 정의한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;응답 시간을 충분히 고려한 스케줄러를 생각해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Round Robin&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;라운드 로빈(RR) 스케줄링의 기본적인 아이디어는 간단하다. RR은 작업을 완료하기 위해 실행하는 대신에 Time Slice(Scheduling Quantum)에 대한 작업을 실행한 다음 실행 대기열의 다음 작업으로 전환한다. 작업들이 마무리될 때까지 해당 방식을 반복한다. &lt;span style=&quot;color: #000000;&quot;&gt;이러한 이유로, RR은 때때로 Time-Slicing이라고 불린다. Time slice의 길이는 타이머 인터럽트 주기의 배수여야 하므로 타이머가 10밀리초마다 중단되는 경우 시간 조각은 10, 20 또는 10ms의 다른 배수가 될 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;RR에 있어 Time Slice의 길이는 매우 중요하다. 짧을수록 응답 시간 측면에서 RR의 성능이 우수하다. 그러나 Time Slice를 너무 짧게 만드는 것은 문제가 된다. Context Switching(컨텍스트 전환) 비용이 과도하게 증가해 전체 성능을 저하시킬 것이다. 따라서 time slice의 길이를 결정하는 것은 시스템 설계자에게 Trade-Off이며, 시스템이 반응하지 않을 정도로 길게 만들지 않고 Context Switching 비용을 감당할 수 있을 만큼 충분히 길어야 한다. Context Switching(&lt;span style=&quot;color: #000000;&quot;&gt;컨텍스트 전환) 비용은 일부 레지스터를 저장하고 복원하는 OS 작업으로부터의 발생 뿐만 아니라, 프로그램이 실행될 때 CPU 캐시, TLB, Branch Predictors(분기 예측 변수) 및 하드웨어에서 많은 상태를 구축하며 발생합니다. 다른 작업으로 전환하면 현재 실행 중인 작업과 관련된 이 상태가 flush되고 현재 실행할 작업을 가져오기 위한 새 상태가 발생하므로, 상당한 성능 비용이 발생할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 Response Time(응답 시간)이 유일한 Metric(메트릭)인 경우, 합리적인 Time Slice를 가진 RR은 우수한 스케줄러이다. 하지만 Turnaround time(소요 시간) 측면에서는 최악의 스케줄링 정책 중 하나다. Turnaround Time 측면에서 RR&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;은 단순한 FIFO보다 훨씬 더 심각하다. &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;일반적으로, 작은 Time Scale로 활성 프로세스 간에 CPU를 균등하게 분배하는 모든 정책(RR 등)은 Turnaround Time과 같은 메트릭에서 저조한 성능을 보일 것이다. 실제로, 이것은 내재적인 Trade-off다. 만약 불공평한 스케줄링을 감수한다면, 더 짧은 작업의 실행을 먼저 완료할 수 있지만, Response Time을 희생해야 한다. 대신에 공정성을 중시한다면, Response Time은 짧아지지만, Turnaround Time을 희생해야 한다. 이런 종류의 Trade-off는 시스템에서 흔히 볼 수 있다.&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;span style=&quot;color: #000000;&quot;&gt;우리는 두 가지 유형의 스케줄러를 살펴보았다. 첫 번째 유형(SJF, STCF)은 Turnaround Time을 최적화하지만 Response Time에는 좋지 않다. 두 번째 유형(RR)은 Response Time을 최적화하지만 Turnaround Time에 좋지 않다. 그리고 우리는 여전히 고려해보아야할 필요가 있는 두 가지 가정을 다루지 못했다. 가정 4(작업에 I/O가 없다는 가정)와 가정 5(각 작업의 런타임이 알려져 있다는 가정). &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 가정들을 다음에 다루자.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Incorporating I/O&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;가정 4를 제거해보자. 모든 프로그램은 물론 I/O를 수행한다. 어떠한 입력도 받지 않는다면, 프로그램은 늘 동일한 출력을 생성할 것이다. 출력이 없다면, 아무도 프로그램의 산출물을 볼 수 없으며 실행하는 의미가 없어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;작업이 I/O를 요청(request)하면 현재 실행중인 해당 작업은 I/O 도중 CPU를 사용하지 않을 것이며 I/O가 완료되길 기다리며 Blocked 상태가 될 것이다. I/O가 Hard disk drive로 보내지는 경우 I/O load에 따라 몇 밀리초 이상 걸릴 수 있고, 따라서 스케줄러는 그 시간 동안 다른 작업을 CPU에 할당하도록(schedule) 결정할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;I/O가 완료되면 인터럽트가 발생하고 OS가 실행되어 I/O를 실행한 프로세스를 Blocked 상태에서 Ready 상태로 다시 이동합니다. 그리고, 스케줄러는 그 시점에서 그 작업을 언제 할당할 지를 결정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;590&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FbWlN/btq3mqQAIa1/tkS3je6freDVURwewcrKKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FbWlN/btq3mqQAIa1/tkS3je6freDVURwewcrKKk/img.png&quot; data-alt=&quot;Poor Use of Resource&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FbWlN/btq3mqQAIa1/tkS3je6freDVURwewcrKKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFbWlN%2Fbtq3mqQAIa1%2FtkS3je6freDVURwewcrKKk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;590&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Poor Use of Resource&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;595&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp6G6U/btq3oUbGpLm/zi1naMlXW7dmA6mBD00YY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp6G6U/btq3oUbGpLm/zi1naMlXW7dmA6mBD00YY1/img.png&quot; data-alt=&quot;Overlap allows better use of resource&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp6G6U/btq3oUbGpLm/zi1naMlXW7dmA6mBD00YY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp6G6U%2Fbtq3oUbGpLm%2Fzi1naMlXW7dmA6mBD00YY1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;595&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Overlap allows better use of resource&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이를 통해 스케줄러가 I/O를 어떻게 통합하는지 확인할 수 있다. 스케줄러는 각각의 CPU Burst를 작업으로 처리하여 대화형(interactive) 프로세스가 자주 실행되도록 한 다. 이러한 대화형 작업이 I/O를 수행하는 동안 다른 CPU-Intesive(CPU 집약적) 작업이 실행되므로 프로세서를 더 잘 활용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;No More Oracle&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;I/O에 대한 기본적인 접근 방식을 배웠고, 이제 우리는 스케줄러가 각 작업의 길이를 알고 있다는 최종 가정을 제거해야 한다. 실제로, 범용적인 OS에서 OS는 일반적으로 각 작업의 길이에 대해 거의 알지 못합니다. 따라서, 그러한 사전 지식 없이 어떻게 SJF/STCF와 같은 방식을 구축할 수 있을까? 또한 RR 스케줄러와 함께 본 몇 가지 아이디어를 통합하여 Response Time 측면에서도 높은 성능을 내려면 어떻게 해야 할까? 다음 장에서 배우도록 하자.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;pages.cs.wisc.edu/~remzi/OSTEP/&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1619272452033&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operating Systems: Three Easy Pieces&quot; data-og-description=&quot;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome &quot; data-og-host=&quot;pages.cs.wisc.edu&quot; data-og-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Operating Systems: Three Easy Pieces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;pages.cs.wisc.edu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/17</guid>
      <comments>https://newdevlog.tistory.com/17#entry17comment</comments>
      <pubDate>Sat, 24 Apr 2021 22:53:36 +0900</pubDate>
    </item>
    <item>
      <title>[Operating System] Swapping : Mechanisms</title>
      <link>https://newdevlog.tistory.com/15</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Virtual Memory : Demand Paging&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;하드디스크의 일부를 마치 메인 메모리처럼 사용할 수 있도록 하는 기술이다. 이는 물리적 메모리(Physical Memory)의 한계를 극복하기 위한 기술이다. 이 기법은 하나의 프로그램을 실행할 때 프로그램 전체가 메모리로 올라와 실행되는 것이 아닌, 필요한 부분만을 불러와 실행하는 것을 기본으로 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 커널은 실제 메모리(RAM)에 올라와 있는 블록들 중에, 쓰이지 않는 것을 디스크에 저장한다. 이를 통해서 사용 가능한 메모리 영역을 늘린다. 만일 디스크에 저장되었던 메모리 블록이 다시 필요하게 되면 실제 메모리 안으로 올려지며 대신에 다른 블록이 메모리로 내려간다. 이런 과정이 일어나고 있다는 것은 사용자가 알 수 없고, 그저 많은 양의 메모리가 있는 것처럼 보일 뿐이어서 점유하고 있는 메모리가 디스크에 있는 실제 메모리에 있는 지는 신경쓰지 않아도 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다만, 디스크를 읽고 쓰는 시간은 메모리를 읽고 쓰는 시간보다 훨씬 느리기 때문에 프로그램의 실행은 그만큼 느려지게 된다. 이렇게 가상메모리로 쓰이는 하드디스크 영역을 스왑 영역(Swap Space)이라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;i&gt;출처 : &lt;a style=&quot;background-color: #dddddd;&quot; href=&quot;http://attiadmin.guyweb.co.kr/linux/swap.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;attiadmin.guyweb.co.kr/linux/swap.html&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swap Space&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;먼저, 페이지를 이동할 수 있도록 디스크의 공간을 확보해야 한다. 운영체제에서는 이를 위해 스왑 공간(Swap Space)를 참조하는데, 이는 메모리에서 페이지를 스왑 공간(디스크)으로 옮기고 스왑 공간으로부터 페이지를 메모리로 옮기기 위해서다. 따라서, 운영체제가 스왑 공간에서 페이지 크기의 단위로 읽고 쓴다고 가정했을 때, 운영체제는 지정된 페이지의 디스크 주소를 기억해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스왑 공간의 크기는 궁극적으로 특정 시간에 시스템에서 사용할 수 있는 최대 메모리 페이지의 수를 결정하기 때문에 중요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;The Present Bit&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;이처럼 디스크에 공간을 확보했으니, 디스크와 페이지를 스왑(swap)하는 것을 지원하기 위해 시스템위에 기계(machinery)를 추가해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Without Swap Space&lt;/h4&gt;
&lt;p&gt;먼저, 하드웨어 기반으로 관리되는 TLB가 있다고 가정했을 때, 메모리를 참조할 때 어떠한 일이 벌어지는지 떠올려보자. 실행 중인 프로세스는 가상 메모리 참조(Virtual Memory References)를 생성하며, 하드웨어는 원하는 데이터를 메모리에서 가져오기 전에 해당 참조를 물리적 주소로 변환한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하드웨어는 먼저 가상 주소에서 VPN을 추출하고 TLB에서 일치(TLB Hit)를 확인하고, 적중 시 해당하는 물리적 주소를 가져와 메모리로부터 데이터를 가져온다. 이 경우에는 추가적인 메모리 액세스가 필요하지 않으므로 속도가 빠르며 일반적인 경우다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;TLB에서 메모리를 찾을 수 없을 때 (TLB Miss), 하드웨어는 메모리에 있는 페이지 테이블로부터 VPN을 인덱스로 사용하여 해당 페이지에 대한 페이지 테이블 항목(PTE)을 조회한다. 페이지가 유효하고 물리적 메모리에 있는 경우 하드웨어는 PTE에서 PFN(Page Frame Number)을 추출하여 TLB에 설치한 후 해당 명령을 다시 시도한다. 이번에는 TLB Hit가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;With Swap Space&lt;/h4&gt;
&lt;p&gt;만약 페이지(pages)가 disk와 스왑(swap) 되기를 원한다면, 즉 가상 메모리를 사용한다면 더 많은 기계를 추가해야 한다. 특히, 하드웨어 및 운영체제가 PTE를 확인할 때 페이지가 물리적 메모리에 존재하지 않는다는 것을 발견할 수 있다. 이처럼 하드웨어 및 운영체제가 페이지의 물리적 메모리 상의 존재 여부를 판단하기 위해 사용하는 새로운 정보는 각각의 PTE 항목에 존재하는 present bit이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Present bit이 1이라면, 페이지가 물리적 메모리에 존재한다는 것을 의미하고, 위와 같이 진행하면 된다.&lt;/p&gt;
&lt;p&gt;Present bit이 0이라면, 페이지는 메모리가 아닌 디스크에 존재한다는 것을 의미한다. 물리적 메모리(physical memory)에 존재하지 않는 페이지에 접근하는 상황을 page fault라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;페이지 폴트가 발생하면 OS가 호출되어 페이지 폴트를 처리한다. Page fault Handler가 실행되고 페이지 폴트를 해결한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;The Page Fault&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;페이지가 메모리에 존재하지 않고 디스크로 스왑되어 있을 경우 운영체제는 page fault를 해결하기 위해 해당 페이지를 메모리로 스왑해야 한다. 그렇다면, 운영체제는 어떻게 해당 페이지의 위치를 디스크로부터 찾을까? 보통, 이러한 정보는 페이지 테이블에 저장되어 있다. 따라서, 운영체제는 PTE의 일부 비트에 해당하는 데이터를 해당 페이지 disk address의 PFN처럼 사용한다. 운영체제는 페이지에 대한 page fault를 수신하면 PTE를 조회하여 disk address를 찾고 &lt;span style=&quot;color: #000000;&quot;&gt;디스크에 해당 페이지를 메모리에 불러오도록 요청을 전송한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Disk I/O(디스크 입출력)가 완료되면, 운영체제는 페이지 테이블을 업데이트하여 해당 페이지의 present bit을 1로 변경하고 PTE의 PFN에 새롭게 가져온 페이지의 in-memoroy location을 기록한다. 그리고 해당 명령을 재시도한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이번 재시도에서는 TLB miss가 발생할 수 있다. 발생 시 위의 변환된 정보로 TLB를 업데이트하고, 서비스할 것이다. 이와 같은 단계를 피하기 위해서 page fault를 해결할 때 TLB를 업데이트할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최종적으로, 변환 정보를 TLB에서 찾아 변환된 물리적 주소(physical address)에 존재하는 원하는 데이터 또는 명령을 메모리로부터 가져온다(fetch).&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;I/O가 발생한 동안에 해당 프로세스는 block상태가 된다. 따라서 운영체제는 page fault를 처리하는 동안에 다른 ready상태의 프로세스를 실행할 수 있다. I/O는 고비용 작업이기 때문에 다른 프로세스와 실행을 overlap(겹침)하는 것은 멀티프로그래밍 시스템이 하드웨어를 가장 효과적으로 사용할 수 있는 방법 중 하나이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;What if Memory is Full?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;위에서 설명한 절차는 페이지를 Swap Space(스왑 공간)으로부터 메모리로 가져오기에 충분한 사용 가능 메모리 용량이 확보되었다는 것을 가정한다. 물론 그렇지 않은 경우도 있다. 메모리가 꽉 찬(full) 상태 또는 거의 꽉 찬 상태일 때이다. 이 때, 운영체제는 가져오려는 새 페이지의 공간을 확보하기 위해 하나 이상의 페이지를 내보낼 수 있다. 내보내거나 교체할 페이지를 선정하는 절차를 Page-Replacement Policy라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;메모리에 존재하는 페이지를 잘못 내보낼 경우 프로그램 성능에 있어 엄청난 비용이 발생하기 때문에 좋은 페이지 교체 정책을 만드는 것이 중요하다. 잘못된 결정을 내린다면 프로그램이 메모리 기반 실행 속도가 아닌 디스크 기반 실행 속도로 실행하게 되며 이는 프로그램이 10,000배 또는 100,000배 느려질 수 있다는 것을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Page Fault Control Flow&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;지금까지 배운 지식들을 총집합하여 메모리 액세스의 전체 제어 흐름(comple control flow of memory access)을 스케치할 수 있다. 누군가가 메모리에서 어떠한 데이터를 가져오고자 할 때 무슨 일이 발생하는가?라는 질문을 한다면 발생할 수 있는 모든 다른 가능성들에 대해서 숙지하고 있어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;602&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RPlzU/btq2cnUXgt2/gRJb9BoptLkDE1Bw4NS641/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RPlzU/btq2cnUXgt2/gRJb9BoptLkDE1Bw4NS641/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RPlzU/btq2cnUXgt2/gRJb9BoptLkDE1Bw4NS641/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRPlzU%2Fbtq2cnUXgt2%2FgRJb9BoptLkDE1Bw4NS641%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;602&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://pages.cs.wisc.edu/~remzi/OSTEP/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 Hardware Control Flow Algorithm을 통해서 알아둬야 할 대표적인 세 가지 사례가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째는, 페이지가 존재하고 유효한 경우다(valid and present, 18-21). 이 경우 TLB Miss Handler는 PTE로부터 PFN을 획득하여 명령을 다시 시도하면 TLB hit가 발생하고 여러번 계속할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;두 번째는,&amp;nbsp; Page fault handler를 실행해야 하는 경우다(Valid but not present, 22-23). 페이지가 프로세스가 접근할 수 있는 유효한 상태이지만 물리적 메모리에 존재하지 않는 상황이다. Page fault handling(스왑공간에서 페이지 읽도록 I/O요청 및 페이지 테이블 업데이트) -&amp;gt; 재시도(TLB Miss) -&amp;gt; 재시도(TLB Hit) 순서다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;세 번째는 프로그램의 버그로 인해 유효하지 않은 페이지에 접근한 경우다(not valid, 22-23). 이 경우 PTE의 나머지 비트들은 관여하지 않고, 하드웨어가 invalid access를 trap하고 운영체제는 trap handler를 실행하여 해당 프로세스가 종료될 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;When Replacements Really Occur&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;지금까지 설명한 대체 방법은 운영체제가 메모리가 완전히 가득 찰 때까지 기다렸다가 다른 페이지를 위한 공간을 확보하기 위해 페이지를 교체하는 것으로 가정했다. 이는 다소 비현실적이며, 이보다는 운영체제가 메모리의 일정 작은 부분을 능동적으로 사용가능한 상태로 유지하는 것이 여러 측면에서 유리하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;메모리의 일부 작은 공간을 확보(free)하기 위해서 대부분의 운영체제는 일종의 high watermark(HW)와 low watermark(LW)를 가지고 있다. 사용가능한 페이지 수가 LW보다 적어질 때 메모리 확보를 담당하는 백그라운드 스레드가 실행된다. 해당 스레드는 사용가능한 페이지 수가 HW에 다다를 때 까지 페이지를 제거한다. Swap daemon 또는 page daemon1이라고 불리는 해당 스레드는 운영체제가 프로세스를 실행하는동안 사용할 여유 메모리를 확보한 후 sleep상태가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러 교체 작업을 한번에 수행하여 성능 최적화를 할 수 있다. 예를 들어, 많은 시스템은 여러 페이지를 클러스터링하거나 그룹화하여 한 번에 스왑 파티션에 기록하므로 디스크의 효율성이 향상된다. 이러한 클러스터링은 disk의 검색 및 회전 오버헤드를 줄여 성능을 현저하게 향상시킨다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;백그라운드 페이징 스레드를 이용하면 직접 교체를 수행하는 대신 사용 가능한 여유 페이지 공간이 있는지 확인할 수 있다. 여유 페이지 공간이 존재하지 않을 경우 백그라운드 페이징 스레드에 페이지가 필요함을 알린다. 사용 가능한 페이지가 존재하게 되면 백그라운드 페이징 스레드는 원래 스레드를 깨워 원하는 페이지로 이동할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해야할 일이 있을 때, 작업의 그룹화를 허용하고 효율성을 높이기 위해서 백그라운드에서 수행하는 것이 종종 좋다. 운영체제는 때때로 백그라운드에서 일을 수행한다. 예를 들어, 실제로 디스크에 데이터를 쓸 때(write), 많은 시스템 버퍼 파일이 메모리에 쓰인다(write). 이렇게 하면 디스크에 한 번에 많은 쓰기(write)를 수행할 수 있어 디스크 효율 향상, 쓰기 지연 시간 단축(improved latency of writes)이 가능하다. 또한 백그라운드 작업은 시스템이 idle 상태일 때도 수행될 수 있어 하드웨어를 더 효과적으로 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;출처 : &lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pages.cs.wisc.edu/~remzi/OSTEP/&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1617957479997&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operating Systems: Three Easy Pieces&quot; data-og-description=&quot;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome &quot; data-og-host=&quot;pages.cs.wisc.edu&quot; data-og-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Operating Systems: Three Easy Pieces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;pages.cs.wisc.edu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/15</guid>
      <comments>https://newdevlog.tistory.com/15#entry15comment</comments>
      <pubDate>Mon, 12 Apr 2021 17:14:45 +0900</pubDate>
    </item>
    <item>
      <title>[Operating System] Mechanism : Limited Direct Execution</title>
      <link>https://newdevlog.tistory.com/16</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Intro&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span&gt;CPU &lt;/span&gt;&lt;span&gt;가상화를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;위해서&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;운영체제는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;physical CPU를&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;여러&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;작업&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;간에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하여&lt;/span&gt;&lt;span&gt;&amp;nbsp;마치&amp;nbsp;&lt;/span&gt;&lt;span&gt;동시에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행되는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;것처럼&lt;/span&gt;&lt;span&gt;&amp;nbsp;만들어야&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;한다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;기본&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;아이디어는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;간단하다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;하나의&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;잠시&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행했다가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;다른&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;프로세스를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;실행하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;방식으로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;진행한다&lt;/span&gt;&lt;span&gt;. &lt;/span&gt;&lt;span&gt;이와&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;같은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;방식으로&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;CPU&lt;/span&gt;&lt;span&gt;를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;공유하면&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가상화가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;이루어지며&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;이를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;time sharing of CPU&lt;/span&gt;&lt;span&gt;라고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;한다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;그러나&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;이러한&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가상화&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;머신을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;구축하는데는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;몇&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;가지&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;과제가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;있다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;첫&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;번째는 성능(performance)이다. 시스템에 과도한 오버헤드를 부가하지 않고 어떻게 가상화를 구현할 수 있을까?&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;두 번째는 제어(control)다. 운영체제가 CPU에 대한 제어를 유지하면서 프로세스를 효율적으로 실행하는 방법은 무엇이 있을까?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Basic Technique: Limited Direct Execution&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span&gt;프로그램을 기대하는 만큼 빠르게 실행하기 위해서 OS 개발자는 Limited Direct Execution을 고안해냈다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Direct Execution은 프로그램을 CPU에서 직접 실행한다는 것이다. 그러므로, 운영체제가 프로그램을 실행하고자 할 때, Process list에 Process Entry를 생성하고, 메모리를 할당하고, 프로그램 코드를 디스크에서 메모리로 로드하고, 진입점(entry point)을 찾아서 jump하여 사용자의 코드를 실행하기 시작한다. 보다 정확한 direct execution protocol은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;793&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfHB6f/btq2lqhTP8z/KrqIbDSYQKn0JykSbwl9Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfHB6f/btq2lqhTP8z/KrqIbDSYQKn0JykSbwl9Z1/img.png&quot; data-alt=&quot;출처 :&amp;amp;amp;nbsp;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfHB6f/btq2lqhTP8z/KrqIbDSYQKn0JykSbwl9Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfHB6f%2Fbtq2lqhTP8z%2FKrqIbDSYQKn0JykSbwl9Z1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;793&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 :&amp;nbsp;https://pages.cs.wisc.edu/~remzi/OSTEP/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Direct Execution 만으로 해결할 수 없는 문제들이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째로, 프로그램을 실행하기만 한다면 OS가 어떻게 프로그램을 효율적으로 실행하면서 동시에 원하지 않는 작업을 수행하지 않도록 할 수 있을까?&lt;/p&gt;
&lt;p&gt;두 번째로, 프로그램을 실행할 때 OS는 어떻게 프로세스를 중지하고 다른 프로세스로 전환하는 time sharing(CPU 가상화)을 구현할 수 있을까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당 질문들을 풀어나가는 과정에서, 우리는 CPU 가상화를 위한 필요 사항들을 더 잘 이해할 수 있다.&lt;/p&gt;
&lt;p&gt;OS는 이러한 문제들을 해결하기 위해 프로그램 실행(direct execution)을 통제 및 제한(limited)한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Problem #1 : Restricted Operations&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;direct execution 은 분명 빠르다는 장점이 있지만 만약 프로그램이 CPU에서 실행 도중에 disk I/O을 요청하거나 CPU나 Memory의 시스템 리소스에 대한 추가적인 액세스를 요청하는 등 제한된 작업(restricted operations)에 대해서 어떻게 처리해야할까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한 가지 접근법은 제한된 작업들(restricted operations)에 대해서 프로세스가 원하는 것을 모두 허용하는 것이다. 물론 이는 바람직하지 못하다. 예를 들어 I/O(입출력)의 경우, 프로세스가 전체 디스크를 읽거나 쓸 수 있다면 모든 보호(protection) 기능이 손실된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서, 일반적으로 취하는 접근법은 user mode(사용자 모드)라고 불리는 새로운 프로세서 모드를 도입하는 것이다. 사용자 모드에서 실행하는 코드는 할 수 있는 것이 제한적이다. 예를 들어, 사용자 모드에서 실행할 때 프로세스는 I/O request를 할 수 없으며 요청 시 프로세스가 exception을 던지고 운영체제는 프로세스를 종료(kill)할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사용자 모드와 달리, 운영체제가 실행되는 kernel mode(커널 모드)에서 실행하는 코드는 I/O 요청 및 restricted operations을 포함하여 권한이 필요한 모든 작업을 수행할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여전히 문제가 남아있다. 만약 유저 프로세스가 디스크 입출력과 같은 권한이 필요한 작업을 수행하고자 한다면 어떻게 할까? 이를 위해 현대의 거의 모든 최신 하드웨어는 유저 프로그램이 시스템 호출(system call)을 수행할 수 있는 기능을 제공한다. 시스템 호출은 커널이 파일 시스템(file system)에 액세스하고, 프로세스를 생성 및 파괴하고, 다른 프로세스와 통신하고, 더 많은 메모리를 할당하는 것과 같은 특정한 핵심 기능들을 사용자 프로그램(user program)에 노출한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;system call을 수행하기 위해 프로그램은 special trap instruction을 실행한다. 해당 명령과 동시에 커널로 jump하고 privilege level(권한 수준)을 kernel mode로 올린다. system call의 목적이었던 권한이 필요한 작업을 수행하고 완료되면 OS에서 return-from-trap 명령을 호출하며 호출된 사용자 프로그램을 다시 user mode로 privvilege level을 낮춘다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;trap 명령을 실행할 때, &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;확실하게 호출자의 레지스터들을 저장해야 한다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OS가 return-from-trap 명령을 실행 후 &lt;span style=&quot;color: #333333;&quot;&gt;호출자의 레지스터들(caller's registers)이&amp;nbsp;&lt;/span&gt;올바르게 return(반환)되게 하기 위해서다. 예를 들어 x86 프로세서는 Program Counter, flags, 그리고 다른 레지스터들을 프로세스당 커널 스택에 push한다. return-from-trap 명령은 그 스택으로부터 values을 pop하여 사용자 모드 프로그램 실행을 재개한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;운영체제 내부(커널 모드)에서 실행할 코드를 트랩이 알게 하기 위해서 커널은 시스템 부팅 시 트랩 테이블(trap table)을 구성한다. 시스템은 커널 모드로 부팅되므로 필요에 따라 시스템 하드웨어를 자유롭게 구성할 수 있다. OS가 가장 먼저 수행하는 작업 중 하나가 특정 예외 이벤트 발생 시 실행할 코드를 하드웨어에 알려주는 것이다. 운영체제는 이러한 트랩 핸들러(특수 명령)의 위치를 하드웨어에 알려준다. 하드웨어에 정보가 입력되면 이러한 핸들러의 위치는 다음 재부팅 전까지 기억하므로 시스템 호출 및 기타 예외 이벤트 발생 시 무엇을 해야 하는 지 알고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;시스템 호출(system call)을 특정하기 위해 각 시스템 호출마다 번호를 할당한다. OS는 트랩 핸들러 내부에서 시스템 호출을 처리할 때 이 번호를 검사하고 유효한지 확인하고, 만약 그렇다면 해당 코드를 실행한다. 이러한 indirection(트랩 핸들러를 거쳐서 간접 실행)은 일종의 보호(protection) 기능을 한다. 사용자 코드(user code)는 jump할 exact address를 specify할 수 없다. 대신에 번호를 통해 특정 서비스를 요청해야 한다. 하드웨어에 trap table의 위치를 지정하는 명령의 실행도 강력한 기능이자 특권화된 작업이므로 사용자 모드에서 허용하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요약해보면 LDE(Limited Direct Execution) Protocol의 두 단계는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1 단계 - (부팅 시) 커널은 트랩 테이블을 초기화하며 CPU는 이후 사용하기 위해 위치를 기억한다. 이러한 과정은 커널의 privileged instruction을 통해 이루어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2 단계 - (프로세스 실행 시) 커널은 프로세스를 실행하기 전 몇 가지(이전 내용 참고) 사항을 설정하고, CPU를 사용자 모드로 전환하여 프로세스를 실행하기 시작한다. 프로세스가 시스템 호출을 하면 운영체제는 trap에 걸려들어 이를 처리하고 return-from-trap을 통해 프로세스 제어 권환을 반환한다. 그 후 프로세스가 작업을 완료하면 main()에서 return(반환)된다. 이를 통해 프로그램이 정상적으로 종료(exit)되고 운영체제가 정리한 후(clean up) 최종적으로 완료된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Problem #2: Switching Between Processes&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;Direct Execution에 있어서 다음 문제는 어떻게 OS가 CPU에 대한 제어권을 가지고 switch between processes를 수행할 수 있는 가이다. Switching Process는 겉보기에는 그저 OS가 실행 중인 프로세스를 멈추고 다음 프로세스를 실행하면 되는 간단한 문제처럼 보이지만 실제로는 그렇게 단순하지 않다. 왜냐하면, 프로세스를 수행하는 주체는 CPU이지 OS가 아니기 때문이다. 따라서 OS는 switch between process(프로세스 간 전환)을 수행하기 위해 CPU에 대한 제어권을 얻어야(regain control) 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;A Cooperative Approach: Wait For System Calls&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;과거에 일부 시스템에서 취했던 한 가지 접근 방식은 협력 접근 방식이다. 이와 같은 방식에서 운영체제는 시스템의 프로세스들이 합리적으로 동작할 것이라고 신뢰한다. 너무 오래 실행되는 프로세스는 주기적으로 CPU를 포기하며 OS가 다른 작업을 수행할 수 있도록 한다고 가정한다. 따라서 프로세스는 종종 시스템 호출을 통해서 CPU의 제어 권한을 OS에 전송한다. 예를 들어, 파일을 열고 읽거나, 다른 컴퓨터에 메시지를 보내거나 새로운 프로세스를 만들 때, 허용되지 않는 불법적인 작업을 할 때 OS가 CPU에 대한 제어 권한을 얻게 되며 이러한 사안에 관해 처리할 수 있게 된다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;따라서, 협력적 스케줄링 시스템에선 OS가 시스템 호출이나&amp;nbsp; 불법적인 작동(illegal operation) 발생 시 CPU의 제어권을 얻는 수동적인 방식이다. 만약, 프로세스에 문제가 생겨 무한 루프(infinite loop) 상태에 빠지거나, 시스템 호출을 하지 않는다면 운영체제는 할 수 있는 것이 없다는 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;A Non-Cooperative Approach: The OS Takes Control&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;위와 같은 방식은 프로세스가 시스템 호출을 하지 않고 CPU에 대한 제어권을 운영체제에 전달하기를 거부할 때 &lt;span style=&quot;color: #333333;&quot;&gt;추가적인 하드웨어의 도움 없이는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;운영체제가 할 수 있는 것이 별로 없다. 프로세스가 무한 루프에 빠졌을 때 우리가 할 수 있는 유일한 방법은 재부팅이다. 따라서, 협력적 접근 방식은 우리가 해결하고자 하는 문제를 명확히 해결하긴 힘들다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 위한 다른 해답은 timer interrupt 이다. 타이머 장치는 milliseconds 단위로 interrupt를 발생시킨다. 인터럽트가 발생하면 현재 실행 중인 프로세스가 중지되고 OS에서 미리 구성한 인터럽트 핸들러가 실행된다. 해당 시점에서 OS는 CPU를 제어할 수 있고 현재 프로세스를 중지하고 다른 프로세스를 실행할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앞서 시스템 호출에서 논의했듯이, OS는 타이머 인터럽트가 발생할 때 실행할 코드를 하드웨어에 미리 알려주어야 한다. 따라서 부팅시 OS는 하드웨어에 알려준다. 다음으로 부팅 작업 중에도 OS는 해당 타이머 장치를 실행하며 타이머가 시작되면 OS는 제어에 대한 안전성을 확보하며 자유롭게 사용자 프로그램을 실행할 수 있다. 타이머를 끌 수도 있으며 이는 동시성(concurrency)에 대한 이해와 함께 추후에 자세히 논의한다. 하드웨어는 시스템 호출 트랩 시 동작과 유사하게 인터럽트가 발생했을 때도 실행 중인 프로그램의 상태(state)를 충분히 저장하고 return-from-trap 명령이 실행될 때 실행중이었던 프로그램을 올바르게 재개할 수 있도록 해야하는 책임이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Saving and Restoring Context&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span&gt;이제 OS가 시스템 호출 또는 타이머 인터럽트를 통해서 제어권을 되찾았으므로 프로세스를 계속 실행할 지 아니면 다르 프로세스로 전환할지 결정해야 한다. 해당 결정은 OS의 일부분인 스케줄러에 의해 내려진다. 이에 대한 자세한 내용은 추후에 논의한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;전환하도록 결정이 내려지면 OS는 Context Switch라고 하는 low-level code를 실행한다. Context switch 시&amp;nbsp; OS는 현재 실행중인 프로세스의 몇몇 레지스터 값들을 저장하고 전환할 프로세스의 몇몇 레지스터 값들을 복원한다. 따라서 OS는 실행중이던 프로세스로 돌아가는 대신 return-from-trap 명령이 실행될 때 시스템이 다른 프로세스를 실행하도록 보장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;현재 실행중인 프로세스의 컨텍스트(context)를 저장하기 위해서 OS는 현재 실행 중인 프로세스의 범용 레지스터, PC 및 커널 스택 포인터를 저장한 다음 실행할 프로세스의 레지스터, PC를 복원하고 실행할 프로세스의 커널 스택으로 전환한다. &lt;span style=&quot;color: #000000;&quot;&gt;스택을 전환함으로써 커널은 인터럽트된 실행중이었던 프로세스의 컨텍스트에서 switch 코드에 대한 호출을 입력하고 곧 실행 될 프로세스의 컨텍스트에서 반환한다. 그&lt;span style=&quot;color: #000000;&quot;&gt;런 다음 OS가 최종적으로 Return-from-trap 명령을 실행할 때, 곧 실행될 프로세스가 현재 실행 중인 프로세스가 되며 컨텍스트 전환이 완료된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 프로토콜 중 발생하는 레지스터 저장/복원에는 두 가지 유형이 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;첫 번째는 타이머 인터럽트가 발생할 때이다. 이 경우엔 실행중인 프로세스의 사용자 레지스터는 해당 프로세스의 커널 스택을 사용하여, 하드웨어에 의해 암시적으로(implicitly) 저장된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;두 번째는 OS가 A에서 B로 Context Switch 할 때이다. 이 경우 커널 레지스터들은 소프트웨어(OS)에 의해 명시적으로(explicitly) 저장되는데, 이번에는 해당 프로세스의 프로세스 구조에서 메모리에 저장된다. 해당 동작은 시스템 커널이 A를 트랩한 상태에서 B를 트랩한 상태로 이동시킨다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Worried About Concurrency?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span&gt;그렇다면, 이런 의문이 생길 수도 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;만약 시스템 호출중에 타이머 인터럽트가 발생한다면 어떻게 되는가?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;하나의 인터럽트를 처리 중일 때 또 다른 인터럽트가 발생하면 어떻게 되는가?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;실제로, OS는 인터럽트나 트랩 핸들링 도중에 또 다른 인터럽트가 발생할 때를 주의 깊게 고려할 필요가 있다. 해당 이슈에 대한 논의는 추후에 Concurrency 파트에서 하도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;pages.cs.wisc.edu/~remzi/OSTEP/&amp;nbsp;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1618215123594&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Operating Systems: Three Easy Pieces&quot; data-og-description=&quot;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome &quot; data-og-host=&quot;pages.cs.wisc.edu&quot; data-og-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pages.cs.wisc.edu/~remzi/OSTEP/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Operating Systems: Three Easy Pieces&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;pages.cs.wisc.edu&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>운영체제</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/16</guid>
      <comments>https://newdevlog.tistory.com/16#entry16comment</comments>
      <pubDate>Mon, 12 Apr 2021 17:11:16 +0900</pubDate>
    </item>
    <item>
      <title>[Software Engineering] Requirement Engineering (요구공학)</title>
      <link>https://newdevlog.tistory.com/14</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Requirement Engineering(요구공학)이란?&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;span&gt;Requirements engineering(&lt;/span&gt;&lt;span&gt;요구공학&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;은&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;고객이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;시스템에&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;요구하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;서비스가&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;무엇인지&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;확립하고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;시스템을&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;개발하고&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;운영하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;동안&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;충족해야하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;제약사항&lt;/span&gt;&lt;span&gt;(constraint)&lt;/span&gt;&lt;span&gt;이&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;무엇인가를&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;정립하는&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;과정이다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실질적인 개발 과정에서 가장 먼저 진행되는 작업이다. 이 작업의 결과를 바탕으로 이후의 설계(design), 구현(implementation), 테스트(test)가 진행되기 때문에 굉장히 중요한 단계라고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요구사항(requirements)으로서 정립되는 것에는 개발하고자 하는 시스템이 제공하는 기능, 서비스, 시스템이 반드시 충족해야하는 제약사항(constraint) 등이 있다. 이러한 요구사항을 도출, 정리, 분석, 검증해나가는 일련의 과정을 Requirement Engineering이라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;시스템에 대한 요구사항을 완전하게(complete), 그리고 요구사항 간의 충돌이 없도록 일관성 있게(consistent) 정립하는 것을 추구한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;User and System Requirements&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;User requirements&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;시스템이 제공하는 서비스와 운영에 있어서의 제약사항들을 우리가 쓰는 언어인 자연어(natural language)와 추가적으로 이해를 높이기 위한 그림이나 표와 같은 diagram을 통해 보완하여 서술한다. 기술적 배경지식이 없어도 이해하기 쉽도록 하며, 주로&amp;nbsp;&lt;/span&gt;고객을 위해 쓰여진다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;System requirements&lt;/h4&gt;
&lt;p&gt;User requirements보다 더 상세하며, 구조화된 기술적인 정보를 포함하는 문서이다. 무엇이 구현되어야 하는지 정의한다. 주로 클라이언트(client)와 계약자(contractor) 사이의 계약 또는 개발자를 위해서 쓰여진다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Functional, non-functional and domain requirements&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;functional requirements&lt;/h4&gt;
&lt;p&gt;시스템이 제공해야하는 서비스에 대한 기능, 시스템이 특정 입력값(input)에 대해서 어떻게 반응해야 하는지, 특정 상황에서 어떻게 동작해야 하는지 기능적인 요구사항을 기술한다. 이에 추가하여 시스템이 하지 말하야 하는 것에 대해서도 기술할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;non-functional requirements&lt;/h4&gt;
&lt;p&gt;시스템에 의해 제공되어지는 서비스 및 기능들에 대한 각종 제약사항들(constraints)과 시스템 전체로서의 특성 및 안정성(safety), 성능(performance)에 대한 System Properties(+ emergent property) 대해 기술한다.&lt;/p&gt;
&lt;p&gt;process requirements(개발 환경 - IDE, Programming Language, Framework, Development Method)도 이에 포함된다.&lt;/p&gt;
&lt;p&gt;전반적인 시스템 구조에 치명적인 영향을 줄 수 있으므로 중요하다. 또한, 하나의 non-functional requirements로부터 여러 개의 functional requirements가 파생될 수 있다. (ex. security -&amp;gt; 각종 보안을 위한 functional requirements)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;domain requirements&lt;/h4&gt;
&lt;p&gt;시스템의 운영 환경에서 사용되는 도메인의 여러 가지 속성들, 주제, 방법, 절차들에 대해 기술한 것으로, 개발자가 해당 시스템의 도메인에 대해 학습하고 내재되어있는 해당 requirements를 도출하도록 노력해야 한다. 전문성이 요구된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Requirement Imprecision&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;요구사항에 대한 유저의 이해와 개발자의 해석은 다를 수 있다.&lt;/p&gt;
&lt;p&gt;고객은 보다 크게 생각하고, 개발자는 이를 작게 나누어 생각한다는 특성이 있다.&lt;/p&gt;
&lt;p&gt;이러한 부분을 명확히 하지 않으면 갈등의 소지가 존재하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Software Requirement Document&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;소프트웨어 개발자를 위해서 시스템이 무엇을 해야하는지 기술한 공식 문서이다.&lt;/p&gt;
&lt;p&gt;User Requirements의 정의와 System Requirements의 명세를 모두 포함해야 한다.&lt;/p&gt;
&lt;p&gt;Design Document가 아니기 때문에 How(How System should do)가 아닌 What(What System should do)에 초점을 둔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Agile Methods and requirements&lt;/b&gt;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;많은 애자일 방법론에서 요구 문서(requirements document)를 작성하는 것은 시간 낭비라고 여기기도 한다. requirements는 끊임없이 변화하기 때문이다. 따라서 XP(Extreme Programming)와 같은 메소드에선 incremental requirement engineering 또는 requirements를 user stories로써 기술하기도 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 관점은 실질적인 비즈니스 시스템에서 충분히 제기될 수 있는 관점이지만, 여러 팀에서 함께 개발하는 대규모 시스템 또는 충분한 사전 분석이 필요한 핵심적인 시스템을 개발할 때는 문제가 될 수 있다.&lt;/p&gt;</description>
      <category>소프트웨어공학</category>
      <author>kseungwoo</author>
      <guid isPermaLink="true">https://newdevlog.tistory.com/14</guid>
      <comments>https://newdevlog.tistory.com/14#entry14comment</comments>
      <pubDate>Thu, 8 Apr 2021 14:20:30 +0900</pubDate>
    </item>
  </channel>
</rss>