<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jungizz_.log</title>
        <link>https://velog.io/</link>
        <description>( •̀ .̫ •́ )✧</description>
        <lastBuildDate>Sun, 25 May 2025 07:09:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jungizz_.log</title>
            <url>https://velog.velcdn.com/images/jungizz_/profile/4737c96d-6e64-4ba7-a31e-cc7c14fccffd/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jungizz_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jungizz_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[운영체제] 15. File System Internals]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-15.-File-System-Internals</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-15.-File-System-Internals</guid>
            <pubDate>Sun, 25 May 2025 07:09:23 GMT</pubDate>
            <description><![CDATA[<h1 id="🔹virtual-file-system-vfs">🔹Virtual File System (VFS)</h1>
<p>Unix, Linux는 모든걸 파일로 적용한다. <strong>파일 종류 상관 없이 동일한 파일 시스템 인터페이스(API)를 제공</strong>한다. 이런 시스템을 Virtual File system(VFS)라고 한다. </p>
<ul>
<li>파일마다 가지는 정보 inode가 있으면, 진짜 파일이 아닌 애들한테는 vnodes가 있다.</li>
<li>VFS interface는 파일 종류를 구별하여 알맞은 로컬 파일 시스템으로 안내해준다.<img src="https://velog.velcdn.com/images/jungizz_/post/c082445c-01f2-4256-8638-109826c97c24/image.png" alt=""></li>
</ul>
<p>리눅스는 4개의 오브젝트 유형을 가진다.</p>
<ul>
<li>inodes: 파일의 데이터 저장</li>
<li>file: 열린 파일에 대한 정보 저장</li>
<li>superblock: 파일 시스템 전체 정보 저장</li>
<li>dentry: 디렉토리 엔트리, 파일과 디렉토리 간의 관계 저장</li>
</ul>
<p>→ 각 오브젝트는 함수 테이블에 대한 포인터를 가지고 있으며, 이 테이블은 해당 오브젝트에서 구현할 함수의 주소를 포함한다. 즉, 각 함수들은 객체에 따라 다르게 정의될 수 있다.</p>
<br>

<h1 id="🔹network-file-system-nfs">🔹Network File system (NFS)</h1>
<ul>
<li>NFS는 서버-클라이언트 모델을 기반으로 한 파일 공유 프로토콜 중 하나</li>
<li>로컬처럼 원격 파일 시스템에 접근 가능</li>
<li>클라이언트에 권한이 있는지 인증을 꼭 해야한다.<ul>
<li>클라이언트가 신분을 속이는 spoofing 등 공격 가능</li>
<li>암호화 등으로 보안 강화</li>
</ul>
</li>
</ul>
<h4 id="작업-과정"><strong>작업 과정</strong></h4>
<ol>
<li>클라이언트 인증 (유저 아이디 확인)</li>
<li>클라이언트가 원격 파일 시스템을 마운트하고 서버에게 파일 요청</li>
<li>네트워크를 통해 요청이 전달되고, 서버는 요청 권한 확인 후 파일 핸들러를 반환</li>
<li>클라이언트는 파일 핸들을 사용해 읽기쓰기 작업 수행<br>
### Distrubuted Information Systems

</li>
</ol>
<p>원격 컴퓨팅에 필요한 정보에 대한 접근을 제공하는 서비스들은 아래와 같다.</p>
<ul>
<li>DNS(domain name system): 호스트 이름을 네트워크 주소로 변환</li>
<li>NIS(network information service): 사용자 정보</li>
<li>CIFS(common internet file system): 마이크로소프트의 파일 시스템, 사용자 인증과 결합하여 네트워크 로그인</li>
<li>Active directory</li>
</ul>
<h4 id="sun의-nfs"><strong>Sun의 NFS</strong></h4>
<ul>
<li>LAN이나 WAN 네트워크에서 원격 파일에 접근</li>
<li>UDP, TCP 등의 데이터 전송 프로토콜 사용<br>
### Heterogeneous environment 독립적

</li>
</ul>
<p>서버에 있는 파일을 요청하는 경우, 요청하는 클라이언트의 파일 시스템에 상관 없이 같은 NFS를 사용한다면 접근할 수 있다.</p>
<ul>
<li>원격 디렉토리를 로컬 파일시스템 디렉토리에 마운트 → 로컬 파일 시스템처럼 보임</li>
<li>NFS의 독립성은 <strong>RPC</strong>(remote procedure call)을 통해 제공되며, 서로 다른 시스템 간의 데이터 전송을 표준화 하기 위해 <strong>XDR</strong>(external data represenatation) 프로토콜을 사용한다. → 3장에 있는 내용</li>
</ul>
<p>아래 그림에서 U는 로컬, S는 원격 디렉토리이다. S1을 U에 마운팅하면 (a), 그 상태에서 S2가 한번 더 마운팅 하면 (b)<img src="https://velog.velcdn.com/images/jungizz_/post/7dc1687f-291f-4036-aeeb-38ea5105a577/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/7a7c6c73-e7be-4b87-8846-de2a9fa63052/image.png" alt=""><br></p>
<h3 id="nfs-mount-protocol">NFS mount protocol</h3>
<p>클라이언트와 서버 간의 초기 논리적 연결을 설정하는 과정</p>
<ul>
<li>서버는 export list를 통해 마운트할 수 있는 서비스들을 제공한다.</li>
<li>이걸 통해 local에 마운팅하여 다양한 서비스를 사용할 수 있음</li>
<li>마운팅 요청이 성공적으로 처리되면 서버는 파일 핸들(키 역할)을 반환, 핸들을 통해 클라이언트가 서버 파일에 접근<Br>
### NFS protocol

</li>
</ul>
<p>원격 파일 작업을 위한 PRC를 제공, 여러 파일 관련 서비스 지원</p>
<ul>
<li>파일 검색, 디렉토리 엔트리 읽기, 링크 및 디렉토리 제어, 파일 속성 접근, 파일 읽기 쓰기 등의 서비스를 제공 → 이걸 로컬에서 쓸 수 있도록 해줌</li>
<li>NFS 서버는 stateless: 각 요청은 독립적이다. 서버가 클라이언트의 정보를 갖고있지 않아 기억하지 않는다.</li>
<li>그리고 NFS protocol은 여러 클라이언트가 동시에 작업을 할 수 있는concurrency-control을 제공하지 않는다. (오버헤드 우려)<br>
### NFS Architecture

</li>
</ul>
<p>세가지 주요 계층으로 이루어져 있다.</p>
<ol>
<li>UNIX file system interface: 사용자 친화적</li>
<li>Virtual File system(VFS) layer: 클라이언트가 동일한 system-call interface를 사용할 수 있도록 VFS가 다양한 파일시스템을 통합시켜줌, 그리고 로컬/원격 파일을 구분하기 때문에 원격 파일 요청에 대해 NFS 프로토콜 절차 호출</li>
<li>NFS service layer: 서버와의 통신을 통해 원격 파일 작업 수행<img src="https://velog.velcdn.com/images/jungizz_/post/913fd1de-ca02-4666-8b00-257a3e663edc/image.png" alt=""><br></li>
</ol>
<h3 id="nfs-path-name-traslation">NFS path-name traslation</h3>
<ul>
<li>lookup call: 로컬에서 원격 정보(vnodes)를 찾기 위해 리소스 이름으로 찾는 것</li>
<li>lookup 속도를 향상시키기 위해 클라이언트 측에 이름 조회 캐시를 사용하기도 함<br>
### NFS remote operations

</li>
</ul>
<p>성능을 위해 버퍼링 및 캐싱 기술 사용</p>
<ul>
<li>File-blocks cache: 파일이 열릴 때 원격 서버의 캐시된 속성을 가져옴, 만약 서버에서 업데이트 되었다면 새로 가져옴</li>
<li>File-attribute cache: 서버로부터 새로운 파일 속성이 도착할 때마다 업데이트, 클라이언트가 최신 파일 속성을 유지할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 14. File System Implementation]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-14.-File-System-Implementation</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-14.-File-System-Implementation</guid>
            <pubDate>Sun, 25 May 2025 07:05:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡14장-목표">💡14장 목표</h4>
</blockquote>
<ul>
<li>파일 시스템 내부 구조</li>
<li>블록 할당 및 free block 관리</li>
<li>성능</li>
<li>에러 처리</li>
<li>WAFL</li>
</ul>
<h1 id="🔹file-system-structure">🔹File-System Structure</h1>
<p>파일 시스템은 데이터를 쉽게 저장, 위치 지정, 검색 등을 할 수 있도록 해준다.</p>
<ul>
<li>파일 구조는 logical storage unit → 연속적 논리 공간</li>
<li>저장소인 디스크는 섹터들의 블록으로 구성되며 블록 단위로 I/O 전송</li>
<li>File control block(FCB): file table block으로, unix와 linux에서는 inode라고 함</li>
<li>Device driver: 파일 시스템과 하드웨어 간의 인터페이스 역할</li>
</ul>
<h3 id="file-system-layer">File system layer<img src="https://velog.velcdn.com/images/jungizz_/post/d987f04e-63bc-4c01-80c8-02a91b44da9d/image.png" alt=""></h3>
<ol start="0">
<li><p>Device dreiver(I/O control)</p>
<ul>
<li>파일 시스템 레이어는 아니고 I/O 영역이다.</li>
<li>디바이스 컨트롤러에게 명령을 내린다.</li>
<li>ex)  &quot;read drive1, cylinder 72, track 2, sector 10, into memory location 1060”</li>
</ul>
</li>
<li><p><strong>Basic file system</strong></p>
<ul>
<li>“retrieve block 123”과 같은 명령을 받아 해당 블록을 읽어옴</li>
<li>Buffer/Caches: CPU와 디스크 사이 버퍼를 두어 속도 및 형식 차이 보완하고 메모리에 캐싱하여 자주 사용 데이터 저장 및 접근 속도 높임</li>
</ul>
</li>
<li><p><strong>File organization module</strong></p>
<ul>
<li>논리 블록을 물리 블록으로 변환</li>
<li>free space 관리</li>
<li>디스크 할당 관리</li>
</ul>
</li>
<li><p><strong>Logical file system</strong></p>
<ul>
<li>파일 이름을 파일 번호, 핸들, 위치로 변환 (file control block(inode)으로)</li>
<li>디렉토리 관리 및 접근 제어</li>
</ul>
</li>
</ol>
<p>이러한 계층 구조 방식은 단순화(추상화)를 통해 복잡성을 줄인다. 하지만 계층화로 인해 성능이 저하된다.</p>
<p>OS 내에 다양한 파일 시스템이 존재한다. UFS, extended file system, ZFS, GoogleFS 등… 이런 파일 시스템에서 공통으로 사용되는 특징들을 알아본다~</p>
<br>

<h1 id="🔹file-system-operation">🔹File-System Operation</h1>
<p>파일 시스템 작업은 system call(API)로 시작되는데, 이러한 호출의 기능이 어떻게 구현하는가</p>
<ul>
<li>Boot control block: OS boot 관련 정보를 가지는 블록</li>
<li>Volume control block(superblock 또는 master file table): volume 정보를 가지는 블록<ul>
<li>블록 총 수, free 블록의 수, 블록 크기, free 블록 포인터 등</li>
</ul>
</li>
<li>Directory 구조: 파일 이름, inode number 등을 가짐</li>
<li>FCB(file control block): 각 파일마다 존재하며, 파일의 세부 정보 등을 포함<img src="https://velog.velcdn.com/images/jungizz_/post/dceba5a6-7163-4aa4-b1d5-8f17b442ef7f/image.png" alt=""><br></li>
</ul>
<h3 id="in-memory-file-system-structure">In-Memory File System Structure</h3>
<p>디스크에 있는 파일에 빠르게 접근할 수 있도록, 파일 관련 정보를 메모리에 올리는 과정</p>
<ul>
<li>Mount table: 파일 시스템이 어느 루트에 마운트 되어있는지</li>
<li><strong>Open file table</strong><ul>
<li>system wide하게 열린 파일의 FCB 정보 등을 가지고 있는 open file table이 있고</li>
<li>각 프로세스는 system wide open file table의 적절한 항목에 대한 포인터를 포함하는 프로세스별 open file table을 가진다.</li>
<li>open file table에 열린 파일에 대한 PCB가 올라와있으면 더욱 빠르게 inode를 얻어 블록에 접근할 수 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/c00b7138-95a3-4203-b481-fd69bb4010dc/image.png" alt=""> <Br></li>
</ul>
</li>
</ul>
<h1 id="🔹directory-implementation">🔹Directory Implementation</h1>
<p>디렉토리를 구현하는 방법</p>
<ol>
<li><p>Linear list</p>
<ul>
<li>파일 이름과 데이터 블록에 대한 포인터(inode)를 일차원 리스트로 가짐</li>
<li>구현 간단</li>
<li>선형 검색 방식으로 파일을 찾음</li>
<li>근데 파일 수가 많아져서 테이블이 커지면 검색 시간 증가</li>
</ul>
</li>
<li><p>Hash table</p>
<ul>
<li>해시 함수를 사용해서 파일 이름을 해시 값으로 변환하여 이를 인덱스로 사용</li>
<li>만약 해시 값이 같다면 linked list로 관리</li>
<li>검색 시간 단축</li>
</ul>
<br>

</li>
</ol>
<h1 id="🔹allocation-methods">🔹Allocation methods</h1>
<p>파일이 저장 공간(블록)을 할당받는 방법</p>
<h3 id="1-contiguous-allocation">1. Contiguous allocation</h3>
<ul>
<li>연속적인 공간 할당</li>
<li>파일 접근 속도가 빨라 성능 우수 (random access에도 우수한 성능)</li>
<li>디렉토리에는 파일 이름과 시작 위치 및 길이 정보를 가짐<ul>
<li><strong>물리 주소 = 논리 주소를 블록 크기로 나눠서 Q번째 블록의 R번째 위치</strong><img src="https://velog.velcdn.com/images/jungizz_/post/266a2ad2-b93a-49c3-8863-baf250948179/image.png" alt=""></li>
</ul>
</li>
<li>하지만 external fragmentation 발생, 새로운 공간 찾기 어려움</li>
<li>이를 해결하기 위해 fragmentation을 모아 사용하는 compaction 필요</li>
<li><strong>Extent-based system</strong>: 보완된 contiguous 할당 방법으로, A 파일에 contiguous 공간을 할당해주고, 뒤로 다른 파일들이 할당되었는데 A파일 할당 공간을 확장하고 싶은 경우, 꼭 이전 contiguous 공간과 이어지지 않아도 다른 곳에 contiguous 공간을 할당해줄 수 있음<img src="https://velog.velcdn.com/images/jungizz_/post/52a40ff2-a361-4feb-9b85-974374254a8c/image.png" alt=""><br></li>
</ul>
<h3 id="2-linked-allocation">2. Linked allocation</h3>
<ul>
<li>linked list로 흩어진 공간 할당, 블록 별 linked list</li>
<li>각 블록에 한 바이트는 다음 블록의 포인터로 사용<img src="https://velog.velcdn.com/images/jungizz_/post/fe788bf6-5995-449b-9b87-e1fa0cc5fff1/image.png" alt=""></li>
</ul>
<ul>
<li>디렉토리에는 파일 이름과 시작 위치, 끝 위치 정보를 가짐<ul>
<li><strong>물리 주소 = 논리 주소를 (블록 크기-1)로 나눠서 Q번째 블록의 (R+1)번째 위치</strong><img src="https://velog.velcdn.com/images/jungizz_/post/ca56165a-6565-4463-b073-1afcfd946006/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li>external fragmentation도 없고 compaction도 필요 없음</li>
<li>하지만 접근 속도가 느리다 → <strong>random access에 취약</strong></li>
<li><strong>Free Allocation table</strong>: 접근 속도를 빠르게 하기 위해 link 관계만 가진 테이블을 메모리에 올려둔다.<img src="https://velog.velcdn.com/images/jungizz_/post/5a303878-f13e-4dfc-a809-3eb6cfd79f76/image.png" alt=""><br></li>
</ul>
<h3 id="3-indexed-allocation">3. Indexed allocation</h3>
<ul>
<li>각 파일에 대한 인덱스 블록을 사용해서 관리 (각 블록의 포인터를 가리킴)</li>
<li>디렉토리는 파일 이름과 시작 위치 정보를 가진다. 시작 위치를 보고 해당 인덱스 블록에 접근<ul>
<li><strong>물리 주소 = 논리 주소를 블록 크기로 나눠서 Q번째 블록(index table의 Q번째)의 R번째 위치</strong><img src="https://velog.velcdn.com/images/jungizz_/post/1b2d1e7d-7100-4d85-a039-12e71f68bdae/image.png" alt=""></li>
</ul>
</li>
<li>속도가 빠르다.</li>
</ul>
<p>하지만, 인덱스 블록도 하드디스크에 있기 때문에 2번 접근하는 문제가 있다. 게다가 파일이 커지면 하나의 인덱스 블록으로 부족할 수 있다.</p>
<ol>
<li>인덱스 블록들을 연결(Link blocks of index table)<ul>
<li><strong>물리 주소 = 논리 주소를 블록 크기^2로 나눠서 Q번째 인덱스 블록의… R을 블록 크기로 나눠서 Q2번째 블록(index table의 Q2번째)의 R2번째 위치</strong></li>
</ul>
</li>
<li>인덱스의 인덱스(two-level index)<ul>
<li><strong>물리 주소 = 논리 주소를 블록 크기^2로 나눠서 Q번째 인덱스의… R을 블록 크기로 나눠서 Q2번째 블록의 R2번째 위치</strong><img src="https://velog.velcdn.com/images/jungizz_/post/ca1e57e3-d294-49f2-9d75-ed0b57a6c937/image.png" alt=""></li>
</ul>
</li>
</ol>
<p>하지만 위 방법은 디스크를 여러번 접근해서 비효율적이다.</p>
<ul>
<li><p><strong>Combined Scheme</strong>(UNIX UFS<strong>):</strong> 블록 개수가 적을수록 아주 빠르게 접근할 수 있는 방법<img src="https://velog.velcdn.com/images/jungizz_/post/a99d7186-04f2-45ce-8174-9bec1cdb8733/image.png" alt="">    </p>
</li>
<li><p><strong>Clustering</strong>: 여러 블록을 그룹으로 묶어 할당하는 방법, 대량 데이터 전송 시 성능 높일 수 있음 → contiguous의 장점 가져옴</p>
<br>

</li>
</ul>
<h1 id="🔹free-space-management">🔹Free-Space management</h1>
<h3 id="1-free-space-list-bit-map">1. <strong>Free space list (Bit map)</strong></h3>
<ul>
<li>빈 블록인지 아닌지를 나타내는 한 비트를 나타내는 리스트 (빈블록이면 1)</li>
<li>빈 블록 수 계산 (1word = 4bit = 4block)<img src="https://velog.velcdn.com/images/jungizz_/post/3cf054f3-7364-4c98-a956-20d307336608/image.png" alt=""></li>
<li>Bit map의 크기 → word 단위로 표현해서 크기 줄이기<img src="https://velog.velcdn.com/images/jungizz_/post/d5f2da5a-c7a6-4572-8e11-2eddef3c62b3/image.png" alt=""><br></li>
</ul>
<h3 id="2-linked-list-free-list">2. <strong>Linked list (Free list)</strong></h3>
<ul>
<li>빈 블록을 linked list로 나타낸다.</li>
<li>앞에서 부터 차례대로 (보통 free는 앞에서부터 사용함)<img src="https://velog.velcdn.com/images/jungizz_/post/7459b5c1-f516-4f40-9031-310b0d5fed05/image.png" alt=""><Br></li>
</ul>
<p>블록을 개별적으로 관리하면 비효율적이다. 아래와 같은 방법을 활용할 수 있음</p>
<h3 id="3-grouping">3. <strong>Grouping</strong></h3>
<ul>
<li>Linked list를 그룹으로 묶어서 관리<br>
### 4. **Counting**</li>
<li>연속적으로 비어있는 블록 수 카운트</li>
<li>ZFS에서 사용하는 방법(metaslab)</li>
<li>contiguous의 장점을 가짐</li>
</ul>
<Br>

<h4 id="trim"><strong>TRIM</strong></h4>
<ul>
<li>파일 시스템이 NVM에서 특정 페이지가 비어있음을 알리는 역할로, 이 명령을 통해 블록이 garbage collection될 수 있도록 함 → 시간 지연
(NVM에서 overwrite를 할 때는 free page 지우고 사용해야하는데, 한 페이지가 아닌 블록 단위로 지워야하는 특징때매)<br></li>
</ul>
<h1 id="🔹performance">🔹Performance</h1>
<p>파일 시스템의 효율성과 성능은 여러가지 요소에 의해 영향을 받는다.</p>
<p>성능을 향상하는 여러가지 방법</p>
<ol>
<li>Buffer cache: 자주 사용되는 블록을 위한 별도 메모리</li>
<li>Asynchrounous write: 버퍼 캐시를 사용하여 버퍼에 데이터를 저장한 후 빠르게 응답<ul>
<li>Synchrounous write: 캐싱한다고 디스크에서 메모리로 데이터를 올리면 빠르긴 하지만 불안정하다. 메모리에 있는 데이터가 수정됐는데 전원이 꺼지면 다 사라지기 때문, 그래서 Synchronous write는 버퍼링/캐싱을 하지 않는다.</li>
</ul>
</li>
<li>Free-behind: 읽자마자 버퍼의 기존 데이터 버리기 (free space 미리 만들기)</li>
<li>Read-ahaed: 다음 데이터 블록도 읽을 걸 예상해서 미리 읽기</li>
</ol>
<p>→ 3, 4번은 미리 예측하여 성능을 향상시키는 방법, 하지만 잘못 예측하면 더 안좋아질 수도)<Br></p>
<h3 id="unified-buffer-cache"><strong>Unified Buffer Cache</strong></h3>
<p>캐싱을 두 번 한다면?</p>
<ul>
<li>Page cache: 블록이 아닌 페이지를 캐싱하는 방법, memory-mapped I/O에서 주로 사용</li>
<li>만약 I/O처리를 할 떄 buffer cache와 page cache를 동시에 적용한 경우<ul>
<li>다른 알고리즘이긴 하지만 어쨌든 2번 접근으로 성능이 저하된다. 둘 중 하나만 사용해야함 → 어떤게 좋은지 잘 따져야 한다.</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li>캐싱 중복<img src="https://velog.velcdn.com/images/jungizz_/post/932443e0-9897-433b-b0c0-ee86a9fe32ed/image.png" alt=""><br></li>
</ul>
</blockquote>
<ul>
<li><p>한 캐싱만 사용 → 이게 더 좋은 성능<img src="https://velog.velcdn.com/images/jungizz_/post/fb55ff5c-622f-401f-afca-099226c6deff/image.png" alt=""></p>
<br>


</li>
</ul>
<h1 id="🔹recovery">🔹Recovery</h1>
<ol>
<li>Consistency Checking<ul>
<li>데이터 일관성 검사, 불일치 시 에러 감지</li>
</ul>
</li>
<li>Backup</li>
<li>Log structered File system<ul>
<li>wirte로 변화된 이력(log) 보관</li>
<li>백업은 특정 시점의 데이터를 복구하는거라 그 이후 데이터를 잃어버리는데, log 그렇지 않음<br>
### WAFL file system

</li>
</ul>
</li>
</ol>
<p>WAFL은 최신 기법들이 포함된 파일 시스템</p>
<ul>
<li>Distributed file system</li>
<li>Write-anywhre file layout</li>
<li>Random I/O 및 write 작업의 성능 최적화에 중점</li>
<li>하드디스크 대신 NVRAM을 사용하여 쓰기 캐싱 수행</li>
</ul>
<p>WAFL에 필요한 다양한 정보들은 전부 파일로 존재한다. 이러한 파일 정보를 가진 table도 존재한다.<img src="https://velog.velcdn.com/images/jungizz_/post/197541f4-6ef1-403f-886b-ea2835eac51b/image.png" alt=""></p>
<p>Snapshot할 때, 원래는 전부 copy하는데, 시간이 오래 걸리므로 공유를 먼저한다. 그리고 write해서 바뀌는게 생기면 그 때 copy하기 (copy on write, 앞에서 했떤 내용임 걍 다!!!!!!! 앞에서 했던 내용이야 비슷해)<img src="https://velog.velcdn.com/images/jungizz_/post/213756b3-3419-46ce-aac5-95abd71e3e5e/image.png" alt=""></p>
<h4 id="apple-file-system"><strong>Apple file system</strong></h4>
<ul>
<li>snapshots</li>
<li>space sharing(볼륨이 파티션들(일부 디스크)을 공유)</li>
<li>atomic safe-sace primitives</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 13. File-System Interface]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-13.-File-System-Interface</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-13.-File-System-Interface</guid>
            <pubDate>Sun, 25 May 2025 06:51:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡13장-목표">💡13장 목표</h4>
<p>사용자 입장에서 파일 시스템에 어떻게 접근하는지</p>
</blockquote>
<ul>
<li>파일 시스템 기능</li>
<li>파일 시스템 인터페이스</li>
<li>파일 시스템의 다양한 방법</li>
</ul>
<p>파일은 연속적인 공간(논리 주소 공간)을 제공한다.
데이터 파일(숫자, 문자, 이진)과 프로그램 파일 유형으로 나눠진다.
텍스트 파일, 소스 파일, 실행 파일 등이 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/c7cb4c96-5af0-444c-9d7a-935444f4a98f/image.png" alt=""></p>
<h4 id="attributes"><strong>Attributes</strong></h4>
<ul>
<li>파일 자원 관리를 위해 file table이 존재하고, 그 안에는 다음과 같은 속성(정보)를 가진다.<img src="https://velog.velcdn.com/images/jungizz_/post/3504d65d-6282-4e02-9a5a-d40b704f9e06/image.png" alt=""></li>
<li>이러한 파일 정보들은 보통 디스크에 있는 트리 구조의 디렉토리 밑에 파일에 저장된다.</li>
</ul>
<h4 id="operations"><strong>Operations</strong></h4>
<ul>
<li>파일 operation은 OS의 file system에서 system call로 지원된다. 이를 통해 application이 파일 operation을 사용한다.<img src="https://velog.velcdn.com/images/jungizz_/post/8ab07982-62fa-4569-9256-c68bed068c85/image.png" alt=""></li>
<li>pointer는 읽거나 쓸 위치(포지션)으로, seek으로 이 포지션을 지정한다.</li>
<li>파일을 먼저 열어서 빠른 접근을 할 수 있도록 준비를 한 다음, 읽기 또는 쓰기가 완료되면 준비 작업들을 종료한다. </li>
</ul>
<h4 id="open-files"><strong>Open files</strong></h4>
<p>OS는 열린 파일들을 open file table과 file pointer, locking 등으로 관리한다.</p>
<ul>
<li><strong>Open-file table</strong>: 각 열린 파일에 대한 정보를 가진다. 상태, 위치, 접근 권한 등<ul>
<li><strong>File pointer</strong>:각 프로세스가 열린 파일의 마지막 읽기/쓰기 위치를 가리키는 포인터</li>
</ul>
</li>
<li><strong>File-open count</strong>: 파일이 열린 횟수 카운트, 마지막 프로세스가 파일을 닫을 때 file table에서 데이터 제거 (이걸 알아야 한 프로세스가 파일 작업을 끝내도 접근 프로세스가 남아있으면 파일이 종료되지 않도록 할 수 있음)</li>
<li><strong>Open-file Locking</strong>: 여러 프로세스가 파일에 접근할 떄의 충돌을 방지하기 위한 잠금 기능<ul>
<li><strong>Shared lock</strong>: 여러 프로세스가 동시 접근 가능 (reader)</li>
<li><strong>Exclusive lock</strong>: 독점권, 한 wrtier가 들어오면 다른 reader, writer 들어올 수 없음</li>
<li>Mandatory lock: 이미 잠겨있으면 접근 거부, 다시 lock 막는 것</li>
<li>Advisorty lock: 잠금 상태를 확인</li>
<li>lock example - java API<img src="https://velog.velcdn.com/images/jungizz_/post/22bd54d5-28f8-47a6-b0b9-641f9d90833e/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/68587540-7945-4274-9735-c8a18a4ef85c/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="file-structure"><strong>File Structure</strong></h4>
<ul>
<li>None: 단순히 단어 또는 바이트의 연속으로 구성 → 이게 근본이고, 이걸 활용해서 다른 구조도 만드는 것</li>
<li>Record: record 단위로 구성</li>
<li>complex: 파일마다 형식이 정해져 있거나, 재배치 가능한 로드 파일 등</li>
</ul>
<br>

<h1 id="🔹access-methods">🔹Access Methods</h1>
<p>파일에 접근하는 다양한 방법</p>
<ul>
<li><strong>Sequential access</strong>: 순차적으로 접근</li>
<li><strong>Direct access</strong>: 읽거나 쓸 위치를 지정
direct access를 아래와 같이 적용하면 sequential access를 할 수 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/e6856f71-e7ff-4410-bc95-541ab282d91d/image.png" alt=""></li>
<li><strong>Index access</strong>: 파일에 대한 인덱스를 메모리에 저장하여 데이터 위치를 빠르게 찾을 수 있도록<ul>
<li>인덱스가 너무 크면 인덱스의 인덱스를 디스크에 저장하는 방법도 있음 → multi Index level</li>
<li>ex) ISAM: 작은 master index와 디스크 블록에 저장된 secondary index로 구성</li>
<li>ex) VMS에서는 index file과 relative file을 제공<img src="https://velog.velcdn.com/images/jungizz_/post/1ba6221a-388d-414d-bc0d-c4ca7efbf812/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h1 id="🔹directory-structure">🔹Directory Structure</h1>
<p>파일에 대한 정보를 포함하는 노드 집합<img src="https://velog.velcdn.com/images/jungizz_/post/62596761-dbb3-4145-bbea-9a50e4d80227/image.png" alt=""></p>
<ul>
<li>Directory: 파일들이 저장되는 곳으로, 파일에 대한 정보를 포함 (계층적, 트리 구조)</li>
<li>트리 구조로 구성<ul>
<li>효율적 관리, 파일 빠르게 찾을 수 있음</li>
<li>사용자가 쉽게 사용할 수 있도록 네이밍 가능</li>
<li>파일 속상에 따라 그룹화</li>
</ul>
</li>
</ul>
<h4 id="operation"><strong>Operation</strong></h4>
<ul>
<li>파일 검색, 생성, 삭제</li>
<li>디렉토리 목록 나열, 파일 서치</li>
<li>파일 이름 변경</li>
<li>파일 시스템 탐색 (traverse, 트리구조 탐색)</li>
</ul>
<h4 id="disk-structure"><strong>Disk structure</strong></h4>
<p>디렉토리와 파일은 모두 디스크에 저장된다.</p>
<ul>
<li>Partition: 디스크를 쪼개서 여러개의 파티션, 각 파티션은 별도의 파일 시스템을 가질 수 있음<ul>
<li>디스크나 파티션은 RAID를 통해 에러 처리 및 성능 향상</li>
<li>파일 시스템 없이 raw하게 직접 사용하거나, 파일 시스템으로 format하여 사용</li>
</ul>
</li>
<li>한 파일 시스템의 크기가 커서 여러개의 파티션을 차지하게 될 수 있으니까 Volume을 사용해서 이를 방지한다.<ul>
<li>‘파일시스템-볼륨-디스크’ 구조로, 파일 시스템과 볼륨은 1:1로, 볼륨과 디스크는 1:n으로 연결</li>
<li>volume table 존재</li>
</ul>
</li>
</ul>
<h4 id="file-system-종류"><strong>File system 종류</strong></h4>
<ul>
<li><strong>general-purpose file system</strong>: 일반적으로 알고있는 다양한 데이터를 파일에 저장하는 시스템<ul>
<li>ufs, zfs</li>
</ul>
</li>
<li><strong>special-purpose file system</strong>: 디바이스, 프로세스, 메모리 등의 리소스를 파일 처럼 쉽게 접근할 수 있는 환경을 제공하는 시스템<ul>
<li>tmpfs, objfs, ctfs, lofs, procfc</li>
</ul>
</li>
</ul>
<h4 id="structure"><strong>Structure</strong></h4>
<p>디렉토리 구조는 tree가 일반적이지만 다양하다. single, two level은 거의 사용 안함</p>
<ul>
<li><p><strong>single level directory</strong>: 모든 사용자가 동일한 디렉토리에 저장<img src="https://velog.velcdn.com/images/jungizz_/post/395c215a-5ff1-4d9e-b596-733779ac103c/image.png" alt=""></p>
</li>
<li><p><strong>two level directory</strong>: 사용자마다 디렉토리 제공<img src="https://velog.velcdn.com/images/jungizz_/post/691b73a2-185d-4437-b650-ddb78c663f89/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><p><strong>Tree-structured directories</strong>: 그룹화해서 원하는 만큼 트리 레벨 생성</p>
<ul>
<li>절대 경로: 루트 디렉토리에서 시작하는 경로</li>
<li>상대 경로: 현재 디렉토리 기준 경로<img src="https://velog.velcdn.com/images/jungizz_/post/8ccaf15e-c420-4622-bc85-9f169c6007a6/image.png" alt=""></li>
</ul>
</li>
<li><p><strong>Acyclic-graph directories</strong>: 여러 디렉토리가 같은 파일을 공유할 수 있다. → link</p>
<ul>
<li>link때문에 동일한 파일이 디렉토리마다 이름이 다를 수 있음, 그래도 cycle은 없음</li>
<li>디렉토리가 끊어지면 밑으로 접근 불가능해서 삭제된다. (dangling pointer 삭제)<img src="https://velog.velcdn.com/images/jungizz_/post/6f7a2017-4183-44e2-9f11-c920877e3ab8/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li><strong>General-graph directories</strong>: cycle도 존재<ul>
<li>위에서는 reference count를 사용해서 현재 자신의 파일을 참고한 것이 0개 이면 연결이 끊어졌다고 보고 삭제할 수 있었는데, cycle이 생기면 연결이 끊어졌더라도 cycle에서는 서로를 참고하기 때문에 reference count가 0이 되지 않는다. 그래서 root부터 서치해서 닿지 않은 부분에 대한 삭제를 하는 <strong>garbage collection</strong>이 필요하다.<img src="https://velog.velcdn.com/images/jungizz_/post/f85962e2-4471-4421-9007-da598ea7d3c1/image.png" alt=""><Br></li>
</ul>
</li>
</ul>
<h1 id="🔹file-system-mounting">🔹File System Mounting</h1>
<p>파일 시스템을 특정 디렉토리 구조에 연결하여 접근하는 것</p>
<p>예를 들어, 한 파일 시스템이 있는데, 루트에 연결되어있지 않아 접근이 불가능한 상태 → 루트와 연결된 파일 디렉토리 구조에 연결하여 접근 가능하도록 함
(mount할 때, 파일 시스템은 consistency check를 한다)</p>
<ul>
<li>왼쪽은 마운팅이 끝난 상태, 오른쪽은 마운팅 전<img src="https://velog.velcdn.com/images/jungizz_/post/c1043bd7-4de3-458d-9910-368cf7b4d566/image.png" alt=""></li>
<li>User를 마운팅 포인트로 잡아 마운팅<img src="https://velog.velcdn.com/images/jungizz_/post/cc024d0a-eee7-48a6-982f-2af211638ad8/image.png" alt=""><br></li>
</ul>
<h1 id="🔹file-sharing">🔹File Sharing</h1>
<p>멀티 유저 시스템에서 중요한 기능</p>
<ul>
<li>여러 사용자가 동시에 파일에 접근하고 수정할 수 있는 환경 제공</li>
<li>사용자 및 그룹에 대한 접근 권한 설정 → 보안 유지<ul>
<li>User ID, Groun ID를 누군지 구별하고 권한 설정 및 파악<Br>
### **Remote file system**

</li>
</ul>
</li>
</ul>
<p>네트워크를 통해 파일 시스템에 접근하는 방법</p>
<ul>
<li>접근 단계<ul>
<li>manually: 수동적 접근, FTP(파일 접근 프로토콜)과 같은 프로그램을 사용하여</li>
<li>automatically: distributed file system/을 통해 자동 접근, 사용자 입장세어 local인지 remote인지 모름</li>
<li>semi automatically: world wide web을 통해 반자동 접근</li>
</ul>
</li>
<li>서버-클라이언트 모델<ul>
<li>서버에서 파일 시스템을 제공하고, 클라이언트들은 그 파일 시스템에 접근해서 local에 있는 것처럼 사용하는 것 (remote mount)</li>
</ul>
</li>
<li>UNIX 기반 표준 파일 공유 프로토콜 <strong>NFS</strong> (15장)</li>
<li>분산 정보 시스템 LDPA, DNS 등은 원격 컴퓨팅을 위한 정보에 대한 통합 접근 도와줌</li>
</ul>
<h4 id="파일-공유-모드"><strong>파일 공유 모드</strong></h4>
<ul>
<li><strong>State information</strong>: 파일에 접근하는 클라이언트 상태 정보를 유지, open-r/w-close를 따르며, 이 과정에서 발생하는 모든 정보 기록</li>
<li><strong>Stateless protocol</strong>: 상태 정보 포함 안함, 바로 r/w함 → 에러 복구가 쉽지만 보안성이 낮다.<Br>
### Consistency Semantics

</li>
</ul>
<p>여러 프로세스가 파일에 접근할 때, 데이터의 일관성을 어떻게 유지할 것인가 (일관성이란 프로세스 순서가 달려져도 항상 같은 결과)</p>
<ul>
<li><p>Unix 파일 시스템 (UFS)에서는 한 사용자가 파일을 수정하면 다른 사용자가 실시간으로 변경 사항을 확인할 수 있다. local기반이라서</p>
</li>
<li><p>하지만 Andrew 파일 시스템 (AFS)는 remot 기반이기 때문에 실시간 변경 사항을 확인하기 어렵다(네트워크 오버헤드), close해야 변경 사항을 확인할 수 있다.</p>
<Br>

</li>
</ul>
<h1 id="🔹protection">🔹Protection</h1>
<p>파일 소유자는 아래 사항을 제어할 수 있다</p>
<ul>
<li>파일에 누가 접근할 수 있는지</li>
<li>파일에 무엇을 할 수 있는지<ul>
<li>읽기 쓰기 실행 추가 삭제 리스트 등</li>
</ul>
</li>
</ul>
<p><strong>Unix/Linux</strong></p>
<ul>
<li>소유자/그룹/공용 접근 권한 설정</li>
<li>RWX(읽기, 쓰기, 수행)의 각 비트로 접근 권한 설정<img src="https://velog.velcdn.com/images/jungizz_/post/59b3b48d-0c51-4404-8e7f-558a9c033aae/image.png" alt=""></li>
</ul>
<p>Window는 더 복잡한 접근 모드를 가진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 12. I/O Systems]]></title>
            <link>https://velog.io/@jungizz_/ch-12.-IO-Systems</link>
            <guid>https://velog.io/@jungizz_/ch-12.-IO-Systems</guid>
            <pubDate>Sun, 25 May 2025 06:42:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡12장-목표">💡12장 목표</h4>
</blockquote>
<ul>
<li>I/O 하드웨어</li>
<li>Application이 I/O interface를 어떻게 사용하는지</li>
<li>I/O Subsystem</li>
<li>I/O요청을 하드웨어 작동으로 바꾸는 것</li>
<li>STREAMS</li>
<li>성능</li>
</ul>
<p>커널은 프로세스 동기화, 메모리 관리, 파일 관리, I/O관리 등을 하는데 그 중 I/O 관리를 살펴본다.</p>
<ul>
<li>시스템은 다양한 I/O 디바이스를 포함한다. (스토리지, 네트워크 장치, 키보드 같은 입력 장치 등)</li>
<li>각 디바이스는 고유한 특성과 작동방식을 가지고 있어서 해당 디바이스를 제어하는 컨트롤러를 가진다. 이 컨트롤러가 OS 간 통신 담당<ul>
<li>디바이스 컨트롤러가 각 디바이스별로 처리하고,</li>
<li>그 위에 있는 I/O subsystem이 공통 처리를 진행</li>
</ul>
</li>
</ul>
<br>

<h1 id="🔹io-hardware">🔹I/O Hardware</h1>
<p>I/O 관련 하드웨어</p>
<ul>
<li>저장 장치(하드디스크, SSD 등), 전송 장치(네트워크 등), 사용자 인터페이스(키보드 등) 장치 등 다양한 종류가 존재</li>
</ul>
<p><strong>이 장치들과 컴퓨터 연결</strong></p>
<ul>
<li>Port: 디바이스 연결을 위한 것(ex. USB포트, 랜포트), I/O디바이스와 컴퓨터가 연결되는 지점</li>
<li>Bus: 데이터 전송 경로, 버스에 다양한 디바이스가 직렬로? 체인처럼 연결되있는 Daisy chain 방식으로 연결<ul>
<li>PCI bus: 메인 버스</li>
<li>expansion bus: 느린 I/O장치를 위한 버스</li>
<li>SAS: 디스크 인터페이스<img src="https://velog.velcdn.com/images/jungizz_/post/c75fa954-cea4-48b2-80cc-c1e4d915aea0/image.png" alt=""></li>
</ul>
</li>
</ul>
<p><strong>Fibre channel(FC)</strong></p>
<ul>
<li>고속 데이터 전송을 위해 설계된 컨트롤러</li>
</ul>
<p><strong>Register</strong></p>
<ul>
<li>각 디바이스는 명령어, 주소, 데이터 등을 저장하기 위한 레지스터를 가진다(디바이스 컨트롤러의 데이터 저장).  CPU가 이 레지스터에 접근<ul>
<li>data-in register</li>
<li>data-out register</li>
<li>satus register: 디바이스 현재 상태 정보</li>
<li>control register: 디바이스 동작 제어 명령 저장</li>
</ul>
</li>
</ul>
<p><strong>Address</strong></p>
<ul>
<li>각 디바이스는 고유한 주소를 가진다. OS가 여러 디바이스를 구분하기 위해 이 주소를 사용한다.<ul>
<li>Direct I/O instruction: 주소로 직접 통신, 명령어</li>
<li>Memory-mapped I/O: 디바이스의 레지스터가 프로세스의 주소 공간에 매핑<img src="https://velog.velcdn.com/images/jungizz_/post/5812daff-e4b5-45ec-b672-509f8e989cfa/image.png" alt=""><Br>    </li>
</ul>
</li>
</ul>
<h2 id="polling">Polling</h2>
<p>CPU가 I/O장치의 상태를 주기적으로 확인하여 데이터 전송을 관리하는 것</p>
<ul>
<li><p>CPU가 I/O 디바이스에게 일을 시키는 경우</p>
<ol>
<li>컨트롤러 busy bit를 확인하여 바쁜지 확인, 0이 될때까지 기다림</li>
<li>주소를 통해 디바이스와 연결하고, 데이터를 쓸지 읽을지 비트 설정</li>
<li>명령 준비 비트 설정하여 request 했음을 알림</li>
<li>컨트롤러는 busy비트를 1로 설정하고 요청을 받아 작업 수행 (데이터 전송)</li>
<li>완료되면 비트들 초기화</li>
</ol>
<br>
## Interrupt

</li>
</ul>
<p>폴링의 비효율성 극복, I/O작업할 때 기다리게 하지 않음</p>
<ul>
<li>Interrupt handler: 인터럽트 처리<ul>
<li>Maskable: CPU가 특정 조건에 따라 인터럽트를 무시하거나 지연시키는 것</li>
</ul>
</li>
<li>Interrupt vector: 여러 인터럽트들 구별하기 위해<img src="https://velog.velcdn.com/images/jungizz_/post/3c7c5fa6-a248-49e6-9e85-92c72eed06f8/image.png" alt=""></li>
</ul>
<h4 id="인터럽트-처리-과정"><strong>인터럽트 처리 과정</strong><img src="https://velog.velcdn.com/images/jungizz_/post/f5a1bfe6-1f73-4c30-bcb2-3904f9358a8b/image.png" alt=""></h4>
<ul>
<li>이를 통해 CPU 사용률 증가, CPU와 I/O의 속도 미스매치 해결</li>
<li>하지만 latency 발생</li>
</ul>
<h4 id="io말고도-interrupt는-다양하게-활용됨"><strong>I/O말고도 Interrupt는 다양하게 활용됨</strong></h4>
<ul>
<li>예외 처리, page fault, trap 등에서 사용</li>
<li>Multi-CPU인 경우는 CPU마다 interrupt 처리 가능 (동시 처리)</li>
</ul>
<h4 id="dma의-interrupt"><strong>DMA의 Interrupt</strong></h4>
<ul>
<li>DMA는 큰 I/O 데이터를 메모리로 전송할 때 CPU를 거치지 않고 전달하게 해주는 것</li>
<li>CPU는 안쓰지만 CPU와 같은 버스를 공유하기 때문에, DMA로 데이터를 다 보냈으면 CPU에게 다했다는 interrupt 보냄<img src="https://velog.velcdn.com/images/jungizz_/post/13d12d7e-5d6d-4230-b987-756ef573a93f/image.png" alt=""><br></li>
</ul>
<h1 id="🔹application-io-interface">🔹Application I/O Interface</h1>
<blockquote>
<p>예를 들어, 파일 접근을 하는 경우,
프로세스가 I/O request를 하여 파일에 접근하고, I/O Subsystem이 파일이 디스크에 저장되어있음을 알아내고, 디스크에 접근하기 위해 디스크 디바이스 드라이버에 접근한다. </p>
<p>여기까지가 OS이고, HW적으로는 드라이버에 의해 디스크 디바이스 컨트롤러를 작동시키는 것이다.<img src="https://velog.velcdn.com/images/jungizz_/post/265d5da2-b971-4f13-9ada-a3fe07b2952b/image.png" alt=""></p>
</blockquote>
<p>이러한 구조가 I/O interface이다. 이는 I/O 시스템을 호출하는 과정의 추성화를 통해 애플리케이션이 디바이스의 다양성을 걱정하지 않고도 I/O작업을 할 수 있도록 한다. 디바이스 드라이버가 다양성을 처리해준다.</p>
<h4 id="io디바이스들의-특징-다양성"><strong>I/O디바이스들의 특징 (다양성)</strong><img src="https://velog.velcdn.com/images/jungizz_/post/e865c000-7009-4ae0-b3cc-7a46273361f6/image.png" alt=""></h4>
<h4 id="io-디바이스-분류"><strong>I/O 디바이스 분류</strong></h4>
<p>OS는 다양한 I/O 디바이스를 효과적으로 관리하기 위해 분류</p>
<ul>
<li><strong>Block Device</strong>: 블록 단위 데이터 처리 (디스크 드라이브)<ul>
<li>RAW I/O와 direct I/O 방식: 파일 시스템을 거치지 않고 블록에 직접 접근하는 방식이다. (원래 하드디스크와 메모리 사이 버퍼를 통해 캐싱을 하는데 이를 안하고)</li>
</ul>
</li>
<li><strong>Character Device</strong> (Stream): 문자 단위 데이터 처리 (키보드, 마우스 등)<ul>
<li>get(): read</li>
<li>put(): write</li>
</ul>
</li>
<li><strong>Memory-mapped file access</strong>: 파일 내용을 가상 메모리 주소 공간에 매핑</li>
<li><strong>Network Device</strong>: 소켓 인터페이스 사용<ul>
<li>소켓을 사용하여 어플리케이션이 통신 프로토콜을 쉽게 사용할 수 있도록 한다. (system call같은 느낌)</li>
<li>소켓 연결 후 read, write</li>
<li>select(): 여러 소켓(포트)을 모니터링해 준비되면 데이터 read/write</li>
</ul>
</li>
</ul>
<p>Block, character 디바이스의 I/O방식에는 RAW I/O와 direct I/O가 있다. 이 방식은 </p>
<ul>
<li>Clocks and Timers: 시간 관련 기능 제공</li>
</ul>
<h4 id="io-디바이스-특성-제어"><strong>I/O 디바이스 특성 제어</strong></h4>
<ul>
<li>Unix의 ioctl() 함수: 디바이스 레지스터 값을 통해 특성 제어, 데이터 전송, 상태 확인 등을 함</li>
</ul>
<h4 id="io-디바이스-구분"><strong>I/O 디바이스 구분</strong></h4>
<ul>
<li>Unix, Linux는 다양한 디바이스들을 구별하기 위해 major번호와 minor 번호를 사용<ul>
<li>주번호: 디바이스 종류</li>
<li>부번호: 그 종류의 몇번째 디바이스 인지</li>
</ul>
</li>
</ul>
<h4 id="vectered-io"><strong>Vectered I/O</strong></h4>
<p>Unix의 readve()함수는 한 systemcall로 여러 I/O작업을 병렬처리한다.
<br></p>
<h3 id="nonblocking-and-asynchronous-io">Nonblocking and Asynchronous I/O</h3>
<ul>
<li><strong>Blocking</strong>: I/O 직업이 완료 될 때까지 반환 안함 → 그동안 프로세스 중단 및 기다림</li>
<li><strong>Nonblocking</strong>: I/O 호출 시 바로 반환, 만약 수행 못한대도 바로 (오류)반환 → 프로세스는 작업 완료를 기다리지 않음, 오류 처리 개발자가 해줘야함</li>
<li><strong>Asynchronous</strong>: I/O호출 시 바로 반환 및 완료 시 프로세스에게 신호 전송 → 오류 처리 수동 관리 필요 없음<img src="https://velog.velcdn.com/images/jungizz_/post/f452207f-1a37-4551-8223-a42aa1e247ab/image.png" alt=""><br></li>
</ul>
<h1 id="🔹kernel-io-subsystem">🔹Kernel I/O Subsystem</h1>
<p>다양한 디바이스들에 대한 공통 작업 처리, 표준 인터페이스를 제공<img src="https://velog.velcdn.com/images/jungizz_/post/5aa7ca65-260c-4d1d-a3d8-fd09509b2af3/image.png" alt=""></p>
<ul>
<li><p><strong>I/O Scheduling:</strong> 여러 프로세스가 한 디바이스에 I/O요청을 보낼 때, 어떤 요청을 먼저 처리할 것인지</p>
</li>
<li><p><strong>Buferring</strong>: 디바이스 간 전송 중에 데이터를 메모리에 저장하는 것</p>
<ul>
<li>속도/크기 불일치 해결</li>
<li>디바이스 별 속도는 아래처럼 다르고, 데이터 형태도 다르니까<img src="https://velog.velcdn.com/images/jungizz_/post/fb5b6638-3fb7-4228-a59e-7f80a28b776e/image.png" alt=""></li>
<li>Double Buferring도 있다. 버퍼 두개 써서 더 효율적으로 처리</li>
</ul>
</li>
<li><p><strong>Device-status table</strong>: I/O 디바이스들을 관리하는 테이블, 대기중인 요청들이 linked list로 연결<img src="https://velog.velcdn.com/images/jungizz_/post/63525e30-4bf3-4df8-b6e3-75e7b488d4ef/image.png" alt=""></p>
</li>
<li><p><strong>Caching</strong>: 빠른 CPU와 느린 I/O 사이 버퍼를 두고 캐싱하여 → 이거 교수님이 말해주신건데.. 이게 맞나</p>
<ul>
<li>빠른 장치가 데이터의 복사본을 보관 (주로 메모리에서 데이터를 임시로 저장)</li>
<li>버퍼링과 함께 사용</li>
</ul>
</li>
<li><p><strong>Spooling</strong>: 디바이스 출력 데이터를 임시로 저장하는 버퍼의 일종</p>
<ul>
<li>ex) 프린터는 여러 출력물을 처리하기 위해 스풀링</li>
</ul>
</li>
<li><p><strong>Device Reservation</strong>: 특정 디바이스에 대한 독점 접근 제공</p>
<ul>
<li>여러 프로세스가 동시에 장치에 접근하는걸 방지</li>
<li>디바이스의 할당 및 해제</li>
</ul>
</li>
<li><p><strong>Error Handling</strong>: OS는 I/O작업 중 발생할 수 있는 다양한 오류 처리 메커니즘을 가짐 (read write 에러, 디바이스 사용 불가 등)</p>
</li>
<li><p><strong>I/O Protection</strong>: OS는 I/O를 보호해주는 기능을 가짐</p>
<ul>
<li>OS는 모든 I/O 명령을 previleged로 정의, 프로세스는 커널을 통해 접근할 수 있음</li>
<li>프로세스가 I/O 작업을 수행하기 위해 system call을 사용하면, 커널로 trap이 발생하고 커널은 해당 system call을 사용할 권한이 있는지 확인하고 작업을 수행한다. 그리고 다시 유저모드로 돌아간다.<img src="https://velog.velcdn.com/images/jungizz_/post/6ca1614c-1123-4558-aac5-6a9f241d1080/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="kernel-data-structure"><strong>Kernel data structure</strong></h4>
<p>커널은 I/O 구성요소의 상태 정보를 유지하기 위한 데이터 구조를 가진다. I/O 상태 정보는 open file table, network connection, character device state 등이 있다.</p>
<ul>
<li>복잡한 데이터 구조를 버퍼로 관리여 I/O 작업 성능을 최적화<ul>
<li>버퍼에 캐시했을 때, 수정된 데이터가 있으면 디스크에 기록한 후 그 자리에 새로 쓰여질 수 있도록 해야된다.</li>
</ul>
</li>
<li>WIndow는 micro kernel을 사용하여 많은 기능들이 유저 모드에서 독립적인 프로세스로 동작하는데, I/O 기능도 포함된다. 근데 message passing 기법이라 느림</li>
<li>UNIX의 kernel structure는 아래와 같다<img src="https://velog.velcdn.com/images/jungizz_/post/9197cc3d-e14a-430a-bb02-bdffe6c47a94/image.png" alt=""></li>
</ul>
<h4 id="power-management"><strong>Power management</strong></h4>
<p>디바이스는 전력을 소모한다. 우리는 이를 소프트웨어적으로 관리해줘야 함</p>
<ul>
<li>Cloud computing은 서버 급 컴퓨터로 많은 디바이스들이 연결되어있어 전기를 많이 사용한다.</li>
<li>Moblie computing은 저전력 설계가 중요하다.</li>
</ul>
<p>이러한 것이 등장하면서 전력 관리가 더 중요해짐</p>
<ul>
<li>안쓰는거 끄기~ 다시 쓰면 깨우기(latency는 있음)</li>
</ul>
<blockquote>
<p>📌 그래서 Kernel I/O Subsystem의 주요 기능을 다시 보면</p>
</blockquote>
<ul>
<li>파일 및 디바이스 종류 관리</li>
<li>파일 및 디바이스 접근 권한 관리</li>
<li>버퍼링, 캐싱, 스풀링</li>
<li>I/O Scheduling</li>
<li>에러 처리</li>
<li>디바이스 드라이버 구성</li>
<li>전력 관리</li>
</ul>
<br>

<h1 id="🔹-trasnforming-io-requests-to-hardware-operations">🔹 Trasnforming I/O requests to Hardware operations</h1>
<p>I/O요청이 HW 디바이스 컨트롤러까지 가는 과정<img src="https://velog.velcdn.com/images/jungizz_/post/d60c0cab-ec5b-46ce-9402-7de0a86303c6/image.png" alt=""><br></p>
<h1 id="🔹-streams">🔹 STREAMS</h1>
<p>유저 레벨 프로세스와 디바이스 사이에서 해야하는 일들을 STREAM이라는 모듈로 관리</p>
<ul>
<li>read queue와 write queue로 stream을 유연하게 추가/제거 가능<img src="https://velog.velcdn.com/images/jungizz_/post/c4626888-aa9a-4e77-83c9-05b2eb7a33df/image.png" alt=""><br></li>
</ul>
<h1 id="🔹performance">🔹Performance</h1>
<p>I/O처리의 성능은 고려 사항이 아주 많다. 아래와 같이 많은 단계를 거치기 때문이다.  <img src="https://velog.velcdn.com/images/jungizz_/post/cab28f8f-37ef-4390-a1e5-dd26f68f85b3/image.png" alt="">성능을 향상시키려면</p>
<ul>
<li>context switch를 줄이기</li>
<li>data copying을 줄이기 (계층별 이동, 저장공간 이동 과정 하나하나가 다 copy다. 공유 데이터 공간을 만들고 각자 접근하게 한다면 줄일 수 있음)</li>
<li>큰 데이터를 한 번에 전달하여 interrupt 줄이기, CPU와 I/O가 효율적으로 동시에 일할 수 있는 smart controller 사용</li>
<li>DMA 사용하기</li>
<li>스마트 하드웨어 장치 사용하기</li>
<li>CPU, 메모리, 버스, I/O 간의 성능을 균형있게 조정하기, 공평하게 일하기 (병목현상 방지)</li>
<li>커널 스레드를 사용하여 효율적으로 처리하기</li>
<li>알고리즘 구현 시 특징에 따라 적절한 계층 선택하기<ul>
<li>위로 갈수록 유연하고 개발이 간편하고 싸지만 처리 속도가 느리고</li>
<li>아래로 갈수록 처리 속도가 빠르고 추상화할 수 있지만 비싸다<img src="https://velog.velcdn.com/images/jungizz_/post/c7674278-0dae-494f-954e-67fb8a84823a/image.png" alt=""></li>
</ul>
</li>
</ul>
<p>스토리지의 I/O 성능은 아래와 같다. (저장용량 및 처리 속도)<img src="https://velog.velcdn.com/images/jungizz_/post/2a1c8425-131c-42ae-95fa-e2b9b942180d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 11. Mass-Storage Systems]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-11.-Mass-Storage-Systems</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-11.-Mass-Storage-Systems</guid>
            <pubDate>Thu, 08 May 2025 16:44:21 GMT</pubDate>
            <description><![CDATA[<h1 id="ch-11-mass-storage-systems">ch 11. Mass-Storage Systems</h1>
<p>대용량 저장 장치</p>
<blockquote>
<h4 id="💡11장-목표">💡11장 목표</h4>
</blockquote>
<ul>
<li>구조<ul>
<li>HDD, NVM</li>
</ul>
</li>
<li>Storage Device Management</li>
<li>Scheduling</li>
<li>RAID</li>
</ul>
<h1 id="🔹structure">🔹Structure</h1>
<p>대용량 저장 장치는 HDD와 NVM(SSD)로 구성되어있다. HDD가 가장 낮고, 그 위에 SSD, 그 위에 메모리의 계층 구조를 가진다.</p>
<p>(Magnetic tape → HDD → SSD → RAM(주기억장치))</p>
<h2 id="1-hdd">1. HDD</h2>
<p>하드 디스크 드라이브</p>
<ul>
<li>디스크 형태, 초당 60-250회 회전</li>
<li>디스크 암이 가리키는 곳으로 데이터를 접근한다.</li>
<li>데이터를 전송하는 속도 <strong>Transfer rate</strong>를 가진다. 이는 드라이브와 컴퓨터 간의 데이타 흐름 속도를 나타낸다.</li>
<li><strong>Positioning Time</strong> 즉, 위치를 지정하는 시간은 아래 두 시간을 합친 것이다.<ul>
<li><strong>seek time:</strong> 디스크 암이 움직이며(디스크 안/바깥 수평 이동) 원하는 sector가 위치한 track을 찾는 것</li>
<li><strong>Rotational latency:</strong> 디스크 자체가 회전하며 (트랙 회전) 원하는 sector를 찾는 것 ****<ul>
<li>latency = 1/(RPM/60) = 60/RPM</li>
<li>평균 latency = 1/2latency (운이 좋으면 바로 찾고, 운이 안좋으면 한바퀴 돌아서)</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>Positioning Time은 데이터 접근 속도에 큰 영향, HDD 성능 평가 지표<img src="https://velog.velcdn.com/images/jungizz_/post/9ba0a7c7-7e64-4088-91f9-5935798f83c8/image.png" alt=""></p>
<h3 id="average-io-time">Average I/O time</h3>
<p>= average accress time + (amout to transfer/transfer rate) + controller overhead</p>
<ul>
<li>첫째 항: 물리적인 지연 시간, average seek time + average latency</li>
<li>둘째 항: 데이터 이동 시간 (전송 데이터 크기/전송 속도)</li>
<li>셋째 항: CPU(controller) 계산 시간</li>
</ul>
<blockquote>
<h4 id="example-4kb블록을-7200rpm-디스크에서-전송하는-경우">example: 4KB블록을 7200RPM 디스크에서 전송하는 경우</h4>
<ul>
<li>전송 데이터 크기: 4KB블록</li>
<li>7200RPM</li>
<li>average seek time: 5ms</li>
<li>transfer rate: 1GB/s</li>
<li>controller overhead = 0.1ms</li>
</ul>
<p>가 주어졌을 때,</p>
<ul>
<li>average latency: 4.17ms = (60s/7200)/2</li>
<li>transfer time = 0.031ms<img src="https://velog.velcdn.com/images/jungizz_/post/0c8dedfa-b1be-4ec3-a827-70fcc6ae4f1e/image.png" alt=""></li>
</ul>
</blockquote>
<p>를 계산할 수 있다. 결론적으로</p>
<blockquote>
<p><strong>Average I/O time =  9.17ms + 0.031ms + 0.1ms = 9.031ms</strong></p>
</blockquote>
</br>

<h2 id="2-nvm">2. NVM</h2>
<p>비휘발성 메모리(Nonvolatile Memory), SSD(solid-stae disks)</p>
<ul>
<li>전윈이 꺼져도 내용 유지</li>
<li>HDD보다 용량이 적고, 전력이 덜 들고, 속도가 빠름, 단지 더 비쌈<ul>
<li>HDD의 캐시 용도로 자주 사용</li>
</ul>
</li>
<li>읽기/쓰기 시간이 다르다.<ul>
<li>쓰기 시 기존 데이터를 덮어 쓸 수 없으므로 지운 뒤 써야하기 때문</li>
<li>그리고 지우는 횟수가 제한되어 있음, 만약 하루에 5번 write해서 1년 쓸 수 있다면 5DWPD라고 함 (드라이브 수명)</li>
</ul>
</li>
</ul>
<h3 id="nand-flash-controller-algorithms">NAND Flash controller algorithms</h3>
<ul>
<li>비휘발성 메모리 장치는 블록들로 관리를 하는데, 각 블록들은 여러 페이지로 구성되어있다.</li>
<li>각 페이지는 유효한지/유효하지 않은 데이터를 가지고 있다.<ul>
<li>유효한 데이터 → 사용 중</li>
<li>뮤효한 데이터 → 사용 완료, 이 공간을 쓰려면 기존 데이터 지워야 함</li>
</ul>
</li>
<li>근데 지우려면 페이지 단위로 지울 수 없고, 블록 단위로 지워야한다.</li>
<li>그래서 유효한 데이터를 다른 블록으로 copy해서 몰아버린다음에 지운다. 이것이 <strong>Garbage Collection</strong><ul>
<li>Garbage collection을 위한 공간을 제공하기 위해 overprovisioning(실제 제공 용량보다 큰 용량 예약)을 할당<img src="https://velog.velcdn.com/images/jungizz_/post/e2c6dde5-979a-4a9d-87f6-8aff89b85268/image.png" alt=""></br></li>
</ul>
</li>
</ul>
<h2 id="volatile-memory">Volatile Memory</h2>
<p>휘발성 메모리, 주기억장치</p>
<ul>
<li>RAM을 사용하여 가상의 디스크 드라이브 생성</li>
<li>속도가 아주 빠른 임시 저장 공간으로 사용</li>
</ul>
</br>

<h2 id="disk">Disk</h2>
<p><strong>Attachment</strong></p>
<ul>
<li>CPU에 연결된 메인 버스가 아닌 디스크 전용 I/O 버스를 통해 디스크에 접근</li>
</ul>
<p><strong>Address Mapping</strong></p>
<ul>
<li>디스크 드라이브는 큰 1차원 배열의 논리블록으로 주소 지정, 연속적인 공간으로 보임</li>
<li>실제 물리주소는 동그란 disk에 존재 (몇번 트랙의 몇번 섹터인지)</li>
</ul>
</br>

<h1 id="🔹scheduling">🔹Scheduling</h1>
<h2 id="1-hdd-scheduling">1. HDD Scheduling</h2>
<p>OS는 여러 프로세스가 하드디스크에 접근을 요청했을 때, 어떤 프로세스를 먼저 받아줄지 정해야한다. 이 때 주안점은 <strong>seek time 줄이기⭐</strong></p>
<p>스케줄링을 통해 디스크 암이 움직이는 경로?를 조정하게 되는 것이라 이걸 효율적으로 움직이게 하는게 중요</p>
<p>프로세스가 하드디스크로의 I/O요청을 보내고, I/O 디바이스 드라이브가 해당 요청 트랙이 디스크의 어디에 있는지를 알아낸다.</p>
<p><strong>1. FCFS</strong></p>
<ul>
<li>요청이 도착한 순서대로 처리</li>
<li>비효율적인 디스크암 이동, 이동 거리 길어짐</li>
</ul>
<p><strong>2. SCAN</strong></p>
<ul>
<li>디스크암이 디스크의 한쪽 끝에서 다른 쪽 끝으로 이동하며 있는 요청 처리<ul>
<li>안쪽→바깥쪽→안쪽→…</li>
</ul>
</li>
<li>엘레베이터 알고리즘이라고도 함</li>
<li>FCFS보다 이동 거리가 짧다. 하지만 요청이 불균형하거나, 엘베 놓친 것처럼 암이 금방 지나간 곳에 요청이 뒤늦게 발생하면 대기 시간 너무 길어짐 → C-SCAN</li>
</ul>
<p><strong>C-SCAN</strong></p>
<ul>
<li>한 방향으로만 스캔, 한쪽 끝에 도달하면 요청을 처리하지 않고 즉시 반대편으로 돌아감</li>
</ul>
<p><strong>3. SSTF</strong></p>
<ul>
<li>Shortest Seek Time first, 가장 가까운 요청 먼저</li>
<li>일반적으로 사용되는 알고리즘</li>
</ul>
<p><strong>+) Deadline</strong>
하지만 여전히 어떤 알고리즘을 쓰더라도 starvation 문제 발생 (너무 오래 기다림)
-&gt; 그래서 각 요청의 <strong>deadline</strong>을 지정해서 설정한 시간 안에는 처리되도록 하는 방법을 추가하기도 함</p>
</br>

<h2 id="2-nvm-scheduling">2. NVM Scheduling</h2>
<p>물리적 이동 시간(디스크 암, 회전 대기 시간)을 고려할 필요가 없어서 그냥 <strong>FCFS</strong>로 스케줄링 </p>
<p>그래도 최적화를 할 수 있는 여지 존재</p>
<ul>
<li>garbage collection</li>
<li>근처 요청 한꺼번에 처리</li>
</ul>
</br>

<h1 id="🔹error-detection-and-correction">🔹Error detection and Correction</h1>
<h4 id="메모리-디스크-등의-오류-감지">메모리, 디스크 등의 오류 감지</h4>
<ul>
<li>패리티 비트</li>
<li>해시 함수</li>
</ul>
<h4 id="오류-수정">오류 수정</h4>
<ul>
<li>추가적인 오류 수정 코드(ECC)</li>
<li>하지만 ECC가 더 크면 좀 그렇겟지</li>
</ul>
</br>

<h1 id="🔹storage-device-management">🔹Storage Device Management</h1>
<p>스토리지가 데이터 저장을 효율적으로 하기 위한 프로세스</p>
<ul>
<li>Formatting: 트랙과 섹터를 다시 설정하는 것<ul>
<li>디스크를 섹터로 나눠 각각 채우는 것은 low-level(physical) formating</li>
<li>물리적 디스크를 <strong>쪼개서 한 파티션을 logical disk처럼 사용</strong>하는 것은 <strong>logical formating</strong><ul>
<li>각 파티션은 한 파일 시스템일 수 있고, 별도의 장치 처럼 취급할 수 있고, ..</li>
</ul>
</li>
</ul>
</li>
<li>Root Partition: OS를 포함하는 파티션, 부팅할 때 가장 먼저 마운트<ul>
<li>OS 로딩을 위한 부트스트랩 로더 프로그램이 부트블록 파티션에 저장이 되어있는 것이다. 즉, 부팅 관련도 하드디스크에 저장되어있다.</li>
<li>마운트 할 때 파일 시스템 초기화</li>
</ul>
</li>
</ul>
<p>물리적 디스크는 아래와 같이 파티셔닝되고, MBR에는 boot code와 파티션 정보를 담은 partition table이 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/1c014218-706a-48af-b7fd-7a3dfe78ec2c/image.png" alt=""></br></p>
<h1 id="🔹swap-space-management">🔹Swap-Space Management</h1>
<p>메인 메모리가 모든 프로세스를 수용할 수 없을 때, 더 많은 공간을 필요로 하는 경우 swap</p>
<ul>
<li>swap 공간도 쪼개서 사용</li>
<li>swap map은 해당 슬롯을 사용 중인 프로세스 개수를 나타냄<img src="https://velog.velcdn.com/images/jungizz_/post/a2c7adc8-0eed-41a3-a0c6-cdb5be0650cb/image.png" alt=""></br></li>
</ul>
<h1 id="🔹storage-attachment">🔹Storage Attachment</h1>
<p>CPU가 저장 장치에 연결하는 방법</p>
<ol>
<li><p><strong>Direct Attached Storage, DAS</strong></p>
<ul>
<li>Host-attached(호스트 연결), 로컬 연결</li>
<li>메인 버스를 통해 디스크 컨트롤러에 직접 접근</li>
<li>Fibre channel과 같은 고속 아키텍처로 빠르게 전달 가능</li>
</ul>
</li>
<li><p><strong>Network-Attached Storage, NAS</strong></p>
<ul>
<li>네트워크를 통해 저장소 접근 (네트워크를 통해 저장소 제공)<img src="https://velog.velcdn.com/images/jungizz_/post/0d13fbcd-0aec-4d69-8c55-874406452fee/image.png" alt=""></li>
</ul>
</li>
<li><p><strong>Cloud storage</strong></p>
<ul>
<li>클라우드를 통해 저장소 접근 (클라우드가 저장소 제공)</li>
<li>NAS는 파일 시스템으로 제공되는 반면, 클라우드 저장소는 API를 사용하여 접근</li>
<li>Dropbox, Amazon, Onedrive, iCloud …</li>
</ul>
</li>
</ol>
<p><strong>Storage Array (RAID)</strong></p>
<ul>
<li>디스크 여러개를 연결하여 배열로 이어 붙임</li>
<li>여러개의 디스크를 동시에 사용 가능, I/O병럴처리를 통한 속도 향상</li>
<li>중요 데이터를 여러 디스크에 복제 저장 가능 → 한 디스크가 고정나더라도 다른 디스크에서 데이터 계속 사용 가능</li>
<li>중복 제거 기능으로 저장 공간 효율적 사용</li>
<li>에러 처리 및 복구</li>
</ul>
<p><strong>Storage Area Network(SAN)</strong></p>
<ul>
<li>하나 이상의 storage array로 구성된 고속 데이터 전송을 위한 <strong>스토리지 전용 네트워크</strong></li>
<li>다수의 호스트가 여러 <strong>storage array</strong>에 연결</li>
<li>클라이언트는 SAN을 통해 위 storage array와 연결<ul>
<li>이때, 클라이언트는 서버 API 또는 데이터 프로세싱을 통해 SAN에 연결 (오류 발생 방지)</li>
</ul>
</li>
<li>그래서 SAN에는 고성능 네트워크가 필요<ul>
<li>Fiber channel switches 또는 InfiniBand등과 같은 고성능 네트워크로 연결<img src="https://velog.velcdn.com/images/jungizz_/post/24307d4f-6e66-4074-b641-561e8297ecee/image.png" alt=""> </br></li>
</ul>
</li>
</ul>
<h1 id="🔹raid-structure">🔹RAID Structure</h1>
<p>여러 개의 하드디스크 배열을 사용 (Redundant Array of Inexpensive(independent) Array)</p>
<ol>
<li>중복성(동시성)을 이용한 신뢰도 향상<ul>
<li>중요 데이터를 복제하여 한 디스크가 고정나더라도 데이터 손실 방지</li>
</ul>
</li>
<li>성능 향상<ul>
<li>여러 디스크의 병렬처리로 속도 향상</li>
<li>NVRAM과 결합하여 더 빠르게 가능</li>
</ul>
</li>
<li>에러 처리</li>
</ol>
<p><strong>RAID 레벨</strong></p>
<p>6개의 레벨로 구성되어 있으며, 각 레벨은 데이터의 저장 방식에 따라 다름</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>RAID 0</td>
<td>Striping</td>
<td>• 줄무늬처럼 데이터를 여러 디스크에 분산하여 저장 (한 디스크 꽉채우고 다음 디스크가 아니라, 디스크별로 하나씩 넣기) </br> • 이는 여러 디스크의 데이터를 동시에 읽을 수 있도록 해줌</td>
</tr>
<tr>
<td>RAID 1</td>
<td>Mirroring/Shadowing</td>
<td>각 디스크의 데이터를 완전 복사</td>
</tr>
<tr>
<td>RAID 1+0 / 0+1</td>
<td>Mirrored stripe / Striped mirror</td>
<td>복사하고 스트라이핑 = 복사해서 스트라이핑</td>
</tr>
<tr>
<td>RAID 4</td>
<td>Block-interleaved distributed parity</td>
<td>• 패리티 비트를 가진 디스크 하나 추가</br> • 병목 현상 발생 가능</td>
</tr>
<tr>
<td>RAID 5</td>
<td>Block-interleaved distributed parity</td>
<td>• 패리티를 각 디스크마다 분산해서 가지도록</br> • 병목 현상 보완</td>
</tr>
<tr>
<td>RAID 6</td>
<td>P+Q redundancy</td>
<td>패리티 뿐만 아니라, 에러 복구 코드도 추가</td>
</tr>
<tr>
<td>Multidimensional RAID 6</td>
<td></td>
<td>양방향, 2차원으로 RAID를 만들어 더욱 효율적으로</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/jungizz_/post/5a5d9ed7-50d2-4541-9eae-44767180e280/image.png" alt=""></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p><strong>추가 기능</strong></p>
<ul>
<li><p>스냅샷: 특정 시점의 파일 시스템 상태를 저장, 복구 가능하도록 함</p>
</li>
<li><p>복제: 데이터 자동 복사를 통해 복구 지원</p>
</li>
<li><p>ZFS: 파일 시스템 레벨 에러 처리</p>
<ul>
<li>RAID는 스토리지 레벨에서만 디스크 에러에 대한 처리 → 파일 시스템 레벨 에러 처리는 안함.</li>
</ul>
<p>아래 그림에서 기존 파일 시스템은 각 볼륨을 가지고, 볼륨은 파티셔닝 된 디스크들을 가진다. (볼륨은 논리적, 디스크는 물리적 개념) 그래서 공유 파일 시스템 간 경계가 뚜렷하고 저장소를 공유하지 않는다. 그리고 각 파일 시스템에 저장소의 불균형이 발생할 수 있다.</p>
<pre><code>  하지만 ZFS를 사용하면 storage pool로 저장소를 공유하고 필요한 만큼 저장소를 제공해준다.![](https://velog.velcdn.com/images/jungizz_/post/d53e3d2b-8698-41ab-a66b-dd76eb807972/image.png)</code></pre></li>
</ul>
<p><strong>Object storage</strong></p>
<p>용량이 큰 데이터를 스토리지에 1:1로 매핑해서 저장하는 것이 아닌, 오브젝트를 사용해서 저장 (오브젝트 단위로 저장)</p>
<ul>
<li>오브젝트는 논리적인 저장 공간으로, 여러 노드에 걸쳐있으며, 복사 기능을 가진다.<ul>
<li>N개의 시스템에 N개의 복사본 저장</li>
</ul>
</li>
<li>오브젝트 저장소 관리 소프트웨어: Hadoop file system(HDFS), Ceph</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 10. Virtual Memory]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-10.-Virtual-Memory</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-10.-Virtual-Memory</guid>
            <pubDate>Sat, 26 Apr 2025 09:11:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡10장-목표">💡10장 목표</h4>
</blockquote>
<ul>
<li>Demand paging</li>
<li>Page replacement<ul>
<li>FIFO, optimal, LRU</li>
</ul>
</li>
<li>Working set of process</li>
<li>OS별 Virtual memory</li>
</ul>
<p>physical memory 공간(메인 메모리 공간)은 시스템 별로 존재
virtual memory 공간(논리 메모리 공간)은 프로세스마다 존재</p>
<p>→ 즉, 물리 메모리 공간은 부족하다. 그래서 각 프로세스마다 가상 메모리 공간을 가지고, 필요할 때만 물리 메모리 공간을 사용한다.</p>
<p><strong>Partially-loaded program</strong></p>
<p>프로그램이 실행되기 위해서는 메모리에 코드가 로드되어야하는데, 전체 프로그램이 동시에 사용되는 경우가 드물다. 왜냐하면 프로그램 내에서 항상 필요하지 않은 코드들이 존재하기 때문이다.</p>
<ul>
<li>프로그램은 일부만 로딩해도 수행할 수 있다(locality).<ul>
<li>메모리 절약 가능</li>
<li>CPU 사용률 증가</li>
<li>I/O감소(프로그램을 메모리에 로드하거나 스와핑하는데 필요한 I/O 줄어듦)</li>
</ul>
</li>
</ul>
<br>

<h1 id="virtual-memory">Virtual Memory</h1>
<ul>
<li>프로세스마다 존재하는 가상 메모리 공간<ul>
<li>이걸 한 개의 물리 메모리 공간에 매핑하는 것</li>
</ul>
</li>
<li>프로그램의 일부만 메모리에 로드할 수 있게 해줌</li>
<li>물리 메모리 공간보다 훨씬 더 큼</li>
<li>여러 프로세스 간 주소 공간 공유 가능 (아래 설명)</li>
<li>프로세스 생성 쉬움</li>
<li>더 많은 프로그램 동시 실행 가능</li>
<li>프로세스 로드 및 스와핑이 줄어들어 해당 I/O 감소</li>
</ul>
<p><strong>추가적인 메모리가 필요할 때 virtual memory 구현 방법</strong></p>
<ul>
<li><strong>Demand Paging</strong>: 필요한 페이지만 메모리에 로드 (아래 설명)</li>
<li>Demand Segmentation: 필요한 세그먼트만 로드<img src="https://velog.velcdn.com/images/jungizz_/post/b5256f7f-7abf-4d70-8c40-c22d6b23e625/image.png" alt=""></li>
</ul>
<p><strong>Virtual address space</strong></p>
<ul>
<li>프로세스가 메모리에 저장되는 logical view (논리 주소, 상대 주소)</li>
<li>프로세스의 입장에서는 연속적인 공간을 가진다고 생각한다.<img src="https://velog.velcdn.com/images/jungizz_/post/f9ded2bf-0497-428f-a80a-7e775f216baf/image.png" alt=""><br></li>
</ul>
<h3 id="shared-library">Shared Library</h3>
<p>virtual memory를 사용하여 프로세스 간 주소 공간을 공유</p>
<ul>
<li>여러 프로세스가 같은 라이브러리를 사용할 때, 가상 메모리는 공유할 수 있는 영역을 만들 수 있음</li>
<li>이 영역을 공유하는 프로세스들은 각자 자신의 논리 주소 상에 있는 것처럼 보이지만, 실제 물리 메모리에서는 같은 주소인 것이다. (같은 주소로 매핑)<img src="https://velog.velcdn.com/images/jungizz_/post/de07c4f6-779b-41a8-915c-8aa554a737cc/image.png" alt=""><br></li>
</ul>
<h1 id="🔹demand-paging">🔹Demand Paging</h1>
<ul>
<li>프로그램 B의 페이지가 물리 메모리에 없다면, 해당 페이지를 swap in</li>
<li>swap in을 하기 위한 공간을 마련하기 위해서 프로그램 A는 swap out<img src="https://velog.velcdn.com/images/jungizz_/post/3a434ef1-20df-4e81-9f8d-e200eac04625/image.png" alt=""></li>
</ul>
<p><strong>Page table의 Valid-Invalid Bit</strong></p>
<p>demanding paging을 위해 MMU는 페이지가 유효한지 무효한지를 확인해야한다. 그래서 page table에 매핑될 frame 번호 뿐만 아니라, valid-Invalid bit도 가진다.</p>
<ul>
<li>Valid: 페이지가 이미 메모리에 있는 경우 (memory resident)</li>
<li>Invalid: 페이지가 필요한데 메모리에 없는 경우 (<strong>Page fault</strong>) → 해당 페이지를 저장소에서 메모리로 로드(swap in)해야됨<ul>
<li>또는, 자신의 영역이 아닌 페이지에 접근하는 경우 → 종료(abort)<img src="https://velog.velcdn.com/images/jungizz_/post/244a38ae-473b-489e-8b4d-3e50a378d968/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/19913b45-a697-4c76-a030-995337f74cdd/image.png" alt="">    </li>
</ul>
</li>
</ul>
<p><strong>Handling Page fault</strong></p>
<ol>
<li>Invalid하면 운영체제에 trap (일종의 Interrupt으로 page fault 발생시킴)</li>
<li>운영체제는 <strong>Context Save</strong>를 하고,
잘못된 영역으로 인한 Invalid면 종료
메모리에 없어 발생한 Invalid면 아래 과정 진행</li>
<li>물리 메모리 공간의 free frame을 찾기 (OS가 관리 중인 free frame List)</li>
<li>저장소에서 해당 페이지를 free frame으로 swap in</li>
<li>테이블 업데이트 (유효비트를 v로)</li>
<li><strong>Context Resume</strong><img src="https://velog.velcdn.com/images/jungizz_/post/458e8f7c-e280-4a1b-b151-3626ce7b7474/image.png" alt=""></li>
</ol>
<p>그래서.. Page demanding은 메모리를 효율적으로 사용할 수 있지만 fault가 발생하면 꽤 큰 딜레이가 발생한다. page fault를 줄이는게 핵심</p>
<ul>
<li>하지만 Locality 경향에 의해 page fault가 발생할 경우가 많지 않다. 웬만하면 hit (hit ratio가 높다)<ul>
<li>Locality: 사용한 부분을 집중적으로 사용하는 경향(재사용), 특정 부분에 오래 있는 경향</li>
</ul>
</li>
</ul>
<p>만약 한 Instruction이 여러 page에 걸쳐있다면? → 물리 메모리 상에서 다른 위치에 존재</p>
<ul>
<li>여러 페이지 모두 로딩이 필요하다.</li>
<li>여러 페이지의 page fault가 발생할 수 있다.</li>
</ul>
<p><strong>Worse Case</strong>
만약 빈 공간이 없다면? … 쫓아내고(swap out), 가져오는(swap in) 과정에서 disk I/O가 2번… → demand paging에서 이런 최악의 경우는 아래와 같다. </p>
<ol>
<li><p>한 프로세스에서 page fault 발생, OS trap</p>
</li>
<li><p>해당 프로세스 Context save</p>
</li>
<li><p>interrupt가 page fault임을 확인</p>
</li>
<li><p>disk의 페이지 유효성 확인 및 위치 결정</p>
</li>
<li><p>free frame 요청</p>
<ul>
<li>디스크 장치에 disk I/O 요청, 큐 기다림</li>
<li>디스크 장치가 free frame을 탐색하는 것 기다림</li>
<li>페이지를 free frame으로 전송</li>
<li>그리고 대기하는 동안 CPU가 놀지 않도록 다른 프로세스에게 할당해주려고 기다림*</li>
</ul>
</li>
<li><p>disk I/O 작업이 완료되면 interrupt 받음</p>
</li>
<li><p><em>아까 놀지 않도록 다른 사용자에게 할당해줬다면 그것 또한 Context Save</em></p>
</li>
<li><p>interrupt가 disk I/O 완료임을 확인</p>
</li>
<li><p>페이지 테이블 수정</p>
</li>
<li><p>다시 이 프로세스가 CPU할당 받기를 기다림</p>
</li>
<li><p>할당 받으면 Context resume</p>
</li>
</ol>
<p>이처럼 page fault는 메모리, I/O, CPU scheduling 등 여러가지를 발생시킨다.</p>
<p><strong>Performance</strong></p>
<ul>
<li>Page fault rate 0≤p≤1<ul>
<li>p=0 → page fault 없음</li>
<li>p=1 → 항상 page fault</li>
<li>보통 p=0.1, 0.01 정도</li>
</ul>
</li>
<li>EAT = (1-p) x memorry access + p x (page fault over head + swap page out + swap page in)<ul>
<li>앞에 항은 fault 없을 때, 뒤에 항은 fault 있을 때</li>
<li>뒤에 항에서는 page fault interrupt와 table update 등의 시간과 빈 공간이 없는 경우의 swap out/in을 포함</li>
</ul>
</li>
<li>example<ul>
<li>메모리 접근 시간: 200ns, 평균 page fault 서비스 시간: 8ms</li>
<li>ETA = (1-p)<em>200 + p</em>8000000</li>
</ul>
</li>
</ul>
<p><strong>Optimization</strong></p>
<ul>
<li>Large chunks: swap 공간이 page단위로 흩어져 있으면 관리가 힘드므로, 모여있는 넓은 공간 제공</li>
<li>페이지를 디스크의 프로그램 binary에서 요구<ul>
<li>binary는 read only instruction으로, 프로그램 코드가 실행 중에 변경되지 않는다.</li>
<li>read only는 swap공간에 저장되지 않는다. swaping의 대상이 아니다.</li>
<li>필요한 페이지가 메모리에 없으면 swap공간이 아닌 디스크에서 excutable 파일을 직접 가져옴</li>
</ul>
</li>
</ul>
<p>모바일 시스템은 swap 지원X, 부족하면 종료, 애플리케이션의 진행 상태의 일부 저장
<br></p>
<h1 id="🔹copy-on-write">🔹Copy-on-Write</h1>
<p>부모 프로세스가 fork하여 자식 프로세스를 만들 때, copy가 일어난다. 하지만 copy는 느리다!</p>
<ul>
<li>copy하기 전에 먼저 공유하고, 두 프로세스가 달라지는 부분이 생기면(write operation이 생기면) 그 때 해당 부분만 copy한다.</li>
<li>공유해서 자식 만드는게 <code>vfork()</code></li>
<li>이를 통해 copy delay 시간을 줄인다.<img src="https://velog.velcdn.com/images/jungizz_/post/90640d97-cf2f-458f-948a-e873d09d76c0/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/4d29c536-87ef-4864-81a0-8b47bbdeffc1/image.png" alt=""><br></li>
</ul>
<h1 id="🔹page-replacement">🔹Page Replacement</h1>
<p>메모리 공간에 빈 공간이 없을 때 페이지 단위로 교체(swapping)하는 방안</p>
<ul>
<li>한 프로세스에게 너무 많은 page를 할당(over-allocation)하지 않도록 해야된다</li>
<li><strong>Modify(dirty) bit:</strong> 메모리에 있는 프레임이 수정됐는지 안됐는지를 나타내는 비트<ul>
<li>만약 수정되지 않았다면, swap out할 필요 없이 새로운 페이지를 해당 프레임에 swap in해서 덮어씌우면 됨</li>
<li>수정되었다면, swap out해서 저장해둔 뒤 새로운 페이지를 해당 프레임 자리에 swap in</li>
<li>text는 수정되지 않고, 메모리나 stack 등은 수정된다.</li>
<li>이 비트는 demand paging 뿐만 아니라 캐시 과정에서도 사용</li>
</ul>
</li>
</ul>
<p><strong>Page Replacement 과정</strong></p>
<ol>
<li>디스크에서 페이지 위치 찾기</li>
<li>free frame 찾기<ul>
<li>있으면 그대로 진행</li>
<li>없으면 <strong>victim frame 선정</strong>(알고리즘을 통해) 및 dirty bit 확인</li>
</ul>
</li>
<li>replacement하고 페이지 및 pagetable 업데이트<img src="https://velog.velcdn.com/images/jungizz_/post/9435b5f8-e533-43e4-b3fc-c606c2d18d78/image.png" alt=""></li>
</ol>
<p><strong>Algorithm</strong></p>
<ul>
<li>프로세스에 프레임을 얼마나 할당할건지 → Frame-allocation algorithm</li>
<li>victim page 누구로 할건지 → <strong>Page-replacement algorithm</strong> (아래 설명)</li>
</ul>
<p>일반적으로 프로세스에 프레임을 많이 할당할수록 page fault의 발생 비율이 낮아진다. algorithm의 성능은 <strong>Page fault가 얼마나 적은지</strong>로 나타낸다.<img src="https://velog.velcdn.com/images/jungizz_/post/43b3bd07-4eab-4df5-83eb-67c526deaebb/image.png" alt=""></p>
<p>아래는 page-replacement alhorithm의 세가지 종류에 대한 설명이다.</p>
<h3 id="1-fifo">1. FIFO</h3>
<ul>
<li>먼저 frame에 들어온 페이지가 victim
  ex) 프로세스마다 3개의 프레임, 아래와 같은 순서로 페이지가 사용될 때<img src="https://velog.velcdn.com/images/jungizz_/post/d0ee038c-05a7-4ea5-a370-7917e3e5d7b8/image.png" alt=""> 15번의 page faults (12번의 replacement) → 성능 별로 안좋음  <br></li>
<li>가끔 frame 수가 늘어났는데 page fault가 늘어나는 시나리오가 발생하기도 한다. → <strong>Belady’s Anomaly</strong><img src="https://velog.velcdn.com/images/jungizz_/post/db5103f5-30c7-4d12-8f54-6d9f98202ffa/image.png" alt=""><br></li>
</ul>
<h3 id="2-optimal-algorithm">2. Optimal Algorithm</h3>
<ul>
<li><p>가장 오래 <strong>사용되지 않을</strong> 페이지를 victim으로</p>
</li>
<li><p>page fault를 가장 최소화하는 알고리즘(이상적, 최적)<img src="https://velog.velcdn.com/images/jungizz_/post/96a277f0-daf2-4bca-bf93-e81d58944c2b/image.png" alt="">9번의 page fault</p>
<br></li>
<li><p>하지만 미리 예측이 어렵다는 단점</p>
<br>
### 3. LRU(Least Recently Used) Algorithm
</li>
<li><p>가장 오랫동안 사용되지 않은 페이지를 victim으로 (과거를 보고 미래 예측)<img src="https://velog.velcdn.com/images/jungizz_/post/e426e2b6-dca5-4547-91d1-76f4b3e11fbb/image.png" alt="">12번의 page fault (Optimal보단 안좋지만, FIFO보단 좋음)</p>
</li>
</ul>
<p><strong>Implementation</strong></p>
<ol>
<li>Counter: page 사용할 때마다 카운터 증가 </li>
<li>Time stamp: page 사용할때마다 time 기록</li>
</ol>
<p>위의 두 방법은 page table에 저장되는데, overhead 발생</p>
<p>3.Stack: 사용한 순서대로 stack에 push, 아래에 있을 수록 사용한지 가장 오래됨 → victim<img src="https://velog.velcdn.com/images/jungizz_/post/d5529983-414a-4e05-8eec-f49dd412c54a/image.png" alt="">하지만 stack도 1, 2번보단 덜하지만 overhead..
<br></p>
<h3 id="4-lru-approximation-algorithms">4. LRU Approximation Algorithms</h3>
<ul>
<li>page 사용을 나타내는 <strong>reference bit</strong>로 LRU를 짐작<ul>
<li>reference bit: 해당 페이지 사용 했으면 1, 아니면 0 (page table에 기록)</li>
</ul>
</li>
</ul>
<p><strong>Second-chance(Clock) algorithm</strong> 적용</p>
<ul>
<li>page순서대로 circular queue를 돌아다니는 포인터(시계의 침)이 존재</li>
<li>포인터가 가리키는 page의 reference bit가<ul>
<li>0이면 → victim으로 설정</li>
<li>1이면 → 0으로 바꾸고 기회를 한 번 더준다<img src="https://velog.velcdn.com/images/jungizz_/post/bdac9177-8bdf-49d0-84ab-8ec4d687c237/image.png" alt="">    </li>
</ul>
</li>
<li>cycle이 돌아오는 도중에 1로 다시 바뀌었다는 것은 자주 사용되는 거고, 0은 cycle이 돌아오는 동안 사용이 여전히 안됐다는걸 나타내므로</li>
</ul>
<p><strong>Enhanced Second-Chance Algorithm</strong></p>
<ul>
<li>reference bit 뿐만 아니라 modify bit도 같이 고려한다.<ul>
<li>modify된 것보다 안된걸 쓰는게 더 나으니까</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>(reference, modify)</th>
<th>victim</th>
</tr>
</thead>
<tbody><tr>
<td>(0, 0)</td>
<td>가장 좋음</td>
</tr>
<tr>
<td>(0, 1)</td>
<td>나쁘지 않음. page out 필수</td>
</tr>
<tr>
<td>(1, 0)</td>
<td>아마 금방 다시 사용될 페이지, 적합하지 않음</td>
</tr>
<tr>
<td>(1, 1)</td>
<td>적합하지도 않고 page out도 해줘야해서 최악</td>
</tr>
<tr>
<td><br></td>
<td></td>
</tr>
</tbody></table>
<h3 id="-counting-algorithm">* Counting Algorithm</h3>
<p>페이지 접근 횟수를 카운트해서 victim 정할 때 두 가지 기준이 있다.</p>
<ul>
<li>LFU(Lease Frequently Used): 사용 빈도 적은 것이 victim (적으니까 계속 사용 안될거다)</li>
<li>MFU(Most Frequently Used): 사용 빈도 큰 것이 victim (많이 사용했으니까 이제 사용 안될거다)<br>
### * Page-Buffering Algorithms

</li>
</ul>
<p>OS는 freee frame을 항상 유지하여 page fault가 발생했을 때 지연이 발생하지 않도록 한다.</p>
<ul>
<li>free frame을 찾는게 아닌, 미리 준비된 free frame을 사용할 수 있도록 victim들 설정</li>
<li>dirty(수정된) 페이지도 free frame pool에 들어갈 수 있고, 수정된걸 유지할 수 있다</li>
</ul>
<p><strong>* Application</strong>
일반적으로 OS가 page replacement를 관리하지만, 특정 Application은 일반적인 page replacement가 아닌 자신에게 잘 맞는 특정 알고리즘을 사용할 수 있다. (애플리케이션 개발자가 설정 가능)</p>
<ul>
<li>Raw disk mode: 하위 계층(디스크)에 계층적이 아닌 직접 접근을 할 수 있음</li>
</ul>
<p>이런거 할 수 있다~
<br></p>
<h1 id="🔹allocation-of-frames">🔹Allocation of Frames</h1>
<p>프로세스에게 프레임 할당을 어떻게 할까</p>
<ul>
<li>각 프로세스는 최소/최대 프레임 수를 가진다.</li>
</ul>
<p>프레임 할당 개수는</p>
<ul>
<li>Equal allocation: 모든 프로세스에게 같은 개수의 frame 할당 (1/n)</li>
<li>Proportional allocation: 프로세스 크기에 따라 frame 할당 (ex.프로세스 크기의 2배)</li>
</ul>
<p>page fault로 replacement가 필요할 때, </p>
<ul>
<li>Global replacement: 전체 프로세스를 다 따져 victim 정함</li>
<li>Local replacement: 자신 프로세스 내 영역에서의 frame 중 victim 정함</li>
</ul>
<h3 id="reclaiming-pages"><strong>Reclaiming pages</strong></h3>
<p>페이지 회수: global replacement를 구현하기 위한 방법으로, free frame을 확보하는 과정이다.</p>
<ul>
<li>OS는 일정 수의 free frame을 유지해야한다고 했다.</li>
<li>free frame의 최대/최소 개수 (threadhold)가 존재한다.<ul>
<li>최대보다 많아지면 free frame을 확보하려고 노력할 필요가 없음, replacement가 진행되며 free frame수가 줄어든다</li>
<li>최소보다 적어지면 커널은 free frame을 확보하려고 노력한다.<img src="https://velog.velcdn.com/images/jungizz_/post/aa1fefa2-8917-4af8-946d-7b4352147628/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="with-numa">with NUMA</h3>
<p>CPU에 가까운 메모리를 할당하는 것이 좋다. 가까워야 delay가 적고 성능 향상<img src="https://velog.velcdn.com/images/jungizz_/post/bba4ba0c-c21f-4288-8bd0-cd5ba8b44984/image.png" alt=""></p>
<ul>
<li>Solaris는 Igroups를 생성해서 NUMA문제를 해결했다.</li>
</ul>
<br>

<h1 id="🔹thrashing">🔹Thrashing</h1>
<p>프로세스에 할당된 page 수가 적어서 page fault가 너무 많이 발생하는 현상, page fault를 처리하기 위해 swapping하는 시간이 더 오래걸리는 경우
→ CPU 사용률 저하</p>
<p>프로세스의 수가 많아지면(멀티프로그래밍 증가) CPU 사용률은 증가하지만, 너무 많아지면 각 프로세스에 할당되는 page 수가 줄어든다. 어느정도를 넘으면 page 수가 부족해 page fault가 발생하여 CPU 사용률이 저하되는 것이다. (CPU가 놀고있음)<img src="https://velog.velcdn.com/images/jungizz_/post/94c65ee9-b95d-4361-9e30-40626b2306d3/image.png" alt=""></p>
<p><strong>with Demanding page, Locality model</strong>
Locality Model에 따라, 프로세스의 loaclity size(=그 영역의 페이지 수)가 존재한다. 프로세스마다 적당한 locality size를 가진다.</p>
<ul>
<li><strong>locality size가 총 메모리 크기보다 크면 (가진 것보다 많이 필요하면) Thrashing</strong>이 발생 (locality size&gt;total memory size)<blockquote>
<p>아래 그림의 가로는 시간, 세로는 전체 프로세스의 page를 나타낸다. 시간별로 locality한 영역의 페이지들이 사용되는 모습이 보인다. 메모리에 locality 영역만 로딩하여 적은 공간의 frame들로만 수행할 수 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/9fc94be5-63a7-4b54-b183-019ef1a8bcf4/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<br>

<h3 id="working-set-model-wss">Working-set model (WSS)</h3>
<p>특정 프로세스에서 최근에 사용한 page 집합을 의미한다.<img src="https://velog.velcdn.com/images/jungizz_/post/526f1a40-5f84-438a-ad6b-eb5494f002e0/image.png" alt=""></p>
<p>모든 프로세스의 working set을 다 더하면, 현재 시스템에 필요한 총 frame 수를 알 수 있다.</p>
<ul>
<li>만약 프로세스가 요구하는 page양이 시스템이 가진 물리메모리 공간보다 크면 (가진 것보다 더 많이 요구하면) Thrashing이 발생</li>
<li>그래서 working set을 메모리에 다 올려두는 것이 중요하고(올라가지 못하면 thrashing), 지속적으로 track해서 working set을 알아낸다.</li>
</ul>
<p><strong>Page fault frequency</strong>
아래 그림은 한 프로세스에게 할당되는 프레임 수가 많아짐에 따라 page fault가 줄어드는 것이다.</p>
<ul>
<li>프레임 수가 적으면 page fault가 너무 많아 부적절</li>
<li>프레임 수가 많으면 page fault 큰 차이도 없고, 한 프로세스에만 페이지가 몰빵된거라 멀티 프로그래밍 어려움</li>
</ul>
<p>→ 그래서 적당히 프레임 수를 조절해가며 적당하게 유지해야한다.<img src="https://velog.velcdn.com/images/jungizz_/post/f1568c92-a09d-4063-af27-e15e6e240d6c/image.png" alt=""></p>
<p><strong>Page fault rate</strong>
시간에 따라 working set 변화 → 새로운 working set에 들어가면 메모리에 해당 페이지들이 없으니 page fault 발생 → local하게 계속 있으면 fault 줄어듦 → 시간이 지나 working set 또 변화 → …</p>
<p>page fault가 시간에 따라 일시적으로 발생<img src="https://velog.velcdn.com/images/jungizz_/post/36ccf593-5de9-4bfc-8543-19699dea3379/image.png" alt=""><br></p>
<h3 id="memory-compression">Memory Compression</h3>
<p>페이징의 대안, 메모리 압축</p>
<ul>
<li>수정된 프레임을 page out하지 않고, 여러 프레임을 하나의 프레임을 압축</li>
<li>이렇게 해서 시스템이 스와핑에 의존하지 않고 메모리 사용량 줄임(적은 오버헤드)</li>
</ul>
<p>아래 그림은 15, 3, 35 3개의 프레임을 7이라는 하나의 프레임으로 압축<img src="https://velog.velcdn.com/images/jungizz_/post/8fe3cac8-d94c-4d1d-9bbe-f3f53b64cbdb/image.png" alt=""><br></p>
<h1 id="🔹allocating-kernel-memory">🔹Allocating Kernel Memory</h1>
<p>커널에게 메모리를 할당해주기</p>
<ul>
<li>커널은 우선순위가 높고, 관리 데이터 크기도 크다 → 요구 page 크기가 크다</li>
<li>그래서 커널에는 작은 page 여러개 할당해주는 것 보다, <strong>연속적인 큰 page 하나를 할당</strong>해주는 것이 더 효율적이다. (할당 방법 여러개, 아래 설명)<br>
### **1. Buddy System Allocator**
물리적 연속 페이지를 쪼개가며 메모리를 할당</li>
<li>만약 커널이 32KB를 요청했고, 256KB의 청크가 있을 때
→ 32KB를 만족시키는 크기까지 2^n단위로 쪼개고 그 페이지를 할당해줌
→ 그 페이지가 해제되면 다시 합쳐지기도 함<img src="https://velog.velcdn.com/images/jungizz_/post/c2273a02-8226-4ff6-b68d-076620d3198f/image.png" alt=""><br></li>
</ul>
<h3 id="2-slab-allocator">2. Slab Allocator</h3>
<p>물리적 연속 페이지로 구성된 slab을 사용하여 메모리를 할당</p>
<ul>
<li>연속된 페이지가 모여 slab → slab이 모여 캐시 → 캐시가 모여 오브젝트</li>
<li>오브젝트는 캐시를, 캐시는 슬랩을 할당받는다.<img src="https://velog.velcdn.com/images/jungizz_/post/9530750c-8043-4dcd-97f0-3a6a4d980750/image.png" alt=""></li>
</ul>
<p><strong>in Linux</strong></p>
<ul>
<li>slab의 상태<ul>
<li>Full(꽉찬 슬랩), Empty(빈 슬랩), Partial(부분적으로 찬 슬랩)</li>
</ul>
</li>
<li>커널의 요청이 들어오면 아래와 같은 우선순위로 슬랩 할당<ul>
<li>Partial → Empty → 새 empty slab 생성</li>
</ul>
</li>
</ul>
<p>변형된 슬랩</p>
<ul>
<li>SLOB: 제한된 메모리를 가진 모바일 시스템 등에서 사용, small, medium, large 오브젝트를 위한 3개의 리스트 관리 → 간단</li>
<li>SLUB: 성능 최적화를 위해 발전된 형태, 메타데이터<br>
# 🔹other Consideration

</li>
</ul>
<p>Paging할 때 고려사항들…</p>
<h3 id="1-prepaging">1. Prepaging</h3>
<ul>
<li>demading page와 반대, 사용이 예상되는 페이지를 미리 올린다.</li>
<li>page fault를 막을 수 있다는 장점</li>
<li>s개 페이지를 미리 올리고, a비율로 페이지가 사용되면
→ prepaging 또는 save page fault 비용: s<em>a
→ 낭비 비용: s</em>(1-a)<br>

</li>
</ul>
<h3 id="2-page-size">2. Page size</h3>
<ul>
<li>상황에 따라 다른 페이지 크기를 설정할 수 있음 (보통 4KB~4MB)</li>
<li>고려할 점<ul>
<li>페이지 테이블 크기를 작게 유지하려면 큰 페이지</li>
<li>fragmentation은 작은 페이지에서 적음</li>
<li>페이지가 작을수록 높은 해상도(더 자세하게?정확하게?)</li>
<li>I/O 오버헤드 줄이려면 작은 페이지</li>
<li>지역성이 강한 프로그램은 작은 페이지가 좋음</li>
</ul>
</li>
</ul>
<br>

<h3 id="3-tlb-reach">3. TLB reach</h3>
<ul>
<li>페이지 테이블의 크기 한계<ul>
<li>TLB reach = TLB 크기 * 페이지 크기</li>
</ul>
</li>
<li>Working set이 TLB에 저장되는게 효율적이다.</li>
<li>페이지 크기를 키우면 TLB reach를 키울 수 있지만, fragmentation으로 메모리 낭비 발생 가능</li>
<li>페이지 크기를 다양하게 제공하면 좋다<br>
### 4. Inverted page table
<br>

</li>
</ul>
<h3 id="5-program-structure">5. Program structure</h3>
<ul>
<li>ex) c에서 2차원 배열(128*128)이 있을 때<ul>
<li>c의 주소 체계?는 행 기준으로 쓰여지고 한 행이 꽉차면 다음 열로 넘어간다. → 각 열은 하나의 페이지에 저장</li>
<li>열 우선 방식으로 배열에 접근하면 한 열을 접근 할 때마다 page fault 발생<img src="https://velog.velcdn.com/images/jungizz_/post/e964c877-4386-4986-ad12-14ef1f387c6c/image.png" alt=""></li>
<li>행 우선 방식으로 배열에 접근하면 각 행마다 1개의 페이지만 필요<img src="https://velog.velcdn.com/images/jungizz_/post/dc1ca744-7846-46dc-ae2a-c242002f376f/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="6-io-interlock-and-page-locking">6. I/O interlock and page locking</h3>
<ul>
<li><p>CPU가 I/O디바이스에 직접 접근하면, CPU는 너무 빠르고 I/O는 너무 느려서 CPU가 쓸데없이 오래 기다리게 됨</p>
</li>
<li><p>그래서 I/O디바이스의 버퍼를 메인 메모리 공간에 배치하여 CPU가 이 버퍼를 접근하도록, 덜 기다리도록 함</p>
</li>
<li><p>이때, 해당 버퍼가 page replace당해버리면 문제가 발생하므로 interlock으로 보호</p>
<br>
# 🔹OS example
Window
</li>
<li><p>Demand paging</p>
</li>
<li><p>clustering → page fault 발생하면 근처 page도 메모리에 가져와서 page fault를 줄임</p>
</li>
<li><p>working set의 min/max 동적 관리 가능(trimming)</p>
</li>
</ul>
<p>Solaris</p>
<ul>
<li>Demand paging</li>
<li><strong>scan</strong><ul>
<li>Second-chance(Clock) algorithm에서 clock이 도는 것을 스캔한다고함</li>
<li>scanrate: clock 속도</li>
</ul>
</li>
</ul>
<p>일정한 free memory를 유지하기 위해 min/max값을 가진다. free memory가 min보다 적으면 scan을 빠르게해서 free memory를 빠르게 얻고, max보다 크면 scan을 안한다. max와 가까워질수록 scan을 천천히한다.<img src="https://velog.velcdn.com/images/jungizz_/post/b2abaae7-27bc-4d05-9729-faa12c9c52bb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 9. Main Memory]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-9.-Main-Memory</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-9.-Main-Memory</guid>
            <pubDate>Sat, 26 Apr 2025 08:37:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡9장-목표">💡9장 목표</h4>
</blockquote>
<ul>
<li>메모리 관리를 위해 제공되는 하드웨어</li>
<li>메모리 관리 방법<ul>
<li>Contiguous Memory Allocation</li>
<li>Paging, Page table</li>
<li>Segmentiation</li>
</ul>
</li>
<li>Swapping</li>
<li>Example</li>
</ul>
<p>프로그램은 disk에 있다가 메모리로 로드되어 프로세스 내에 배치되어야 실행된다. 
CPU에서 메모리에 있는 데이터에 접근할 때 주소가 필요하다</p>
<ul>
<li>읽을 때는 주소+읽기 요청</li>
<li>쓸 때는 주소+데이터+쓰기 요청</li>
</ul>
<h1 id="background">Background</h1>
<h3 id="protection">Protection</h3>
<p>프로세스는 자기가 할당받은 영역만 사용해야한다. </p>
<ul>
<li>영역의 시작 주소는 base, 끝 주소는 base+limit(프로세스 크기)<img src="https://velog.velcdn.com/images/jungizz_/post/5a66f8db-f62c-45be-b4f8-984af610dd1b/image.png" alt=""></li>
<li>CPU는 메모리에 접근할 때 할당 영역을 넘어가는지 계속 확인해야한다.<ul>
<li>자주 확인하는 값이라 base와 limit는 해당 레지스터에 존재한다.</li>
<li>영역을 넘어가면 trap을 발생시켜 프로그램 종료<img src="https://velog.velcdn.com/images/jungizz_/post/6d310735-1e44-4b1e-81aa-0ca294ae533c/image.png" alt=""></li>
</ul>
</li>
<li>base와 limit을 로드?수정?하는 명령어는 privileged로 커널 모드에서만 가능<br>
### Address Binding

</li>
</ul>
<p>주소가 언제 정해지는지</p>
<ul>
<li>프로그램의 단계에 따라 주소는 다양한 방식으로 표현된다. (각각 다른 주소 공간으로 매핑하는 과정)<ul>
<li>source code: symbolic으로 표현</li>
<li>compiled code: relocatable 주소로 표현됨 (상대주소, 재배치 가능한 상태)</li>
<li>Linker or Loader: 위의 상대 주소로 재배치하여 절대 주소로 표현</li>
</ul>
</li>
</ul>
<p><strong>명령어와 데이터의 주소 바인딩(결정)</strong>은 여러 타이밍에서 발생할 수 있다.</p>
<ul>
<li>Compile Time: 상대 주소만 결정된다. 근데 메모리 위치가 사전에 알려져 있으면 절대 주소가 결정될 수도 있다.(boot loader 등)</li>
<li>Load Time: 위의 상대 주소를 활용하여 절대 주소 결정 (재배치)</li>
<li>Execution Time: 실행 중에 주소가 결정되는 경우 → 특정 코드/라이브러리가 호출될 때 로드하는 dynamic link loader(DLL)<img src="https://velog.velcdn.com/images/jungizz_/post/5ceec62c-c545-49eb-8542-c5be06fd24cb/image.png" alt=""><br></li>
</ul>
<h3 id="memory-management-unitmmu">Memory-Management Unit(MMU)</h3>
<blockquote>
<ul>
<li><strong>Logical address</strong>: CPU에 의해 생성된, CPU 입장에서의 주소로 프로세스마다 가지는 가상 주소이다. (상대 주소)</li>
</ul>
</blockquote>
<ul>
<li><strong>Physical address</strong>: 실제 메모리 공간에 할당된 주소이다. (절대 주소)</li>
</ul>
<p>메모리 관리 장치 MMU는 CPU에 포함되어 있으며, 논리주소를 물리주소로 매핑하는 역할을 한다.</p>
<ul>
<li>Relocation register의 값(base 등)을 논리 주소에 더해 물리 주소로 재배치</li>
<li>유저 프로그램은 실제 물리 주소를 결코 보지 못한다. 논리 주소와만 상호작용 한다.<img src="https://velog.velcdn.com/images/jungizz_/post/16ea5e6c-e994-4c23-ab06-5a8924239fd4/image.png" alt=""><br></li>
</ul>
<h3 id="dynamic-loadinglinking">Dynamic Loading/Linking</h3>
<ul>
<li>프로그램 실행할 때 전체를 메모리에 로드하는 것이 아닌 동적으로 로딩</li>
<li>static linking은 exe파일을 만들 때 한꺼번에 링킹하는 것이고, dynamic linking은 execution time(특정 부분이 호출될 때) 링킹하는 것이다.<ul>
<li>공유 라이브러리나 에러처리 등과 같은 기능에 주로 사용</li>
</ul>
</li>
<li>메모리 관리 및 코드 자사용 효율적으로 지원, 유연성 높임<br>
# 🔹Memory management

</li>
</ul>
<h2 id="1-contiguous-allocation">1. Contiguous Allocation</h2>
<p>메모리 공간을 연속적으로 할당해준다. 각 프로세스 등이 차지가는 공간을 partition이라고 한다.</p>
<ul>
<li>Base register와 Limit register로 MMU가 논리 주소를 dynamic하게 매핑한다.<img src="https://velog.velcdn.com/images/jungizz_/post/80be89fe-2c3e-473f-8b78-f1243f05d0b7/image.png" alt=""><br></li>
</ul>
<h3 id="variable-partition-dynamic-partition">Variable partition (dynamic partition)</h3>
<ul>
<li>partition의 크기는 다 다르다. 프로세스가 요구한 크기에 따라 partition의 크기가 결정되고 그만큼 할당한다.<ul>
<li>static partition: OS초기화 시 미리 partition의 크기 지정, 메모리 낭비<img src="https://velog.velcdn.com/images/jungizz_/post/ce2ccdee-7496-403d-bf0a-742712efa882/image.png" alt=""></li>
</ul>
</li>
<li>빈 공간들이 있을 때 어느 공간을 할당해 줄 것인가?<ol>
<li>First-fit: 충분한 크기를 가지는 첫 공간</li>
<li>Best-fit: 충분한 크기를 가지는 공간들 중 가장 작은 공간</li>
<li>Worst-fit: 가장 큰 크기를 가지는 공간</li>
</ol>
<ul>
<li>보통 1, 2번은 성능이 좋고 3번은 성능이 안좋다.</li>
<li>best-fit은 모든 공간을 다 찾아야해서 시간이 걸린다. 속도 저하</li>
</ul>
</li>
</ul>
<br>

<h3 id="fragmentation">Fragmentation</h3>
<p>메모리 공간에서 애매한 크기로 사용하지 못하는 공간을 의미한다.</p>
<ul>
<li>External Fragmentation: 파티션 사이 작은 공간으로 사용 못하는 공간</li>
<li>Internal Fragmentation: static partition에서 정해진 파티션 공간에 프로세스가 들어오고 남은 공간</li>
</ul>
<p>이를 해결하기 위해 파티션을 위로 모으는 <strong>Compaction</strong>을 한다.</p>
<ul>
<li>쓸모있는 공간 만들어내기, 조각 모음<br>
## 2. Paging

</li>
</ul>
<p>Contiguous 공간을 찾기 어렵다. 그래서 메모리 공간을 잘게 쪼개서 사용하자</p>
<ul>
<li>physical 메모리는 frame단위로, logical 메모리(프로세스)는 page 단위로 쪼갠다. (frame과 page는 절대/상대 주소 차이)</li>
<li>프로세스에 메모리를 할당 할 때 n개의 페이지를 할당하는 것</li>
<li>하지만 여전히 Internal fragmentation이 발생할 수 있다. (page의 남은 공간)</li>
</ul>
<p>프로세스는 physical 메모리의 연속적이지 않은 frame들을 page로 가져와서 자기 공간에 연속적으로 배치해서 사용한다. 프로세스 입장에서는 연속적인 공간을 사용한다고 느낀다.
<br></p>
<h3 id="page-table">Page table</h3>
<p>프로세스의 page를 메모리의 frame으로 매핑하기위한(절대 주소로 바꾸기 위한) 정보를 담고있으며, 프로세스마다 존재한다.</p>
<p>아래와 같은 자료 구조가  page table의 입출력으로 사용된다.</p>
<ul>
<li>Page/Frame number(p/f): 프로세스에서 page 번호, page table의 인덱스로 사용된다. page table을 거치면 frame 번호로 변환된다.</li>
<li>Page offset(d): 해당 page의 주소(상대 주소), 절대 주소로 변환하기 위해 base주소와 더해질 값<img src="https://velog.velcdn.com/images/jungizz_/post/8f8a881a-d588-4a84-b4c7-5e9c633710fb/image.png" alt=""></li>
</ul>
<p>아래와 같이 매핑<img src="https://velog.velcdn.com/images/jungizz_/post/0626e619-e2d7-49c1-ad37-5cf18c9c16ba/image.png" alt=""> <img src="https://velog.velcdn.com/images/jungizz_/post/c17a222e-22da-40f7-9183-e825cc5769fb/image.png" alt=""> <img src="https://velog.velcdn.com/images/jungizz_/post/89e239a8-2544-4504-8f77-d1be41c1f588/image.png" alt=""><br></p>
<h3 id="internal-fragmentation">Internal fragmentation</h3>
<p>계산은 아래와 같다.<img src="https://velog.velcdn.com/images/jungizz_/post/4103cdda-c360-425d-aed8-185bd7bc5315/image.png" alt=""></p>
<p>page는 protection, 재배치? 등의 역할도 해줘야하므로 메모리가 커지면 page도 커져야한다.
→ 그래서 Solaris는 크기가 다른 2개의 페이지를 지원하여 page 개수를 많이 사용할 수 있게했다.
<br></p>
<h3 id="free-frames">Free frames</h3>
<p>OS는 메모리의 빈 공간을 관리한다. 새로운 프로세스가 메모리 할당을 요청하면 OS가 빈 공간을 내어주고 그 공간으로 page table이 결정된다.<img src="https://velog.velcdn.com/images/jungizz_/post/739a2022-360f-4f06-9dc1-d79296c5bcbc/image.png" alt=""><br></p>
<h3 id="implementation-of-page-table">Implementation of Page table</h3>
<p>페이지 테이블은 메인 메모리에 있다. </p>
<p>page table은 자주 사용되는 값이니까 레지스터를 사용한다.</p>
<ul>
<li>PTBR(page table base register)</li>
<li>PTLR(page table length register)</li>
</ul>
<p>그래도 page table을 가지러 메모리에 한 번, page table을 보고 알맞은 메인 메모리 위치로 또 한 번 → 2번 거쳐야해서 속도 2배 느려짐</p>
<ul>
<li>TLBs(Translation look-aside buffers)라는 캐시를 사용해서 빠르게 조회할 수 있도록 한다.<br>
### TLBs

</li>
</ul>
<p>여러 프로세스들이 공유하는 버퍼로, page table의 캐시이다. </p>
<ul>
<li>page table에서 페이지 번호를 찾을 때, TLBs에서 먼저 찾고 있으면 TLB hit, 없으면 TLB miss</li>
<li>miss가 발생하면 page table의 값을 메모리에서 캐시로(TLBs로) 가져오기<ul>
<li>가져올 때 페이지 번호와 프레임 번호를 매핑하여 가져와야한다. (하나만 갖고오면 나머지를 알 수 없어 주소 변환 수행 불가능)<img src="https://velog.velcdn.com/images/jungizz_/post/5b827274-7d62-48a2-857c-a5ec32550206/image.png" alt=""></li>
</ul>
</li>
</ul>
<p>TLB에서 페이지 번호를 찾는 비율을 Hit ratio라고 한다. </p>
<ul>
<li><p>80%의 hit ratio, 메모리 접근 시간이 10ns라고 하면</p>
<ul>
<li>20%의 페이지를 못찾는 경우는 메모리 두 번 접근으로 20ns이다. (2배)</li>
<li>이때의 Effective Access Time(ETA)를 구하면 아래와 같다.
→ 0.8 * 10 + 0.2 * 20 = 12 ns</li>
</ul>
<br>

</li>
</ul>
<h3 id="memory-protection">Memory Protection</h3>
<p>각 프레임에 protectioin bit를 연결하여 할당 영역만 사용할 수 있도록하고, 읽기 전용 또는 읽기 쓰기 접근 허용을 나타낸다.<img src="https://velog.velcdn.com/images/jungizz_/post/52bad624-9986-467b-9763-a767f02a242f/image.png" alt=""><br></p>
<h3 id="shared-pages">Shared Pages</h3>
<p>여러 프로세스 간에 공유되는 페이지</p>
<p>ex) 여러 프로세스가 같은 라이브러리를 사용하는 경우, 메인 메모리에 공유 페이지를 만들어 같이 사용<img src="https://velog.velcdn.com/images/jungizz_/post/1bbf9588-2e41-48d0-ae39-0fad9e58a47c/image.png" alt=""><br></p>
<h3 id="structure-⭐">Structure ⭐</h3>
<p>단순하게 페이지 처리를 하면 페이지 테이블이 매우 커질 수 있다는 단점을 가진다.</p>
<p>ex) 32비트 logical address 공간</p>
<ul>
<li>페이지 크기 4kb (2^12)</li>
<li>페이지 테이블은 1백만개의 항목을 가진다 (2^32, w^12)</li>
<li>각 항목이 4바이트라면, 각 프로세스는 페이지 테이블만을 위해 4mb의 physical address 공간을 차지하게된다.</li>
</ul>
<p>이를 보완하기 위한 ⭐<strong>3가지 페이지 테이블 구조</strong>⭐는 아래와 같다. (흐름 알기)</p>
<p><strong>1. Hierarchical Paging</strong></p>
<ul>
<li>2 level page table: 페이지 테이블도 페이지로 나눠 페이지 테이블의 페이지 테이블을 만든다.<img src="https://velog.velcdn.com/images/jungizz_/post/dbcd98af-762d-420f-addd-5ddf03e47d21/image.png" alt=""></li>
</ul>
<p>ex) 32비트 logical address</p>
<ul>
<li>아래와 같은 구조를 가진다. p1은 outer page number, p2는 page number<img src="https://velog.velcdn.com/images/jungizz_/post/e44ff3de-2858-4d6f-abcc-9ccd5bc2da58/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/f64e4eb1-d056-4f93-aa10-32e3c9cbf381/image.png" alt=""></li>
</ul>
<p>ex) 64비트 logical address</p>
<ul>
<li>3 level로도 가능<img src="https://velog.velcdn.com/images/jungizz_/post/68187c74-7e83-46e2-a365-f64e9a5b1cd9/image.png" alt=""><br></li>
</ul>
<p><strong>2. Hash Page Tables</strong></p>
<ul>
<li>논리 주소를 해시 함수를 통해 해시화하여 테이블에 매핑 → 인덱스로 빠르게 찾을 수 있도록 해줌<ul>
<li>해시값이 같은 논리 주소 값들이 존재할 수 있으므로 linked list로 관리</li>
<li>테이블은 페이지 번호, 프레임 번호, 다음 요소의 포인터로 구성<img src="https://velog.velcdn.com/images/jungizz_/post/7ee313dd-c5df-4271-8abc-1fa448f0669d/image.png" alt=""></li>
</ul>
</li>
<li>하지만 크기가 너무 커지면 오히려 더 복잡해질 수 있다. 그래서 모여있는 페이지 그룹을 가리키는 clustered page table도 있다.</li>
</ul>
<br>

<p><strong>3. Inverted Page Tables</strong></p>
<ul>
<li><p>기존 페이지 테이블과 반대로 논리 주소가 아닌 물리 주소의 frame 순서에 따라 page table을 구성</p>
</li>
<li><p>프로세스마다가 아닌, 한 시스템이 가지는 테이블 (어떤 프로세스가 사용하는지 구별하는 pid 필요)<img src="https://velog.velcdn.com/images/jungizz_/post/e8118e62-a6df-407a-90ee-5c37db9e3cf4/image.png" alt=""></p>
</li>
<li><p>테이블 크기는 줄어들지만, page number를 찾는데 시간이 걸린다.</p>
<br>
### in Oracle Solaris
</li>
<li><p>hashing page table</p>
</li>
<li><p>가상 contiguous area를 가리킴 → 각 페이지에 대한 해시 테이블보다 효율적</p>
</li>
<li><p>TBL (cache)</p>
<br>
# 🔹Swapping

</li>
</ul>
<p>메인 메모리의 하위 계층(하드디스크 등) 공간을 swap 공간으로 사용한다.</p>
<ul>
<li>Backing store: 하위 계층 공간, 넓은 공간</li>
<li>거의 모든 OS에서 사용하며, 메모리 공간이 부족할 때만 Swapping을 한다.</li>
<li>메인 메모리의 공간 확보<ul>
<li>Roll out: 당장 CPU가 필요없는 wait 상태나 우선 순위가 낮은 프로세스를 잠시 backing store로 보냄</li>
<li>Roll in: backing store에 있는 것들이 CPU가 필요해지면 다시 메인 메모리로 올려서 CPU 할당<img src="https://velog.velcdn.com/images/jungizz_/post/2ff93625-6efb-4e42-8dce-b27d8614eab4/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="context-switch">Context switch</h3>
<p><strong>swapping time</strong></p>
<ul>
<li>context switch는 CPU에 다음으로 할당할 프로세스가 메인 메모리에 없으면, swapping 해야해서 시간이 걸린다.<ul>
<li>이때 swapping 한다는 것은 context switch로 놓아주는 프로세는 swap out을 하고, 필요한 프로세스를 swap in하는 것</li>
</ul>
</li>
<li>만약 100mb 프로세스를 50mb/s 속도로 하드 디스크로 swapping한다면 swap out 시간은 2초, swap in도 2초로 총 4초가 걸린다.</li>
<li>이런 swapping time을 줄이기 위해서는 스와핑 메모리 크기 줄이기</li>
</ul>
<p><strong>Pending I/O</strong></p>
<ul>
<li>I/O를 대기 중인 프로세스를 swapping하면, 잘못된 프로세스에 I/O가 발생할 수 있다. (꼬일 수 있다..)<ul>
<li>왜냐하면 I/O는 operation을 하기 위해 메모리 공간을 가지고 있다(double buffering, 커널이 관리하는 공간).  근데 이 메모리 공간을 가지고 있는 프로세스를 swapping해버리면(메모리에서 뻬버리면)</li>
</ul>
</li>
<li>그래서 I/O 대기 중일 때는 swapping을 금지하는 등의 방안이 있음<br>
### in Mobile System

</li>
</ul>
<p>모바일 시스템은 일반적으로 스와핑을 지원하지 않는다. 메모리 공간이 부족해서 스와핑을 하기 보단 종료를 해버림</p>
<ul>
<li>모바일 기기는 주로 flash memory를 사용하는데, 이는 저장 공간이 작고, wrtie속도가 느리며 write할 수 있는 횟수가 제한적 → 쓰기를 많이 하면 수명 단축</li>
<li>그래서 swap out보단 종료해버리기</li>
</ul>
<p>각 모바일 운영체제는 메모리 공간이 부족할 때 아래와 같은 방법을 사용한다.</p>
<ul>
<li>IOS<ul>
<li>애플리케이션이 메모리를 자발적으로 반납하도록 요청</li>
<li>가급적 read-only 데이터를 삭제 (write 데이터는 꼭 하위 계층에 저장하고 삭제)</li>
<li>메모리 해제 안하면 애플리케이션 종료</li>
</ul>
</li>
<li>Android<ul>
<li>애플리케이션 종료</li>
<li>종료 전 상태를 플래시에 기록하여 빠른 재시작 가능하게 함</li>
</ul>
</li>
</ul>
<br>

<h3 id="with-paging">with paging</h3>
<p>페이지 단위의 프로세스라면 backing store에서 페이지들이 swap in/out된다. → page in/out<img src="https://velog.velcdn.com/images/jungizz_/post/84288b9a-2e73-42d4-a1dc-9a0243bf2a1e/image.png" alt=""><br></p>
<h1 id="🔹intel-architectures">🔹Intel Architectures</h1>
<p>하드웨어적으로(Intel CPU) 메모리 관리를 지원하는 예시</p>
<h3 id="intel-cpu-32bit">Intel CPU 32bit</h3>
<ul>
<li>segmentation, paging 지원<ul>
<li>paging은 일정한 크기로 쪼개는 반면에, segmentation은 성질(text, data, stack등)에 따라 다른 크기로 쪼갬</li>
<li>segment table은 logical 시작 주소를 physical 시작 주소로 바꿔준다. (page table은 logical 주소를 physical주소로 바꿔줬음)<ul>
<li>LDT(local descriptor table): private segment table</li>
<li>GDT(global descriptor table): 프로세스가 공유 가능한 segment table</li>
</ul>
</li>
</ul>
</li>
<li>둘을 융합한 segmentation with paging 지원</li>
</ul>
<p><strong>주소 변환 과정</strong><img src="https://velog.velcdn.com/images/jungizz_/post/3aa2aad1-b0e2-4635-a706-f75c8450b701/image.png" alt=""></p>
<ul>
<li>CPU가 logical 주소를 생성하고, 이는 segmentation unit에 전달되어 linear 주소로 변환된다.<img src="https://velog.velcdn.com/images/jungizz_/post/282ae270-22ff-4869-8a3c-0c92fa591f6f/image.png" alt=""></li>
<li>linear 주소는 paging unit에 전달되어 physical 주소로 변환된다.<ul>
<li>이때 page의 크기는 4kb 또는 4MB</li>
<li>segment를 page로 관리하는 2 level 기법<img src="https://velog.velcdn.com/images/jungizz_/post/c7bcaba3-dcb9-4196-a200-c43575bad18a/image.png" alt=""></li>
</ul>
</li>
</ul>
<p>segment number로 segment table 인덱싱해서 시작 주소를 얻음. 이를 offset과 더해서 liner 주소로 변환<img src="https://velog.velcdn.com/images/jungizz_/post/f6e558aa-9a49-4cfe-a476-3f68c45117c8/image.png" alt=""></p>
<p>작은 page는 2 level로, 큰 page는 1level로 paging하여 physical 주소 얻음<img src="https://velog.velcdn.com/images/jungizz_/post/4ebe1c91-ce0a-4c8e-bd3c-f4098587802b/image.png" alt=""></p>
<p><strong>Page address extension (PAE)</strong></p>
<ul>
<li><p>32비트 주소 한계로 인해 도입</p>
<ul>
<li>32bit address는 4GB의 메모리 공간 사용 가능</li>
</ul>
</li>
<li><p>3 level의 paging으로 변경하고.. 어쩌구해서 4bit가 증가된 36bit address 공간으로 바꿈 → 64GB 메모리 공간 사용 가능 (16배)</p>
<br>
### Intel CPU 64bit
</li>
<li><p>64bit 주소 공간 → 16exabytes로 매우 큰 메모리 공간을 사용 가능하지만 다 쓰진 않고 48bit의 주소 공간만 사용한다.</p>
<ul>
<li>PAE를 쓰면 virtual address 48bit, physical address 52bit</li>
</ul>
</li>
<li><p>페이지의 크기는 필요에 따라 4kb, 2mb, 1gb 사용 가능</p>
</li>
<li><p>4 level paging<img src="https://velog.velcdn.com/images/jungizz_/post/925f94c0-7d8e-49e7-9b4a-8ce7eb9f6ecd/image.png" alt=""><br></p>
</li>
</ul>
<h1 id="🔹arm-architecture">🔹ARM Architecture</h1>
<p>모바일에서 많이 사용되는 아키텍처(칩)</p>
<ul>
<li>저전력 중점</li>
<li>paging 지원<ul>
<li>4kb, 16kb</li>
<li>1mb, 16mb의 큰 페이지는 section이라고 부름</li>
</ul>
</li>
<li>작은 페이지는 2level, 큰 페이지는 1level</li>
<li>TLBs(캐시) 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 8. Deadlocks]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-8.-Deadlocks</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-8.-Deadlocks</guid>
            <pubDate>Sat, 26 Apr 2025 08:20:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡-8장-목표">💡 8장 목표</h4>
</blockquote>
<ul>
<li>데드락 특징</li>
<li>데드락 해결책<ul>
<li>Prevention, Avoidance, Detection, Recovery</li>
</ul>
</li>
</ul>
<p><strong>System model</strong></p>
<p>프로세스가 자원을 사용하는 것을 모델링</p>
<ul>
<li>시스템은 리소스로 구성되어있고, 여러 리소스 타입(R1, R2, …)이 존재</li>
<li>각 리소스 타입(Ri)은 여러 인스턴스(Wi개)를 가질 수 있다.</li>
<li>각 프로세스는 reqeust, use, release로 리소스 사용</li>
</ul>
<p><strong>Deadlock in multithread application</strong></p>
<p>멀티스레드에서 데드락이 발생하는 상황</p>
<ul>
<li>두 개의 mutex lock, 두 개의 스레드</li>
<li>한 스레드는 mutex1을 먼저 잡고 mutex2를 잡고, 다른 스레드는 mutex2를 먼저 잡고 mutex1을 잡는다.</li>
<li>이 때, 스케줄러에 의해 우연이 두 스레드가 동시에 실행돼서 각 스레드가 mutex1, mutex2를 잡게되면, 서로 가진 걸 요청하고 기다리게되며 deadlock<img src="https://velog.velcdn.com/images/jungizz_/post/d722ee19-1b63-4fc8-bec7-19b61f8aabc9/image.png" alt=""></li>
<li>resource allocation graph는 아래와 같다<img src="https://velog.velcdn.com/images/jungizz_/post/ac68da65-a916-48c9-aa5c-cb5b57a1c8a8/image.png" alt=""><br></li>
</ul>
<h1 id="deadlock-characterization">Deadlock Characterization</h1>
<p>데드락은 아래 네 가지 조건이 동시에 만족될 때 발생한다.</p>
<ol>
<li>Mutual Exclusion: 한 번에 한 프로세스만 자원 사용 (독점권)</li>
<li>Hold and Wait: 최소 하나의 자원을 잡고 있는 프로세스가 다른 프로세스가 잡고있는 자원을 얻기 위해 기다리는 상태</li>
<li>No preemption: 선점 불가, 강제로 빼앗을 수 없다</li>
<li>Circular wait: 서로 기다리는 관계 (대기 중인 프로세스 집합 {P0, P1, …, Pn}이 존재하며, P0는 P1이 보유하고 있는 자원을 기다리고, P1은 P2가 보유하고 있는 자원을 기다리며, …, Pn–1은 Pn이 보유하고 있는 자원을 기다리고, Pn은 P0가 보유하고 있는 자원을 기다리는 형태이다.)<br>
# Resource-Allocation Graph
</li>
</ol>
<ul>
<li>node: 프로세스 P = {P1, P2, …}, 리소스 R= {R1, R2, …}</li>
<li>edge: P→R은 프로세스가 리소스를 요청, R→P는 프로세스에 리소스 사용권 할당<img src="https://velog.velcdn.com/images/jungizz_/post/bd276b13-d6d7-4689-ba45-711f8cb819ec/image.png" alt=""></li>
<li>deadlock의 resource allocation graph는 아래와 같다. circle을 가진다.<img src="https://velog.velcdn.com/images/jungizz_/post/18a76f22-2a76-474f-936f-a8bef9dbe1ed/image.png" alt=""></li>
<li>circle을 가져도, 아래와 같은 경우라면 deadlock이 아닐 수 있다<img src="https://velog.velcdn.com/images/jungizz_/post/f65fb1f5-4e2b-4ba8-814c-8a31f5686bc9/image.png" alt=""></li>
</ul>
<p><strong>graph로 Deadlock 판단</strong></p>
<ul>
<li>Cycle이 없다 → Deadlock 아님</li>
<li>Cycel이 있다
→ 모든 리소스에 인스턴스 1개라면 Deadlock
→ 리소스에 인스턴스 여러개라면 Deadlock일 수도, 아닐 수도 있음</li>
</ul>
<br>

<h1 id="methods-for-handling-deadlocks">Methods for Handling Deadlocks</h1>
<ul>
<li>1, 2번은 데드락을 사전 방지 → 사전 방지 하면 좋지만, 리소스 활용도가 떨어지고 알고리즘이 어려움</li>
<li>3, 4번은 데드락 발생 후 처리</li>
</ul>
<h3 id="1-prevention">1. Prevention</h3>
<p>데드락 발생 조건 4가지 중 하나를 없애는 방법</p>
<ul>
<li>Mutual Exclusion: 이건 없애면 race condition이 발생하기 때문에 없애기가 사실상 불가능</li>
<li>Hold and Wait: 프로세스 실행 전 모든 자원을 한꺼번에 할당하고, 한꺼번에 해제<ul>
<li>자원을 요청할 때 다른 자원을 보유하지 않도록 보장</li>
<li>필요하지도 않은 자원까지 잡아버리므로 자원 낭비, 활용도 떨어짐 (starvation 현상)</li>
</ul>
</li>
<li>No Preemption: 자원을 요청했는데 못잡았다면 전에 잡았던 자원도 다 놓기(선점으로 바꾸기) → 자원 활용도 떨어짐</li>
<li>Circular Wait: 자원마다 번호를 매기고, 프로세스가 자원을 요청할 때 순서대로 요청하도록 요구<img src="https://velog.velcdn.com/images/jungizz_/post/8d64261e-6451-42f0-96a6-0819b3e04e62/image.png" alt=""><br></li>
</ul>
<h3 id="2-avoidance">2. Avoidance</h3>
<ul>
<li><p>데드락이 발생할 것 같으면 리소스를 할당해주지 않는다.</p>
</li>
<li><p>프로세스가 자원을 요청할 때, 할당해도 <strong>safe state를 유지</strong>하는지 판별해야한다. safe state일 때만 리소스를 할당해준다.</p>
<ul>
<li><p>safe sate라면 아래 조건을 만족하는 프로세스 시퀀스&lt;P1, P2, …&gt;이 존재</p>
<p>  Pi의 자원 요구가 즉시 사용 가능하지 않다면, Pi는 모든 Pj가 완료될 때까지 기다릴 수 있다. (j&lt;i)</p>
<p>  Pj가 완료되면 Pi는 필요 자원을 얻어 실행하고, 자원을 반환한 후 종료된다.</p>
<p>  Pi가 종료되면 Pi+1은 필요한 자원을 얻을 수 있다.</p>
</li>
<li><p>Safe State → no deadlock</p>
</li>
<li><p>unSafe State → deadlock이 생길 수도 있고 아닐 수도 있고</p>
</li>
</ul>
</li>
<li><p>Safe state 확인 방법</p>
<ul>
<li>모든 자원이 싱글 인스턴스라면 → resoruce allocation graph러 circle이 생기는지 확인<img src="https://velog.velcdn.com/images/jungizz_/post/b93a14b5-84ab-48d5-bf11-dd79ce58c949/image.png" alt="">   </li>
<li>인스턴스가 여러개라면 → Banker’s Algorithm으로 확인</li>
</ul>
</li>
</ul>
<blockquote>
<h3 id="bankers-algorithm">Banker’s Algorithm</h3>
<p>사용 데이터 구조는 아래와 같다.</p>
</blockquote>
<ul>
<li>Avaiable: OS에 남아있는 자원 개수 (사용 가능한 자원 개수)<ul>
<li>j번째 리소스가 k개 남아있다.</li>
</ul>
</li>
<li>Max: 프로세스가 필요로 하는 자원 개수<ul>
<li>i번째 프로세스가 j번째 리소스를 k개 필요로 한다.</li>
</ul>
</li>
<li>Allocation:프로세스가 사용 중인 자원 개수<ul>
<li>i번째 프로세스가 j번째 리소스를 k개 사용 중이다.</li>
</ul>
</li>
<li>Need: 프로세스가 추가적으로 더 요청할 자원 개수 (Max - Allocation)<ul>
<li>i번째 프로세스가 j번째 리소스를 k개 더 필요로 한다.<blockquote>
</blockquote>
<br>
></li>
</ul>
</li>
<li>Safety를 확인하는 알고리즘은 아래와 같다.<img src="https://velog.velcdn.com/images/jungizz_/post/d24a8dcb-f003-4d90-8031-87911f11738a/image.png" alt=""><br></li>
<li>i번째 프로세스가 리소스를 요청하는 알고리즘은 아래와 같다. 중요한 것은 자원 충분하다고 바로 할당하는게 아니라,<strong>할당했을 때 안전한지 확인하는 것</strong><img src="https://velog.velcdn.com/images/jungizz_/post/98ae1937-4b86-4c19-bd1a-8e7db4175352/image.png" alt=""><br><h4 id="example">example<img src="https://velog.velcdn.com/images/jungizz_/post/a0e77273-74be-407a-8c40-e0d775d6bbea/image.png" alt=""></h4>
<img src="https://velog.velcdn.com/images/jungizz_/post/fbab674b-3cd7-40e3-8d37-082c14a26ee5/image.png" alt=""><blockquote>
</blockquote>
만약 P1이 (1, 0, 2)를 요청했다면, </li>
<li>(1, 0, 2) ≤ (3, 3, 2)니까 할당은 가능함</li>
<li>할당 했을 때 안전한지 다시 확인하고 안전하면 그때 진짜 할당<img src="https://velog.velcdn.com/images/jungizz_/post/57604c49-b1b7-4aa4-b145-40ee2cbcbcb3/image.png" alt=""></li>
</ul>
<br>

<h3 id="3-detection-and-recover">3. Detection and Recover</h3>
<p><strong>Detection</strong></p>
<ul>
<li>SIngle Instance인 경우 → wait-for 그래프에서 cycle이 있는지 확인<ul>
<li>wait-for그래프는 리소스 없이 프로세스로만 나타낸 그래프</li>
<li>pi → pj는 프로세스i가 프로세스j가 사용 중인 리소스를 기다린다를 의미</li>
<li>하지만 cycle 찾는 것은 노드가 n개 일 때, n^2시간 걸림<img src="https://velog.velcdn.com/images/jungizz_/post/c8bcce13-071d-459b-8140-d63a1d9c2315/image.png" alt=""></li>
</ul>
</li>
<li>Serveral Instance인 경우 → detection algorithm 사용 (bankers와 비슷함)<ul>
<li>근데 이 알고리즘은 자원이 m개, 프로세스가 n개일 때 m*n^2시간이나 걸림<img src="https://velog.velcdn.com/images/jungizz_/post/10b5f113-1cd0-44d4-b938-8022939b8b17/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/2f950257-483b-4d74-ac7f-2961d55668b1/image.png" alt=""></li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="example-1">example</h4>
<ul>
<li>아래와 같이 알고리즘을 적용했을 때 전부 작업이 완료되는 시퀀스 시나리오가 존재하면 deadlock없음<img src="https://velog.velcdn.com/images/jungizz_/post/98a20ff9-2b66-4163-a46a-d6b63385b747/image.png" alt=""><br></li>
<li>근데 완료되지 않은 작업이 발생하면 deadlock<img src="https://velog.velcdn.com/images/jungizz_/post/6724ebb4-f5b1-4a92-9db1-3ac1d166c0b1/image.png" alt=""></li>
</ul>
</blockquote>
<ul>
<li>이 알고리즘은 너무 복잡하고 시간도 오래 걸리기 때문에 아래를 고려해야한다.<ul>
<li>데드락이 얼마나 자주 발생하는지</li>
<li>얼마나 많은 프로세스가 데드락을 발생시켜 roll back을 하게 하는지</li>
</ul>
</li>
</ul>
<p><strong>Recovery</strong></p>
<ul>
<li><p>데드락과 관련된 프로세스를 <strong>abort(종료)</strong>하거나 <strong>roll back(일정 시점까지만 취소, 자원 해제)</strong></p>
<ul>
<li><p>데드락과 관련된 프로세스들 중 어떤 프로세스를 종료할 것인가?</p>
<p>  → 우선순위, 얼마나 길게 수행했는지, 얼마나 자주 자원을 사용했는지 등을 고려</p>
</li>
</ul>
</li>
<li><p>Recovery시 고려할 점</p>
<ul>
<li>victim을 설정할 때 리소스 낭비를 최소화, 비용 최소화</li>
<li>리소스 낭비를 최소화하기 위해 abort보단 rollback</li>
<li>하지만 데드락이 너무 자주 발생하면 abort나 rollback도 자주 일어나며 starvation 발생 가능</li>
</ul>
</li>
</ul>
<br>

<h3 id="4-ignore">4. Ignore</h3>
<ul>
<li>데드락 발생 무시</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 7. Synchronization Examples ]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-7.-Synchronization-Examples</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-7.-Synchronization-Examples</guid>
            <pubDate>Sat, 26 Apr 2025 08:01:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡7장-목표">💡7장 목표</h4>
</blockquote>
<ul>
<li>동기화 문제의 3가지 모델</li>
<li>여러 OS의 동기화 툴</li>
<li>POSIX와 JAVA</li>
</ul>
<h1 id="synchronization-schemes">Synchronization schemes</h1>
<ol>
<li>Bounded-Buffer problem (=producer-consumer)</li>
<li>Readers and Writer problem</li>
<li>Dining-Philosopher problem</li>
</ol>
<p>강노 참고~
<br></p>
<h1 id="kernel-synchronization">Kernel Synchronization</h1>
<p>커널에서 제공하는 동기화 기능</p>
<h2 id="window">Window</h2>
<ol>
<li>Interrupt masks: 하드웨어적으로 CS를 보호하기 위해 interrupt disable하는 등의 기능 → single CPU에서 유용(multi면 disable 다 해줘야하고 확장성 부족)</li>
<li>Spinlock: 독점권을 얻기 위해 CPU를 놓지 않고(wait상태가 되지 않고) 반복문에 들어가서 기다리기 → 기다리는 시간이 짧은 경우 유용</li>
<li>Dispatcher objects: mutexes, semaphores, events, timers 등 기능 제공 → 자원 사용 가능한 signaled-state 또는 사용 불가능으로 기다려야하는 non-signaled-state이다.<br>
## Linux
</li>
</ol>
<ul>
<li>2.6버전 이전에는 short critical section에 대해 disanle interrupts기능 사용</li>
<li>2.6버전 이후로는 모두 선점형으로 바뀜 (비선점은 race condition을 발생시키지 않지만 높은 우선순위가 오래 기다리는 문제)</li>
</ul>
<ol>
<li>Semaphores</li>
<li>atomic intergers</li>
<li>spinlocks → single CPU에서는 enabling and disabling kernel preemption(선점)</li>
<li>reader-writer version of both<br>
# POSIX Synchronization

</li>
</ol>
<p>POSIX는 API 표준이다. POSIX를 따르는 OS는 UNIX, Linux, macOS가 있으며 이들끼리는 호환성을 가진다.</p>
<p>POSIX API가 제공하는 동기화 기능은 아래와 같다. (코드 강노 참고)</p>
<ol>
<li>Mutex lock</li>
<li>Semaphores → named와 unnamed 프로세스 둘 다 사용 가능 (named는 상관관계가 없는 프로세스)</li>
<li>Condition Variable <br>
# Java Synchronization

</li>
</ol>
<p>코드 강노 참고</p>
<ol>
<li>Java monitors<ul>
<li>synchronized로 함수 선언</li>
<li>기존 monitor와 동일한 구조<ul>
<li>object에 한 스레드만 들어갈 수 있음</li>
<li>object에 들어가기를(사용권한을) 기다리는 스레드들이 모여있는 entry set</li>
<li>object 내에서 기다리는 wait set (object 내 코드를 수행하다가 wait() condition 발생하여 기다리는 스레드들)</li>
</ul>
</li>
<li>signal()대신 <strong>notify()</strong>가 wait()를 깨움</li>
</ul>
</li>
<li>Reentrant locks (mutex와 유사)</li>
<li>Semaphores</li>
<li>Condition variables<br>
# Alternative Approaches

</li>
</ol>
<p>미들웨어에서 제공하는 다양한 동기화 기능 </p>
<p>코드 강노 참고</p>
<ol>
<li>Transactional Memory<ul>
<li>transaction은 쪼갤 수 없는 한 덩어리를 의미 → atomic하게 처리</li>
<li>기존 mutex나 semaphor사용하는 것보다 간단</li>
</ul>
</li>
<li>OpenMP<ul>
<li>#pragma omp critical하면 안에 있는거를 critical section으로 보호해줌</li>
<li>#pragma omp pararrel하면 코어 수만큼 스레드를 만들어 자동으로 병렬처리 해줌</li>
</ul>
</li>
<li>Functional Programming Languages<ul>
<li>재귀 기반 프로그래밍</li>
<li>공유 데이터 없음 → critical section이 자동으로 보호됨</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 6. Synchronization Tools]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-6.-Synchronization-Tools</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-6.-Synchronization-Tools</guid>
            <pubDate>Sat, 26 Apr 2025 07:57:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡6장-목표">💡6장 목표</h4>
</blockquote>
<ul>
<li>Critical-section problem와 해결책</li>
<li>전통적인 Process Synchronization 문제</li>
<li>Process Synchronization 해결 도구</li>
</ul>
<p>프로세스 또는 thread는 동시에 실행될 수 있으며, 언제든지 interrupt가 발생할 수 있어 실행이 부분적으로 완료될 수 있다. 또한, 동시 실행은 공유 데이터에 대한 data inconsistency(데이터 불일치)를 초래할 수 있다. 데이터의 일관성을 유지하려면 협력 프로세스의 <strong>순차적 실행을 보장</strong>하는 메커니즘이 필요하다. </p>
<p>예를 들어, comsumer-producer문제를 해결하기 위해 모든 버퍼를 채운다고 생각하자. 전체 버퍼 수를 추적하기 위해 integer counter를 사용한다. 초기 counter는 0으로 설정하고, 생산자는 새로운 버퍼를 생산한 후 counter를 증가시키고, 소비자는 버퍼를 소비한 후 counter를 감소시킨다. </p>
<pre><code class="language-c">// producer
while (true) {
         /* produce an item in next produced */ 
        while (counter == BUFFER_SIZE)  
                ; /* do nothing */ 
        buffer[in] = next_produced; 
        in = (in + 1) % BUFFER_SIZE; 
        counter++; 
} 

// consumer
while (true) {
         while (counter == 0) 
                ; /* do nothing */ 
        next_consumed = buffer[out]; 
        out = (out + 1) % BUFFER_SIZE; 
        counter--; 
        /* consume the item in next consumed */ 
} </code></pre>
<p>counter를 증감하는 과정은 machine instruction관점에서 register를 사용한 여러 과정을 거친다. 이 과정에서 순서가 보장되지 않으면, counter의 값이 일관성을 잃을 수 있다. (아래 예시는 이상적으로 5의 값을 가져야할 counter가 실행 순서에 따라 4의 값을 갖게됨) → <strong>race condition</strong><img src="https://velog.velcdn.com/images/jungizz_/post/de051ca5-924c-4208-942f-508a48b0a0ca/image.png" alt=""></p>
<p>또 다른 Race condition의 예시는 아래와 같다.</p>
<ul>
<li>프로세스 P0과 P1이 fork()로 자식 프로세스를 생성할 때, kernel variable ‘next_available_pid’가 다음 사용가능한 pid를 나타냄</li>
<li>이때, mutual exclusion(상호 배제)가 없는데 fork()가 동시에 호출되면 서로 다른 프로세스에 동일한 pid가 할당될 수 있음 <strong>→ Race condition</strong></li>
<li>즉, 여러 개의 프로세스가 동일한 data에 접근하여 변경하고, 그 결과가 접근이 발생한 순서에 의존하는 상황을 의미<img src="https://velog.velcdn.com/images/jungizz_/post/a3357b70-a8bc-4f57-bf12-1aa3806601ae/image.png" alt=""></li>
</ul>
<p>이러한 상황을 방지하기 위해 한순간에 하나의 프로세스만이 data를 변경하도록 보장해야함
<br></p>
<h1 id="critical-section">Critical Section</h1>
<ul>
<li>각 프로세스는 critical section(임계 구역)이라는 민감한 segment를 가짐</li>
<li><strong>한 프로세스가 critical section에 있을 때, 다른 어떤 프로세스도 그 critical section에 들어갈 수 없음</strong> (thread도 마찬가지)<ul>
<li>이러한 프로토콜을 설계하는 것이 Critical-section problem</li>
</ul>
</li>
<li>각 프로세스는 자신의 critical section으로 진입하기 위해 허가를 요청<ul>
<li>이러한 요청을 구현하는 Entry Section 코드</li>
<li>critical section 뒤에는 Exit Section 코드가 존재할 수 있음</li>
<li>코드의 나머지 부분은 remainder section이라고 함</li>
</ul>
</li>
<li>프로세스의 일반적인 구조<img src="https://velog.velcdn.com/images/jungizz_/post/468de5c8-2ed1-4eaa-b2c5-c1575d0fe3cb/image.png" alt=""><br></li>
</ul>
<h2 id="condition">Condition</h2>
<p>Critical-section problem에 대한 해결안은 아래 세 가지 조건을 충족시켜야 한다.</p>
<ul>
<li>각 프로세스는 nonzero speed로 실행된다고 가정</li>
<li>n개의 프로세스 간 상대속도에 대한 가정은 없음</li>
</ul>
<ol>
<li><strong>Mutual Exclusion</strong> (상호 배제)<ul>
<li>critical section의 독점권 차지</li>
</ul>
</li>
<li><strong>Progress</strong> (진행)<ul>
<li>cirtical section에 들어간 프로세스가 없는 경우, 들어가려는 프로세스를 막으면 안됨</li>
</ul>
</li>
<li><strong>Bounded Waiting</strong> (한정된 대기)<ul>
<li>critical section에 프로세스가 들어가있는 경우, 다른 프로세스가 들어가려고 할 때 대기 시간이 무한히 지연되면 안됨</li>
</ul>
</li>
</ol>
<br>

<h3 id="handling-in-os">Handling in OS</h3>
<p>OS에 존재하는 critical-section은 커널이 선점형인지 비선점형인지에 따라 handling 방식이 다르다. 예를 들어, critical section이 보호되지 못하는 선점형의 경우는 Mutual Exclusion을 보장해주는 코드를 구현해야한다.</p>
<ul>
<li>Preemptive (선점형)<ul>
<li>커널 모드에서 실행 중인 프로세스가 선점되는 것을 허용<ul>
<li>우선순위가 높은 프로세스가 CPU사용권을 뺏을 수 있음</li>
</ul>
</li>
<li>race condition이 발생할 수 있어 신중한 설계 필요</li>
</ul>
</li>
<li>Non-preemptive (비선점형)<ul>
<li>커널 모드에서 수행되는 프로세스의 선점을 허용하지 않음</li>
<li>커널 모드 프로세스는 커널 모드가 종료될 때까지, 블록될 때까지, 자발적으로 CPU를 양보할 때까지 실행</li>
<li>경쟁 조건에 대해 고려하지 않아도 됨</li>
</ul>
</li>
</ul>
<blockquote>
<p>Critical-section을 보호하기 위한 여러 Solution에 대해 소개한다. 이 Solution들은 아래와 같이 분류할 수 있따. </p>
</blockquote>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>software 기반</td>
<td>Peterson’s Solution</td>
</tr>
<tr>
<td>hardware 기반</td>
<td>Uni Processor, Memory barrier, Hardware instruction,  Atomic vairable</td>
</tr>
<tr>
<td>hardware를 쉽게 사용할 수 있도록 도와주는 API</td>
<td>Mutex Lock, Semaphore</td>
</tr>
</tbody></table>
<h2 id="petersons-solution">Peterson’s Solution</h2>
<p>critical section problem을 해결하는 고전적인 <strong>소프트웨어 기반 해결책</strong>이다. 현대 컴퓨터 구조에서 항상 올바르게 작동한다고 보장할 수 없다.</p>
<ul>
<li><p>While문을 통해 critical section을 보호하는 해결책</p>
</li>
<li><p><strong>Load</strong>와 <strong>Store</strong> machine language instruction이 <strong>atomic</strong>이라고 가정</p>
<ul>
<li>즉, 중단될 수 없는 instruction</li>
</ul>
</li>
<li><p>두 프로세스는 2개의 변수를 공유</p>
<ul>
<li>int <strong>turn</strong>: 어떤 프로세스가 critical section에 들어갈 차례인지를 나타냄</li>
<li>boolena <strong>flag</strong>[2]: 각 프로세스가 critical section에 들어갈 준비가 되었는지를 나타냄</li>
</ul>
<pre><code class="language-c">   while (true){ 
          flag[i] = true; // i프로세스가 들어갈 준비가 됨
          turn = j; // 자신이 아닌 상태 프로세스로 설정
          while (flag[j] &amp;&amp; turn == j)
                       ; /* critical section에 들어갈 수 없어 대기 */ 
           /* critical section */
           flag[i] = false;
           /* remainder section */
   }</code></pre>
<p>❗turn을 상대 프로세스로 설정하는 이유: 두 프로세스가 동시에 접근하려고 하는 경우에 turn을 각자 자신으로 설정한다고 가정해보자. i가 먼저 turn을 i로 해서 critical section에 들어가려고하는데, 그 사이에 j가 turn을 j로 바꾸면 덮어씌워지면서 j가 critical section에 들어가게 된다. 늦게 온 프로세스가 들어가게 되는 불공정함이 발생하게 된다. 그래서 이를 반대로 구현해야 한다. 즉, i가 들어가려고 시작했고, j도 들어가려고 시작하면 그때 i가 들어가는 것이다.</p>
</li>
</ul>
<h4 id="증명-critical-section-problem의-세가지-조건과-비교">증명: Critical section problem의 세가지 조건과 비교</h4>
<ol>
<li>Mutual exclusion<ul>
<li>프로세스 Pi가 critical section에 들어가기 위해 반드시 flag[j]=false 또는 turn=i이어야 함</li>
<li>만약 flag[0]과 flag[1]이 모두 true여도, turn의 값에 따라 critical section에 동시 진입 불가능</li>
</ul>
</li>
<li>Progress<ul>
<li>Pj가 들어가지 않은 flag[j] = false인 상태면 Pi가 들어갈 수 있음</li>
</ul>
</li>
<li>Bounded waiting<ul>
<li>while문을 수행하는 동안 turn값을 바꾸지 않음 → 한 번은(Bounded waiting) 들어갈 수 있게 보장(Progress)</li>
</ul>
</li>
</ol>
<h4 id="현대-컴퓨터-구조에서-올바르게-동작됨을-보장할-수-없는-이유">현대 컴퓨터 구조에서 올바르게 동작됨을 보장할 수 없는 이유</h4>
<ul>
<li>성능을 향상시키기 위해(optimize), 프로세스나 컴파일러는 dependencies(종속성)이 없는 operation(연산)을 reorder(재정렬)할 수 있음</li>
<li>이는 Single-thread 환경에서는 괜찮은데, multi-thread 환경에서는 결과에 영향(inconsistent)을 미칠 수 있음</li>
</ul>
<h4 id="reordered-example">reordered example</h4>
<ul>
<li><p>두 thread의 공유 데이터</p>
<pre><code class="language-c">  boolean flag = false;
  int x = 0;</code></pre>
</li>
<li><p>thread 1 performs</p>
<pre><code class="language-c">  while (!flag); 
  print x;</code></pre>
</li>
<li><p>thread 2 performs</p>
<pre><code class="language-c">  x = 100; 
  flag = true;</code></pre>
</li>
<li><p>expected output</p>
<ul>
<li>thread 1은 flag가 true가 될 때까지 기다림</li>
<li>thread 2가 x에 100을 할당한 후, flag = true로 설정</li>
<li>thread 1은 while문을 빠져나와 print x</li>
<li>100이 expected output</li>
</ul>
</li>
</ul>
<br>
하지만, thread 2의 operation이 재정렬 된다면?

<ul>
<li><p>reordered thread 2</p>
<ul>
<li><p>x에 100이 할당되기 전에 thread 1이 실행돼서 0이 출력됨 → race condition</p>
<pre><code class="language-c">flag = true;
x = 100; </code></pre>
<p>이러한 문제는 두 프로세스가 동시에 critical section에 들어가도록 허락할 수 있다.</p>
</li>
</ul>
</li>
</ul>
<br>
Peterson’s Solution에서도 turn과 flag 세팅 순서가 바뀌면  critical section에 동시에 들어가게 될 수도 있다.

<p><img src="https://velog.velcdn.com/images/jungizz_/post/001906bb-320c-4190-a767-1c5598cc8e09/image.png" alt=""><br></p>
<h2 id="synchronization-hardware">Synchronization Hardware</h2>
<p>Peterson’s Solution과 같은 소프트웨어 기반 해결책은 현대의 컴퓨터 구조에서 올바른 작동을 보장하지 못하며 속도가 느리다. 많은 시스템은 critical section 구현을 위한 하드웨어를 지원한다. <strong>하드웨어를 사용하는 해결책</strong>들은 critical section을 보호하기 위해 Lock을 사용한다는 점에 기반을 둔다.</p>
<h3 id="uniprocessors">Uniprocessors</h3>
<p>single processor환경에서는 공유 변수가 변경되는 동안 interrupt 발생을 허용하지 않음으로써 해결한다.</p>
<ul>
<li><strong>Uniprocessors</strong>: critical section을 수행 중인 경우, interrupts를 비활성화하여 현재 실행 중인 코드가 중단되지 않도록 한다. cs 실행이 끝나면 다시 interrupt를 활성화한다.<ul>
<li>이는 preemption(선점)을 방지하여 critical section내에서의 안전성을 보장 → Nonpressmptive kernel이 사용하는 방법</li>
<li>하지만 multiprocessor 시스템에서는 비효율적</li>
</ul>
</li>
</ul>
<p>하드웨어 지원에는 Memory barriers, Hardware instructions, Atomic variables의 세가지 형태가 존재한다.</p>
<br>

<h3 id="memory-barriers"><strong>Memory barriers</strong></h3>
<ul>
<li><p>Memory model</p>
<ul>
<li>Strongly ordered: 한 프로세스의 수정이 다른 모든 프로세스에 즉시 알려지게하는 모델 → 성능에 영향</li>
<li>Weakly ordered: 한 프로세스의 수정이 다른 모든 프로세스에 즉시 알려지지 않는 모델 → 필요할 때 알림</li>
</ul>
</li>
<li><p>Memory barriers는 Weakly ordered 모델을 사용</p>
<ul>
<li><p><strong>memory_barrier()</strong>라는 명령어를 사용할 때 메모리의 변화를 모든 다른 프로세스에게 알림</p>
</li>
<li><p>이를 통해 성능에 덜 영향을 미치면서 메모리 연산의 순서를 보장함</p>
<p>  → 다른 프로세스가 메모리의 상태를 일관되게 유지할 수 있도록 함</p>
</li>
</ul>
</li>
</ul>
<h4 id="ex-100-출력을-보장하는-thread-1">ex) 100 출력을 보장하는 thread 1</h4>
<ul>
<li><p>thread 1 performs</p>
<pre><code class="language-c">  while (!flag)
          memory_barrier();
  print x;</code></pre>
</li>
<li><p>thread 2 performs</p>
<pre><code class="language-c">  x = 100;
  memory_barrier(); // reorder 방지
  flag = true;</code></pre>
</li>
</ul>
<br>

<h3 id="hardware-instructions"><strong>Hardware instructions</strong></h3>
<ul>
<li><p>특정 atomic 하드웨어 명령을 사용하여 control section 접근 제어</p>
<ul>
<li><p><strong>Test-and-Set</strong>(읽고 쓰기) instrction, <strong>Compare-and-Swap</strong>(비교하고 바꾸기) instrction</p>
</li>
<li><p>이 instrction은 아래 Definition의 일을 수행하는 건 맞는데, 함수는 아니고 <strong>CPU에서 지원하는 Atomic Machine instruction</strong> 자체</p>
<p>  (Definition은 무슨 일을 하는지 보여주려고 적은 것 뿐~)</p>
</li>
</ul>
</li>
</ul>
<h4 id="test-and-set">Test-and-Set</h4>
<ul>
<li><p>특정 변수의 값을 읽고, 그 값을 수정하는 Atomic 작업 수행</p>
</li>
<li><p><strong>Definition</strong></p>
<pre><code class="language-c">  boolean test_and_set(boolean *target) {
      boolean rv = *target;
      *target = true;
      return rv;
  }</code></pre>
<ul>
<li>Atomic하게 실행</li>
<li>전달된 매개변수의 원래 값을 반환</li>
<li>전달된 매개변수의 새로운 값을 true로 설정</li>
</ul>
</li>
<li><p><strong>Solution</strong></p>
<ul>
<li><p>공유 변수: boolean lock (초기값은 false)</p>
<pre><code class="language-c">do {
  while (test_and_set(&amp;lock)) 
      ; /* do nothing */ 
  /* critical section */ 
  lock = false; 
  /* remainder section */ 
} while (true);</code></pre>
<p>lock이 false이면 잠겨있지 않아서 critical section에 들어갈 수 있음을 의미한다. test_and_set에서는 false를 반환하고 lock의 값을 true로 바꿔 잠군다. while 루프에서 나와 critical section을 수행하고 난 뒤에는 lock을 false로 바꿔 잠금을 해제한다. critical section에 들어올 수 있는 상태로 만든다.</p>
</li>
</ul>
</li>
</ul>
<h4 id="compare-and-swap">Compare-and-Swap</h4>
<ul>
<li><p><strong>Definition</strong></p>
<pre><code class="language-c">  int compare_and_swap(int *value, int expected, int new_value) { 
      int temp = *value; 
      if (*value == expected) 
          *value = new_value; 
      return temp; 
  }</code></pre>
<ul>
<li>Atomic하게 실행</li>
<li>전달된 매개변수의 원래 값을 반환</li>
<li>*value가 expected와 같을 때만 value를 new_value로 설정 → 즉, 이 조건을 만족할 때만 교환 발생</li>
</ul>
</li>
<li><p><strong>Solution 1</strong></p>
<ul>
<li>공유 변수: int lock (초기값은 0)<pre><code class="language-c">while (true) {
  while (compare_and_swap(&amp;lock, 0, 1) != 0) 
      ; /* do nothing */ 
  /* critical section */ 
  lock = 0; 
  /* remainder section */ 
}</code></pre>
lock이 0이면 잠겨있지 않아서 critical section에 들어갈 수 있음을 의미한다. compare_and_swap에서는 0을 반환하고, *value(0)가 expected(0)과 동일하므로 value(0)를 new_value(1)로 설정한다. 즉, lock이 잠겨있지 않음을 확인하고, 1로 바꿔 잠구는 과정이다. while 루프에서 나와 critical section을 수행하고 난 뒤에는 lock을 다시 0으로 설정하여 잠금을 해제한다. 
이 알고리즘은 Mutual Exclusion 조건은 만족하지만, Bounded Waiting을 만족하지 못한다…. 프로세스가 3개라면 p1이 cs를 탈출한 뒤 p2와 p3 중 어떤것을 선택할지 보장할 수 없기 때문이다.. 이를 만족시키기 위해 아래와 같은 알고리즘을 사용할 수 있다. </li>
</ul>
</li>
<li><p><strong>Solution 2</strong>: Bounded-waiting Mutual Exclusion(유한 대기 상호 배제)</p>
<ul>
<li>공유변수: boolean <strong>waiting[n]</strong>, lock (초기값은 false)</li>
<li>여러 노드(프로세스 혹은 쓰레드)들이 있을 때, 우선순위가 낮아서 CPU scheduling을 못받은 노드도 고려해주기 위해 공평하게 번갈아가면서 사용권을 가질 수 있도록 한다.</li>
</ul>
<pre><code class="language-c">  while (true) {
      waiting[i] = true;
      key = 1;
      while (waiting[i] &amp;&amp; key == 1) 
          key = compare_and_swap(&amp;lock, 0, 1); 
      waiting[i] = false; 
      /* critical section */ 
      j = (i + 1) % n; 
      while ((j != i) &amp;&amp; !waiting[j]) 
          j = (j + 1) % n; 
      if (j == i) 
          lock = 0; 
      else 
          waiting[j] = false; 
      /* remainder section */ 
  }</code></pre>
<p>  첫 while문에 들어온 경우 critical section에 들어가려고 시도하는 것이므로 waiting을 true로 설정한다. 그러면 다음 while문에 들어가게 되고, lock이 false인 상태로 compare_and_swap을 호출한다. false가 반환되어 key는 0이 돼서 critical section에 들어오지 못하도록 잠기고, lock은 true로 바뀐다. 이제 들어온 프로세스는 기다리는 상황도 아니니까 waiting도 false로 바꾸고, critical section을 수행한다.</p>
<p>  Pi가 critical section을 수행하고 나오면 i+1해서 j를 만든다. j에게 우선권을 주며 Pj가 waiting상태인지 확인한다. waiting상태가 아니라면 j+1해서 또 다음 노드로 넘어가 우선권을 준다. Pj가 waiting상태라면 lock을 false로 해서 잠금을 해제하고, waiting[j]를 false로 바꾸며 critical section에 들어갈 수 있도록 해준다.</p>
<p>  결국 프로세스는 <span style="color: red"><strong>Lock==0</strong>&amp;&amp; <strong>waiting[i]==true</strong> &amp;&amp; <strong>thread[i-1]이 cs를 나온 경우</strong></span>에 들어갈 수 있다는 것이다.</p>
</li>
</ul>
<br>

<h3 id="atomic-variables"><strong>Atomic variables</strong></h3>
<ul>
<li><p>int, boolean과 같은 data type에게 atomic한 업데이트를 보장한다. (중단되지 않는다)</p>
</li>
<li><p>ex) atomic variable ‘sequence&#39;를 사용해서 ‘increment(&amp;sequence)’ 명령어를 실행하는 경우</p>
<ul>
<li>compare_and_swap에서 v가 temp와 달라졌다는 것은, 또 다른 프로세스가 이에 접근하여 v를 증가시켰다는 것이다. 그럼 현재 프로세스는 v를 증가시킬 필요가 없고, 다시 temp에 v를 담아서 compare_and_swap을 실행한다. 즉, 다른 프로세스가 v를 증가시키지 않은 상태임을 확인하고 v를 증가시키는 것이다.</li>
</ul>
<pre><code class="language-c">  void increment(atomic_int *v)
   {
           int temp;
           do {
                   temp = *v;
           }
           while (temp != (compare_and_swap(v,temp,temp+1));
   } </code></pre>
</li>
</ul>
<p>위의 하드웨어 기반 해결책은 복잡한 형태를 가진다. 이를 프로그래머가 쉽게 사용할 수 있도록 간단한 API들이 개발되었으며, 대표적으로 Mutex Lock과 Semaphore가 있다. 
<br></p>
<h2 id="mutex-locks">Mutex Locks</h2>
<ul>
<li><p>critical section을 보호하기 위해 acquire()로 lock을 얻고, relase()로 lock을 해제한다.</p>
<ul>
<li>boolean acailable 변수가 lock의 상태를 나타낸다.</li>
</ul>
</li>
<li><p>acquire()과 release() 호출은 atomic해야한다.</p>
<ul>
<li>compare-and-swap과 같은 <strong>하드웨어 atomic instruction으로 구현</strong>된다.</li>
</ul>
<pre><code class="language-c">  while (true) { 
      acquire lock 
      /* critical section */ 
      release lock 
      /* remainder section */ 
  } 

  acquire() {
           while (!available) 
                  ; /* busy wait */ 
          available = false;
  } 

  release() { 
          available = true; 
  } </code></pre>
</li>
</ul>
<p>하지만, <strong>Busy waiting</strong>을 해야한다는 단점을 가진다. 이는 한 프로세스가 critical section에 있는 동안 다른 프로세스들은 acquire()로 lock을 얻기 위해 계속해서 반복문을 호출한다(CPU사용권을 놓지 않음). lock이 풀리길 기다리며 계속 회전하므로 <strong>spinlock</strong>이라고도 부른다. 이는 multiprogramming system에서 CPU사이클을 낭비하게 만든다.</p>
<p>그러나, 이는 lock을 기다리는동안 시간을 소모하는 context switch를 전혀 필요로 하지 않는다는 장점을 가져다주기도 한다. 그래서 프로세스들이 짧은 시간 동안만 lock을 소유할 것이라고 예상되면, spinlock이 적절할 수 있다.
<br></p>
<h2 id="semaphore">Semaphore</h2>
<p>Mutex lock보다 더 정교한 방법을 제공하는 동기화 도구이다.</p>
<ul>
<li><p>interger 변수로 정의된 Semaphore <strong>S</strong></p>
<ul>
<li>Semaphore는 오직 <strong>wait()</strong>와 <strong>signal()</strong>을 통해 접근할 수 있다. (atomic operation)</li>
</ul>
<pre><code class="language-c">  wait(S) { 
          while (S &lt;= 0)
                   ; // busy wait
           S--; // 자원을 하나 사용하겠다
   }

   signal(S) { 
          S++; // 자원 사용을 끝마쳤다
   }</code></pre>
</li>
<li><p>Semaphore에는 2가지 종류가 존재한다.</p>
<ul>
<li><strong>Counting semaphore</strong>: 제한 없는 domain을 가지는 integer 값</li>
<li>Binary semaphore: 0과 1의 값을 가지는 interager 값 (Mutex lock과 유사)</li>
</ul>
</li>
</ul>
<h4 id="example-of-counting-semaphore">example of counting semaphore</h4>
<ul>
<li><p>프로세스 P1의 작업 S1이 P2의 S2보다 먼저 수행되어야 하는 문제</p>
<ul>
<li>critical section 뿐만 아니라, 작업 순서의 보장을 필요로하는 경우에도 사용 가능</li>
</ul>
</li>
<li><p>semaphore ‘synch’를 만들고 초기값을 0으로 설정한다.</p>
<pre><code class="language-c">  P1:
       S1;
       signal(synch);
       // 작업 S1을 수행하고 semaphore를 증가하여 P2를 깨움(interrupt처럼)

  P2:
       wait(synch);
       S2;
       // signal interrupt 대기하다가 신호가 오면 작업 S2를 수행</code></pre>
</li>
</ul>
<p>signal()과 wait()두 함수는 동시에 두 개의 프로세스가 실행할 수 없도록 보장해야한다. 이를 위해 두 함수는 critical section 내에서 실행되어야 한다. 하지만, 이는 Busy waiting을 하게 만든다. 다른 프로세스가 이미 critical section에 있을 경우 계속 기다려야한다.</p>
<p>그래도 두 함수 구현 코드 자체가 짧아서 busy waiting 시간이 짧을 수 있다. 그러나 critical section이 자주 차지되는 경우는 application은 많은 시간을 기다리게되고 CPU 사용권을 놓지 않아 성능 저하가 발생할 수 있다. </p>
<p>이를 해결하기 위해 아래와 같은 방법을 고려한다.</p>
<h4 id="semaphore-implementation-with-no-busy-waiting">Semaphore implementation with no Busy waiting</h4>
<ul>
<li><p>각 semaphore를 <strong>waiting queue</strong>에 담아 busy waiting과정을 없앤다.</p>
<ul>
<li>waiting queue는 정수형 value와 다음 항목을 가리키는 pointer를 가진다.</li>
<li>이를 구현하기 위해 semaphore의 구조는 아래와 같이 구현된다.<pre><code class="language-c">typedef struct {
  int value;              // 세마포어의 현재 값
  struct process *list;   // 대기 큐의 첫 번째 프로세스를 가리키는 포인터
} semaphore;</code></pre>
</li>
</ul>
</li>
<li><p>두 가지 주요 연산:</p>
<ul>
<li><p>block: wait()함수가 호출될 때, 해당 프로세스를 적절한 waiting queue에 추가</p>
</li>
<li><p>wakeup: signal()함수가 호출될 때, waiting queue에서 프로세스를 빼고 ready queue로 옮긴다.</p>
<pre><code class="language-c">wait(semaphore *S) {
  S-&gt;value--;  // 세마포어의 값을 감소시킴 (자원 사용)
  if (S-&gt;value &lt; 0) { // 사용할 자원이 없으면(크리티컬 섹션에 들어갈 수 없으면)
      add this process to S-&gt;list;  // 대기 큐에 현재 프로세스를 추가
      block();  // 프로세스를 블록 상태로 전환
  }
}

signal(semaphore *S) {
  S-&gt;value++;  // 세마포어의 값을 증가시킴 (자원 반납)
  if (S-&gt;value &lt;= 0) { // 반납해도 음수라는 것은 기다리는 애들이 있다는 것(크리티컬 섹션에 들어가고 싶은 애들이 있음)
      remove a process P from S-&gt;list;  // 대기 큐에서 프로세스를 제거
      wakeup(P);  // 제거한 프로세스를 준비 큐로 옮김
  }
}</code></pre>
</li>
</ul>
</li>
</ul>
<br>

<ul>
<li><p>semaphore와 같은 동기화 도구를 잘못 사용하면 여러가지 문제를 초래할 수 있다.</p>
<pre><code class="language-c">  // 1. 잘못된 순서로 Semaphore 연산 사용
  signal(mutex);
  . . .
  wait(mutex);

  // 2. 중복 호출
  wait(mutex);
  . . .
  wait(mutex);

  // 3. 생략
  wait(mutex); 
  . . .
  //signal(mutex) -&gt; 생략</code></pre>
</li>
</ul>
<br>

<h2 id="monitors">Monitors</h2>
<ul>
<li>middleware(complier)가 제공하는 동기화 메커니즘</li>
<li>한 번에 하나의 프로세스만 모니터에 들어갈 수 있다<img src="https://velog.velcdn.com/images/jungizz_/post/282e5aac-1b95-4d26-8d39-bace459c89b2/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/7e39ad47-293a-45e7-a848-7532edb35b87/image.png" alt=""></li>
<li>critical section 보호하는 것 이외에도 특정 조건을 기다리는 것도 가능하다 (ex-버퍼에 자리가 날 때까지 기다림)<ul>
<li>x.wait(): x.signal()이 호출될 때까지 기다림</li>
<li>x.signal(): x.wait()를 호출한 프로세스 중 하나를 깨운다.<ul>
<li>이때, 우선순위에 따라 깨울 프로세스를 결정<img src="https://velog.velcdn.com/images/jungizz_/post/d8c94802-10c9-40d3-ae34-ca1cb55d8826/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
<li>만약 프로세스 P는 x.signal()로 특정 컨디션에 깨우고, 프로세스 Q는 x.wait()로 특정 컨디션을 기다린다면, P가 Q에게 신호를 주는 셈이다.<ul>
<li>이때, P가 Q를 깨우고 Q가 모니터 코드를 실행하면 signal and wait</li>
<li>P가 Q를 꺠우고 P가 모니터 코드를 실행하면 signal and continue</li>
</ul>
</li>
</ul>
<br>

<p>세마포를 사용해서 monitor를 구현하면 아래와 같다.</p>
<p><strong>1. F함수 보호</strong><img src="https://velog.velcdn.com/images/jungizz_/post/e979ffd6-729f-4f02-be9c-bf3478e4fe18/image.png" alt=""></p>
<p><strong>2. 조건 변수 사용</strong> (뭘 깨우고 어떤걸 기다리는지~)<img src="https://velog.velcdn.com/images/jungizz_/post/26382dd9-c260-42e2-98d8-1606c023f4a1/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/563c18b8-262e-478f-a5bd-dbde35876f15/image.png" alt=""></p>
<ul>
<li>단일 리소스를 경쟁하는 경우, 프로세스가 리소스를 사용할 최대 시간을 지정할 수 있다. (Single resource allocation)<img src="https://velog.velcdn.com/images/jungizz_/post/bf4c3b72-1df2-46f2-b97e-e9323f88b0f2/image.png" alt=""><br></li>
</ul>
<h2 id="liveness">Liveness</h2>
<p>보통 동기화 프로그램은 ‘기다리고-깨우고’하는 형태이다. 이때, 프로그램이 계속 실행되기 위해서 무한정 기다리는 상황이 발생하면 안된다. → Liveness failure</p>
<ul>
<li><p>Deadlock: 두 개 이상의 프로세스가 서로를 기다리는 상황으로 프로세스가 진행되지 않는다.<img src="https://velog.velcdn.com/images/jungizz_/post/6435e2a1-47de-4a65-b0ac-a16c3b5e1c7b/image.png" alt=""></p>
</li>
<li><p>Starvation: 우선순위가 낮은 프로세스가 계속 기다리는 상황</p>
<br></li>
<li><p>Priority Inversion: 우선순위 역전</p>
<ul>
<li><p>낮은 우선순위가 특정 리소스를 점유하고 있고, 높은 우선순위가 그 리소스를 원해서 기다리고 있다. 그때, 그 리소스가 필요없는 중간 우선순위가 CPU를 할당받아버리는 경우</p>
<p>  → 높은 우선순위는 낮은 우선순위한테 리소스도 밀리고, 중간 우선순위한테 CPU할당도 밀림</p>
</li>
<li><p>리소스로 인해 문제가 발생한 경우, 낮은 우선순위는 리소스를 해제하지 않고 / 기다리는 프로세스 중 가장 높은 우선순위의 프로세스를 상속받아 / 리소스를 사용하여 프로세스를 수행하는  <strong>Priority-inheritance protocol</strong>로 해결할 수 있다.</p>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 5. CPU Scheduling]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5.-CPU-Scheduling</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-5.-CPU-Scheduling</guid>
            <pubDate>Sat, 26 Apr 2025 07:01:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡-5장-목표">💡 5장 목표</h4>
</blockquote>
<ul>
<li>스케줄링 유형 및 알고리즘</li>
<li>스레드 스케줄링</li>
<li>멀티 프로세서 스케줄링</li>
<li>실시간 스케줄링</li>
<li>운영체제별 예시</li>
</ul>
<p>multi-programming의 목적은 <strong>CPU 이용률(Maximum CPU utilization)을 최대화</strong>하기 위해 항상 실행 중인 프로세스를 갖게 하는 것이다. 즉, 할 일이 있을 때 CPU가 무조건 일하게 하기 위해서 멀티 프로그래밍을 하는 것이다.</p>
<h3 id="cpu-io-burst-cycle">CPU-I/O Burst Cycle</h3>
<ul>
<li>프로세스 실행은 <strong>CPU 실행</strong>과 <strong>I/O대기</strong>의 Cycle로 구성<ul>
<li><strong>CPU burst</strong>: CPU만 연속적으로 사용하는 것, CPU를 필요로 하는 부분?</li>
<li>CPU burst로 시작하여 두 상태를 교대로 왔다갔다 하며 프로세스 실행</li>
</ul>
</li>
<li>CPU burst의 프로세스에 어떤 순서로 CPU를 할당할 것인지가 CPU scheduling</li>
<li>보통 <strong>CPU burst-time(CPU를 필요로 하는 시간)</strong>은 짧다. AI계산 등 몇몇 경우에 길기도 함<img src="https://velog.velcdn.com/images/jungizz_/post/c3e3e94a-5c15-4c15-97e4-8140415484c1/image.png" alt=""><br>
# CPU Scheduler

</li>
</ul>
<p>CPU 스케줄러는 CPU를 필요로 하는 ready queue에 있는 프로세스 중 하나를 선택하여 CPU 코어에 할당한다. ready queue는 다양한 방식으로 정렬된다.</p>
<ul>
<li>CPU 스케줄링은 아래와 같은 상황에서 발생한다.<ol>
<li>프로세스가 running state에서 waiting state로 바뀔 때 (I/O를 기다릴 때) </li>
<li>프로세스가 running state에서 ready state로 바뀔 때 (time sharing 등의 interrupt, 혼자 CPU를 독점하지 않도록) </li>
<li>프로세스가 waiting state에서 ready state로 바뀔 때 (I/O완료, 우선 순위 기다림)</li>
<li>프로세스가 terminates (작업 종료)</li>
</ol>
</li>
<li>1, 4번은 CPU가 더이상 필요하지 않은 상태라서 CPU사용 권한을 넘긴다 → non-preemptive(비선점)<ul>
<li><strong>비선점 스케줄링:</strong> CPU를 사용 중인 프로세스의 CPU사용 권한을 강제로 빼앗지 않는다. 그 프로세스가 더이상 CPU가 필요없다고 하는 경우에만 권한을 가져온다. (그 프로세스는 계속 CPU 점유 가능)</li>
</ul>
</li>
<li>2, 3번은 일울 다 마치지 못해 CPU가 필요하지만 일단 CPU 사용 권한을 넘긴다 → preemtive(선점)<ul>
<li><strong>선점 스케줄링:</strong> CPU를 사용 중인 프로세스의 CPU 사용 권한을 강제로 빼앗을 수 있다. (time sharing이나 우선 순위의 이유로)</li>
<li>성능이 좋지만, 아래와 같은 민감한 상황에서 위험할 수 있다.<ul>
<li>공유 데이터에 접근, 커널 모드에서의 선점, 중요한 OS작업 중 인터럽트 발생 가능성 → race condtion 발생 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
<br>

<h2 id="dispatcher">Dispatcher</h2>
<p>CPU스케줄링에 의해 프로세스가 바뀔 때, 기존 프로세스의 정보는 저장하고 새로운 프로세스의 정보를 로드하는 작업 </p>
<ul>
<li>switching context</li>
<li>switching to user mode</li>
<li>프로그램을 다시 시작하기 위해 user program을 적절한 위치로 이동</li>
</ul>
<p>dispatch가 소요되는 시간을 <strong>Dispatch latency</strong>라고 한다.<img src="https://velog.velcdn.com/images/jungizz_/post/75df5219-6287-4f9d-a5bc-8c7905b6e6a0/image.png" alt=""><br></p>
<h1 id="scheduling-criteria">Scheduling Criteria</h1>
<p>스케줄링 기준</p>
<ol>
<li>CPU 사용률(utilzation) <code>최대화</code><ul>
<li>가능한 한 CPU를 바쁘게 유지 시키기</li>
</ul>
</li>
<li>처리량(Throughput) <code>최대화</code><ul>
<li>단위 시간 당 완료된 프로세스의 개수</li>
</ul>
</li>
<li>총 처리 시간, 반환 시간(Turnaround time) <code>최소화</code><ul>
<li>특정 프로세스가 완료되는데 소요되는 시간</li>
<li>메모리에 들어가기 위해 기다린 시간 + ready queue에서 기다린 시간 + CPU에서 자신을 실행한 시간 + I/O시간</li>
</ul>
</li>
<li>대기 시간(Waiting time) <code>최소화</code><ul>
<li>ready queue에서 CPU를 기다린 총 시간</li>
</ul>
</li>
<li>응답 시간(Response time) <code>촤소화</code><ul>
<li>처음으로 CPU를 할당 받을 때까지 기다린 시간 (waiting time과 다름)</li>
</ul>
</li>
</ol>
<br>

<h1 id="🔹scheduling-algorithm">🔹Scheduling Algorithm</h1>
<h2 id="1-first-come-first-servedfcfs-scheduling">1. First-Come, First-Served(FCFS) Scheduling</h2>
<ul>
<li>비선점형 알고리즘</li>
<li>CPU를 먼저 요청하는 프로세스가 CPU를 먼저 할당받음</li>
<li>FIFO Queue로 쉽게 관리, 구현 가능</li>
<li>공평하지만 상황에 따라 평균 대기 시간이 매우 길어질 수 있다.<blockquote>
<p>만약 프로세스 3개가 아래와 같을 때,<img src="https://velog.velcdn.com/images/jungizz_/post/9e7dc521-fe2d-4685-a9ac-50cb83e8e684/image.png" alt="">
프로세스가 P1, P2, P3 순서로 도착했다면, P1의 대기시간은 0ms, P2는 24ms, P3는 27ms이다. (평균 대기 시간 17ms)<img src="https://velog.velcdn.com/images/jungizz_/post/8dbd377a-c633-448a-9835-7fc3bf30e05b/image.png" alt="">
프로세스가 P2, P3, P1 순서로 도착했다면, P1의 대기시간은 6ms, P2는 0ms, P3는 3ms이다. (평균 대기 시간 3ms)<img src="https://velog.velcdn.com/images/jungizz_/post/a509160c-5bcd-4646-9ec1-ee685e31d73d/image.png" alt=""></p>
</blockquote>
</li>
<li>이처럼 다른 프로세스들이 하나의 긴 프로세스를 기다리는 것을 호<strong>Convoy effect</strong>라고 한다.<ul>
<li>이 효과로 인해 짧은 프로세스들이 먼저 처리되도록 허용될 때보다 CPU와 장치 이용률이 저하되는 것이다.</li>
</ul>
</li>
</ul>
<br>

<h2 id="2-shortest-job-firstsjf-scheduling">2. Shortest-Job-First(SJF) Scheduling</h2>
<ul>
<li>비선점형이거나 선점형일 수 있다.<ul>
<li>선점형이면 shortest-remaining-time-first algorithm (아래 예시 있음)</li>
</ul>
</li>
<li>CPU burst time이 가장 작은 프로세스를 먼저 CPU에 할당</li>
<li>평균 대기 시간이 가장 작은 알고리즘<blockquote>
<p><img src="https://velog.velcdn.com/images/jungizz_/post/69c4ad90-a553-452f-877b-682e3c84a0ce/image.png" alt=""></p>
</blockquote>
</li>
<li>하지만, 다음 CPU burst time을 미리 아는 것이 어려움</li>
</ul>
<h4 id="next-cpu-burst-길이-추정하기">Next CPU Burst 길이 추정하기</h4>
<ul>
<li>다음 CPU 버스트 길이를 정확히 알 수 없으므로, <strong>이전 버스트와 유사할 것으로 추정</strong>한다.</li>
<li>이전 버스트들의 값을 평균내는데, 최근 버스트에 더 큰 가중치를 두는 지수 평균(exponential averaging)을 사용한다.<ul>
<li>한 단계만 떼어 보면, <strong>실제 이전 버스트</strong>와 <strong>추정한 이전 버스트</strong> 값을 평균내서 <strong>다음 버스트</strong> 값을 추정<img src="https://velog.velcdn.com/images/jungizz_/post/8f7c8287-6724-4078-a2c4-29703f19b41a/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/4b3f11a9-2f30-4315-b725-ad9a479b915d/image.png" alt=""></li>
<li>전체적으로 보면, 최근 버스트에 더 큰 가중치를 두는 모습<img src="https://velog.velcdn.com/images/jungizz_/post/03ace005-99de-4165-b8ce-d7a80f3470a8/image.png" alt=""></li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="shortest-remaining-time-first-algorithm-선점형">shortest-remaining-time-first algorithm (선점형)</h4>
<ul>
<li>비선점형이 비효율적이어서 나온 방법</li>
<li>수행 하다가 남은 burst보다 더 짧은 burst를 가진 프로세스가 있으면 권한 넘김<img src="https://velog.velcdn.com/images/jungizz_/post/512b2117-5ba4-4859-acf5-22a20fc3a119/image.png" alt=""></li>
</ul>
</blockquote>
<br>

<h2 id="3-round-robindrr-scheduling">3. Round-Robind(RR) Scheduling</h2>
<ul>
<li>time-sharing system</li>
<li><strong>시간 조각(time quantum)</strong>이라고 하는 작은 단위의 시간을 정의하여 사용 (보통 10-100ms)</li>
<li>ready queue는 circular queue로 동작하며, CPU스케줄러는 ready queue를 돌면서 한 번에 한 프로세스에세 한 번의 시간 조각 동안 CPU를 할당</li>
<li>ready queue에 n개의 프로세스, time quantum이 q일 때<ul>
<li>각 프로세스는 1/n 비율로 CPU 시간을 할당받고, 하나의 실행에서 최대 q시간 단위까지 사용 가능</li>
<li>할당 받은 한 프로세스를 제외한 나머지 기다리는 프로세스는 n-1개이므로, 최대 (n-1)q시간을 기다린다</li>
</ul>
</li>
<li>각 time quantum이 끌날 때마다 타이머가 인터럽트를 발생시켜 다음 프로세스를 스케줄링</li>
</ul>
<h4 id="성능">성능</h4>
<ul>
<li>q가 너무 크면 → FCFS와 유사</li>
<li>q가 너무 작으면 → 기다리는 (n-1)q시간은 줄어들지만, context switch의 오버헤드가 너무 커짐 (context switch가 너무 많이 발생)<img src="https://velog.velcdn.com/images/jungizz_/post/d2a71576-557f-4fe8-beb8-6a9957bb3c41/image.png" alt="">    </li>
<li>RR의 단점은 q에 따른 turnaround time인데, 운이 좋아서 time quantum에 맞게 일이 끝나면 turnaround time이 짧을 순 있지만 그만큼 길어질 수도 있다. 일정하지 않다<img src="https://velog.velcdn.com/images/jungizz_/post/56c80378-1abf-45ce-a3ad-b0eefca5049e/image.png" alt=""></li>
</ul>
<p>→ RR은 time quantum의 크기에 매우 많은 영향을 받는다.</p>
<ul>
<li>context swich &lt; 10 usec가 되도록 q를 적당히 정해야 함</li>
<li>보통 time quantum은 <strong>context switch 시간에 비해 충분히 커야하고, CPU burst의 80%는 time quantum보다 짧아야 한다.</strong></li>
</ul>
<blockquote>
<p>q=4일 때,<img src="https://velog.velcdn.com/images/jungizz_/post/10c477b1-d9b0-4543-8c86-340fab5deef1/image.png" alt=""></p>
<ul>
<li>SJF보다 turnaround time(30)이 길지만, response time(11)은 짧다</li>
</ul>
</blockquote>
<br>

<h1 id="priority-scheduling">Priority Scheduling</h1>
<ul>
<li>위의 3가지 방식들처럼 각 프로세스는 우선 순위를 가진다.</li>
<li>가장 높은 우선 순위를 가진 프로세스에게 CPU 할당<img src="https://velog.velcdn.com/images/jungizz_/post/9543da8b-ba1d-4e55-8f90-68e928290ccd/image.png" alt=""></li>
</ul>
<p><strong>주요 문제 - Starvation</strong></p>
<ul>
<li>낮은 우선 순위를 가진 프로세스들이 CPU를 무한히 대기(starvation)할 수도 있다</li>
<li>이를 해결하기 위해 <strong>Aging</strong> → 오랫동안 시스템에서 대기하는 프로세스들의 우선순위를 점진적으로 높이거나, CPU할당을 많이 받은 프로세스의 우선순위를 점진적으로 낮추기</li>
</ul>
<p><strong>우선 순위가 같은 경우 → RR</strong></p>
<ul>
<li>우선 순위가 같은 프로세스를 RR로 해결  (q=2ms)<img src="https://velog.velcdn.com/images/jungizz_/post/4d35b47c-f72a-4d37-a024-11e1d8bae591/image.png" alt=""><br></li>
</ul>
<h2 id="multilevel-queue-scheduling">Multilevel Queue Scheduling</h2>
<ul>
<li>priority scheduling에서, 우선 순위에 따라 ready queue를 그룹으로 분리</li>
<li>높은 우선순위를 가진 그룹 먼저 수행</li>
<li>각 queue들은 자신의 스케줄링 알고리즘을 가질 수 있다<img src="https://velog.velcdn.com/images/jungizz_/post/192237ac-2b54-4c3a-b90b-0efae68d8700/image.png" alt=""></li>
<li>우선순위는 프로세스 타입에 따라 결정<img src="https://velog.velcdn.com/images/jungizz_/post/507bf551-a518-446b-8612-8b01618b6542/image.png" alt=""></li>
</ul>
<p>원래는 한 큐가 끝날 때까지 수행하는데, <strong>Feedback</strong>을 사용하여 <strong>여러 queue 사이에서 이동</strong>하며 수행할 수 있다.</p>
<ul>
<li>Aging을 구현하는데 사용될 수 있다.</li>
<li>각 프로세스의 CPU 사용 패턴에 따라 <strong>동적으로 우선순위를 조절</strong><h4 id="주요-파라미터">주요 파라미터</h4>
</li>
</ul>
<ol>
<li>queue의 수</li>
<li>각 queue의 스케줄링 알고리즘</li>
<li>프로세스가 높은 우선 순위 queue로 이동하는 조건(upgrade)<ul>
<li>ex) 프로세스가 특정 시간 동안 CPU를 사용하지 않거나, 특정 작업 완료할 때</li>
</ul>
</li>
<li>프로세스가 낮은 우선 순위 queue로 이동하는 조건(demote)<ul>
<li>ex) 프로세스가 특정 시간 이상 CPU를 점유하거나, 자원을 과도하게 사용할 때</li>
</ul>
</li>
<li>서비스가 필요한 프로세스가 들어갈 queue를 결정하는 방법<ul>
<li>새로운 프로세스가 어떤 queue에 배치될지를 결정</li>
</ul>
</li>
</ol>
<blockquote>
<p>3개의 큐(RR(8ms), RR(16ms), FCFS)가 있는 경우 (높은 우선순위 순)<img src="https://velog.velcdn.com/images/jungizz_/post/31f88a56-3d91-47e1-9360-33f79668adcc/image.png" alt=""></p>
<ul>
<li>새로운 프로세스가 Q0에 들어와서 CPU를 할당받으면 8ms동안 실행</li>
<li>만약 8ms 내에 작업이 완료되지 않으면 해당 프로세스는 Q1으로 이동</li>
<li>Q1에서 16ms내에 작업이 완료되지 않으면 해당 프로세스는 선점(preempted)되어 Q2로 이동</li>
<li>Q2에서 FCFS으로 처리 (가장 낮은 우선 순위)</li>
</ul>
</blockquote>
<p>짧은 작업을 우선으로 끝내고, 한 큐에서 다 끝나지 않는 작업은 다음 우선순위의 큐로 나머지 작업들을 넘김(demote)</p>
<blockquote>
<p>→ 짧은 작업에 높은 우선순위를 두는 방법(SJF과 유사)이며, 다양한 알고리즘 방법을 융합하여 사용할 수 있는 장점을 가짐</p>
</blockquote>
<br>

<h1 id="🔹thread-scheduling">🔹Thread Scheduling</h1>
<p>프로세스 안에는 여러 스레드가 존재할 수 있다. </p>
<p>유저 레벨 스레드와 커널 레벨 스레드 사이는 Light Weight Process(LWP)라는 경량 프로세스가 있다. 커널마다 존재하는 LWP는 many-to-one과 many-to-many 모델에서 유저 레벨 스레드를 커널 레벨 스레드에 매핑하는 역할을 한다</p>
<ul>
<li>이 때, 한 프로세스에 있는 여러 유저 레벨 스레드에 중 한 스레드를 LWP를 할당하기 위해 스케줄링하는게 process-contention scope(PCS)이다.</li>
<li>더 나아가, 커널 레벨 스레드에 CPU를 할당하기 위해 스케줄링 하는게 system-contention scope(SCS)이다. → 이는 PCS와 다르게 한 프로세스가 아닌 모든 시스템 차원에서 경쟁<br>

</li>
</ul>
<p>스레드를 만들 때 PTHREAD_SCOPE_PROCESS 또는 PTHREAD_SCOPE_SYSTEM라는 스코프 옵션을 제공하는 API를 사용하여 스레드 스케줄링 방식을 정할 수 있다.</p>
<ul>
<li><p>Linux와 macOS는 PTHREAD_SCOPE_SYSTEM만 지원한다</p>
<p><img src="https://velog.velcdn.com/images/jungizz_/post/668f6672-4988-4ef4-94bc-5700fe45d97d/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/c0972f0f-495a-4246-a273-7e1669447fef/image.png" alt=""><br></p>
</li>
</ul>
<h1 id="🔹multiple-processor-scheduling">🔹Multiple-Processor Scheduling</h1>
<ul>
<li>Multiple-processor는 CPU가 여러개인 경우이다</li>
<li>Multicore processor는 한 CPU 내에 코어가 여러개인 경우로, compute cycle과 memory access일을 돌아가면서 수행한다.<img src="https://velog.velcdn.com/images/jungizz_/post/6e8ffc41-3462-4e8c-9ade-cc61c2893de0/image.png" alt=""><ul>
<li>여러 CPU/core가 한 개의 ready queue를 공유하거나, 각자의 ready queue를 가질 수 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/5902c7ef-3623-420d-8c24-20868da9d019/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h2 id="multithreaded-multicore-system">Multithreaded Multicore system</h2>
<p>Multithreaded multicore는 한 코어 내에 하드웨어 스레드가 여러개인 경우로, 한 스레드가 compute cycle할 때 다른 스레드가 memory access를 동시에 수행할 수 있다<img src="https://velog.velcdn.com/images/jungizz_/post/a3d62380-bfcc-45c9-84bc-a3c9c56e27ff/image.png" alt=""></p>
<p>위와 같이 1개의 CPU 내에 여러 코어, 여러 스레드를 가지는 구조를 <strong>Chip-multhtreading(CMT)</strong>라고 하며, 소프트웨어 입장에서는 8개의 CPU를 사용하는 것과같은 효과를 가질 수 있다.<img src="https://velog.velcdn.com/images/jungizz_/post/c770098b-1842-40a8-baa2-b2d39e2e91b9/image.png" alt=""></p>
<p>위에서 소프트웨어 내의 스레드 스케줄링에 대해 봤는데, 하드웨어 스레드가 추가되면서 스케줄링 단계가 하나 더 추가됐다.<img src="https://velog.velcdn.com/images/jungizz_/post/9513306c-68f5-4749-8a64-1e22d74da5c4/image.png" alt=""></p>
<h3 id="load-balancing">Load balancing</h3>
<p>SMP(symmetric multiprocessing; CPU마다 같은 역할)이라면, 모든 CPU가 공평하게 일을 분배 받아야 한다. 이것이 Load balancing</p>
<p>공평하지 않다면, 아래와 같은 migration을 진행</p>
<ul>
<li>Push migration: 일부 CPU만 일이 많은 경우, 일이 적은 CPU로 일을 보냄</li>
<li>Pull migration: 일부 CPU만 일이 적은 경우, 일이 많은 CPU로 부터 일을 받아옴<br>
## Processor Affinity

</li>
</ul>
<p>CPU가 여러개일 때, 어떤 작업이 어떤 CPU에 적합한지(친밀, affinity)</p>
<ul>
<li>Soft affinity: 이전에 일했던 CPU → 캐시가 남아있어 성능에 도움이 될 수 있다.</li>
<li>Hard affinity: 하드웨어적으로 작업이 잘 맞는, 성능이 좋은 CPU</li>
</ul>
<h4 id="numa-and-scheduling">NUMA and scheduling</h4>
<p>메모리마다 접근 시간이 다르다는 NUMA를 고려하여 CPU를 할당<img src="https://velog.velcdn.com/images/jungizz_/post/dc5cd5a1-2189-45cf-8bb8-2f25bebb9aac/image.png" alt=""><br></p>
<h1 id="🔹real-time-cpu-scheduling">🔹Real-Time CPU Scheduling</h1>
<p>빨리 수행하는 것이 아닌, 특정 기한(Dead line)내에 작업을 완료했는지가 성능을 나타낸다.</p>
<ol>
<li>Soft real-time system: dead line 내에 작업을 완료하지 못해도 나머지 작업을 수행하긴 한다. 하지만 성능이 떨어지고 스케줄링이 보장되지 않는다.</li>
<li>Hare real-time system: 작업이 반드시 dead line 내에 완료되어야 하며, 그렇지 않은 나머지 작업은 수행되지 않는다.<br>
## Event Latency

</li>
</ol>
<p>Interrupt, Dispatcher 등과 같은 이벤트를 기다리고 수행하는 시간은 실시간 시스템에 큰 영향을 미친다. 이를 알면 dead line 내에 수행 가능한지 알 수 있기 때문<img src="https://velog.velcdn.com/images/jungizz_/post/cfdd44a0-d080-47e6-8254-db6f76943ae1/image.png" alt=""></p>
<p>이벤트를 기다리고 수행하는 시간은 Event latency라고 하며, 두가지 종류가 있다.</p>
<p><strong>1. Interrupt Latency</strong>: 인터럽트가 발생한 순간부터 인터럽트를 처리하는 루틴(ISR, interrupt service routine)이 시작될 때까지 걸리는 시간<img src="https://velog.velcdn.com/images/jungizz_/post/01ca23eb-c2a7-498d-a3e9-a41ac1046948/image.png" alt=""></p>
<p><strong>2. Dispatch latency</strong>: 현재 프로세스를 CPU에서 제거하고, 다른 프로세스로 스위치하는데 걸리는 시간
    - Conflict phase(충돌 단계): 낮은 우선순위의 프로세스가 특정 리소스를 독점하고 있을 때, 해당 리소스를 사용하기 위해 높은 우선순위의 프로세스가 기다리게 되는 시간<img src="https://velog.velcdn.com/images/jungizz_/post/6bfbc357-d27c-4ff7-81cf-bc571762f545/image.png" alt=""><br></p>
<h2 id="priority-based-scheduling">Priority-Based Scheduling</h2>
<p>실시간 스케줄러는 선점적(preemptive) 우선순위 기반 스케줄링을 지원해야한다. (실시간 프로세스가 CPU를 필요로 할 때, 바로 응답해주는 것)</p>
<p>하지만 soft real-time만을 보장하며, hard real-time을 위해서 주어진 기한 내에 처리할 수 있는 능력을 제공해야한다.</p>
<h4 id="perodic-process-→-periodic-job">Perodic Process → Periodic Job</h4>
<p>(위에서 본 event 작업 이외에는) 일정한 간격으로 CPU를 필요로 하는 주기적 프로세스가 존재한다.</p>
<ul>
<li>process time(cpu brust) <strong>t</strong>, deadline <strong>d</strong>, period <strong>p</strong></li>
<li>0 ≤ t ≤ d ≤ p</li>
<li>주기적 작업의 비율 rate = 1/p → 얼마나 자주 실행되는지를 나타냄<img src="https://velog.velcdn.com/images/jungizz_/post/b55dbd5c-5698-4cbe-b994-de48cf5d4dfa/image.png" alt=""><br></li>
</ul>
<h3 id="1-rate-montonic-scheduling">1. Rate Montonic Scheduling</h3>
<ul>
<li>주기가 짧은 프로세스가 우선 순위를 가짐 (자주 수행되므로)</li>
</ul>
<blockquote>
<p>아래와 같은 두 개의 프로세스가 있는 경우 (deadline은 주기와 같음)<img src="https://velog.velcdn.com/images/jungizz_/post/aeae76f6-6aa2-4ae2-b97a-03a8b95c64fd/image.png" alt=""></p>
<p>만약, 우선순위가 지켜지지 않아 P2가 먼저 실행되면, 스케줄러는 P1의 기한을 지키지 못한다. (50을 넘음)<img src="https://velog.velcdn.com/images/jungizz_/post/8ca88983-9fd9-4451-9db5-27bef6076321/image.png" alt=""></p>
<p>우선순위에 따라 P1이 먼저 실행되면, P1이 작업을 마치고 P2가 실행된다. P2는 35 중 30만 작업을 완료하지만, P1의 주기가 돌아와 선점된다. P1이 끝나면 P2가 이어서 실행되고 나머지 작업 5를 마친다. → 두 프로세스는 75에 완료되고 다시 반복하기 전 25가 유휴상태가 된다.<img src="https://velog.velcdn.com/images/jungizz_/post/be50b3bf-12ef-4355-a53a-72631edf5590/image.png" alt=""></p>
</blockquote>
<ul>
<li><strong>non-optimal</strong>: 최적의 기법이긴 하지만, CPU이용률에 한계가 있기 때문에 CPU자원을 최대화해서 사용하는 것은 불가능하다.</li>
<li><strong>Deadline miss</strong>: 만약 P2의 주기와 기한이 80이라면, P2는 기한 내에 작업을 마치지 못한다.<img src="https://velog.velcdn.com/images/jungizz_/post/375163ed-7878-4072-92b3-768205d6721a/image.png" alt=""><br></li>
</ul>
<h2 id="2-earliest-deadline-firstedf-scheduling">2. Earliest Deadline First(EDF) Scheduling</h2>
<ul>
<li>deadline이 이른 프로세스가 우선 순위를 가짐 (동적으로)</li>
</ul>
<blockquote>
<p>50에서 P1의 한 주기가 끝났고, 이제는 P2의 deadline이 더 빠르게 때문에 P2가 우선 순위를 가져 60까지 P2 수행<img src="https://velog.velcdn.com/images/jungizz_/post/80ae5ae7-aced-46a5-9365-af55b97eaa33/image.png" alt=""></p>
</blockquote>
<ul>
<li>deadline miss 없음, Optimal함 → reatime scheduling에서 최적의 방법</li>
<li>Rate-Monotonic 스케줄링보다 유휴시간이 적지만, ready queue에 프로세스가 들어올 때마다 deadline을 확인해야 해서 overhead가 크다.<br>
## 3. Proportional Share Scheduling

</li>
</ul>
<p>프로세스에게 CPU 시간을 공정하게 할당하기 위해, 스케줄러는 시간을 쪼개서 각 프로세스에게 분배</p>
<ul>
<li>총 공유 수 T: 시스템의 모든 프로세스가 사용할 수 있는 CPU시간의 총 합</li>
<li>프로세스에 할당된 공유 N: 각 프로세스는 T중에서 N을 할당받는다 (N &lt; T)</li>
<li>각 프로세스는 N/T 비율만큼 CPU 시간을 할당받는다.</li>
</ul>
<blockquote>
<p>T=100이고, 3개의 프로세스 A, B, C에게 각각 50, 15, 20씩 할당한 경우 → A는 CPU시간의 50%, B는 15%, C는 20%를 할당받는다. A는 50%의 CPU를 독점하는 것이다.</p>
</blockquote>
<br>

<h2 id="posix-api">POSIX (API)</h2>
<p>우선 순위가 같은 경우 FIFO 또는 RR 알고리즘을 사용한다.
스레드를 만들 때, POSIX API를 통해 SCHED_FIFO 또는 SCHED_RR 옵션을 지정할 수 있다</p>
<p><img src="https://velog.velcdn.com/images/jungizz_/post/4d330981-91b6-4110-97c0-301277989ea0/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/e62cd25d-2ade-4564-99f0-94d50888f9ca/image.png" alt=""></p>
<h5 id="❗일반적으로-real-time-테스크가-non-real-time-테스크보다-더-높은-우선순위를-가진다-아레-운영체제들의-공통-내용">❗일반적으로 Real-time 테스크가 non Real-time 테스크보다 더 높은 우선순위를 가진다. (아레 운영체제들의 공통 내용)</h5>
<br>

<h1 id="🔹os-examples">🔹OS examples</h1>
<h2 id="1-linux">1. Linux</h2>
<ul>
<li><p>2.5버전 이전</p>
<ul>
<li>선점형 및 우선순위 기반</li>
<li>우선순위 범주는 time-sharing과 real-time로 나눠짐<ul>
<li>0<del>99는 real-time, 100</del>140은 nice value</li>
<li>real time이 더 높은 우선순위를 가진다</li>
<li>nice value는 낮은 우선 순위로 다른 프로세스에게 nice하다는 것이다</li>
</ul>
</li>
<li>RR방법을 사용하여, 높은 우선순위는 긴 time quantum을 할당받는다<ul>
<li>할당 받은 시간을 다 못쓰면 active, 다 쓰면 expired</li>
</ul>
</li>
<li>테스크는 CPU마다 가지는 Runqueue 자료 구조를 track<ul>
<li>active, expired 두 그룹의 배열</li>
<li>active가 더이상 없으면, 두 배열 바뀜</li>
</ul>
</li>
<li>위와 같은 방식으로 우선순위가 낮아도 기회가 오는데, interrupt 반응 시간이 느리다는 단점</li>
</ul>
</li>
<li><p>2.6.23버전 이상</p>
<ul>
<li>프로세스마다 우선순위 <strong>레벨(class)</strong> 배정<ul>
<li>default, realtime, … 등</li>
<li>각 레벨끼리는 독립적</li>
<li>스케줄러는 높은 우선 순위의 레벨과 높은 우선 순위의 테스크를 선택</li>
</ul>
</li>
<li>target latency로 모든 프로세스가 한 번의 기회는 가지도록, 공평성</li>
<li><strong>vruntime</strong>: 각 테스크의 vruntime을 유지하여 우선 순위에 따른 감소율 decay factor를 적용<ul>
<li>vruntime은 얼마나 cpu 시간을 할당 받았는지를 나타낸다. 할당을 많이 받을수록 높은 우선순위를 가졌다는 것을 알 수 있다. (vruntime이 크면 높은 우선 순위)<ul>
<li>vruntime이 크면 작은 decay factor를 적용하여 vruntime이 조금씩 줄어들도록 한다. 즉, 우선순위가 조금씩 떨어진다.</li>
<li>vruntime이 작으면 큰 decay rate?를 적용하여 vruntime이 빠르게 증가하도록 한다. 즉, 우선순위를 크게 높인다.</li>
</ul>
</li>
<li>트리 형식으로 vruntime을 나타내면 아래와 같고, 가장 오른쪽에 낮은 우선순위의 테스크가 배치된다.<ul>
<li>n개의 노드가 있을 때, vruntime이 가장 작은 노드를 찾는데 log(n)시간이 걸린다. 근데 보통 포인터를 쓴다 함<img src="https://velog.velcdn.com/images/jungizz_/post/ec18d8d1-d1fe-4467-84e3-598837a7fe39/image.png" alt="">     </li>
</ul>
</li>
</ul>
</li>
<li>우선순위 영역 존재<img src="https://velog.velcdn.com/images/jungizz_/post/4a792f3e-60fb-44e2-901d-473962831851/image.png" alt=""></li>
<li>load balancing 및 numa를 인지하고, 코어가 빠르게 사용할 수 있는 메모리 공간과 함께 scehdling domain을 지정<ul>
<li>웬만하면 domain 안에서만 움직여서 캐시 효과를 극대화하고 속도 저하를 최소화<img src="https://velog.velcdn.com/images/jungizz_/post/416926e0-6e22-4b12-b642-d4b3774d3838/image.png" alt=""><br></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="2-window">2. Window</h2>
<ul>
<li><p>32레벨의 우선순위 class</p>
<p>  REALTIME_PRIORITY_CLASS
  HIGH_PRIORITY_CLASS
  ABOVE_NORMAL_PRIORITY_CLASS
  NORMAL_PRIORITY_CLASS
  BELOW_NORMAL_PRIORITY_CLASS
  IDLE_PRIORITY_CLASS</p>
</li>
<li><p>각 class 마다 상대적인 우선순위를 정할 수 있는 값도 가진다.</p>
<p>  TIME_CRITICAL
  HIGHEST
  ABOVE_NORMAL
  NORMAL (얘가 기본)
  BELOW_NORMAL
  LOWEST
  IDLE</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jungizz_/post/08fc7d13-fece-40f8-b76f-2b1cd3333eea/image.png" alt=""></p>
<ul>
<li><p>CPU를 사용하지 않는 wait 상태에서는 우선순위가 낮아지고, ready로 가야 우선순위가 높아진다.</p>
</li>
<li><p>forground window(전경 창)은 3배의 우선순위 부스트</p>
<br>
## 3. Solaris
</li>
<li><p>역시나 얘도 우선순위 class를 가진다.</p>
<p>  시간 공유(Time Sharing, TS): 기본, multi-level feedback queue
  대화형(Interactive, IA)
  실시간(Real Time, RT)
  시스템(System, SYS)
  공정 공유(Fair Share, FSS)
  고정 우선순위(Fixed Priority, FP)<img src="https://velog.velcdn.com/images/jungizz_/post/e65dc180-a8c9-4524-9193-902d8e38ea78/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/da94278d-db45-4aed-9a1a-07416e46b47e/image.png" alt=""><br></p>
</li>
</ul>
<h2 id="algorithm-evaluation">Algorithm Evaluation</h2>
<p>운영체제마다 디테일한 알고리즘이 다르며, 이를 평가하기 위한 다양한 방법들이 존재한다.</p>
<ol>
<li>Deterministic modeling<ul>
<li>정해진 입력값으로 성능을 측정, 수치적 계싼</li>
</ul>
</li>
<li>Queueing Models<ul>
<li>queue가 서버에 연결되어 서비스 받기를 기다리는 모델 (마치 ready queue가 CPU 할당을 기다리는 것 처럼)</li>
<li>확률 분포로 성능을 평가</li>
<li>Little’s formula<ul>
<li>Queueing model에서 <strong>n = ʎ x W</strong> 라는 이론<ul>
<li>큐의 길이, 대기 작업 개수 n</li>
<li>서비스 받기까지 기다린 시간 W</li>
<li>1초에 큐에 도착하는 프로세스 개수 ʎ</li>
</ul>
</li>
<li>만약 1초에 7개가 도착하고, 큐에 평균적으로 17개가 있다면 2만큼 기다릴 것이다.</li>
</ul>
</li>
</ul>
</li>
<li>Simulation<ul>
<li>시뮬레이션을 위한 데이터는 랜덤/계산/실측값 을 사용</li>
</ul>
</li>
<li>Implementation<ul>
<li>실제 구현, 정확하지만 구현 비용이 발생</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 4. Threads & Concurrency]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-4.-Threads-Concurrency</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-4.-Threads-Concurrency</guid>
            <pubDate>Sat, 26 Apr 2025 05:56:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡-4장-목표">💡 4장 목표</h4>
</blockquote>
<ul>
<li>CPU 이용의 기본 단위 Threads</li>
<li>Multi-threaded programming</li>
<li>Thread Library API<ul>
<li>implicit threading, thread pools, fork-join, Grand Central Dispatch</li>
</ul>
</li>
</ul>
<p>Thread는 프로세스의 resource를 공유하기 때문에 multi-processor에 비해 multi-thread는 경량화되고 좋은 병렬성을 가진다.</p>
<h1 id="🔸multi-thread-process">🔸Multi-thread process</h1>
<h3 id="motivation">Motivation</h3>
<p>최근 대부분의 application은 multi-thread를 활용한다. Multi-thread를 사용하면 하나의 application이 여러 작업을 동시에 수행할 수 있다. 예를 들어, 웹 서버에서는 화면 업데이트, 데이터 가져오기, 스펠링 체크, 네트워크 관련 기능 등 각 기능을 독립적인 thread로 만들어 동시에 수행함으로써 성능을 향상시킬 수 있다. 많은 운영체제의 kernel들은 multi-thread의 형태를 가진다.</p>
<p>Multi-thread는 multi-processor를 사용하는 것보다 효율적인데, </p>
<ul>
<li>thread는 프로세스보다 생성 시간이 빠르고</li>
<li>한 프로세스의 자원을 여러 thread가 공유하기 때문에 컴퓨터 자원을 적게 차지하며</li>
<li>thread 간 통신이 더 빠르다</li>
</ul>
<p>결과적으로 multi-thread를 통해 프로그래밍이 단순화되고 효율성이 증가한다. Thread는 커널과 라이브러리 수준에서 지원된다.
<br></p>
<h3 id="multi-thread">Multi-thread</h3>
<ul>
<li>프로세스 안에 하나의 Thread vs <strong>한 프로세스 안에 여러개의 Thread</strong><ul>
<li>각 Thread는 register, stack, PC는 각자 가짐</li>
<li>공유할 수 있는 code(instruction), data, files은 공유함
→ multi processor보다 multi thread로 concurrency를 구현하는 것이 더 좋은 이유 (thread 생성이 빠르고, 자원을 적게 차지하고, thread간 통신이 빠름)<img src="https://velog.velcdn.com/images/jungizz_/post/5ec56523-e75e-4d99-b3ea-507a168c238b/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="server에-활용되는-multi-thread-example">Server에 활용되는 Multi-thread example</h4>
<p>서버는 수많은 여러 클라이언트의 요청을 처리해야한다. 서버는 요청이 들어오면 서버는 thread를 생성하고, 해당 요청을 도맡아 처리한다. 즉, 서버를 multi-thread로 구현하여 여러 요청을 동시에 처리한다.<img src="https://velog.velcdn.com/images/jungizz_/post/afa895a8-23e6-4d2e-9b02-42387e35c11b/image.png" alt=""></p>
<h3 id="benefits">Benefits</h3>
<ul>
<li>Responsiveness(응답성)<ul>
<li>일부분(thread)이 blocking 되더라도 다른 쓰레드는 계속 일하므로 프로그램의 수행이 계속 됨</li>
</ul>
</li>
<li>Resource sharing(자원 공유)<ul>
<li>thread는 자신이 속한 프로세스의 resource들과 memory를 공유하기 때문에 리소스를 적게 차지</li>
<li>global data를 공유하기 때문에 쓰레드 간 통신 가능</li>
</ul>
</li>
<li>Economy(경제성)<ul>
<li>프로세스 생성을 위해 memory와 resource를 할당하는 것은 비용이 많이 듦</li>
<li>Thread는 자신이 속한 process의 resource를 공유하기 때문에 생성에 적은 자원을 차지</li>
<li>많이 만들 수 있음, context switching이 빠름</li>
</ul>
</li>
<li>Scalability(규모 적응성)<ul>
<li>multi-processor 구조에서 더욱 증가할 수 있음</li>
<li>각 thread가 다른 processor에서 parallel하게 수행될 수 있기 때문</li>
<li>ex) 쓰레드를 많이 만들어서 multi-core 아키텍처 (코어마다 쓰레드 할당 가능)</li>
</ul>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>Concurrency(병행)</strong>: 소프트웨어적인 측면에서 동시에 수행하는 것 (multi-processor, thread)</li>
<li><strong>Parrallel(병렬)</strong>: 실제로 하드웨어가 여러 개 있어서 동시에 수행 (multi-computer, core, CPU)</li>
</ul>
<br>

<h1 id="🔸multi-core-programming">🔸Multi-core Programming</h1>
<ul>
<li>단일 CPU인데, CPU의 핵심 디바이스인 Core가 여러개</li>
</ul>
<h3 id="concurrency-vs-parallelism">Concurrency vs Parallelism</h3>
<ul>
<li>single-core 시스템에서 <strong>Concurrent</strong> 실행<ul>
<li>단순히 thread의 실행이 시간에 따라 교대로 실행</li>
<li>ex) CPU schedular는 시스템의 프로세스 사이를 빠르게 오고 가며 프로세스를 진행시켜 마치 병렬 실행하는 듯한 착각<img src="https://velog.velcdn.com/images/jungizz_/post/469a9dc2-151f-46e5-bce6-ddb926863206/image.png" alt=""></li>
</ul>
</li>
<li>multi-core 시스템에서 <strong>Parallelism</strong><ul>
<li>물리적으로 한 순간에 동시 실행<img src="https://velog.velcdn.com/images/jungizz_/post/ae9238fe-1bf0-4205-8f69-b44b217940e7/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="type-of-parallelism">Type of Parallelism</h3>
<ul>
<li>Data Parallelism: 데이터를 쪼개서 동시 수행 → 각 코어에서 동일한 연산 (하나의 일을 여러 코어가 함께 수행)</li>
<li>Task Parallelism: task를 쪼개서 동시 수행 → 동일한 데이터지만 task가 많은 경우, 각 코어마다 다른 연산 수행<img src="https://velog.velcdn.com/images/jungizz_/post/295611f0-3fec-48ca-a48f-4dd1a5ed8a0e/image.png" alt=""><br></li>
</ul>
<h3 id="challenges">Challenges</h3>
<p>멀티코어, 멀티 프로세서 환경에서 프로그래밍은 난이도가 어렵다</p>
<ul>
<li>Dividing activities: Application을 분석하여 여러 task로 나눠서 분배할 영역 찾아야 함</li>
<li>Balance: 찾아진 부분들이 전체 작업에 균등한 기여도록 가지도록 해야함</li>
<li>Data splitting: Application이 여러 task로 나뉘는 것처럼, task가 접근하는 data도 각 core에서 사용할 수 있도록 나눠야함</li>
<li>Data dependency: task가 접근하는 데이터가 dependency가 있는지 없는지 검토해야함<ul>
<li>ex) A를 처리해야 B를 처리할 수 있는 로직 → 동시 수행 불가능</li>
<li>검토해서 동시 처리 할 것과 안할 것을 따져야함</li>
</ul>
</li>
<li>Testing and Debugging: 디버깅 어렵다!<br>
### Data dependency (Amdahl’s Law)
Serial(직렬) 및 Parallel(병렬) component가 혼합된 application에서 코어 수에 따른 성능 향상에 대한 법칙![](https://velog.velcdn.com/images/jungizz_/post/728e4d7a-7e3c-4d46-886d-841031df06cb/image.png)</li>
<li><strong>S</strong>: Serial components의 비율<ul>
<li>직렬, 순서에 따라 해야하는 일 = dependency를 가지는 일</li>
</ul>
</li>
<li><strong>1-S</strong>: Parallel components의 비율<ul>
<li>동시에 수행할 수 있는 일</li>
</ul>
</li>
<li><strong>N</strong>: 코어의 수</li>
<li><strong>speed up</strong>: 코어의 처리 속도를 얼마나 올릴 수 있는가</li>
</ul>
<p>application에서 S는 줄일 수 없다. 1-S는 N개의 코어가 동시에 수행하면서 시간을 줄일 수 있다. 분모는 처리 시간을 의미하고, 역수는 처리 속도를 의미한다.</p>
<p>예를 들어서 S가 25%이고, 코어가 1에서 2로 증가한다면, 1.6배 속도를 향상시킬 수 있다.</p>
<blockquote>
<h4 id="코어-수와-처리-속도는-비례하지-않는다">코어 수와 처리 속도는 비례하지 않는다.</h4>
<p>application에서 serial component가 없는 이상적인 경우라면(빨간색), 코어의 수에 비례해서 linear하게 처리 속도가 증가할 것이다. 하지만 serial component는 존재하고, 비율이 커질수록 코어 수에 따라 ideal하게 속도가 증가하지 않는다. <img src="https://velog.velcdn.com/images/jungizz_/post/8592e70b-535b-4c59-b391-6d99df61dd75/image.png" alt="">그래서, serial component가 적을수록 application에서 parallel하게 동작하기 좋은 것이다.</p>
</blockquote>
<br>

<h1 id="🔸userkernel-threads">🔸User/Kernel threads</h1>
<p>thread는 사용자/커널 수준에 따라 사용되는 종류가 다르다.</p>
<ul>
<li><strong>User thread</strong>: 사용자 수준 라이브러리에서 thread를 제공하고 관리<ul>
<li>kernel의 thread를 할당 받아야 함 (kernel의 thread 위에서 지원됨)</li>
<li>주요 라이브러리: POSIX Pthreads, Window threads, Java threads, …</li>
</ul>
</li>
<li><strong>Kernel thread</strong>: 커널(OS)에서 thread를 제공하고 직접 관리<ul>
<li>CPU가 kernel thread를 scheduling</li>
<li>Window, Linux, Mac OS X, iOS, Andriod<img src="https://velog.velcdn.com/images/jungizz_/post/74ad76a0-55c0-4c64-83b3-6ae810af8726/image.png" alt=""><br>

</li>
</ul>
</li>
</ul>
<p><strong>User thread와 Kernel thread의 mapping 관계</strong>에 따라 나뉘는 Multi-threading Model들은 아래와 같다</p>
<h3 id="many-to-one">Many-to-One</h3>
<ul>
<li>많은 user thread가 하나의 kernel thread로 mapping</li>
<li>간단하고 효율적임</li>
<li>하지만, 한 kernel thread가 blocking되면, user thread들도 모두 blocking될 수 있음 → process 전체가 block</li>
<li>한 번에 한 thread만이 kernel에 접근할 수 있기 때문에 core가 많더라도 parallel으로 실행될 수 없음</li>
<li>user space의 multi-thread가 multi-core시스템에서 parallel하게 실행될 수 없음<img src="https://velog.velcdn.com/images/jungizz_/post/a7fe3a91-7189-4225-aec5-4e58a4f11a82/image.png" alt=""></li>
</ul>
<h3 id="one-to-one">One-to-One</h3>
<ul>
<li>각 user thread가 하나의 kernel thread로 mapping</li>
<li>한 kernel thread가 blocking되더라도 다른 thread가 실행될 수 있음 → many to one 보다 더 많은 parallel</li>
<li>하지만, user thread를 생성할 때 그에 따른 kernel thread를 생성해야함 → overhead가 발생하여 application의 성능을 저하시킬 수 있음 (그래서 시스템에 의해 thread의 수 제한됨)<img src="https://velog.velcdn.com/images/jungizz_/post/b4c9261d-f3e8-4109-ae02-d86d99149059/image.png" alt=""></li>
</ul>
<h3 id="many-to-many">Many-to-Many</h3>
<ul>
<li>여러 개의 user thread가 그보다 작은 수 혹은 같은 수의 kernel thread로 multiplex</li>
<li>kernel thread의 수는 OS나 application에 따라 결정</li>
<li>흔하진 않음!<img src="https://velog.velcdn.com/images/jungizz_/post/99197bb6-75ee-40f8-8b58-743b13c1b16c/image.png" alt=""></li>
</ul>
<h3 id="two-level-model">Two-level Model</h3>
<ul>
<li>Many-to-Many와 One-to-One이 결합한 모델<img src="https://velog.velcdn.com/images/jungizz_/post/390012f4-6935-42f1-a979-fb519e88cdd6/image.png" alt=""><br></li>
</ul>
<h1 id="🔸thread-libraries">🔸Thread Libraries</h1>
<p>Thread library는 프로그래머에게 thread를 쉽게 생성하고 관리할 수 있는 API를 제공한다. 구현하는 데에는 크게 2가지 방법이 있다.</p>
<h3 id="implement">Implement</h3>
<ol>
<li>user space에서 라이브러리 제공 (kernel 지원X)</li>
<li>OS에 의해 지원되는 kernel-level 라이브러리 구현</li>
</ol>
<h3 id="librarys--apis">Librarys &amp; APIs</h3>
<p>여러 thread library가 존재하고, 각 library는 프로세스 fork과정과 비슷하게 thread 관련 API를 제공한다.(생성, 기다림, 종료, …)</p>
<ul>
<li><strong>POSIX Pthreads</strong><ul>
<li>POSIX 표준, UNIX, Linux, Mac OS등에서 사용<img src="https://velog.velcdn.com/images/jungizz_/post/081c282b-1838-43ce-a451-278285b3d12d/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/e4497251-3fa2-477b-b4d9-e766a892a650/image.png" alt=""></li>
<li>만약 thread를 10개 만들었다면 10번 for문 돌아서 기다림..<img src="https://velog.velcdn.com/images/jungizz_/post/67766233-22e3-478c-a653-9a7472795104/image.png" alt=""></li>
</ul>
</li>
<li><strong>Windows</strong><img src="https://velog.velcdn.com/images/jungizz_/post/32753452-f8d1-4958-96a2-9af34e2b7dd3/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/9beb43ff-cc76-4b8d-b16a-202ca151102d/image.png" alt=""></li>
<li><strong>JAVA</strong><ul>
<li><strong>Explicitly</strong> creating threads: thread class를 생성하거나, 수행할 interface 구현하거나 (후자가 일반적)<img src="https://velog.velcdn.com/images/jungizz_/post/4e4b2f4e-bb9a-4e71-88d3-f045c5952437/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/3b45cfda-199d-4b09-a29a-137a16723c49/image.png" alt="">   </li>
<li><strong>Executor</strong> interface: thread pool을 미리 생성해두고, task가 생겼을 때 일 시킴 → 생성/수행 과정을 분리해서 유연성을 증가시키고 지연 시간을 줄임<img src="https://velog.velcdn.com/images/jungizz_/post/a57faa5d-f402-4c1d-a795-bbc13ab8948f/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/24af807c-d149-4005-b901-e1aa5f5d1352/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/dff5086a-01a3-458e-a001-e1c4895b9658/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h1 id="🔸implicit-threading">🔸Implicit Threading</h1>
<p>multi-core processing이 발전함에 따라,수백 또는 수천개의 Thread를 가진 application이 등장했다. 이에 따라 <strong>프로그래머가 직접 API를 사용해서 thread를 만드는 Explicit(명시적) threading</strong>에서 프로그램의 정확성을 유지하기가 어려워졌다. 이를 해결하기 위해 Implicit(암묵적) threading이 등장한다.</p>
<ul>
<li><strong>Implicit Threading</strong>: Thread생성과 관리를 <strong>프로그래머가 아닌 compiler와 run-time libraries가 수행</strong></li>
<li>프로그래머의 부담을 줄이고 코드의 복잡성 감소, 더 쉽게 multi-threading환경 구현 가능</li>
</ul>
<blockquote>
<h4 id="implicit-threading을-사용해서-설계하는-5가지-방법">Implicit threading을 사용해서 설계하는 5가지 방법</h4>
</blockquote>
<ul>
<li>Thread Pools</li>
<li>Fork-join</li>
<li>OpenMP</li>
<li>Grand Central Dispatch</li>
<li>Intel Threading Building Blocks</li>
</ul>
<br>

<h3 id="thread-pools">Thread Pools</h3>
<ul>
<li>Thread pool은 <strong>미리 생성된 여러 thread</strong>가 작업을 기다리는 구조<ul>
<li>새로운 thread를 생성하는 것보다 만들어둔 thread로 요청을 처리하는 것이 더 빠름</li>
<li>Thread pool 크기에 맞춰 application 내의 thread 수를 제한할 수 있음 → 시스템 resource 효율적 관리(유연하게)</li>
<li><strong>수행할 task</strong>와 <strong>task 생성</strong>을 분리하여 다양한 실행 전략 적용 (ex. 작업을 주기적으로 실행하도록 예약)</li>
</ul>
</li>
<li>Window, Java에는 thread pool을 지원하는 API가 존재<ul>
<li>Window<img src="https://velog.velcdn.com/images/jungizz_/post/21597ea4-71ff-4f4d-9373-03b6f224ed17/image.png" alt=""></li>
<li>Java<img src="https://velog.velcdn.com/images/jungizz_/post/bcdc0ee0-df22-4b96-9efa-5ae537b18ee7/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/9691e7ab-9255-4b95-8608-aef588ff3b47/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="fork-join-parallelism">Fork-Join Parallelism</h3>
<ul>
<li>여러 thread(task)를 Fork한 후, 작업이 완료되면 다시 Join하여 결과를 얻는 방식</li>
<li>task를 분할하여 병렬로 처리할 수 있음<img src="https://velog.velcdn.com/images/jungizz_/post/3753fb95-7edc-4f69-8277-004253f37d32/image.png" alt=""></li>
</ul>
<h4 id="fork-join의-일반적인-알고리즘">Fork-Join의 일반적인 알고리즘</h4>
<ul>
<li>작업이 충분히 작으면 바로 해결</li>
<li>그렇지 않다면 fork하여 subtask로 분할 → 병렬로 처리</li>
<li>모든 작업이 완료된 후 결과를 join하여(끝나기를 기다림) 결합<img src="https://velog.velcdn.com/images/jungizz_/post/71129bbd-2d52-4c8a-97b1-34f7dc4b8ff7/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/1b249434-9bc1-43c8-887b-053a6d9e7e0b/image.png" alt=""></li>
</ul>
<h4 id="fork-join-in-java">Fork-Join in Java</h4>
<p>ex)리스트의 모든 값을 더하는 task가 있을 때, 리스트를 쪼개서 task 실행</p>
<ul>
<li>ForkJoinTask: abstract base class</li>
<li>RecursiveTask: ForkJoinTask를 확장한 class<ul>
<li>task가 작으면(threadhold) 직접 계산하고, 크면 fork() → 재귀적으로</li>
<li>join()한 최종 결과를 반환</li>
</ul>
</li>
<li>RecursiveAction: RecursiveTask와 같은 역할이지만 결과를 반환하지 않음<img src="https://velog.velcdn.com/images/jungizz_/post/b8ed31eb-ff4e-41e8-81a2-2293947428b4/image.png" alt="">
<img src="https://velog.velcdn.com/images/jungizz_/post/e69d900c-de6d-4607-936e-8ba8ed0a4afe/image.png" alt=""><br></li>
</ul>
<h3 id="openmp">OpenMP</h3>
<ul>
<li>C, C++, FORTRAN을 위한 컴파일러 directives(지시문)과 API의 집합</li>
<li>shared-memory 환경에서의 parallel programming을 지원</li>
<li>OpenMP는 병렬로 실행할 수 있는 코드 블록인 parallel regions를 식별<ul>
<li><code>#pragma omp parallel</code>: <strong>컴파일러에게 Core 수만큼의 thread 생성해서 parallel하게 수행해달라</strong>는 directive → OpenMP가 별렬로 실행할 수 있는지 식별할 수 있도록 힌트를 주는 것<img src="https://velog.velcdn.com/images/jungizz_/post/0fa67206-3b9e-4b88-9c9e-b1a8a07a07c0/image.png" alt=""></li>
<li>컴파일러가 parallel하게 loop를 실행할 수도 있음<img src="https://velog.velcdn.com/images/jungizz_/post/bb650f26-ecf4-4dfd-ab4c-90dcf9ddc903/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="grand-central-dispatch">Grand Central Dispatch</h3>
<ul>
<li>MacOS 및 iOS를 위한 Apple 기술, [C, C++, objective-C]언어의 확장, API, runtime-library 제공</li>
<li>GCD는 병렬로 실행할 수 있는 섹션을 식별해주고, 대부분의 thread 관련 사항 관리<ul>
<li>블록은 “<strong>^{ }</strong>”으로 정의되며, 블록 안에 parallel하게 수행할 부분을 넣음 → OpenMP와 비슷하게, 블록을 통해 힌트를 주는 것<img src="https://velog.velcdn.com/images/jungizz_/post/528f755a-21e8-4dd1-bb52-13dc31939d1f/image.png" alt=""></li>
<li>블록은 dispatch queue에 배치되며, 큐에서 제거된 블록은 thread pool의 thread에 할당 됨</li>
</ul>
</li>
<li>Dispatch queue의 2가지 유형<ul>
<li><strong>Serial Queue</strong>(직렬 큐): FIFO 순서대로 블록 제거, 프로세스 당 하나의 큐(main queue), 프로그래머가 추가적으로 생성 가능</li>
<li><strong>Concurrent Queue</strong>(병렬 큐): FIFO순서로 블록이 제거되지만 여러 블록이 동시에 제거될 수 있음</li>
</ul>
</li>
<li>Swift에서는 <code>dispatch_async()</code> API를 사용해 parallel한 영역을 지정하고 큐에 들어감<ul>
<li>Swift에서는 task를 closure(block과 비슷한 개념)로 정의<img src="https://velog.velcdn.com/images/jungizz_/post/f21244bb-d2aa-46c5-89c9-ee42e3649907/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="intel-threading-building-blocks">Intel Threading Building Blocks</h3>
<ul>
<li><p>parallel C++ 프로그램을 설계하기 위한 template libarary</p>
</li>
<li><p>TBB를 사용한 parrallel_for 루프 (v array에 있는 task를 parallel하게 수행)</p>
<pre><code class="language-c">#include &lt;tbb/tbb.h&gt;

// parallel_for(size_t(0), n, [=](size_t i) {apply(v[i]);});

tbb::parallel_for(0, N, [](int i) {
    // 작업 수행
});</code></pre>
</li>
</ul>
<br>

<h1 id="🔸threading-issues">🔸Threading Issues</h1>
<h3 id="semantics-of-fork-and-exec">Semantics of fork() and exec()</h3>
<p>Multi-thread program에서는 의미가 달라질 수 있다.</p>
<ul>
<li>만약, 한 프로그램의 Thread가 fork()를 호출하면, 새로운 프로세스는 모든 thread를 복제해야하는가? 아니면 호출한 thread만 복제해야하는가?</li>
<li>exec() 또한 모든 thread가 새로운 task를 할 것인지, 하나의 thread만 일을 할 것인지</li>
</ul>
<p>→ 몇 UNIX는 두 가지 버전의 fork()와 exec()를 모두 지원, Application 목적에 따라 설정</p>
<h3 id="signal-handling">Signal handling</h3>
<p>UNIX 시스템에서 Signal은 특정 이벤트가 발생했음을 프로세스에게 알리기 위해 사용한다. Signal은 software interrupt라고 생각할 수 있다.</p>
<ul>
<li>Signal 처리를 위한 signal handler가 존재<ol>
<li>특정 이벤트에 의해 신호 생성</li>
<li>신호가 프로세스에 전달</li>
<li>신호는 두 가지 signal handler 중 하나에 의해 처리<ol>
<li>default</li>
<li>user-defined</li>
</ol>
</li>
</ol>
</li>
<li>모든 신호는 kernel이 신호를 처리할 때 사용하는 default handler를 가짐<ul>
<li>user defined signal handler는 default handler를 오버라이드할 수 있음</li>
</ul>
</li>
<li>single-thread의 경우, 신호는 프로세스에 전달 됨</li>
<li>multi-thread의 경우, 신호를 받을 thread를 설정할 수 있음<ul>
<li>신호를 프로세스 내 모든 thread에 전달</li>
<li>프로세스 내 특정 thread에 전달</li>
<li>프로세스의 모든 신호를 수신할 특정 thread 지정</li>
</ul>
</li>
</ul>
<br>

<h3 id="thread-cancellation">Thread Cancellation</h3>
<p>thread가 완료되기 전에 종료하는 과정으로, 종료될 thread를 <strong>target thread</strong>라고 한다.</p>
<ul>
<li><strong>Asynchrounous Cancellation(비동기적 취소)</strong>: target thread를 즉시 종료</li>
<li><strong>Deffered Cancellation(지연 취소)</strong>: target thread가 주기적으로 취소되어야하는지를 확인하고, 취소해도 되는 상태일 때 취소<br></li>
<li>thread cancellation 요청을 호출해도, 실제 취소는 thread 상태에 달려있음<img src="https://velog.velcdn.com/images/jungizz_/post/fdb5ca8e-cf2c-4256-89ea-8eb581061c5b/image.png" alt=""><ul>
<li>만약 thread에서 취소가 비활성화된 경우, cancellation은 이를 활성화할 때까지 기다림(pending)</li>
<li>default는 deffered cancellation으로 설정되어있음<ul>
<li>thread가 cancellation point에 도달할 때만 취소가 발생</li>
<li>ex) pthread_testcancel()</li>
</ul>
</li>
</ul>
</li>
<li>Linux에서는 thread cancellation은 신호를 통해 처리됨</li>
<li>Java에서는 interrupt() 메소드를 사용하여 thread의 interrupted status를 설정하고, thread는 interrupt되었는지 확인하며 cancellation을 실행<img src="https://velog.velcdn.com/images/jungizz_/post/8befc701-ff52-4dea-b65f-cf21007834e8/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/11988759-7daf-40db-bd81-7917f7d7d499/image.png" alt=""><br></li>
</ul>
<h3 id="thread-local-storage">Thread-Local storage</h3>
<p>Thread마다 독립적으로 가지는 storage가 있고, thread를 구분할 수 있는 데이터를 가진다.</p>
<ul>
<li>각 thread가 자신의 데이터 복사본을 가질 수 있게 해줌</li>
<li>Thread creation process를 제어할 수 없는 경우 유용 (ex. thread pool)</li>
<li>thread에는 local variables을 담는 stack도 존재하는데, 이는 function을 호출하는 동안 존재하고 function이 끝나는 경우 사라짐</li>
<li>하지만 TLS는 function이 끝나도 사라지지 않음 → static data와 유사<br>
### Schedular Activations

</li>
</ul>
<p>kernel thread가 CPU나 core를 할당받아 동작하는 것처럼, user thread는 lightweight process(LWP)를 할당받아야 동작할 수 있다. lightweight process는 kernel thread마다 존재한다. → 계층구조<img src="https://velog.velcdn.com/images/jungizz_/post/8d60a97c-5edd-4054-9273-a422ccf2798f/image.png" alt=""></p>
<ul>
<li>이러한 계층구조는 보통 상위 레이어에서 function을 call하고 하위 레이어에서 실행하여 상위 레이어에게 서비스를 제공</li>
<li>근데 Schedular Activation은 하위 레이어에서 상위 레이어를 호출할 수 있는 upcall을 제공<ul>
<li>kernel에서 thread library의 upcall hander로의 통신 메커니즘</li>
</ul>
</li>
<li>이러한 통신을 통해 user thread와 kernel thread의 소통이 가능해짐<ul>
<li>ex) Many-to-Many모델과 two-level모델 모두 적절한 수의 kernel thread가 application에 할당되도록 유지하기 위한 통신이 필요</li>
</ul>
</li>
</ul>
<br>

<h1 id="🔸os-examples">🔸OS Examples</h1>
<p>각 OS에서 thread가 어떻게 구현되있는지를 알아본다.</p>
<h3 id="windows-threads">Windows Threads</h3>
<ul>
<li><p>Windows API는 kernel level에서 One-to-One mapping으로 구현되는 thread를 제공</p>
</li>
<li><p>window의 Thread 구성요소</p>
<ul>
<li>thread ID</li>
<li>프로세서의 상태를 나타내는 register set</li>
<li>user/kernel stack (thread가 user mode와 kernel mode에서 실행될 때 독립적인 스택을 가짐)</li>
<li>private storage area (thread마다 독립적으로 가지는 공간)</li>
</ul>
<br>
-> 위 구성요소에서 ID를 제외하고, 동작하는 thread에서 변화하는 값으로 thread의 context를 의미 (context switching과정을 위해 저장해야하는 값)

</li>
</ul>
<br>

<ul>
<li>Thread를 관리하기 위한 window의 data structures<ul>
<li><strong>ETHREAD(Executive Thread Block)</strong>: thread 시작 주소, parent 프로세스의 포인터, KTHREAD에 대한 포인터를 포함 (in kernel space)</li>
<li><strong>KTHREAD(Kernel Thread Block)</strong>: 스케줄링 및 동기화 정보, kernel stack, TEB에 대한 포인터를 포함 (in kernel space)</li>
<li><strong>TEB(Thread Environment Block)</strong>: thread ID, user stack, thread-local storage를 포함 (in user space)<img src="https://velog.velcdn.com/images/jungizz_/post/48b5c1f5-c004-4013-b29b-eea3e3b198f1/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="linux-threads">Linux Threads</h3>
<ul>
<li>Linux에서는 thread를 <strong>task</strong>라고 나타냄</li>
<li>thread는 clone() system call을 호출하여 생성<ul>
<li>clone()은 child task가 parent task(프로세스)의 주소 공간을 공유할 수 있도록 허용</li>
</ul>
</li>
<li>다양한 flag를 통해 thread의 동작 제어 (parent task의 어디까지 공유받을 것인지)<img src="https://velog.velcdn.com/images/jungizz_/post/ed01c0c8-78bc-4776-9817-0edea2014346/image.png" alt="">    </li>
<li><code>struct task_struct</code> : 프로세스 데이터 구조</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 3. Processes]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2.-Processes</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2.-Processes</guid>
            <pubDate>Sat, 26 Apr 2025 05:35:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="💡3장-목표">💡3장 목표</h4>
</blockquote>
<ul>
<li>프로세스의 개념과 특성</li>
<li>프로세스 간 통신<ul>
<li>Shared-Memory, Message Passing</li>
<li>Client-Server System</li>
</ul>
</li>
</ul>
<blockquote>
<p>Program: Passive entity(수동적 존재)
Process: Active entity(능동적 존재)</p>
</blockquote>
<p>프로그램을 수행하는 것이 프로세스이다. 프로그램이라는 사용설명서를 따라 프로세스가 실행되는 것이다. OS는 프로세스를 수행하여 다양한 프로그램을 실행한다.</p>
<h1 id="🔸process-concept">🔸Process Concept</h1>
<h2 id="🔹process-components">🔹Process Components</h2>
<ul>
<li><strong>text section</strong>: program code(Instruction)</li>
<li><strong>Program counter</strong>: 다음에 어떤 instruction을 수행할 지 알려주는 register값 (CPU내에 존재)<ul>
<li>각 프로세스마다 PC를 가진다</li>
</ul>
</li>
<li><strong>Stack</strong>: 일시적인 데이터 보관 장소 (ex. function parameter, return address, local variable)</li>
<li><strong>Data section</strong>: global variable 보관(초기화 유무에 따라 구분되어 보관)</li>
<li><strong>Heap</strong>: 프로세스 실행 중에 동적으로 할당되는 메모리</li>
</ul>
<p>메모리에 프로세스가 올라갈 때, Data section의 크기는 고정되어있고(Static memory), Stach과 Heap은 실행 중에 필요에 따라 동적으로 할당되고 해제된다(Dynamic memory)</p>
<ul>
<li>process in memory<img src="https://velog.velcdn.com/images/jungizz_/post/28f1cb1b-26de-4090-9a4d-f9330cf402a0/image.png" alt=""></li>
<li>example of memory layout (c program)<img src="https://velog.velcdn.com/images/jungizz_/post/9dca1315-e2c0-4e1d-bb05-c9ab7de9c6d7/image.png" alt=""><br></li>
</ul>
<h2 id="🔹process-state⭐">🔹Process State⭐</h2>
<p>프로세스는 능동적 존재이기 때문에 실행되면서 상태가 변화한다. 프로세스가 생성되고 종료되는 과정에서 Running, waiting, ready 상태 변화가 반복된다. </p>
<ul>
<li>State Transition Diagram<img src="https://velog.velcdn.com/images/jungizz_/post/e5a5339c-0831-4853-a689-8d666c408a73/image.png" alt=""></li>
<li>New: 프로세스 생성 됨</li>
<li>Running: Instruction 실행 중 (CPU를 할당받아 실질적으로 동작)</li>
<li>Waiting: 프로세스가 event가 일어나기를 기다리는 중</li>
<li>Ready: 프로세스가 processor(CPU)에 할당되기를 기다리는 중</li>
<li>Terminated: 프로세스의 excution이 종료</li>
</ul>
<p>New 상태에서 수행되면(admitted) Ready상태가 되고, CPU scheduler에 의해 본인이 CPU를 할당받은 경우 Running상태가 된다. Running 상태에서 exit로 종료할 수도 있고, IO request 등으로 waiting 상태로 전환할 수도 있다. event가 발생하면 다시 Ready상태로 변환된 것이다. 또는, Time sharing의 경우 아주 작은 시간 단위로 CPU를 할당받고 해제되는데, 이때 time interrupt가 발생하며 Running 상태에서 바로 Ready상태로 가기도 한다. time interrupt가 아닌 그냥 interrupt도 마찬가지다.
<br></p>
<h2 id="🔹process-control-blockpcb">🔹Process Control Block(PCB)</h2>
<p>프로세스를 관리하기 위한 테이블로, 특정 프로세스와 연관된 여러 정보를 가지고 있다.</p>
<ul>
<li><strong>Process State:</strong> new, ready, running, waiting, terminated</li>
<li><strong>Program Counter</strong></li>
<li><strong>CPU Registers</strong><ul>
<li>PC와 register는 CPU안에 존재하긴 하지만, CPU에는 다양한 프로세스의 정보가 있어서 구분이 안됨. 그래서 PCB에 각 프로세스에 해당하는 값을 저장하는 것</li>
</ul>
</li>
<li><strong>CPU scheduling information:</strong> 우선순위, 큐에 대한 포인터 등과 같은 scheduling 파라미터 포함</li>
<li><strong>Memory-management information</strong></li>
<li><strong>Accounting information:</strong> CPU 사용 시간, open한 파일 정보</li>
<li><strong>IO status information</strong><img src="https://velog.velcdn.com/images/jungizz_/post/22c8b295-7f93-4136-a252-335bc77acc77/image.png" alt=""><br></li>
</ul>
<h3 id="threads">Threads</h3>
<p>Thread는 프로그램이 수행하는 동작을 의미한다. 지금까지는 한 프로세스에서 한 동작만 하는 single thread를 살펴본 것이다. Multi thread는 <strong>한 프로세스의 자원을 공유</strong>하여 다수의 thread를 가지는 것으로, 한 프로세스가 한 번에 여러 개의 동작(task)을 수행할 수 있는 것이다.</p>
<ul>
<li>여러 개의 프로세스를 사용하는 것보다 자원을 절약할 수 있어 비용이 낮고, 속도가 빠르다 (메모리를 공유하기 때문에 IPC를 하지 않아도 됨)</li>
<li>이러한 점은 여러 Thread가 병렬로 수행되는 multi-core system에서 이익을 얻을 수 있음</li>
<li>Thread를 지원하는 System에서, PCB는 각 Thread에 대한 정보를 포함</li>
</ul>
<h4 id="pcb-in-linux-→-task-structure">PCB in Linux → Task structure</h4>
<ul>
<li>PCB와 동일하게 프로세스의 다양한 정보 포함</li>
<li>프로세스를 복제하는 과정을 fork라고 하고, parent를 fork해서 children을 만든다<img src="https://velog.velcdn.com/images/jungizz_/post/1c956ce0-92e1-4344-aede-08d6be8dcea7/image.png" alt=""></li>
</ul>
<h2 id="🔹process-scheduling">🔹Process Scheduling</h2>
<p>어떠한 multi~ 방식이든 CPU에 비해 CPU가 처리할 프로세스의 수가 더 많다(여러개의 프로세스 ↔ 1개의 CPU). CPU사용을 최대화하기 위하여 프로세스들 사이에서 CPU를 자주 switching해줘야 한다. 그래서 CPU가 언제 어떠한 프로세스를 처리할 것인지 Scheduling이 필요하다. 
→ 시분할의 목적은 각각의 프로세스들이 실행되는 동안 서로 상호작용할 수 있도록 프로세스들 사이에서 CPU를 계속해서 교체하는 것</p>
<h3 id="scheduling-queue">Scheduling Queue</h3>
<p><strong>리소스</strong>를 관리하는 큐로, 주로 linked list로 구현되며, header는 리스트의 첫번째와 마지막 PCB를 가리키는 포인터를 가짐</p>
<ul>
<li><strong>Ready queue</strong>: CPU 할당을 기다리는 프로세스를 나타냄<ul>
<li>아래 그림은 ready queue가 7번과 2번 PCB를 관리하고 있으며, 2번과 7번 프로세스는 자신에게 CPU가 할당될때까지 기다림<img src="https://velog.velcdn.com/images/jungizz_/post/bd79683c-b40a-445c-9f3a-fa5a9d9d2cdf/image.png" alt=""></li>
</ul>
</li>
<li><strong>Wait queue</strong>: IO, event를 기다리는 프로세스를 나타냄 (각 event마다 큐 존재)<ul>
<li>아래 그림은 wait queue가 3, 14, 6번 PCB를 관리하고 있으며, 이 프로세스들은 해당 디바이스 장치를 사용하기를 기다리고 있음(wailting state)<img src="https://velog.velcdn.com/images/jungizz_/post/22bc5b6d-28ed-4e34-bd4d-9a23bf686d61/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="queuing-diagram">Queuing Diagram</h3>
<p>각 원은 서비스이다. 각 프로세스는 자신이 원하는 서비스를 받기 위해 알맞는 곳에 줄서기 위해 큐에 들어간다.</p>
<ul>
<li>프로세스는 ready state에서 시작해서 ready queue에서 CPU할당을 기다리고, 할당받으면 running state가 됨</li>
<li>실행되다가 IO request가 들어오면, waiting state가 되며 IO wait queue에서 대기하고, 서비스를 수행하고 interrupt가 발생하면 다시 ready state가 되며 ready queue에서 기다림</li>
<li>또는, 주어진 CPU slice를 다 사용하면 timer interrupt가 발생하고 다시 ready queue에서 기다림</li>
<li>fork해서 child에게 일 시키는 것과 interrupt와 관련해서도 위와 같은 주기를 반복하게 되고, 종료되면 모든 큐에서 삭제되며 자신의 PCB와 resource를 반납함<img src="https://velog.velcdn.com/images/jungizz_/post/f91345e7-c9d1-4564-bbd8-944d367a2807/image.png" alt=""></li>
<li>Running queue가 없는 이유는? CPU를 점유할 수 있는 프로세스는 1개뿐이라서 리스트를 유지할 필요가 없음! (single processe의 경우)<br>
## 🔹CPU Switching

</li>
</ul>
<p>Scheduling에 따라 CPU에게 할당되는 프로세스를 바꾸는 과정</p>
<h3 id="context-switch">Context Switch</h3>
<p>CPU를 다른 프로세스로 swtich하기 위해서, 이전의 프로세스 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업</p>
<ul>
<li><strong>Context</strong>: 프로세스 상태, CPU register값, PC, 메모리 관리 정보 등 → 프로세스의 <strong>PCB</strong>에 표현 됨</li>
<li>커널은 과거 프로세스의 context를 PCB에 저장하고(context save), 실행할 프로세스의 PCB에서 saved context를 복구(context reload/resume)<img src="https://velog.velcdn.com/images/jungizz_/post/6dc08a38-58ae-482f-a562-d967034a5f29/image.png" alt=""><br></li>
</ul>
<h2 id="🔹multitasking-in-moblie-systems">🔹Multitasking in Moblie Systems</h2>
<p>초기 모바일 시스템은 화면 공간과 사용자 인터페이스의 제한 때문에 한 번에 하나의 프로세스만 실행할 수 있었다. (백그라운드 실행X)</p>
<ul>
<li>IOS Multitasking<ul>
<li><strong>single foreground process</strong>: 사용자 interface를 통해 제어되는 프로세스 (사용자 interaction 존재)</li>
<li><strong>Multiple background process</strong>: 메모리에서 실행 중이지만 화면에 표시되지 않고, 사용자 interface없이 뒤에서 동작하는 프로세스<ul>
<li>한계: single/short task, 이벤트 알림 수신, 특정 장기 실행 작업(오디오 재생) 등</li>
</ul>
</li>
</ul>
</li>
<li>Andriod Multitasking<ul>
<li>foreground, background 프로세스 실행 가능</li>
<li>IOS에 비해 한계가 적음</li>
<li><strong>Service</strong>: background 프로세스 작업 수행을 위한 중간 역할로 background 프로세스가 중단되더라도 계속 실행 가능, 인터페이스X, 적은 메모리 사용량</li>
</ul>
</li>
</ul>
<br>

<h1 id="🔸operations-on-processes">🔸Operations on Processes</h1>
<p>프로세스가 생성되고 종료되는 과정의 동작 살펴보기</p>
<h2 id="🔹process-creation">🔹Process Creation</h2>
<p>실행되는 동안 프로세스는 여러 개의 새로운 프로세스를 생성할 수 있다. 생성하는 프로세스를 <strong>Parent</strong>, 생성된 프로세스를 <strong>child</strong>라고 하며, 이 과정을 <strong>Fork</strong>라고 한다(Copy). child프로세스는 또 새롭게 다른 프로세스를 생성할 수 있으며, 그 결과 ‘프로세스의 트리’를 형성한다.</p>
<ul>
<li>하나의 프로세스로는 수많은 request를 처리하기 힘들기 때문에, Fork로 Child 프로세스를 만들고 일을 시켜서 여러개의 프로세스를 동시에 수행 → <strong>Concurrently(동시성)</strong><ul>
<li>ex) 웹 서버에서 수많은 client의 requset, client마다 child를 생성</li>
</ul>
</li>
</ul>
<h4 id="parent↔child의-resource-공유-옵션-3가지">Parent↔child의 resource 공유 옵션 3가지</h4>
<ul>
<li>모두 공유</li>
<li>parent의 resource만 공유</li>
<li>공유 안함</li>
</ul>
<h4 id="parent↔child의-실행-옵션-2가지-w-address-측면에서">Parent↔child의 실행 옵션 2가지 (w. address 측면에서)</h4>
<ul>
<li><p>parent와 child 동시 실행 → Concurrently (child는 parent의 복제본)</p>
</li>
<li><p>parent가 child가 종료될 때까지 대기 (child는 프로그램이 로드 된 상태)</p>
<br>
### in Unix, Linux
</li>
<li><p>Unix, Linux와 같은 대부분의 현대 운영체제들은 Process Identifier(pid)라는 식별자로 프로세스를 구분</p>
<ul>
<li>parent와 child process의 유일한 차이점 → pid</li>
<li>Linux에서는, 초기화 될 때 create로 첫 프로세스 ‘<strong>Demon</strong>’이 생성되고(pid=1), 나머지 프로세스는 Demon의 fork로 생성됨, 각 프로세스는 excute로 실행<img src="https://velog.velcdn.com/images/jungizz_/post/9c9600af-b9ad-4352-878c-688176cf1a03/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="parent가-child가-종료될-때까지-대기하는-unix-linux-example">parent가 child가 종료될 때까지 대기하는 Unix, Linux example</h4>
<ul>
<li>parent는 fork()로 child를 생성함<ul>
<li>fork()의 return값 → parent에게는 child의 pid값, child에게는 0 (이 값으로 parent와 child 구분)</li>
</ul>
</li>
<li>child는 exec()로 다른 프로그램을 실행시키거나 함수를 실행시켜 일을 시키고, 수행이 끝나면 exit()으로 종료</li>
<li>parent는 child가 일을 끝날 때까지 기다리고, exit()가 실행되면 다시 parent의 일을 이어서 수행<img src="https://velog.velcdn.com/images/jungizz_/post/4826e031-beb9-4d49-a0f9-124a31913b89/image.png" alt=""></li>
<li>in C Program<ul>
<li>parent가 main을 수행하며 fork하고, child도 같은 main을 수행하기 됨</li>
<li>이때, pid를 통해 parent와 child를 구분해서 일을 시킴<img src="https://velog.velcdn.com/images/jungizz_/post/2f090e0d-e546-404f-b018-d63d6d429e90/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="in-window">in Window</h3>
<p>리눅스와 비슷한 구조인데 system call 이름이 다름</p>
<ul>
<li>fork() → CreateProcess()</li>
<li>wait() → WaitForSingleObject<img src="https://velog.velcdn.com/images/jungizz_/post/456e2cf8-7c88-4b6f-914b-4fda59244201/image.png" alt=""><br></li>
</ul>
<h2 id="🔹process-termination">🔹Process Termination</h2>
<ul>
<li>프로세스가 실행을 끝내고, exit() system call을 사용하여 프로세스를 종료<ul>
<li>parent가 child의 종료를 기다리는 경우, child는 exit()함수로 status를 return하고 wait()함수를 통해 parent에게 status를 전달<pre><code class="language-c">pid = wait(&amp;status); // 종료된 프로세스의 pid값 반환</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="abort">Abort()</h3>
<ul>
<li>parent는 <strong>abort()</strong> system call로 child process들 중 하나의 실행을 종료할 수 있다<ul>
<li>child가 자신에게 할당된 자원을 초과하여 사용하는 경우</li>
<li>child에게 할당된 task가 더 이상 필요 없는 경우</li>
<li>parent가 exit()하는데, 운영체제는 parent가 종료된 뒤 child가 실행되는 것을 허용하지 않는 경우</li>
</ul>
</li>
</ul>
<br>

<h3 id="cascading-termination">Cascading termination</h3>
<ul>
<li>부모가 종료되면 자식도 종료 (Cascading: 폭포, 위에서 아래로 쏟아지는)</li>
</ul>
<blockquote>
<ul>
<li><strong>좀비(Zombie) 프로세스</strong>: parent가 wait()하지 않는 경우 → child는 정상종료 되지 않고 좀비가 됨 (wait()를 다시 호출하면 정상종료 될 수 있음)</li>
</ul>
</blockquote>
<ul>
<li><strong>고아(Orphan) 프로세스</strong>: parent가 먼저 종료되어 wait()를 호출하지 않은 경우 (정상 종료 불가능)<h5 id="운영체제마다-위의-해결-방법이-다름-cascading-termination도-해결-방법-중-하나">운영체제마다 위의 해결 방법이 다름 (cascading termination도 해결 방법 중 하나)</h5>
</li>
</ul>
<br>

<h3 id="in-andriod">in Andriod</h3>
<p>모바일 운영체제는 메모리를 회수하기 위해 프로세스를 종료해야한다. Andriod는 가장 중요하지 않은 프로세스부터 종료하기 시작한다. </p>
<p>위로 갈수록 중요한 프로세스:</p>
<ul>
<li>Foreground process</li>
<li>Visible process: 프로세스 동작 상태를 foreground process를 통해 볼 수 있음</li>
<li>Service process: background process지만 동작 상태를 간접적으로 볼 수 있음</li>
<li>Background process</li>
<li>Empty process<br>
### in Chrome Browser

</li>
</ul>
<p>크롬 브라우저는 <strong>multiprocess</strong> 구조를 사용해서 다양한 프로세스로 구현된다 (단일 프로세스 사용 시, 하나의 웹사이트에서 문제가 발생하면 전체 브라우저가 멈출 수 있음)</p>
<ul>
<li>Browser process: user interface, disk, network IO관리</li>
<li>Renderer process: 웹 페이지 렌더링, javaScript 처리 (각 웹사이트 열 때마다 독립적인 renderer process 생성)</li>
<li>Plug-in process: 각 유형의 플러그인에 대해 별도의 프로세스 생성<br>
# 🔸Interprocess Communication (IPC)

</li>
</ul>
<p>OS내에서 동시에 실행되는 프로세스들은 independent하거나 cooperating하다. 다른 프로세스와 데이터를 공유하고 영향을 주고 받는 프로세스는 cooperating하다고 한다. </p>
<ul>
<li>independent → 문서, 음악 프로그램 동시 실행 (서로 영향X)</li>
<li>dependent → 크롬 웹 브라우저의 renderer와 plug-in 프로세스 (서로 영향O)<br>

</li>
</ul>
<p>프로세스 cooperating을 허용하는 이유는 아래와 같다. (장점)</p>
<ul>
<li>Information sharing: 여러 사용자가 동시에 동일 정보에 접근 가능</li>
<li>Computation speedup: 특정 task를 나누어 병렬로 실행 가능</li>
<li>Modularity: 시스템 기능을 프로세스들로 나누어 모듈식 형태로 구성 → 유지보수</li>
<li>Convenience: 한 사용자가 동시에 작업할 다수의 task를 가질 수 있음<br>

</li>
</ul>
<p>cooperating 프로세스들은 InterProcess Communication(IPC, 프로세스 간 통신) 기법이 필요하다. OS에서 IPC를 지원해주며, 대부분 두 가지 모델을 가진다.</p>
<ol>
<li><strong>Shared memory</strong>: 프로세스들이 shared memory 공간을 통해 데이터 공유</li>
<li><strong>Message passing</strong>: 프로세스에서 프로세스로 데이터를 직접 전달하기 위해 kernel의 message queue 사용<img src="https://velog.velcdn.com/images/jungizz_/post/a7e3dd71-a10c-4e4e-9569-bbfc291dee0a/image.png" alt=""><br></li>
</ol>
<h3 id="producer-consumer-problem">Producer-Consumer Problem</h3>
<p>많은 종류의 애플리케이션이 존재하는데, 이걸 몇 가지 모델로 나눌 수 있다. 그 중 하나가 생산자-소비자 모델이다.</p>
<ul>
<li>대부분의 애플리케이션 프로그램은 멀티 프로세스로 동작하며, 이 때, 데이터를 생산하고 소비하는 관계를 생산자-소비자 모델이라고 함<ul>
<li>ex) 사용자는 프린트하기 위해 데이터를 올리고(생산자), 프린터 디바이스는 데이터에 따라 출력함(소비자)</li>
</ul>
</li>
<li>생산자 프로세스는 버퍼에 데이터를 올리고, 소비자 프로세스는 올라온 순서에 따라 버퍼에서 꺼내 수행하기 위해 <strong>프로세스 간 공유(IPC)</strong> → Shared memory 또는 message passing 사용 가능<ul>
<li><strong>unbounded-buffer</strong>: 버퍼 크기에 실질적 제한이 없음</li>
<li><strong>bounded-buffer</strong>: 버퍼 크기 고정, 가득 차면 대기</li>
<li>버퍼가 꽉 차더라도 다시 처음으로 돌아가도록 연결하여 생산자가 계속 정보를 생산할 수 있음(원형배열)</li>
</ul>
</li>
</ul>
<br>

<h2 id="🔹ipc-in-shared-memory">🔹IPC in Shared memory</h2>
<ul>
<li>여러 프로세스가 공유할 수 있는 shared memory 공간이 존재</li>
<li>커널의 도움 필요 없이 read/write를 통해 전달</li>
<li>shared memory 공간을 만들 때만 system call이 일어나므로 빠름</li>
<li><strong>Synchronize</strong>, 메모리 공간에 동시 접근할 때 충돌이 발생하지 않도록 책임져야 함⭐(ch.6-7)</li>
</ul>
<p><strong>Producer-Consumer model</strong>은 데이터와 2개의 포인터를 담고있는 shared memory를 사용할 수 있다.</p>
<h4 id="bound-buffer-wshared-memory-solution"><strong>Bound-buffer</strong> w.Shared-memory solution</h4>
<ul>
<li>생산자와 소비자가 메모리(버퍼)를 공유</li>
<li>in 포인터 → 버퍼 내 next free position (생산자가 넣을 위치) → inque</li>
<li>out 포인터 → 버퍼 내 first full position (소비자가 꺼낼 위치) → deque<ul>
<li>초기화 과정에서 둘 다 0으로 초기화<img src="https://velog.velcdn.com/images/jungizz_/post/cb87ac48-0333-48c1-a5cb-a28153dd713d/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="producer-process-wshared-memory-solution"><strong>Producer Process</strong> w.Shared-memory solution</h4>
<ul>
<li>((in+1) % BUFFER_SIZE) == out → 버퍼가 꽉차있다 → 기다리기<ul>
<li>원형 배열이라 끝까지 왔을 때 앞으로 되돌아가기 위해 위와 같은 식 사용</li>
</ul>
</li>
<li>꽉차있지 않다면 생산아이템을 버퍼에 넣고, in 포인터 증가<img src="https://velog.velcdn.com/images/jungizz_/post/4f25a12e-20ca-437f-9336-be208b63657a/image.png" alt=""></li>
</ul>
<h4 id="consumer-process-wshared-memory-solution"><strong>Consumer Process</strong> w.Shared-memory solution</h4>
<ul>
<li>in == out → 버퍼가 비어있다 → 기다리기</li>
<li>비어있지 않다면 버퍼에서 소비될 아이템을 꺼내고, out포인터 증가<img src="https://velog.velcdn.com/images/jungizz_/post/e98b9f04-0603-40f7-b09b-e6f3add2bd7a/image.png" alt=""><br></li>
</ul>
<h2 id="🔹ipc-in-message-passing">🔹IPC in Message passing</h2>
<ul>
<li>프로세스에서 프로세스로 데이터를 직접 전달</li>
<li>프로세스 간 communication link를 형성하여 연결하고, kernel의 message queue를 통해 send/receive로 데이터를 전달<ul>
<li>OS마다 다른 send/recive IPC API가 존재</li>
</ul>
</li>
<li>작은 data를 교환할 때 유용하고 구현이 쉽지만 느림<br>
### Communication link

</li>
</ul>
<p>프로세스를 link하는 방법은 다양하며, OS는 이 다양한 옵션을 지원한다. 애플리케이션 요구에 따라 옵션을 지정한다.</p>
<ul>
<li>Physical<ul>
<li>Shared memory, Hardware bus, Network</li>
</ul>
</li>
<li>Logical<ul>
<li>direct of indirect, Synchronous or asynchrounous, Automatic or explicit buffering</li>
</ul>
</li>
</ul>
<br>

<h3 id="direct-communication">Direct Communication</h3>
<ul>
<li><p>보내는/받는 프로세스의 이름을 꼭 명시해야 함</p>
<ul>
<li>send(P, message): process P(목적지)에게 메세지 전달</li>
<li>receive(Q, message): process Q(발송지)로부터 메세지 받음</li>
<li>P와 Q가 직접적으로 연결</li>
</ul>
</li>
<li><p>전화를 걸거나, 목적지로 우편물을 보내는 것으로 비유</p>
<br>
### Indirect Communication
</li>
<li><p>제 3의 공간인 Mailbox를 공유해서 데이터를 주고받음</p>
</li>
<li><p>Mailbox는 프로세스들이 메세지를 저장하고 제거하는 객체로, 고유한 id를 가진다</p>
</li>
</ul>
<ol>
<li>mailbox 생성</li>
<li>mailbox를 통해 메세지 send, receive<ul>
<li>send(A, message): mailbox A로 메세지 전달</li>
<li>receive(A, message): mailbox B로부터 메세지 받음</li>
</ul>
</li>
<li>mailbox 제거</li>
</ol>
<blockquote>
<h4 id="둘-이상의-프로세스가-mailbox를-공유하는-경우">둘 이상의 프로세스가 mailbox를 공유하는 경우</h4>
<p>P1, P2, P3가 mailbox A를 공유하고, P1이 메세지를 전달했을 때, P2와 P3 중 누가 메세지를 받을까?</p>
</blockquote>
<ul>
<li>다양한 옵션의 해결책 존재<ul>
<li>link가 최대 두 프로세스만 될 수 있도록 제한</li>
<li>최대 한 번에 한 프로세스만 receive()할 수 있도록 제한</li>
<li>어떤 프로세스가 메세지를 receive할지 선택</li>
</ul>
</li>
</ul>
<br>

<h3 id="synchronization">Synchronization</h3>
<p>프로세스 간 통신 과정에서 Synchronous(blocking)과 Asynchrounous(non-blocking)을 구분하여 관리</p>
<ul>
<li><strong>Blocking</strong> (Synchronous) → 완성될 때까지 기다림<ul>
<li>send()하고, 그 메세지가 receive될 때까지 sender 대기</li>
<li>메세지가 avail할 때까지 receiver 대기</li>
</ul>
</li>
<li><strong>Non-bloking (</strong>Asynchrounous) → 완성되지 않아도 보내고 받음<ul>
<li>대기 없이 메세지 send()</li>
<li>receiver는 메세지를 받거나, null을 받거나 (avail메세지가 없으면 receive()는 null을 return)</li>
<li>null이 return된 경우, avail 메세지가 생기면 signal handler가 신호를 보내줌 (interrupt처럼)</li>
</ul>
</li>
<li>send와 receive 종류에 따라 4가지 조합이 가능함<ul>
<li>전부 blocking인 경우에는 Rendezvous가 발생 (아래 zero capacity 참고)</li>
</ul>
</li>
</ul>
<h4 id="producer-consumer-process-wmessage-passing-solution"><strong>Producer-Consumer Process</strong> w.message passing solution</h4>
<ul>
<li>shared memory 모델과 다르게 <strong>send, receive</strong>로 진행<img src="https://velog.velcdn.com/images/jungizz_/post/d49f53d5-67c9-488e-964b-f9f658d39f68/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/bb757cf2-1d73-46b6-afe0-a28aa74b2716/image.png" alt=""><br></li>
</ul>
<h3 id="buffering">Buffering</h3>
<p>프로세스 간 통신을 하는데, 각 프로세스의 차이(시간, 속도, 단위 등)를 완화하기 위해 Buffer를 사용한다. 예를 들어, CPU와 I/O는 처리 속도 차이가 있어 버퍼를 사용해 통신한다.</p>
<ul>
<li><strong>Zero capacity</strong>: 버퍼 사용X → 직접 만나서 전달하는 rendezvous(랑데뷰)</li>
<li><strong>Bounded capacity</strong>: n개의 메세지만 보관할 수 있는 제한된 버퍼</li>
<li><strong>Unbounded capacity</strong>: 무한개 보관 가능 버퍼, 다쓰면 앞으로 돌아가서 무한대인 것처럼..<br>
## 🔹Example of IPC systems

</li>
</ul>
<h3 id="posix">POSIX</h3>
<ul>
<li>UNIX, LINUX 기반 shared memory를 위한 API</li>
</ul>
<ol>
<li>shared memory 생성 → shm-open()</li>
<li>memory 크기 설정 → ftruncate()</li>
<li>shared memory객체를 메모리 주소 공간으로 매핑 → mmap()<ul>
<li>shared memory에 쓰고 읽을 때 mmap()으로 return된 포인터 값을 사용<img src="https://velog.velcdn.com/images/jungizz_/post/31f942c3-8229-40f8-8ea6-ae90ff869406/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/59f02a29-cd22-4d67-b1cd-12c9531a2a6d/image.png" alt=""><br></li>
</ul>
</li>
</ol>
<h3 id="mach">Mach</h3>
<ul>
<li><p>Mac OS의 Messsage Passsing System, micro kernel의 IPC API 지원</p>
<ul>
<li>mach_msg(): 메세지 전송 및 수신</li>
<li>mach_port_allocate(): communication port 설정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jungizz_/post/b3b7f501-a770-4ddf-bf43-059af8eb6de2/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/143417a8-de85-4891-88d0-a717c02c29f0/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/c620a3aa-c559-4f4b-aca3-67d4da537616/image.png" alt=""></p>
</li>
<li><p>mailbox가 가득찬 경우, 4가지 옵션</p>
<ul>
<li>무한 대기, 최대 n ms대기, 즉시 반환, 메세지를 임시로 캐시</li>
</ul>
</li>
</ul>
<br>

<h3 id="lpclocal-procedure-call">LPC(L<strong>ocal Procedure Call)</strong></h3>
<ul>
<li>windows는 <strong>advanced local procedure call(LPC)</strong> 기능을 통해 Message passing system으로 IPC를 구현<ul>
<li>LPC는 한 컴퓨터 내에서 다른 프로세스의 function call을 할 수 있음 (그냥 function call이라는 것은 한 프로세스 안에서의 call을 의미)</li>
<li>추가적으로, <strong>RPC</strong>(remote procedure call)은 다른 컴퓨터의 프로세스의 function call 가능 (밑에 나옴)</li>
</ul>
</li>
<li>Port(mailbox와 유사)를 사용해서 communication channel 설정 및 유지</li>
</ul>
<ol>
<li>client는 connection port를 open</li>
<li>client는 connection request를 보냄</li>
<li>server는 두 개의 private communication ports 생성, 그 중 하나의 handle은 clinet에게 반환</li>
<li>clinet와 server는 해당 port handle을 사용해서 통신<img src="https://velog.velcdn.com/images/jungizz_/post/6f872320-67af-4d1c-8b26-7fda9aed1e5b/image.png" alt=""><br></li>
</ol>
<h3 id="pipes">Pipes</h3>
<p>파이프는 두 프로세스 간 통신을 가능하게 하는 통로 역할을 한다. 파이프에서 물이 흘러가듯 데이터를 전달한다. 이때, parent-child 관계 존재 여부에 따라 두가지 종류로 분류된다.</p>
<ul>
<li><strong>Ordinary pipes</strong><ul>
<li>parent-child 관계가 있는 경우 사용됨</li>
<li>parent가 pipe를 생성하면, fork된 child도 같이 pipe를 물려받게되며, 이 파이프로 parent와 child 프로세스 간 통신 (외부에서 접근X)</li>
<li>데이터가 한 방향으로만 흐르는 Unidirectional(단방향)으로 작동하며, 양방향 통신을 위해 2개의 pipe 필요 → 길이가 2인 fd(file discriptor) 배열 생성</li>
<li>fd[0]을 read, f[1]에 write 하는 producer-consumer style<img src="https://velog.velcdn.com/images/jungizz_/post/937117cf-ecff-4de0-915f-ad15ce87096e/image.png" alt=""></li>
</ul>
</li>
</ul>
<ul>
<li><strong>Named pipes</strong><ul>
<li>pipe에 이름이 있기 때문에 parent-child 관계가 없이 이름으로 접근 가능, 연관성이 없는 두 프로세스 간 통신<ul>
<li>이름 덕분에 여러 프로세스가 사용 가능</li>
</ul>
</li>
<li>Bidirectional(양방향)으로 데이터 전송</li>
<li>First in First out</li>
</ul>
</li>
</ul>
<br>

<h2 id="🔹client-server-systems">🔹Client-Server Systems</h2>
<p>네트워크에 연결되어 있는 두 시스템(컴퓨터와 컴퓨터) 사이의 통신하는 방법을 client-server system이라고 한다. clinet-server 시스템의 통신는 <strong>Pipe, Socket, RPCs</strong>와 같은 기법이 사용될 수 있다.</p>
<h3 id="sockets">Sockets</h3>
<ul>
<li>TCP/IP프로토콜을 쉽게 이용할 수 있는 네트워크 API</li>
<li><span style="color: red">IP주소</span>와 <span style="color: blue">Port number</span>를 결합한 형태<ul>
<li>Port: service number, 이 서비스를 제공하는 프로세스를 가리킴</li>
<li>ex) <span style="color: red">161.25.19.8</span>: <span style="color: blue">1625</span></li>
</ul>
</li>
<li>두 개의 소켓을 통해 두 프로세스가 네트워크 상에서 소통<img src="https://velog.velcdn.com/images/jungizz_/post/6ad1b894-bcfc-4e17-b69e-600fed6645d8/image.png" alt=""></li>
<li>socket은 연결한 뒤 데이터를 주고받는 TCP(connection-oriented) 옵션과 연결 설정을 하지 않고 주소와 데이터를 동시에 보내는 UDP(connectionless) 옵션이 존재</li>
<li>special IP address: 127.0.0.1 (loopback) → 한 대의 컴퓨터에서 테스트 시 사용</li>
</ul>
<h4 id="socket-example-in-java">Socket example in Java</h4>
<p>시간과 날짜를 알려주는 서비스 예제</p>
<ul>
<li><p>server는 자신의 socket을 생성하고, clinet의 connection 요청을 accept()로 받음<img src="https://velog.velcdn.com/images/jungizz_/post/953d7697-77c9-4fd5-b905-a3693f7db040/image.png" alt=""></p>
</li>
<li><p>clinet도 자신의 socket을 생성하고, 동시에 연결 설정(요청?)을 함, 그리고 server에서 보낸 데이터를 read<img src="https://velog.velcdn.com/images/jungizz_/post/9c599803-f3af-4664-98c1-edd744385edc/image.png" alt=""><br></p>
</li>
</ul>
<h3 id="rpcremote-procedure-call">RPC(Remote <strong>Procedure Call)</strong></h3>
<ul>
<li>두 시스템(컴퓨터) 사이 통신을 연결하기 위한 프로시저 호출을 추상화(abstracts)하여, 원격지의 함수를 로컬 함수처럼 호출할 수 있음<ul>
<li>Socket처럼 서비스 식별을 위한 port 사용</li>
</ul>
</li>
<li><strong>Stubs</strong>: client, server 측에 각각 존재하며, 복잡한 네트워크 통신을 도와줌<ul>
<li>client-side stub: 실제 서버 프로시저에 대한 proxy역할, 서버를 찾고 매개변수를 패킹(marshalls)함</li>
<li>server-side stub: client로부터 메세지를 수신하고, 패킹된 매개변수를 언패킹(unpacking)한 후 서버에서 프로시저 실행</li>
<li>client와 server는 애플리케이션이고, 둘의 통신을 위해 데이터를 주고받는 과정이라 <strong>Stub는 일종의 middle ware</strong>라고 볼 수 있음</li>
<li>Window에서는 stub code를 MIDL(Microsoft Interface Definition Language)로 작성</li>
</ul>
</li>
<li><strong>Marshalls</strong>: client의 파라미터를 메세지 형태로 변환하는 과정</li>
<li><strong>XDR</strong>(External Data Representation): 서로 다른 아키텍처 간의 데이터 표현을 처리하기 위해 XDR형식 사용<ul>
<li>컴퓨터마다 big-endian과 little-endian이 다름 → 큰/작은 자리수 표현이 컴퓨터마다 달라서 변환 필요</li>
</ul>
</li>
<li>procedure, RPC에 대한 정보를 제공하는 <strong>Match maker</strong></li>
<li>실패하기 쉽기 때문에 메세지는 최대 한 번(at most once)이 아니라 정<strong>확히 한 번(exactly once)</strong> 전달될 수 있도록 보장</li>
</ul>
<ol>
<li>clinet가 procedure X를 실행하기 위해 RPC 보냄</li>
<li><strong>Matchmaker</strong>가 procedure X를 제공하는 port number P를 알려줌</li>
<li>client는 port P로 원격지 호출</li>
<li>server는 요청을 받고 해당 호출을 실행하여 output을 client에게 보냄<img src="https://velog.velcdn.com/images/jungizz_/post/5dc82a9d-78e8-4725-b9d3-feb5f6923f51/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 2. Operating-System Structures]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2.-Operating-System-Structures</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-2.-Operating-System-Structures</guid>
            <pubDate>Fri, 25 Apr 2025 17:36:25 GMT</pubDate>
            <description><![CDATA[<h3 id="operating-system-서비스">Operating System 서비스</h3>
<ul>
<li>프로그램 실행 환경 및 유저에게 서비스를 제공</li>
</ul>
<ol>
<li>다양한 스타일의 User Interface를 제공하여 쉽게 사용할 수 있도록 함<ul>
<li>Command-Line(CLI)</li>
<li>Graphics User Interface(GUI) → 일반 사용자도 접근 쉬움</li>
<li>touch-screen</li>
</ul>
</li>
<li>프로그램 작성, 디버깅, 컴파일, 메모리에 로딩 등과 같은 일련의 과정을 제공하여 Program excution</li>
<li>UI, 하드디스크, 네트워크 등 IO operation 제공<ul>
<li>모바일에서 센서를 통해 물리 시스템을 제어하거나 IoT와 같은 것도 IO operation</li>
</ul>
</li>
<li>파일 시스템 관리 (파일 읽기, 쓰기,  찾기, 권한 등)</li>
<li>프로세스간의 정보 교환, 네트워크, shared memory 등의 ommunications</li>
<li>하드웨어/소프트웨어 적인 에러, 외부 접근 등의 Error detection</li>
<li>다수의 유저 또는 job이 실행될 때 각각 알맞게 리소스 할당 ⭐<ul>
<li>주요 자원: CPU cycles, main memory, file storage, IO system</li>
<li>자원 관리가 운영체제의 kernel</li>
</ul>
</li>
<li>OS가 실행되면서 누가, 언제, 어떤 리소스를 사용했는지, 어떤 에러가 발생했는지 등 이벤트에 대한 logging</li>
<li>리소스에 대한 권한(protection), 외부 공격으로 부터 보호(security)<img src="https://velog.velcdn.com/images/jungizz_/post/9ab641fd-5c5f-4f90-aa98-394e26676b34/image.png" alt=""></li>
</ol>
<ul>
<li><p>가장 밑에 하드웨어</p>
</li>
<li><p>OS안에 다양한 서비스, system calls, UI</p>
<br>
### User Operating System Interface
</li>
<li><p>CLI (command line interpreter, shell)</p>
<ul>
<li>사용자가 명령어를 직접 입력</li>
<li>어떤 명령어가 있는지 알아야 함</li>
<li>개발의 편리함, 정교하고 빠르게 명령어 입력 가능</li>
</ul>
</li>
<li><p>GUI</p>
<ul>
<li>일반 사용자가 쉽게 사용할 수 있는 desktop metaphor interface</li>
<li>파일, 프로그램, 액션을 나타내는 아이콘</li>
<li>보통 CLI도 같이 사용할 수 있음 (UNIX나 LINUX는 CLI기반인데 GUI를 제공하는 편)</li>
</ul>
</li>
<li><p>Touchscreen</p>
<ul>
<li>모바일, 스마트폰, 직관적</li>
<li>virtual keyboard, voice command</li>
</ul>
</li>
</ul>
<br>

<h3 id="system-calls">System calls</h3>
<ul>
<li>다양한 application이나 시스템 프로그램이 공통으로 필요한 기능을 OS에서 미리 구현해 둠</li>
<li>system call보다 좀 더 확장한게 API (Application Porgramming Interface)<ul>
<li>다양한 라이브러리나 미들웨어 기능을 쉽게 사용할 수 있도록 제공하는 API기능</li>
<li>OS를 포함해서 시스템 소프트웨어(미들웨어, 데이터베이스, 라이브러리 등)에서 application을 쉽게 작성할 수 있도록 함</li>
<li>세가지 주 API는 Win32 API, POSIZ API(for unix, linux), Java API<br></li>
</ul>
</li>
<li>System call example (file copy) → 여러가지 System call로 이뤄짐<ul>
<li>input 파일 이름 요청 (어떤 파일을 copy할건지)</li>
<li>화면에 띄워서 사용자가 입력</li>
<li>output 파일 이름 요청</li>
<li>화면에 띄워서 사용자가 입력</li>
<li>input 파일을 열기 (근데 그 파일이 없으면 err)</li>
<li>output 파일 생성 (기존에 그  파일이 있었으면 err)</li>
<li>input파일에서 한줄씩 읽고 output파일에 한줄씩 쓰기 → 읽을게 없을 때까지 반복</li>
<li>output 파일 닫기</li>
<li>완료 메세지 화면에 띄우기<img src="https://velog.velcdn.com/images/jungizz_/post/46ab8eaf-ee7d-4f76-bd7a-7a7e35ce9dec/image.png" alt=""></li>
</ul>
</li>
<li>Standard API example (file read)<img src="https://velog.velcdn.com/images/jungizz_/post/a65df664-d7da-4dd4-a823-be32407f91f0/image.png" alt=""><br></li>
</ul>
<h3 id="system-call-implementation">System call Implementation</h3>
<ul>
<li>index table로 system call 종류를 구별한다 (system call interface)</li>
<li>system call 실행은 kernel이 알아야한다<ol>
<li>application <code>main()</code>에서 system call 함수 호출</li>
<li>user mode에서 kernel mode로 trap (software interrupt)</li>
<li>kernel은 system call interface를 통해 해당 system call의 index 파악</li>
<li>kernel이 해당 system call이 있는 주소를 통해 system call 실행</li>
<li>완료 시 user에게 return</li>
</ol>
</li>
</ul>
<br>

<h3 id="system-call-parameter-passing">System call parameter passing</h3>
<ul>
<li>일반적인 function call은 stack을 통해 넘김</li>
<li>근데 user와 kernel mode는 각자 다른 stack을 가짐</li>
<li>그래서 parameter를 register를 통해 전달 (빠르게 전달 가능)<ul>
<li>but 사이즈 한계가 있으므로, user와 kernel이 공유할 수 있는 공간에 parameter를 넣고, 해당 주소를 register로 전달할 수도 있음 (linux, solaris)<img src="https://velog.velcdn.com/images/jungizz_/post/51ac8e21-0a86-436d-b0d5-a02200ae322f/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="system-call-types">System call Types</h3>
<ul>
<li>process control<ul>
<li>프로세스 생성, 종료, 로드, 실행</li>
<li>event(interrupt)</li>
<li>메모리 할당</li>
<li>debugger</li>
<li>locking (여러 프로세스가 같은 리소스에 접근할 때 충돌이 발생하지 않도록)</li>
</ul>
</li>
<li>File management<ul>
<li>파일 생성, 제거, 읽고, 쓰고, 열고, 닫고..</li>
<li>파일 속성 읽고 쓰기</li>
</ul>
</li>
<li>Device management<ul>
<li>디바이스 할당, 읽기, 쓰기</li>
</ul>
</li>
<li>Information maintenance<ul>
<li>시간, 날짜, 파일, 디바이스 등과 같은 시스템 정보</li>
</ul>
</li>
<li>Communications<ul>
<li>communication port 생성, 제거, port를 통해 메세지를 받고 보내고</li>
<li>shared-memory model: 여러 프로세스가 한 메모리를 통해 데이터 공유</li>
<li>messeage passing mode: 프로세스 간에 실제로 데이터를 주고받으며 공유</li>
</ul>
</li>
<li>Protection<ul>
<li>리소스에 대한 권한<img src="https://velog.velcdn.com/images/jungizz_/post/10187ac4-7b0c-4d6b-a791-d50df54040aa/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/5e66548f-14d0-426c-8534-0edc23e8c2c6/image.png" alt=""></li>
</ul>
</li>
<li>EX: Arduino<ul>
<li>OS범주에 속하지 않고, 단순히 센서로부터 데이터를 읽어들이고 컨트롤러를 제어하는 프로그램(Sketch)</li>
<li>single-task<img src="https://velog.velcdn.com/images/jungizz_/post/d6d94810-bdba-4914-b410-b1e7926ddb3d/image.png" alt=""></li>
</ul>
</li>
<li>EX: FreeBSD<ul>
<li>unix의 일종</li>
<li>multitasking가능 (multiprocessor)</li>
<li>새로운 프로세스 생성<ul>
<li><code>fork()</code> 로 부모 프로세스를 복제하여 자식 프로세스 생성</li>
<li>만약 자식 프로세스가 부모와 다른 일을 하고싶다면<code>excute()</code> 를 통해 실행<img src="https://velog.velcdn.com/images/jungizz_/post/5c9e9c55-728d-4a73-888b-a2824985bc3c/image.png" alt=""><br></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="system-services">System services</h3>
<ul>
<li><p>system call을 포함해서 더 많은 서비스를 제공</p>
<ul>
<li>ex) 드래그하여 파일 copy → 여러 system call을 사용하여 용이한 서비스 제공</li>
</ul>
</li>
<li><p>파일 관리</p>
<ul>
<li>생성, 제거, copy, rename 등</li>
</ul>
</li>
<li><p>시스템에 대한 다양한 상태 모니터링 (status information)</p>
<ul>
<li>date, time, memory, disk, user, file…</li>
</ul>
</li>
<li><p>파일 수정</p>
<ul>
<li>text editor와 같은 system software 사용</li>
<li>Application에서 system call로 직접 OS에 접근하여 수정할 수도 있지만</li>
<li>system software를 거쳐가며 더 편하게 수정 가능</li>
</ul>
</li>
<li><p>다양한 컴퓨팅 언어로 소프트웨어를 제작, 컴파일, 로딩, 실행</p>
</li>
<li><p>다양한 communication 메카니즘, 통신 API를 제공 → 프로그래머가 쉽게 작성</p>
</li>
<li><p>Background services</p>
<ul>
<li>메모리 관리, 인터넷 접속 처리, 로깅 모니터링</li>
<li>OS가 부팅될 때(초기화될 때) 만들어짐</li>
<li>services, subsystems, daemons</li>
</ul>
</li>
<li><p>Application Program을 작성, 컴파일, 로딩 등</p>
<br>
### Linkers and Loaders
</li>
<li><p>소스코드를 컴파일하면 object file이 됨</p>
</li>
<li><p>기능별로 독립적인 object file들이 생성되고, <strong>linker</strong>가 object file들을 연결하여 excutable file을 만듦</p>
</li>
<li><p><strong>loader</strong>가 excutable file을 메모리에 올려 프로그램 실행</p>
<ul>
<li>static loader: 정해진 자리에 로딩</li>
<li>Relocation loader: 로딩 위치가 매번 다름 (메모리의 빈 자리를 차지)</li>
<li>Dynamic link loader(dll): 프로그램의 모든 부분을 한번에 로드하는게 아니라, 해당 라이브러리 함수가 호출될 때 로드</li>
</ul>
</li>
<li><p>전체적인 흐름</p>
</li>
</ul>
<ol>
<li>소스코드 작성 <code>main.c</code></li>
<li>컴파일러에 의해 obj file로 만들어짐 <code>main.o</code></li>
<li>linker가 obj file들을 연결해서 executable file 생성 <code>main.exe</code></li>
<li>excutable file을 실행하면 loader가 메모리에 로드하면서 프로그램이 실행됨 (dynamically linked libraries)<img src="https://velog.velcdn.com/images/jungizz_/post/78276625-2c9d-4eb8-bb82-a2c8809f5c33/image.png" alt=""><br><h3 id="operating-system-specific">Operating System Specific</h3>
</li>
</ol>
<ul>
<li>운영체제마다 System call이 다름 (OS dependency)</li>
<li>Virtual machine을 사용하여 보완<ul>
<li>ex)  VM을 포함하는 java는 다양한 OS에서 실행될 수 있음</li>
<li>소프트웨어의 호환성을 위해</li>
</ul>
</li>
<li>Application Binary Interface (ABI)<ul>
<li>obj레벨의 인터페이스</li>
<li>OS나 하드웨어에 상관없이 공통으로 호환</li>
</ul>
</li>
</ul>
<br>

<h3 id="operating-system-design-and-implementation">Operating System Design and Implementation</h3>
<ul>
<li>사용자 목표: OS가 편리해야함, 잘 안죽어야함, 안정적, 빠른 반응</li>
<li>시스템 목표: 사용자 목표 달성을 위해 공정하게, 여러 사용자에게 동시에 확장성있게 제공할 것인가</li>
<li>중요 원리<ul>
<li>Policy: 무엇을 할건지 (정책)</li>
<li>Mechanism:  어떻게 할건지 (구체적 알고리즘)<br></li>
</ul>
</li>
<li>Implementation<ul>
<li>다양한 언어로 운영체제 작성<ul>
<li>초반에는 어셈블리어로 작성됐고, 요즘은 호환성을 위해 C나 C++로 작성됨</li>
<li>IO 디바이스의 low-level 컨트롤을 위해 일부가 어셈블리어로 작성되기도 함</li>
<li>high-level language로 작성하면 하드웨어에 port가 필요하고 느림</li>
</ul>
</li>
<li>하드웨어가 달라서 동작이 안될 때, Emulation으로 해당 하드웨어에 알맞게 application program을 변환해주는?</li>
</ul>
</li>
</ul>
<br>

<h3 id="operating-system-structure">Operating System Structure</h3>
<ul>
<li>다양한 형태의 OS<ul>
<li>MS-DOS: 초기, 간단</li>
<li>Unix: 단일 체제 OS, 성능은 좋지만 유연성이 떨어짐</li>
<li>layered architecture: 계층별 체제로 위의 단점 보완</li>
<li>Module 구조: 모듈별로 프로그램을 OS에 쉽게 붙일 수 있음 (OOP 개념)</li>
<li>Microkernel 구조: 커널을 작게, 유연성, 안정성</li>
</ul>
</li>
</ul>
<h4 id="monolithic-structure-단일운영체제구조">Monolithic Structure (단일운영체제구조)</h4>
<ul>
<li>OS전체가 한 덩어리 (계층X, 모듈X)</li>
<li>속도가 빠르다</li>
<li>모든 OS기능이 physical hardware interface</li>
<li>하드웨어 리소스 관리</li>
<li>초기 unix, 약간의 계층구조를 가지지만 완벽하지않음<ul>
<li>Hardware Abstraction Layer: 다양한 하드웨어를 일반화, OS는 하드웨어별로 고려할 필요X</li>
<li>OS 기능: CPU scheduling, 메모리 관리(페이지 단위로), demand paging, virtual memory<img src="https://velog.velcdn.com/images/jungizz_/post/92f7097a-44eb-4ec6-9468-edff68c13d17/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="linux-system-structure">Linux System Structure</h4>
<ul>
<li>unix type OS</li>
<li>Monolithic + modular design → 유연성 향상</li>
<li>kernel이 module로 구성<img src="https://velog.velcdn.com/images/jungizz_/post/659de454-f37a-4ae4-9c2f-33e79fff9d39/image.png" alt=""></li>
</ul>
<h4 id="layered-approach">Layered Approach</h4>
<ul>
<li>얘도 Monolithic의 단점인 유연성 부족을 보완</li>
<li>위에 있는 레이어는 아래 레이어의 서비스(function call)를 받음</li>
<li>해당 레이어를 원하는대로 수정 가능<img src="https://velog.velcdn.com/images/jungizz_/post/d9843d19-8daf-41fd-af40-f9c816c23df1/image.png" alt=""></li>
</ul>
<h4 id="microkernels">Microkernels</h4>
<ul>
<li>OS의 최소한의 핵심 기능은 kernel에 남겨두고, 나머지는 user program처럼 동작(<strong>서버로</strong>)<ul>
<li>서버를 통해 작동하기 때문에 확장이 용이</li>
<li>한 기능이 문제가 생겨도 영향을 받지 않는다 (원래 OS는 하나로 뭉쳐있으니까, 어디서 문제가 발생하면 전체가 고장남) → 신뢰성, 안정성</li>
<li>but, 서버를 통해 작동하기 때문에 느린 속도 (performance overhead)</li>
</ul>
</li>
<li>ex) Mach OS (Mac OS X kernel이 Mach을 기반으로 만듦)<img src="https://velog.velcdn.com/images/jungizz_/post/eb9c20df-ed62-4c2a-90dd-8d5ad93fb24a/image.png" alt=""><ul>
<li>위 이미지에서 file system이랑 device driver가 kernel이 아닌 user mode로 갔지만 어쨌든 OS다!!!! 그냥 application program처럼 user mode에서 동작할 뿐</li>
</ul>
</li>
</ul>
<h4 id="modules">Modules</h4>
<ul>
<li>객체지향 방식으로 접근하여 OS를 모듈로 구성</li>
<li>OS에 모듈을 추가하거나 제거하기가 쉬워 유연성 증가</li>
<li>ex) Linux, Solaris</li>
</ul>
<br>

<h3 id="hybrid-operating-system">Hybrid Operating System</h3>
<ul>
<li>다양한 OS 구조를 채택 (각각 장단점이 확실함)</li>
<li>위에 나왔 듯, Linux와 Solaris는 Monolithic+modular</li>
<li>Window는 주로 Monolithic인데 부분적으로 microkernel</li>
<li>Max OS X는 다윈 운영체제(mack기반→ microkernel)+ Unix(monolithic) 같이 채택</li>
</ul>
<h4 id="macos-and-ios">macOS and IOS<img src="https://velog.velcdn.com/images/jungizz_/post/4d701963-7f3d-4999-a0b9-0fdea1cc94f0/image.png" alt=""></h4>
<ul>
<li>아래는 Darwin OS<img src="https://velog.velcdn.com/images/jungizz_/post/1db326da-2ed2-44ef-b9d4-486819a20ded/image.png" alt=""></li>
</ul>
<h4 id="android">Android</h4>
<ul>
<li>Linux kernel 기반</li>
<li>저전력, 저사양에 맞춰 power management 기능 추가<img src="https://velog.velcdn.com/images/jungizz_/post/ec096f43-7a19-4ff3-bf7c-d76370d94533/image.png" alt=""><br><h3 id="building-and-booting-an-operating-system">Building and Booting an Operating System</h3>
</li>
</ul>
<ol>
<li>운영체제 설계 구조에 따라 소스코드 작성</li>
<li>운영체제 Configuration잡기 (메모리 크기, CPU종류, 디바이스 종류 등)</li>
<li>소스코드 컴파일</li>
<li>운영체제 설치</li>
<li>운영체제를 메모리에 로딩 후 실행 → Booting</li>
</ol>
<ul>
<li><p>ex) Linux</p>
<ol>
<li>Linux 소스코드 다운로드</li>
<li>configuration 잡은 후 컴파일</li>
<li>컴파일할 때, kernel image를 생성 (application에서 excutable file 처럼)</li>
<li>kernel에 다양한 모듈을 만들기</li>
<li>새로운 kernel을 시스템에 install<br>
### System Boot
</li>
</ol>
</li>
<li><p>부팅프로그램(bootstrap loader)은 컴퓨터를 꺼도 유지되도록 비휘발성인 ROM이나 EEPROM에 위치</p>
</li>
<li><p>bootstrap loader가 운영체제를 올림</p>
<ul>
<li>but, 운영체제가 크고 로딩이 복잡해서 ROM에 다 넣긴 무리</li>
<li>그래서 보통 <strong>Bootstrap이 Boot block을 올리고, 하드디스크에 있는 boot block이 운영체제를 올림</strong> → 2step으로 ROM 공간 절약 및 운영체제 종류 선택 부팅이 가능해서 유연성 증가<br>
### Operating System Debugging
</li>
</ul>
</li>
<li><p>어쨌든 OS도 소프트웨어이기 때문에 디버깅 필요</p>
</li>
<li><p>OS의 다양한 configuration값을 조정하며 performance tuning</p>
<ul>
<li>작업관리자로 CPU상태, 메모리, disk 등의 하드웨어 리소스가 어떻게 사용되는지 파악 가능</li>
</ul>
</li>
<li><p>log file을 사용하여 trace (프로그램의 흐름 추적)</p>
<ul>
<li>core dump: Application 에러 발생 시, 프로세스의 메모리를 캡쳐한 파일 생성</li>
<li>crash dump: OS 에러 발생 시, kernel 메모리를 포함한 파일 생성</li>
</ul>
</li>
<li><p>BCC (BPF Compiler Collection)</p>
<ul>
<li>Linux의 trace toolkit</li>
<li>ex) disk 성능, 접근 시간, 읽기인지 쓰기인지, 크기, 접근 latancy time, 리소스 사용 이력 등 측정 가능</li>
<li>다양한 OS 기능별로 전담 tracing 툴을 가짐<img src="https://velog.velcdn.com/images/jungizz_/post/fde80280-8379-4742-a553-182285cc62f3/image.png" alt=""></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[운영체제] 1. Introduction]]></title>
            <link>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-1.-Introduction</link>
            <guid>https://velog.io/@jungizz_/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-1.-Introduction</guid>
            <pubDate>Fri, 25 Apr 2025 17:24:55 GMT</pubDate>
            <description><![CDATA[<h3 id="computer-system-structure">Computer System Structure</h3>
<ul>
<li>컴퓨터 구조 계층</li>
</ul>
<pre><code>| User | - 사용자, 프로그래머, 다른 컴퓨터/디바이스 등  |
| --- | --- |
| Application | - Word processor, 비디오 게임 등 |
| Middleware | - 컴파일러, 라이브러리, 데이터베이스 등 |
| Operating System | - 운영체제가 하드웨어 자원을 제어해서 사용자와 Application이 쉽게 사용할 수 있는 환경 제공 |
| Hardware | - 기본 컴퓨팅 자원 제공 -&gt; CPU, 메모리, I/O 장치 (센서, 컨트롤러 등) |

![](https://velog.velcdn.com/images/jungizz_/post/d53830be-8d1b-4d6d-b44a-9a106ba86f2c/image.png)&lt;br&gt;</code></pre><h3 id="operating-systems-역할">Operating Systems 역할</h3>
<ul>
<li><p>컴퓨터 리소스를 쉽게 사용할 수 있도록 (resource allocator, control program)</p>
<ul>
<li>사용자가 컴퓨터를 쉽게 사용할 수 있도록 UI 제공</li>
<li>프로그래머는 OS의 Sysyem call(API)을 사용해 쉽게 개발 가능</li>
</ul>
</li>
<li><p>Main frame, Mini computer, Cloud, Block chain → 넓은 의미의 서버</p>
<ul>
<li>여러 사용자가 연결해서 각자 원하는 서비스 제공 받음</li>
<li>workstations(lab tab, mobile device)에서 사용자 혼자 원하는 작업 진행</li>
</ul>
</li>
<li><p>모바일 디바이스는 리소스 용량이 상대적으로 적어서 네트워크로 서버에 연결해서 필요한 부분을 보충</p>
</li>
<li><p>임베디드 시스템은 항공기, 자율주행 자동차, 스마트 티비, 공장 제어(IOT시스템) 등 기기 안에 내장되어 디바이스를 제어</p>
<br>
### Operating Systems 기능
</li>
<li><p>산업용, 군수용, 민간용 등 다양한 목적을 가짐</p>
</li>
<li><p>System software &gt; OS &gt; Kernel</p>
</li>
<li><p>Kernel: OS의 핵심 기능</p>
</li>
<li><p>System program: OS와 함께 제공되지만 kernel은 아닌 프로그램</p>
</li>
<li><p>Application program: OS와 관련이 없는 모든 프로그램</p>
</li>
<li><p>Middleware: OS를 제외한 System program</p>
</li>
<li><p>개발자에게 databases, multimedia, graphics등을 제공하는 software frameworks</p>
<br>
### Computer System 구조
</li>
<li><p>하나 이상의 CPU와 여러 컨트롤러(오디오, 비디오 디스플레이 등)가 Bus로 연결되어있으며, shared Memory에 대한 접근을 제공</p>
<ul>
<li><p>디바이스마다 각자 다른 컨트롤러를 가지고 있음 (독립적)</p>
<p>  <img src="https://velog.velcdn.com/images/jungizz_/post/2304ebda-05bb-4d6b-9c1c-9b3272346c20/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<ul>
<li>CPU와 IO디바이스는 개별적으로 동작<ul>
<li>CPU가 상대적으로 좀 더 빠름</li>
<li>CPU가 IO에게 명령을 내리고, IO를 기다리려면 오래 걸림 (상대적으로 느려서) → interrupt</li>
</ul>
</li>
<li>각 디바이스 컨트롤러는<ul>
<li>작은 메모리와 같은 local buffer를 가짐</li>
<li>디바이스 컨트롤러를 제어하는 디바이스 드라이버는 OS의 일부<ul>
<li>디바이스 컨트롤러, 디바이스: 하드웨어</li>
<li>디바이스 드라이버: 운영체제</li>
</ul>
</li>
</ul>
</li>
<li>CPU는 메모리, IO간에 데이터 이동<ul>
<li>로컬버퍼는 일시적인 io데이터를 저장하는 공간</li>
</ul>
</li>
<li>Interrupt<ul>
<li>이벤트를 처리하는데 사용</li>
<li>ex) 디바이스 컨트롤러가 cpu가 시킨 일을 다 한 이벤트</li>
</ul>
</li>
<li>프로그램이 실행되려면 메모리로 로딩되어야함<br>
### Interrupts 기능</li>
<li>다양한 종류의 interrupt를 구별하기 위해 interrupt vector<ul>
<li>각 interrupt마다 다른 service routines를 가진다</li>
</ul>
</li>
<li>interrupt가 오면 이에 해당하는 service routine으로 점프하고, 수행한 뒤, 원래 위치로 돌아감<ul>
<li>원래 위치로 돌아갈 수 있도록 그 위치를 저장해놔야함</li>
</ul>
</li>
<li>function call은 우리가 호출을 지정하고, interrupt는 언제 올지 모름</li>
<li>trap, exception은 software interrupt</li>
<li>interrupt메카니즘은 하드웨어에서 지원되야하고, 이 메카니즘은 OS에서 이용<br>
### Interrupt Handling</li>
<li>ex) IO에서 interrupt가 발생하는 경우</li>
<li>OS는 interrupt가 걸렸을 때, 일을 멈췄다가 다시 돌아오기 위해 program counter와 register값 등의 상태를 기억해야 함 <ul>
<li>interrupt가 없다면 계속 주기적으로 점검 필요(polling)</li>
</ul>
</li>
<li>ex) Interrupt-drive IO cycle<ol>
<li>디바이스 드라이버(OS)가 IO request</li>
<li>IO가 할일을 수행하고 다 하면 interrupt signal 전송 (그동안 CPU는 다른 일 하고 있음)</li>
<li>CPU가 interrupt를 받아서 interrupt service routine 실행 (IO가 읽어온 데이터 처리)</li>
<li>원래 하던 일로 돌아감 (resumes)</li>
</ol>
</li>
</ul>
<br>

<h3 id="io-structure">IO Structure</h3>
<ul>
<li>System call은 시스템에서 지원하는 다양한 서비스 루틴<ul>
<li>ex) file에 접근하기 위한 read, write</li>
<li>얘도 일종의 interrupt (처럼 수행하니까)</li>
</ul>
</li>
<li>Device-status table<ul>
<li>IO를 관리하는 테이블, IO마다 가짐</li>
</ul>
</li>
</ul>
<br>

<h3 id="storage-structure">Storage Structure</h3>
<ul>
<li>하드웨어 자원</li>
</ul>
<ol>
<li>Main memory (주 기억장치)<ul>
<li>ramdom access, 메모리 영역을 랜덤하게 접근</li>
<li>volatile (휘발성)</li>
</ul>
</li>
<li>Secondary storage (보조기억장치)<ul>
<li>nonvolatile (비휘발성)</li>
</ul>
<ol>
<li>Hard disk (하드디스크)</li>
<li>SSD (비휘발성 메모리)</li>
</ol>
</li>
</ol>
<ul>
<li>기억장치 용량<ul>
<li>word: 컴퓨터 시스템마다 다름 64bit or 32bit</li>
</ul>
</li>
</ul>
<br>

<h3 id="storage-hierarchy-⭐">Storage Hierarchy ⭐</h3>
<ul>
<li>다양한 메모리는 계층 구조를 가진다</li>
<li>CPU와 가까울수록 속도가 빠르지만 작은 용량 (단위 용량 당 비용이 높음)<img src="https://velog.velcdn.com/images/jungizz_/post/506012be-e173-425a-9a79-71b8c35d3404/image.png" alt=""></li>
</ul>
<ul>
<li><p>cache: 자주 사용하는 것을 아래에서 위로 올리는 것</p>
<ul>
<li>빠르게 접근 가능</li>
</ul>
</li>
<li><p>대용량 접근 능력이 필요할 때는 밑으로 저장</p>
<br>
### Modern Computer 동작 원리
</li>
<li><p>메모리 안에는 프로세스가 있다</p>
<ul>
<li>프로그램을 실행하는게 프로세스</li>
<li>프로세스는 instruction, data, stack로 구성됨<ul>
<li>stack은 return을 위한 정보를 가짐</li>
</ul>
</li>
</ul>
</li>
<li><p>프로세스가 메모리에 로딩되고, CPU가 프로세스의 instruction을 가져와서(fetch) CPU에서 프로그램을 실행</p>
<ul>
<li>연산을 하거나</li>
<li>메모리와 데이터를 주고받거나</li>
<li>디바이스와 데이터를 주고받거나 (IO instruction, interrupt)</li>
</ul>
</li>
<li><p>thread: 프로그램을 실행하는 기본 단위</p>
</li>
<li><p>DMA: 메모리와 디바이스 사이의 데이터 이동</p>
<ul>
<li>큰 용량의 데이터를 옮길 때, CPU가 너무 바빠질 수 있으니 CPU중재 없이 바로 접근할 수 있도록 (CPU 부담 덜어줌)<img src="https://velog.velcdn.com/images/jungizz_/post/a1c69858-5a5a-40a7-bcce-5e7b14f47f52/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="multiprocessors-multicpu">Multiprocessors (MultiCPU)</h3>
<ul>
<li>여러개의 CPU (CPU = Processor라고 생각)</li>
<li>단일 CPU 한계(병목현상)를 극복하기 위해</li>
<li>parallel system</li>
<li>여러개의 CPU는 메모리를 공유 (shared memory)<ul>
<li>CPU간에 긴밀한 관계가 유지됨 (tightly-coupled systems)</li>
</ul>
</li>
<li>장점<ul>
<li>CPU처리 능력(throughput) 향상</li>
<li>경제적 (한 개의 CPU 성능을 늘리기 위한 비용이 선형적이지 않고 지수적으로 증가해서..)</li>
<li>CPU 성능은 한계가 있음</li>
</ul>
</li>
<li>Multiprocessing: multiprocessor 구조<ul>
<li>Asymmetric: 각 CPU 역할이 다름</li>
<li>Symmertic: 각 CPU 역할이 같음<img src="https://velog.velcdn.com/images/jungizz_/post/1f9915e8-fd95-4fcf-8a0e-d0b6cfeee423/image.png" alt=""><br></li>
</ul>
</li>
</ul>
<h3 id="multicore">Multicore</h3>
<ul>
<li>단일 CPU인데, CPU의 핵심 디바이스인 Core가 여러개</li>
<li>한 개의 칩이라서 사이즈가 작고, 전력을 아낄 수 있음</li>
<li>하드웨어적으로는 한 개의 칩인데, 여러개의 CPU 역할을 함<img src="https://velog.velcdn.com/images/jungizz_/post/b39a9430-db78-47dd-8130-8f969ff22fd2/image.png" alt=""><br></li>
</ul>
<h3 id="non-unifrom-memory-access-system-numa">Non-Unifrom Memory Access System (NUMA)</h3>
<ul>
<li><p>각 CPU의 local memory에는 빠르게 접근할 수 있지만,</p>
</li>
<li><p>다른 CPU의 local memory에 접근하려면 interconnect를 이용하기 때문에 느리다 → non-uniform하다</p>
</li>
<li><p>여러 CPU가 같은 메모리를 공유하는 경우는, 전부 접근 시간이 같기 때문에 Common Memory Access(COMA)라고 함</p>
<br>
### Clustured Systems (Multi-Computer)
</li>
<li><p>여러 컴퓨터가 모여있음</p>
</li>
<li><p>storage 공유 가능 (storage-area network(SAN))</p>
</li>
<li><p>각 컴퓨터 기능이 다른지 같은지에 따라 Assymmetric, Symmeric clustring</p>
</li>
<li><p>고성능 (high-performance computing(HPC))</p>
</li>
<li><p>분산 환경에서 메모리 동시 사용때문에 충돌나는걸 막아주기 위해 동기화 기법 사용 (ex-distrubuted lock manager)</p>
</li>
<li><p>메모리는 각자, storage는 공유<img src="https://velog.velcdn.com/images/jungizz_/post/04962602-50a7-428e-8b54-7f256d29b2df/image.png" alt=""><br></p>
</li>
</ul>
<hr>
<br>

<h3 id="operating-system-operation">Operating-System Operation</h3>
<ul>
<li>처음에는 부팅 프로그램을 실행<ul>
<li>컴퓨터를 키면 Bootstrap program이 kernel을 로딩</li>
</ul>
</li>
<li>다양한 system daemons를 실행 (초기화 과정)<ul>
<li>kernel위에 다양한 daemon들이 동작함</li>
</ul>
</li>
<li>interrupt drive<ul>
<li>hardware, software에 따른 다양한 interrupt가 존재</li>
<li>시스템에서 제공하는 유용한 함수(system call)도 interrupt로 처리 (software interrupt)</li>
</ul>
</li>
</ul>
<br>

<h3 id="multiprogramming">Multiprogramming</h3>
<ul>
<li>메모리에 여러 프로세스를 동시 로딩</li>
<li>CPU가 프로세스의 instruction을 fetch할 때, 여러 프로세스가 있으면 다양한 프로그램을 빠르게 실행할 수 있음<ul>
<li>만약 메모리에 한 프로세스만 올라가 있는데 다른 프로그램을 실행하려면, 하드디스크에서 해당 프로세스를 메모리에 올린 뒤~ instruction을 fetch해야 함 → 많은 시간 소요</li>
<li>치과 의자에 앉아서 기다리는 환자들처럼..</li>
</ul>
</li>
<li>job scheduling: 어떤 프로세스를 메모리에 로딩할건지<img src="https://velog.velcdn.com/images/jungizz_/post/8bd65a85-f759-4e5a-be70-daa453899c8d/image.png" alt=""><br></li>
</ul>
<h3 id="multitasking-timesharing">Multitasking (Timesharing)</h3>
<ul>
<li>multiprogramming구조에서 CPU 시간을 쪼개서 여러 프로세스를 로딩?</li>
<li>CPU가 워낙 빨라서 쪼개도 많은 instruction을 실행할 수 있음</li>
<li>response time(응답시간)이 줄어듦 → <strong>이게 가장 큰 목적</strong></li>
<li>CPU scheduling: CPU가 할 일이 있을 때, 시간을 어떻게 할당할건지</li>
<li>여러 프로세스를 메모리에 로딩하는데 메모리 용량이 부족한 경우, virtual memory 사용<ul>
<li>swap: 메모리 부족해서 가상공간으로 미뤄지는걸 swapping out, 실제 메모리 공간으로 올라가는걸 swapping in이라고 함</li>
<li>하드디스크의 공간을 메모리인척하는 가상 공간으로 사용함</li>
</ul>
</li>
</ul>
<br>

<h3 id="dual-mode">Dual-mode</h3>
<ul>
<li><p>CPU에는 user/kernel mode가 있음</p>
</li>
<li><p>일반 프로그램은 user mode로 실행됨</p>
</li>
<li><p>자원을 컨트롤하는 previleged instruction을 실행하려면 kernel mode로 접근해야함</p>
<ul>
<li>previleged instruction은 컴퓨터 시스템에 영향을 줄 수 있는 민감한 정보를 건듦</li>
</ul>
</li>
<li><p>virtual machine manager(VMM): 하드웨어가 없는데, 소프트웨어로 하드웨어가 있는 척 하는 것</p>
<br>
### User/Kernel mode 전환 과정
</li>
<li><p>모드 비트가 1이면 user mode, 0이면 kernel mode로 세팅된 경우</p>
<ol>
<li>사용자가 user mode로 프로세스에 접근하여 일반 프로그램을 실행</li>
<li>파일을 읽거나, 메모리 할당을 하거나, IO사용 등 system call을 부름<ul>
<li>System call은 kernel에 있는 service routine</li>
</ul>
</li>
<li>mode bit를 0으로 바꾸고 kernel mode로 시스템 resource에 접근하여 system call(interrupt service routine) 실행</li>
<li>mode bit를 1로 바꾸고 user mode로 다시 돌아감</li>
</ol>
</li>
<li><p>systemcall도 interupt처럼 처리</p>
<ul>
<li>trap, return은 소프트웨어 interrupt<img src="https://velog.velcdn.com/images/jungizz_/post/8a5c7b79-e60e-4403-b1e1-92740c719391/image.png" alt=""></li>
</ul>
</li>
<li><p>Timer hardware</p>
<ul>
<li>CPU scheduling(Time sharing)할 때, Timer가 각 프로세스에 대한 시간 조정</li>
<li>프로세스가 할당 받은 시간이 끝나면(counter zero) timer interrupt 생성</li>
</ul>
</li>
</ul>
<blockquote>
<h4 id="💡-운영체제-커널에는-크게-4가지-기능">💡 운영체제 커널에는 크게 4가지 기능</h4>
</blockquote>
<ol>
<li>프로세스 관리</li>
<li>메모리 관리</li>
<li>파일 관리</li>
<li>IO 관리</li>
</ol>
<br>

<h3 id="process-관리">Process 관리</h3>
<ul>
<li>Program → passive entity (정적)<ul>
<li>그냥 파일, CPU가 따라할 메뉴얼이 적혀있음</li>
<li>하드디스크에 존재, 소스코드를 컴파일하면 excutable file이 생김</li>
</ul>
</li>
<li>Process → active entity (동적)<ul>
<li>다른 자원(CPU, memory, IO, files)을 사용하여 메뉴얼에 따라 직접 동작함</li>
<li>프로그램이 프로세스를 실행한다</li>
</ul>
</li>
<li>Thread: 프로그램을 수행하는 기본 단위 (일단 process와 같은 개념으로 보기)<ul>
<li>Single-threaded process: 다음 instruction을 실행하는 program counter(PC)가 한 개</li>
<li>Multi-thread: 한 프로세스 안에 여러 개의 Thread (thread마다 PC),  function call을 관리하는 stack이 여러개<br></li>
</ul>
</li>
<li>Process management activities<ul>
<li>프로세스 생성 및 종료 / 중단(suspending) 및 이어서 실행(resuming)</li>
<li>여러가지 프로세스는 동시에 동작 → 프로세스 간 통신 (process commincation)</li>
<li>프로세스 간 충돌 방지 → 동기화 (synchronization)</li>
<li>deadlock handling (프로세스가 서로 기다리지 않도록)</li>
</ul>
</li>
</ul>
<br>

<h3 id="memory-management">Memory Management</h3>
<ul>
<li>프로그램을 실행하기 위해서는 모든 instruction과 data가 메모리에 있어야 함</li>
<li>메모리에 어떤게 올라갈지 언제 올라갈지를 관리하여 CPU 사용과 response를 최적화</li>
<li>가상메모리<br></li>
<li>Memory management activities<ul>
<li>메모리에 어떤 부분이 사용되고 있는지, 누가 차지하고 있는지 tracking</li>
<li>어떤 프로세스와 데이터가 메모리에 올라가고 나갈지 결정</li>
<li>메모리 주소 공간의 어느 부분을 사용할지 할당(allocating <code>malloc()</code> 및 해제(deallocating <code>free()</code>)</li>
</ul>
</li>
<li>메모리를 쪼갤 때, 일정한 크기로 자르면 page table, 성질에 따라 다른 크기로 자르면 segment table<br>
### File-system Management</li>
<li>파일이 연속인 공간에 있는 것처럼 사용 (logical view)<ul>
<li>실제로는 하드디스크나 SSD 메모리 공간(secondary storage)에 빈자리에 할당되기 때문에 연속적으로 있지 않음 → 인덱싱하여 사용</li>
</ul>
</li>
<li>폴더, 디렉토리와 같은 구조로 파일 관리</li>
<li>OS에서 지원하는 기능<ul>
<li>파일 생성 및 제거 / 읽기 및 쓰기</li>
<li>파일을 secondary storage에 mapping</li>
</ul>
</li>
</ul>
<br>

<h3 id="mass-storage-management">Mass-storage Management</h3>
<ul>
<li>파일이 저장되는 hard-disk storage</li>
<li>주기억장치와 다르게 오랜 기간 저장</li>
<li>OS에서 지원하는 기능<ul>
<li>하드디스크를 파일시스템에 연결하는 것 (mounting, unmounting)</li>
<li>하드디스크 빈 공간 관리</li>
<li>어떤 파일이 어떤 블록을 사용할지 할당</li>
<li>여러 프로세스가 동시에 하드디스크에 접근할 때  disk scheduling</li>
<li>storage를 쪼개서 하나의 파일 시스템에 할당 (partitioning)</li>
<li>그룹 파일 권한 설정 (protection)</li>
</ul>
</li>
</ul>
<br>

<h3 id="caching">Caching</h3>
<ul>
<li>storage hierachy에서 하위 storage의 데이터를 위로 올려서(copy) 빠르게 접근할 수 있도록 하는 과정 (자주 사용하는 데이터를 올림)<ul>
<li>메모리에 있는 데이터를 hardware cache에 caching → 컴퓨터구조에서의 caching</li>
<li>SSD의 데이터를 주기억장치(메모리)에 caching → 운영체제에서의 caching</li>
</ul>
</li>
<li>cache hit율을 높이는 것이 좋음<ul>
<li>cache한 데이터가 실제로 사용될 확률?<br></li>
</ul>
</li>
<li>storage 종류에 따른 특징<ul>
<li>위로 올라갈수록 접근 속도가 빨라 단위 시간 당 보내는 데이터의 양(bandwidth)이 많지만, 용량이 작다</li>
<li>register와 cache는 hardware가 관리하고, 나머지는 os가 관리</li>
<li>storage 공간이 부족하면 하위 storage로 밀려남<img src="https://velog.velcdn.com/images/jungizz_/post/d2b95ad6-6169-469e-93f4-c8dfa87f70c7/image.png" alt="">  <br>     </li>
</ul>
</li>
</ul>
<h3 id="migration-of-data">Migration of data</h3>
<ul>
<li>disk에 있는 파일을 읽어서 CPU가 처리하기 위해 register까지 올리는 과정<img src="https://velog.velcdn.com/images/jungizz_/post/78d48c10-26d8-4527-9ff7-554ead6fd559/image.png" alt=""></li>
<li>multi processor환경에서 성능 향상을 위해 같은 계층에서 copy(replication)할 수도 있음</li>
<li>성능 향상을 위해 cache나 replication이 실행되는데, 중복된 데이터가 충돌 없이 수행될 수 있도록 해야 함<ul>
<li>cache coherency: 중복이 발생하면 복제본간의 일치성을 유지해야한다<ul>
<li>가장 최근에 write한 read할 수 있도록 보장</li>
</ul>
</li>
<li>consistency: 동시에 한 데이터를 접근할 때 문제가 발생하지 않도록 일정한 순서로 데이터 처리</li>
</ul>
</li>
</ul>
<br>

<h3 id="io-subsystem">IO Subsystem</h3>
<ul>
<li><p>디바이스마다 다른 역할을 하는 디바이스 드라이버를 가지지만, 공통 역할을 하는 부분도 있다 → IO Subsystem</p>
<ul>
<li>buffering: 빠른 CPU와 느린 IO에 대한 완충작용을 위해</li>
<li>caching:</li>
<li>spooling:</li>
<li>(전부 메모리에 일시적으로 저장하는 일)</li>
</ul>
</li>
<li><p>디바이스별로 같은 역할을 하는 general device driver interface, 다른 역할을 하는 specific device driver</p>
<br>
### Protection and Security
</li>
<li><p>protection: 누가 어떤 리소스에 어떤 접근을 할 수 있는지 제어</p>
</li>
<li><p>security: 외부 공격으로부터 시스템 리소스를 보호</p>
<ul>
<li>CIA (기밀성, 무결성, 가용성)</li>
</ul>
</li>
<li><p>사용자 ID, 그룹 ID등으로 identity를 밝혀야함</p>
</li>
<li><p>privilege escalation: kernel mode로 동작할 때 높은 권한을 갖게되는 것</p>
<br>
### Virtualization
</li>
<li><p>메인 하드웨어 외에 다른 하드웨어가 있는 것처럼 흉내</p>
<ul>
<li>ex) java virtual machine을 메인 하드웨어 위에 올려서 JVM위에서 자바 프로그램이 실행될 수 있음 (메인 하드웨어가 달라도 같음)</li>
</ul>
</li>
<li><p>하나의 하드웨어에 여러개의 virtual machine을 올려서 여러개의 OS를 실행</p>
<ul>
<li>소프트웨어적으로 하드웨어 역할</li>
</ul>
</li>
<li><p>하나의 하드웨어의 host OS 위에 virtual machine을 올려서 guest OS를 올려 두가지의 OS를 사용</p>
</li>
<li><p>VM machine<img src="https://velog.velcdn.com/images/jungizz_/post/276d8596-6fdd-405f-83e9-fa4a7636df1a/image.png" alt=""><br></p>
</li>
</ul>
<h3 id="distributed-systems">Distributed systems</h3>
<ul>
<li>여러대의 컴퓨터(multi computer)가 네트워크로 연결되어 리소스 공유<ul>
<li>네트워크: LAN, WAN, MAN, PAN</li>
</ul>
</li>
<li>물리적으로는 여러대의 컴퓨터를 사용하지만 OS와 MW가 올라가서 마치 한 대의 컴퓨터인 것 처럼<ul>
<li>커다란 리소스를 가진 한 대의 시스템 (illusion of single system)</li>
</ul>
</li>
</ul>
<br>

<h3 id="kernel-data-structures">Kernel data structures</h3>
<ul>
<li>sequential list</li>
<li>linked list<ul>
<li>singly, doubly, circular</li>
</ul>
</li>
<li>binary search tree → 데이터 찾기 빠름 (log n)</li>
<li>hash function<ul>
<li>중복되면 linked list로 연결</li>
</ul>
</li>
<li>bitmap<ul>
<li>블록이 비어있는지 사용되고 있는지 확인하기 위해 0 또는 1로 표시</li>
</ul>
</li>
</ul>
<br>

<h3 id="computing-environments">Computing Environments</h3>
<ul>
<li>tranditional<ul>
<li>컴퓨터가 서버 형태로 있고, 터미널을 통해 서버에 접근</li>
<li>모바일, 노트북, web terminal과 같은 최소한의 기능만 있는 thin clients은 서버에 연결하여 서비스를 요청하고 받음 (client-server model)</li>
<li>방화벽(firewalls)로 외부 공격 방어</li>
</ul>
</li>
<li>mobile<ul>
<li>휴대가 가능해서 다양한 센서(gps, gyroscope)</li>
<li>물리적인 것과 연결이 쉬움 (augmented reality)</li>
<li>저사양, 저전력</li>
<li>모바일의 운영체제는 위와같은 특성에 맞게 변형됨 (android, ios)</li>
</ul>
</li>
<li>Client-server<ul>
<li>서버에 모든 리소스 존재</li>
<li>클라이언트에는 컴퓨터, 랩탑, 모바일 등</li>
<li>클라이언트가 서버에 서비스를 요청하고 서버가 서비스를 제공</li>
</ul>
</li>
<li>Peer-to-peer<ul>
<li>서버 없음. 서버에 의존적이지 않음</li>
<li>클라이언트이자 서버</li>
<li>누가 어떤 리소스를 가지고 있는지 몰라서 discovery protocol가 중요<ul>
<li>lookup service: 각 peer가 어떤 리소스를 가지고 있는지에 대한 정보를 서버가 가지고 있음 (진정한 p2p가 아님)</li>
</ul>
</li>
</ul>
</li>
<li>Cloud computing<ul>
<li>흩어진 리소스를 공유<br></li>
<li>public: 무료 또는 유료로 사용</li>
<li>private: 개인 회사</li>
<li>hybrod: public+private<br></li>
<li>어떠한 서비스를 받을 수 있는가<ul>
<li>SaaS: software</li>
<li>PaaS: platform (데베, 미들웨어 플랫폼)</li>
<li>IaaS: infra(하드웨어, 스토리지, 운영체제)</li>
</ul>
</li>
<li>구조<ul>
<li>인터넷을 통해 클라우드 서비스를 할당 받음</li>
<li>스토리지, 버추얼 머신, 서버 등 다양한 형태의 리소스</li>
<li>보안을 위한 firewall</li>
<li>request가 오면 load balancing</li>
<li>customer interface</li>
</ul>
</li>
</ul>
</li>
<li>Real-time embedded systems<ul>
<li>시간 제약이 존재, 일반 computing에서 응답시간, 처리율이 중요한 것과 다르게 시간 안에만 수행하면 됨</li>
</ul>
</li>
<li>Open source OS<ul>
<li>linux, unix와 같은 운영체제와 virtual machine 등이 공개 소프트웨어로 전환</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템프로그래밍] 6. IOT / Process&Thread Management]]></title>
            <link>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-6.-IOT-ProcessThread-Management</link>
            <guid>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-6.-IOT-ProcessThread-Management</guid>
            <pubDate>Fri, 25 Apr 2025 17:22:45 GMT</pubDate>
            <description><![CDATA[<h1 id="🔹iot">🔹IoT</h1>
<p>디바이스에 센서, 프로세싱 기능, 소프트웨어 및 다른 기술들을 합쳐 여러가지 서비스를 제공하는 것을 IoT라고 한다.</p>
<h4 id="발전-과정">발전 과정</h4>
<ol>
<li>센서의 값을 display</li>
<li>무선 통신 기능을 사용해 센서들의 값을 모아 가치있는 데이터 얻음</li>
<li>다양한 IT기술을 적용하며 데이터 처리 및 서비스 제공</li>
</ol>
<p>이처럼 센서에 통신(connectivity)과 인터넷 기능을 붙이니까</p>
<ul>
<li>긴 기간의 데이터 얻음</li>
<li>여러 센서의 데이터 얻음</li>
<li>여러 공간의 데이터 얻음
  → 많은 데이터를 처리(빅데이터, 인공지능 등)하며 의미있는 정보 얻음</li>
</ul>
<p>IoT는 비쌈, 잘 고장남.. 사람 쓰는게 더 쌈</p>
<h4 id="⭐-iot-개발의-requirement"><strong>⭐ IoT 개발의 Requirement:</strong></h4>
<ul>
<li><strong>싼 하드웨어 및 시스템, 효율적인 소프트웨어</strong></li>
<li><strong>쉬운 설치 및 관리, 잘 안고장나게 → 오래가는 배터리 동작, 세팅 안해도 동작 잘 되도록, 오작동 시 스스로 복구</strong></li>
</ul>
<h4 id="에너지-부분">에너지 부분</h4>
<ul>
<li>적은 비용, 가벼운 하드웨어로 배터리 오래가게 하기 (최소 1년)<ul>
<li>기회를 보고 많이 자야 오래가게 할 수 있다!!
  → <strong>Sensing optimization</strong>, <strong>Wireless communication optimization</strong></li>
</ul>
</li>
<li>다양한 센서(노드)가 비슷하게 수명이 다하도록 하기<br>
### Sensing optimization
센서는 보통 특정 소자의 저항을 측정하여 데이터를 얻는다. 그래서 에너지가 많이 소비되는 작업이다. → 최적화 해서 필요한 부분만 센싱해야한다
에너지와 정확도의 tradeoff

</li>
</ul>
<h4 id="고려해야-하는-것">고려해야 하는 것</h4>
<ul>
<li><strong>deadline</strong>: 센서 데이터가 제공되어야하는 기한 (빈도가 deadline보다 짧아야 함)</li>
<li><strong>age of information</strong>: 센서 데이터의 유효 기한 (안에 데이터가 전달되어야 함)</li>
</ul>
<h4 id="policy">policy</h4>
<ul>
<li>periodic sensing: 주기를 고정하여 센싱</li>
<li><strong>triggered sensing</strong>: 싼 간접 센서를 사용하여 센싱 유무 결정</li>
</ul>
<h4 id="robust-system-design">Robust system design</h4>
<ul>
<li>강인해야한다. 쉽게 설치할 수 있고 관리가 적어야 한다.<ul>
<li>plug and play: 큰 세팅 없이 동작이 잘 되도록</li>
<li>오류가 안날만큼 간단하게 시스템 구축</li>
<li>오류 발생 시 스스로 복구할 수 있는</li>
</ul>
</li>
</ul>
<br>

<h1 id="🔹processthread-management">🔹Process/Thread Management</h1>
<h2 id="🔸process">🔸Process</h2>
<ul>
<li>프로그램은 instruction의 집합으로 저장소에 저장되어있음<ul>
<li>소스코드가 동일하면 같은 프로그램</li>
</ul>
</li>
<li>프로세스는 loader에 의해 메모리에 올라간 프로그램<ul>
<li>소스코드가 같은 프로그램이 여러개 실행되면 각자 다른 프로세</li>
</ul>
</li>
</ul>
<h3 id="memory-layout">memory layout</h3>
<p>프로세스는 text, data, heap, stack 영역으로 구분되어있다</p>
<ul>
<li><strong>Program counter</strong>: 다음에 어떤 instruction을 수행할 지 알려주는 register값 (CPU내에 존재)<ul>
<li>각 프로세스마다 PC를 가진다</li>
</ul>
</li>
<li><strong>text section</strong>: program code(Instruction)</li>
<li><strong>Data section</strong>: global variable 보관(초기화 유무에 따라 구분되어 보관)</li>
<li><strong>Stack</strong>: 일시적인 데이터 보관 장소 (ex. function parameter, return address, local variable)</li>
<li><strong>Heap</strong>: 프로세스 실행 중에 동적으로 할당되는 메모리 (malloc, free)</li>
</ul>
<p>메모리에 프로세스가 올라갈 때, Data section의 크기는 고정되어있고(Static memory), Stach과 Heap은 실행 중에 필요에 따라 동적으로 할당되고 해제된다(Dynamic memory)<img src="https://velog.velcdn.com/images/jungizz_/post/9cb2ba80-0e15-492d-a600-edea9c270f06/image.png" alt=""> <img src="https://velog.velcdn.com/images/jungizz_/post/dd84bd28-dc77-4f3e-8b85-fcd39074031b/image.png" alt="">
위는 user space이고, kernel space까지 붙이면 아래와 같다. kernel space는 모든 프로세스가 공유하는 공간이다.<img src="https://velog.velcdn.com/images/jungizz_/post/fe4a2cbf-2579-4ebc-b93d-4484126b0f2b/image.png" alt=""></p>
<h3 id="process-state">Process state</h3>
<p>프로세스는 능동적 존재이기 때문에 실행되면서 상태가 변화한다. 프로세스가 생성되고 종료되는 과정에서 Running, waiting, ready 상태 변화가 반복된다. <img src="https://velog.velcdn.com/images/jungizz_/post/d57457e1-2a0c-4f10-a8a8-72686399ee10/image.png" alt=""></p>
<p>^ <strong>State Transition Diagram</strong></p>
<ul>
<li>New: 프로세스 생성 됨</li>
<li>Ready: 프로세스가 실행 대기, processor(CPU)에 할당되기를 기다리는 중</li>
<li>Running: Instruction 실행 중 (스케줄링되어 CPU를 할당받아 실질적으로 동작)</li>
<li>Waiting: 프로세스가 event가 일어나기를 기다리는 중</li>
<li>Terminated: 프로세스의 excution이 종료</li>
</ul>
<p>ready는 일을 끝내고 스케줄러에 들어갈 준비 된거고, waiting은 일을 끝내고 I/O까지 기다리는 중
<br></p>
<h3 id="process-control-block-pcb-or-tcb">Process control block (PCB or TCB)</h3>
<p>프로세스를 관리하기 위한 테이블로, 특정 프로세스와 연관된 여러 정보를 가지고 있는 자료구조</p>
<ul>
<li><strong>Process State:</strong> new, ready, running, waiting, terminated</li>
<li><strong>Program Counter</strong></li>
<li><strong>CPU Registers</strong></li>
<li><strong>CPU scheduling information:</strong> 우선순위, 큐에 대한 포인터 등과 같은 scheduling 파라미터 포함</li>
<li><strong>Memory-management information</strong></li>
<li><strong>Accounting information:</strong> CPU 사용 시간, open한 파일 정보</li>
<li><strong>IO status information</strong><img src="https://velog.velcdn.com/images/jungizz_/post/0dc22dca-b687-4c76-9a51-fc4f899e5668/image.png" alt=""></li>
</ul>
<p>리눅스에서 task_struct라는 구조체에 PCB가 정의되어있다. 
리눅스는 여러가지 프로세스를 linked list로 관리하는데, linked list의 포인터는 running중인 프로세스를 나타낸다. running 프로세스가 바뀌면 PCB의 값을 업데이트하며 포인터 값을 바꾼다.
<br></p>
<h3 id="process-scheduling">Process scheduling</h3>
<p>muli programming이나 multi tasking을 만족하기 위해 스케줄러는 다양한 알고리즘을 사용한다.
스케줄링에 따라 CPU에게 할당되는 프로세스를 바꾸게 된다 → context switching
<br></p>
<h3 id="context-switch">Context Switch</h3>
<p>CPU를 다른 프로세스로 swtich하기 위해서, 이전의 프로세스 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업</p>
<ul>
<li><strong>⭐ context</strong>: 지금까지 진행된 일의 문맥? 정보를 나타내는 값 (프로세스 상태, CPU register값, PC, 메모리 관리 정보 등 → 프로세스의 <strong>PCB</strong>에 표현 됨)<ul>
<li>context를 파악하고 있어야 효율적으로 일을 할 수 있다. 제대로 전달되지 않으면 오류 또는 반복될 수 있기 떄문</li>
</ul>
</li>
<li>interrupt나 system call이 들어오면 과거 프로세스의 context를 PCB에 저장하고(context save), 실행할 프로세스의 PCB에서 saved context를 복구(context reload/resume)<img src="https://velog.velcdn.com/images/jungizz_/post/f53b9267-fa8d-47d5-8d94-18a6ff838007/image.png" alt=""></li>
</ul>
<h4 id="context-overhead">Context overhead</h4>
<ul>
<li>과거 context를 저장하고, 실행할 context의 정보를 로드하는 과정에서 딜레이가 발생할 수 밖에 없다 → CPU가 노는 시간 발생(overhead)</li>
<li>예전엔 이 overhead가 좀 커서 이를 보완하기 위해 thread라는 개념이 나옴 → 최대한 공유하는 리소스를 늘리자<br>
### Operations on processes
지금까지는 프로세스 관리, 이제는 linux에서 어떻게 동작하는지
실행되는 동안 프로세스는 여러 개의 새로운 프로세스를 생성할 수 있다. 생성하는 프로세스를 **Parent**, 생성된 프로세스를 **child**라고 한다. child프로세스는 또 새롭게 다른 프로세스를 생성할 수 있으며, 그 결과 ‘프로세스의 **트리**’를 형성한다.![](https://velog.velcdn.com/images/jungizz_/post/29ba2bc1-8caf-4d71-99b7-34d96d133e67/image.png)

</li>
</ul>
<p>부모 프로세스에서 자식 프로세스를 만드는 2가지 방식</p>
<ol>
<li>OS에서 새로운 프로세스에 대한 자원을 할당</li>
<li>부모가 가진 자원 일부를 자식 프로세스에게 할당 → 보통 이거</li>
</ol>
<p><strong>fork()</strong></p>
<ul>
<li>나와 동일한 프로세스를 만드는 명령어 (copy)</li>
<li>fork() 명령어가 나오는 줄부터 프로세스가 복제, 그 밑에부터 동시 수행</li>
<li>pid를 return하므로, pid값만 다르다 → 보통 if문으로 구분해서 다른 동작을 할 수 있도록<ul>
<li>parent pid=1, child pid=0</li>
</ul>
</li>
</ul>
<p><strong>exec()</strong></p>
<ul>
<li>복제본이 아닌 다른 프로그램의 프로세스를 실행</li>
<li>exec()명령어가 나오는 순간 다른 프로그램을 실행하므로 그 밑의 코드는 아예 실행 안됨</li>
</ul>
<p><strong>wait()</strong></p>
<ul>
<li>부모 프로세스가 자식 프로세스가 끝날 때까지 기다림</li>
<li>동기화, 선후관계 가능</li>
<li><strong>waitpid()</strong>: 특정 자식이 끝나는걸 기다릴 수 있음</li>
</ul>
<p><strong>system()</strong></p>
<ul>
<li>fork-exec-wait를 통합한 system call</li>
<li>fork해서 두 개의 동일한 프로세스가 생성되고, 하나는 exec에 의해 다른 프로그램을 실행한다. 그리고 부모 프로세스는 다른 프로그램을 실행하러간 자식 프로세스를 기다린다.<br>
### Process termication
프로세스가 실행을 끝내면, **exit()** system call을 사용하여 프로세스를 종료
<br>
부모 프로세스는 **abort()**로 자식 프로세스를 강제 종료할 수 있다.</li>
<li>child가 자신에게 할당된 자원을 초과하여 사용하는 경우</li>
<li>child에게 할당된 task가 더 이상 필요 없는 경우<br>

</li>
</ul>
<p>정상적으로 종료하기 위해서는 꼭 부모는 자식이 종료되도록 기다려야한다. 그렇지 않으면 아래와 같은 문제가 발생할 수 있다.</p>
<ul>
<li><strong>좀비(Zombie) 프로세스</strong>: 자식은 끝났는데 부모가 wait()하지 않은 경우 → 자식의 자원은 해제됐지만 PCB상에는 남아있어 스케줄링 때 방해될 수 있다.</li>
<li><strong>고아(Orphan) 프로세스</strong>: 자식이 살아있는데 부모가 먼저 종료된 경우(wait()를 못한 경우) → 자식을 끝내줄 부모가 없어 자원 관리에 문제가 발생할 수 있다.</li>
</ul>
<p>이를 해결하기 위해 OS는 부모가 종료되면 자식도 종료되는 cascading termination 방식을 주로 사용한다. (폭포)
<br></p>
<h2 id="🔸thread">🔸Thread</h2>
<p>text, data, heap은 전부 공유하고, stack만 따로 관리하여 thread간 switching을 할 때 stack만 스위칭할 수 있도록한다. → 좀 더 빠르고 효율적<img src="https://velog.velcdn.com/images/jungizz_/post/f7929a2d-1e46-4ae5-b206-223b9aac5e1c/image.png" alt=""></p>
<p><strong>clone()</strong></p>
<ul>
<li>linux에서 thread를 만드는 system call</li>
<li>clone과 fork는 거의 똑같은 동작을 한다..</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템프로그래밍] 5. OS structures]]></title>
            <link>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-5.-OS-structures-Linux-overview</link>
            <guid>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-5.-OS-structures-Linux-overview</guid>
            <pubDate>Fri, 25 Apr 2025 17:11:52 GMT</pubDate>
            <description><![CDATA[<h3 id="separating-policy-and-mechanism">Separating policy and mechanism</h3>
<ul>
<li>Policy: <strong>무엇</strong>을 할건지(정책) → 리소스 할당 문제에 중요</li>
<li>Mechanism: <strong>어떻게</strong> 할건지(구체적 알고리즘) → 정책을 구현할 도구</li>
</ul>
<p>policy가 변할 수 있기 때문에 호환성(유연성)을 가져야함 → <strong>mechanism과 policy를 분리해야함</strong></p>
<ul>
<li>ex) 운전 시스템: 안전 주행 보조<ul>
<li>Policy: 전방 추돌 방지을 위한 특정 알고리즘</li>
<li>Mechanism: 브레이크 &amp; 가속
→ mechanism이 특정 알고리즘에 종속되어있지 않고 잘 분리되어있다면 policy의 알고리즘이 바뀌어도 ㄱㅊ음</li>
</ul>
</li>
<li>ex) CPU schedular: CPU 큐에 일 할당하고 싶음<ul>
<li>Policy: scheduling algorithm(FIFO, SJF, priority…)</li>
<li>Mechanism: CPU dispatcher(스케줄링 결과에 따라 CPU에게 특정 일 할당)</li>
</ul>
</li>
</ul>
<h3 id="implementation">Implementation</h3>
<p>다양한 언어로 운영체제가 작성된다</p>
<ul>
<li>초반에는 어셈블리어, 요즘은 호완성을 위해 c, c++</li>
<li>다양한 언어가 섞이기도 함(예를 들어, low level 컨트롤을 위해 일부는 어셈블리로 작동하고, 메인은 C)</li>
<li>high-level 언어로 작성하면 구현이 쉽지만 느리고 성능이 낮아짐..</li>
</ul>
<h3 id="structure">Structure</h3>
<p>일반적으로 OS는 아주 큰 프로그램이다. 커널은 PC가 작동 중일 때 항상 메모리에 로드되기 때문에 성능에 큰 영향을 미친다.
<br>
다양한 형태의 OS가 존재한다.</p>
<h4 id="simple-structurems-dos"><strong>simple structure(MS-DOS)</strong></h4>
<ul>
<li>초기, 간단 → 모듈로 나눠지지 않고, OS전체가 한 덩어리</li>
<li>single tasking, single user</li>
<li>Shell(command interpreter)로 동작</li>
<li>프로그램을 시작하면 shell범위를 줄이고 프로세스를 올려서 동작<img src="https://velog.velcdn.com/images/jungizz_/post/4875492c-a022-42c7-aa53-bff1c952b0dd/image.png" alt=""><h4 id="monolothicunix"><strong>Monolothic(Unix)</strong></h4>
</li>
<li>직관적, 단일 체제 OS</li>
<li>단일 체제 → 단일 메모리 공유 → 모듈 간 공유가 빠름, 부담 적음 → 성능 좋음</li>
<li>policy/mechanism분리 불가능 → 유연성 떨어짐</li>
<li>드라이버의 오류가 전체 시스템의 오류로 이어질 수 있음, 중요하지 않은 부분이 오류나면 좀..</li>
<li>업데이트가 어려움(업데이트 할 때마다 시스템 유지가 안되고 새로 까는 느낌) → 확장성 나쁨<h4 id="layered-structureabstraction-based"><strong>layered structure(Abstraction-based)</strong></h4>
</li>
<li>계층별 체제로 위의 단점 보완</li>
<li>인접 커널과만 상호작용 가능</li>
<li>특정 부분의 오류 시 해결 및 업데이트가 쉬움</li>
<li>통신 layer에 사용<h4 id="microkernelmach"><strong>Microkernel(Mach)</strong></h4>
</li>
<li>커널을 작게하여 진짜 필요한 것 빼고 나머지는 유저 모드로</li>
<li>핵심 커널이 작고 abstruction이 잘 되어있어서 대부분의 하드웨어에 쉽게 올라갈 수 있음</li>
<li>여러 기능을 여러 모듈로 분리하여 관리 → 유연성, 안정성</li>
<li>system call을 쓸 수 없고, message passing 사용 → 성능 떨어짐</li>
</ul>
<blockquote>
<p>Monolithic vs Microkernel
리눅스, 윈도우는 결국 mono로 갔다..
개발자가 힘들고 성능 좋은게 짱이다~</p>
</blockquote>
<h4 id="module"><strong>Module</strong></h4>
<ul>
<li>모듈별로 프로그램을 OS에 쉽게 붙일 수 있음 (모듈화, OOP 개념)</li>
<li>layered, microkernel과 비슷<ul>
<li>인접 커널과만 상호작용 하던 layered보단 자유도가 높다</li>
<li>모듈이 커널에 들어가면 소프트웨어에 종속되도록해서(메모리 영역을 공유하도록해서) microkernel처럼 message passing을 사용하지 않아도 됨</li>
</ul>
</li>
</ul>
<h4 id="hybrid"><strong>Hybrid</strong></h4>
<ul>
<li>이게 현실</li>
<li>다양한 방식이 함께 사용된다~~</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템프로그래밍] 4. Operating systems overview]]></title>
            <link>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-4.-Operating-systems-overview</link>
            <guid>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-4.-Operating-systems-overview</guid>
            <pubDate>Fri, 25 Apr 2025 17:06:30 GMT</pubDate>
            <description><![CDATA[<h2 id="🔸operating-systems">🔸Operating systems</h2>
<ol>
<li>유저: 컴퓨팅 문제를 풀고싶은 사람, 기계, 컴퓨터</li>
<li>프로그램: 유저의 컴퓨팅 문제를 리소스를 사용해서 해결</li>
<li><strong>OS</strong>: 하드웨어 사용 제어 → 얘만 실물이 아닌 컴포넌트</li>
<li>하드웨어: 리소스 제공 (CPU, memory, I/O)</li>
</ol>
<ul>
<li>어플리케이션을 위해 <strong>하드웨어를 사용하기 쉬운 형태로 변환</strong>하는 소프트웨어</li>
<li>커널</li>
</ul>
<h3 id="목적">목적</h3>
<p>편리성, 효율성, 안전성</p>
<ul>
<li>유저가 프로그램을 실행하고 문제를 쉽게 해결할 수 있도록 하는 환경 제공</li>
<li>컴퓨터 시스템 사용하기 쉽도록, 프로그램이 의도하지 않아도 잘 실행되도록</li>
<li>컴퓨터 하드웨어를 효율적이고 안전한 방식으로 사용하도록</li>
</ul>
<h3 id="하는-일">하는 일</h3>
<ul>
<li>리소스 매니저 (자원 관리)<ul>
<li>여러 요청에 효율적이고 공평하게 리소스 사용하도록 결정</li>
<li>CPU, 메모리, I/O디바이스 간 자원관리가 핵심 작업</li>
</ul>
</li>
<li>control program: 에러와 부적절한 컴퓨터 사용 막는 프로그램</li>
</ul>
<h3 id="services">services</h3>
<p>OS는 유저에게 도움이 되는 기능 제공</p>
<ul>
<li>UI: command-line interface, graphics user interface</li>
<li>program excution: 메모리로부터 프로그램을 로드하고 실행</li>
<li>I/O: 파일이나 기기의 I/O 요청</li>
<li>file system: 파일과 폴더 생성, 삭제, 읽기, 쓰기, 찾기, 접근 허용</li>
<li>communications: 프로세스가 정보 교환</li>
<li>error detection: 에러 감지 및 적절한 액션 취함<br>
## 🔸Computer systems (자원관리)
I/O디바이스를 어떻게 처리하고 자원관리는 어떻게 할까?
CPU와 device controllers가 common bus로 연결 (common bus는 메인 메모리로의 접근 제공)</li>
<li>각 I/O디바이스마다 device controller를 가지고, 로컬 버퍼가 존재<ul>
<li>input들은 로컬 버퍼에 있다가 버스를 통해 메인 메모리로 이동</li>
</ul>
</li>
</ul>
<p>이때 이동하는 규칙 세가지는 아래와 같다.</p>
<h3 id="1-programmed-io-polling">1. Programmed I/O (=Polling)</h3>
<p>평등, 모든 디바이스를 하나씩 돌아가며 처리</p>
<ul>
<li>CPU는 디바이스 컨트롤러를 통해 I/O디바이스의 상태를 반복적으로 확인 (예를 들어 I/O동작 요청이 완료됐는지..)</li>
<li>ready 상태면 CPU는 데이터를 I/O operation에 데이터 제공</li>
<li>한 디바이스에서 많은 input이 있는 경우, CPU가 비효율적으로 사용될 수 있음<ul>
<li>I/O디바이스가 ready될 때까지 계속 기다림, 시간 낭비, busy waiting</li>
</ul>
</li>
</ul>
<h3 id="2-interrupt-driven-io">2. Interrupt-driven I/O</h3>
<p>디바이스 컨트롤러는 interrupt를 발생시켜 CPU에게 이벤트 알림</p>
<ul>
<li>I/O디바이스에서 이벤트 발생할 때</li>
<li>디바이스 컨트롤러가 I/O operation를 끝냈을 때</li>
</ul>
<p>CPU는 메모리 일만 하다가, interrupt가 오면 I/O일을 하게됨
<br>
Interrput가 발생하면 (interrupt handling)</p>
<ol>
<li>OS는 CPU의 현재 상태 보존<ul>
<li>interrupt handling 후 다시 돌아오기 위해</li>
<li>레지스터와 interrupted instruction의 메모리 주소 저장</li>
</ul>
</li>
<li>interrupt service routine(interrupt handler)으로 점프</li>
<li>보존한 상태를 복원하여 복귀<img src="https://velog.velcdn.com/images/jungizz_/post/77147141-0b3e-4b23-ba33-cbf74faba83f/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/fe54c459-4846-417d-8faf-387d4baa580d/image.png" alt=""></li>
</ol>
<h3 id="3-dma-io">3. DMA I/O</h3>
<p>Direct Memory Access
interrup로 CPU의 busy waiting은 없어졌지만, 많은 양의 데이터를 메모리로 이동시키기 위해 interrupt처리하면 시간이 오래걸려 하나의 I/O가 CPU를 점유하게 될 수 있다 → DMA</p>
<ul>
<li>I/O operation을 다루기 위해 각 디바이스 컨트롤러에 DMA controller라는 하드웨어 사용(small cpu역할)</li>
<li>DMA는 CPU를 거치지 않고 device controller와 메인 메모리 사이에서 데이터를 전송 가능<img src="https://velog.velcdn.com/images/jungizz_/post/af117cef-79eb-40bd-98f5-8acbd5a8fe9e/image.png" alt=""></li>
<li>하지만 DMA컨트롤러가 비싸고, CPU가 처리해주는 것보단 느리다</li>
</ul>
<h5 id="💡-이-세가지-방식은-적당하게-용도에-따라-같이-사용된다">💡 이 세가지 방식은 적당하게 용도에 따라 같이 사용된다~</h5>
<br>

<h2 id="🔸multipleprogramming">🔸Multipleprogramming</h2>
<p>좀 더 구식</p>
<ul>
<li>멀티 프로그램은 CPU나 I/O 디바이스가 항상 바쁘도록, 할 일이 무조건 있도록 할 수 있음 (never idle)<ul>
<li>I/O때문에 기다려야하면, OS는 다른 일을 하도록 교체 → 계속 바쁨</li>
</ul>
</li>
<li>메모리에 할 일들이 있고, 그 중 하나 선택되고 실행하는 것은 job scheduling<img src="https://velog.velcdn.com/images/jungizz_/post/580a6524-7c87-4411-8411-772f5580c1ac/image.png" alt=""></li>
</ul>
<br>

<h2 id="🔸multitaskingtime-sharing">🔸Multitasking(Time sharing)</h2>
<p>좀 더 신식?</p>
<ul>
<li>멀티프로그래밍의 확장, CPU가 주기적으로 할 일을 switch<ul>
<li>각 일은 time slice가 주어지고, time slice만큼 실행하면 다른 일로switch</li>
<li>switch로 돌다가 다시 돌아오는데 걸리는 시간: response time &lt; 1초 여야함<img src="https://velog.velcdn.com/images/jungizz_/post/e87a94ac-e988-453a-b0fc-8b6399121ac2/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h2 id="🔸dual-mode-operation">🔸Dual-mode operation</h2>
<p>어플리케이션이 디스크 드라이브에 바로 접근한다면? interrupt handlers가 override된다면? HLT instruction을 실행한다면? → 시스템에 피해가 갈 수 있다 → OS는 시스템을 지키기 위해 user/kernel mode로 동작한다.</p>
<ul>
<li>user/kernel mode<ul>
<li>하드웨어의 mode bit로 kernel, user mode 설정</li>
<li>HLT같은 privileged instruction(위험한 액션)은 kernel 모드에서만 실행 가능</li>
</ul>
</li>
</ul>
<blockquote>
<p>Privileged instruction(조심히 다뤄야하는 instruction)</p>
</blockquote>
<ul>
<li><p>direct I/O access</p>
<ul>
<li>저장장치(디스크)의 데이터를 읽고 쓸 때 캐시를 통해 indirect하게 작동한다. direct하게 작동하면 보안에 취약해질 수 있음</li>
</ul>
</li>
<li><p>Accessing/manipulating system registers</p>
<ul>
<li>레지스터의 값을 바꿔서 서브루틴 후 리턴이 안되게 하거나, 이상한 주소를 가리키거나.. 보안에 취약함. 또는 실수로 건드리더라도 하드웨어에 안좋은 영향을 미칠 수 있음</li>
</ul>
</li>
<li><p>memory state management</p>
</li>
<li><p>HLT
얘네들을 user mode에서 실행하면 exception 발생</p>
</li>
<li><p>최근 CPU들은 2개 이상의 모드를 제공하기도 함</p>
<ul>
<li>ex) virtual machine manager</li>
</ul>
</li>
</ul>
<blockquote>
<h3 id="interrupt-vs-exception">Interrupt vs Exception</h3>
</blockquote>
<ol>
<li>Interrupt = <strong>hardware interrupt</strong><ul>
<li>하드웨어 장치에 의해 발생</li>
<li>비동기적(아무때나) 발생</li>
</ul>
</li>
<li>Exception(system call) = <strong>software interrupt</strong><ul>
<li>instruction에 의해 발생 (software error: 접근 제한 데이터에 접근, 0으로 나누기, 미할당 주소 접근 등)</li>
<li>동기적 (CPU가 instruction을 실행할 때 발생하니까)</li>
</ul>
</li>
</ol>
<p>유저모드와 커널모드를 변환하는 2가지 방법</p>
<ol>
<li><strong>by interrupt(hardware interrupt)</strong>: 비동기적 (ex. timer)<ul>
<li>커널모드에서 OS가 interrupt 발생 타이머 세팅, 유저모드에서 CPU를 통해 어플리케이션이 동작하다가 timer interrupt 발생, 커널모드로 바뀌고 할일 하고 다시 유저모드로 돌아가고</li>
</ul>
</li>
<li><strong>by system call(software interrupt)</strong>: 동기적 ⭐<ul>
<li>유저모드에서 system call을 발생시켜서 커널 모드로 변환되고 일 처리 후 유저모드로 돌아옴<img src="https://velog.velcdn.com/images/jungizz_/post/4ea265a3-267c-42fc-b39c-9f9cd74ff741/image.png" alt=""></li>
</ul>
</li>
</ol>
<br>

<h2 id="🔸system-calls">🔸System calls</h2>
<p>OS가 제공하는 유용한 함수(programming interface), 위험해서 high level 언어로 숨겨둔?</p>
<ul>
<li>OS services<img src="https://velog.velcdn.com/images/jungizz_/post/8e7fbe5c-d386-49b2-aad8-646e356beef6/image.png" alt=""></li>
</ul>
<h3 id="종류">종류</h3>
<ul>
<li>Process control</li>
<li>File management</li>
<li>Device management</li>
<li>Information maintenance</li>
<li>Comminications</li>
<li>Protection
즉, 얘네들은 보안 취약점인 것임.. 그래서 system call로 묶어버려</li>
</ul>
<p>ex) 다른 파일로 내용을 복사하는 과정에서의 system call<img src="https://velog.velcdn.com/images/jungizz_/post/07b622d4-4241-4274-b359-3f60baf7439e/image.png" alt=""></p>
<h3 id="parameter-passing">Parameter passing</h3>
<p>system call을 사용할 때 파라미터를 넘겨주는 방법</p>
<ol>
<li><p>레지스터를 통해 파라미터 전달 → 레지스터보다 파라미터가 많을 수 있음 (그래도 이게 젤 빠름)</p>
</li>
<li><p>파라미터들은 메모리의 블록에 저장되고, 그 블록의 주소를 레지스터로 전달 (Linux, Solaris)<img src="https://velog.velcdn.com/images/jungizz_/post/231d52f3-c8e2-4b08-a946-032d972a524f/image.png" alt=""></p>
</li>
<li><p>파라미터들이 user program에 의해 스택에 넣어지고, OS에 의해 pop됨</p>
</li>
</ol>
<p>block과 stack 방법은 파라미터 크기 제한이 없다~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템프로그래밍] 3. Linkers and Loaders]]></title>
            <link>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3.-Linkers-and-Loaders</link>
            <guid>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3.-Linkers-and-Loaders</guid>
            <pubDate>Fri, 25 Apr 2025 16:53:35 GMT</pubDate>
            <description><![CDATA[<p>지금까지 어셈블러에 대해 알아보았다. 어셈블러는 어셈블리어를 기계가 이해할 수 있는 object code로 변환시킨다. 이제 그 다음 과정을 위해 사용되는 linker와 loader에 대해 알아본다.</p>
<h1 id="🔸tnaslating-and-starting-a-program">🔸Tnaslating and Starting a Program</h1>
<p>고급 언어 프로그램의 번역 과정을 다시 살펴보자. </p>
<ol>
<li>Complier: high-level language(C Program)를 low-level language(Assembly)로 변환</li>
<li>Assembler: Assembly를 기계어로 변환하여 오브젝트 생성<ul>
<li>Assembly language: 기계어와 1:1대응되는 low-level 언어</li>
</ul>
</li>
<li><strong>Linker</strong>: 여러 개의 오브젝트 파일을 하나의 실행 파일로 결합하여 기계어 프로그램을 만듦</li>
<li><strong>Loader</strong>: 실행 파일을 메모리에 올려 실행할 준비<img src="https://velog.velcdn.com/images/jungizz_/post/33be13b4-9eb7-45ff-963e-c03f8020368f/image.png" alt=""></li>
</ol>
<p>Translation 과정을 빠르게하기 위해 일부 단계가 생략되거나 결합될 수 있지만 논리적인 4단계는 명확하다.</p>
<ul>
<li>컴파일러가 object module을 직접 만들거나</li>
<li>마지막 두 단계를 수행하는 linking loader를 사용하는 등..</li>
</ul>
<br>

<h2 id="separate-compilation">Separate compilation</h2>
<ul>
<li>프로그램은 논리적으로 관련된 서브루틴과 자료구조의 집합이 담긴 여러가지의 소스파일로 이루어져 있음</li>
<li>이 파일들은 어셈블러에 의해 object file로 만들어진다.</li>
<li>이는 하나의 소스 파일을 수정해도 다른 소스파일을 다시 assemble할 필요가 없도록 만듦<img src="https://velog.velcdn.com/images/jungizz_/post/da6d32c3-bff7-486c-ad95-63188c0d23a3/image.png" alt=""></li>
</ul>
<p>그래서 object file은 여러개가 생기게 되고, 이들을 엮어서 하나의 프로그램으로 만드는 것이 Linker의 역할이다.</p>
<br>

<h1 id="🔸linkers">🔸Linkers</h1>
<ul>
<li>독립적으로 어셈블된 machine language program을 결합하고, 정의되지 않은 레이블을 수정하여 executable file 생성<ul>
<li>각 object module의 relocation 정보와 symbol table 사용</li>
<li>생성된 executable file과 object file의 유일한 차이점은 해결되지 않은 reference 존재 여부</li>
</ul>
</li>
<li>주요 작업<ul>
<li>프로그램에서 사용되는 <strong>library routine 찾기</strong> (프로그램 라이브러리(prewritten routines)를 검색)</li>
<li>각 object module이 차지할 <strong>메모리 위치 결정</strong>하고 absolute reference를 조정하여 instruction 재배치</li>
<li>object file 간의 <strong>external reference 해결</strong></li>
</ul>
</li>
</ul>
<p>이렇게 만들어진 program은 디스크와 같은 보조 저장 장치에 위치한다. 이 프로그램을 실행하기 위해 Loader가 필요하다.</p>
<br>

<h1 id="🔸loader">🔸Loader</h1>
<ul>
<li>linker가 생성한 program을 메모리로 가져와 실행을 시작</li>
<li>메모리에 할당된 프로그램은 아래와 같은 모양<img src="https://velog.velcdn.com/images/jungizz_/post/64da8ca7-d19b-405c-991a-bd6a83cae580/image.png" alt=""></li>
</ul>
<h4 id="start-a-program-과정">Start a program 과정</h4>
<ol>
<li>헤더 읽기: executable file의 헤더를 읽어 텍스트와 data segment의 크기 결정</li>
<li>주소 공간 생성: 텍스트, data segment, stack segment를 포함할 수 있는 프로그램용 주소 공간 새로 생성</li>
<li>명령어 및 데이터 복사: executable file에서 명령어와 데이터를 새로 생성한 주소 공간(메모리)으로 복사</li>
<li>매개변수 복사: 메인 프로그램에 대한 매개변수가 존재하는 경우 스택에 복사</li>
<li>레지스터 초기화: machine 레지스터를 초기화하고, 스택 포인터를 첫 번째 빈 위치로 설정</li>
<li>Start-up routine으로 점프: 스택의 매개변수를 레지스터로 복사하고 프로그램의 메인 루틴을 호출, 메인 루틴에서 반환하면 start-up루틴은 종료 시스템을 호출하여 프로그램 종료</li>
</ol>
<p>Loader는 오직 loading만 하는 absolute loader와 linking과 relocation 기능도 지원하는 linking loader 2가지 종류를 가진다.</p>
<h2 id="🔹absolute-loader">🔹Absolute Loader</h2>
<ul>
<li>가장 간단한 형태의 loader로, link나 relocation 작업을 수행하지 않음</li>
<li>모든 기능을 단일 패스로 수행<ul>
<li>Header record을 확인하여 올바른 프로그램이 load되었는지, 사용 가능한 메모리에 적합한지 검증</li>
<li>Text record를 읽고 object code를 메모리의 지정된 주소로 이동</li>
<li>End record를 만나면 loader는 지정된 주소로 점프하여 load된 프로그램의 실행을 시작<img src="https://velog.velcdn.com/images/jungizz_/post/ad50cd99-13a2-4f47-bb04-28e69a0fdc5c/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="object-program-example">object program example</h4>
<ul>
<li>H record를 확인하여 1000부터 시작함을 확인하고, 107A만큼의 공간이 있는지 판단</li>
<li>T record를 거치면서 해당 object code가 메모리로 이동</li>
<li>E record를 만나면 시작 주소 1000으로 점프하여 프로그램 실행 시작<img src="https://velog.velcdn.com/images/jungizz_/post/527f9521-417d-43ac-9df1-8ce9c99b1a77/image.png" alt=""></li>
<li>위 과정을 거쳐 메모리에 프로그램이 로드된 모습은 아래와 같음<img src="https://velog.velcdn.com/images/jungizz_/post/1a301d1f-7616-4bfb-8923-ba85bb66c53d/image.png" alt=""></li>
</ul>
<h3 id="ex-boot-loader">ex) Boot Loader</h3>
<p>정해진 위치에 올리기만 하는 Absolute loader는 일반적이지 않고 특별한 상황에서 쓰인다. 대표적인 예시로 컴퓨터를 처음 킬 때 실행되는 특수한 유형의 Boot Loader가 있다.</p>
<ol>
<li>실행할 첫 번째 프로그램을 로드(보통 OS를 로드함)</li>
<li>HW로직이 ROM의 0 주소에 해당하는 프로그램(boot loader)를 읽음<ul>
<li>ROM은 제조업체에서 설치하며, bootstrap program(boot loader)과 하드웨어를 제어하는 기타 루틴(ex.BIOS)를 포함</li>
</ul>
</li>
</ol>
<ul>
<li>NO relocation, No linking</li>
</ul>
<h3 id="pros--cons">Pros &amp; Cons</h3>
<p>장점</p>
<ul>
<li>1pass로 수행되기에 단순성과 효율성을 가짐</li>
</ul>
<p>단점</p>
<ul>
<li>프로그래머는 프로그램이 메모리에 로드될 실제 주소를 지정해야 함<ul>
<li>더 크거나 advanced된 기계에서는 불가능</li>
<li>근데 프로그래머가 메모리 계산을 잘 한다면,, 효율적일 수도 있음</li>
</ul>
</li>
<li>모든 서브루틴에 사전에 할당된 절대 주소가 필요<ul>
<li>서브루틴 라이브러리를 효율적으로 사용하기 어려움</li>
</ul>
</li>
<li>여러 프로그램을 함께 실행할 수 없음</li>
</ul>
<br>

<h2 id="🔹linking-loader">🔹Linking Loader</h2>
<ul>
<li>SIC/XE 시스템 및 대부분의 최신 컴퓨터에서 사용하기 적합한 더 복잡한 loader</li>
<li>단순 loading기능 뿐만 아니라, relocation, linking 기능을 수행<ul>
<li>모듈 간 reference를 해결하는데 유용</li>
</ul>
</li>
</ul>
<p>Loader에서 relocation이 구현되는 방식은 기계의 특성에 따라 다르다.(Machine-dependant feature) 재배치 지정 방식은 Modification record또는 Relocation bit을 사용하는 2가지가 존재한다.</p>
<h3 id="relocation-w-modification-record">Relocation w. Modification record</h3>
<p>SIC/XE에서 수정이 필요한 instruction은 모두 immediate addressing으로 나타나있고, 이의 address field는 해당 Symbol(RDREC, WRREC)이 프로그램 시작 주소에서 얼마나 떨어져있는가를 나타내고 있다. 하지만 해당 Symbol의 절대적인 주소는 현재 address field값에 프로그램 시작 주소(COPY)를 더한 값이다. 그래서 Modification record에서 해당 address field에 프로그램 시작 주소를 더하라고 나타낸다.<img src="https://velog.velcdn.com/images/jungizz_/post/c70516b2-1eb6-4f6e-9413-0c9a218c9d97/image.png" alt=""></p>
<ul>
<li>위와 같이 relative 또는 immediate addressing을 사용하는 SIC/XE 시스템에 적절한 Relocation 방법으로, SIC 버전 시스템에는 적절하지 않음<ul>
<li>그 이유는 SIC는 모든 주소값이 direct addressing이므로, 모든 instruction이 재배치를 해야함</li>
<li>이에 따라 모든 instruction에 대한 modification record가 필요
→ 많은 modification record로 object program의 크기가 2배 이상이 되며 비효율적</li>
</ul>
</li>
</ul>
<p>이러한 SIC에서의 relocation 문제를 해결하기 위해 Bit mask 방식을 사용한다.</p>
<h3 id="relocation-w-bit-mask⭐">Relocation w. Bit mask⭐</h3>
<p>주로 direct addressing을 사용하고, fixed instruction format을 가지는 machine에서 machine(SIC)에서 유용하다. </p>
<ul>
<li>기존 modification record는 [수정할 주소, 길이] 형태를 가짐<ul>
<li>fixed instruction format이라면 길이를 나타낼 필요X</li>
<li>주소 필드 대신 비트벡터를 사용</li>
</ul>
</li>
<li>Relocation Bit<ul>
<li><strong>1</strong>: 프로그램의 시작 주소를 주소 필드에 추가하여 재배치를 수행해야함을 나타냄</li>
<li><strong>0</strong>: 수정이 필요하지 않음<img src="https://velog.velcdn.com/images/jungizz_/post/42ee8f0d-527d-49c1-97b7-53fb7c249dc2/image.png" alt=""></li>
</ul>
</li>
<li>위처럼 모든 instruction에 bit를 표시하고, 이를 나열하여 16진수로 나타냄</li>
<li>첫번째 T record에서 object code의 길이를 표현하는 열 이후의 ‘EFC’는 ‘1111 1111 1100’으로 나열된 bit mask를 나타냄<ul>
<li>‘1111 1111 1100’의 가장 왼쪽비트부터 첫번째 instruction의 relocation bit가 됨<img src="https://velog.velcdn.com/images/jungizz_/post/56c31b3f-fab0-4123-a879-50890703a01c/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h1 id="🔸example">🔸Example</h1>
<p>프로그램은 관련된 모든 Control section들이 linking으로 결합된 logical entity이다. 하지만 loader의 관점에서는 프로그램이라는 개념이 없고, link되고 재배치되고 load될 control section들이 존재할 뿐이다. EXTDEF &amp; EXTREF로 정의된 symbol들의 정보는 linker에게 잘 알려줘야하고, 이를 통해 loader가 재배치할 수 있도록 해야한다.</p>
<h3 id="1-assembly-→-object-code">1. Assembly → Object code</h3>
<p>아래 각 코드들은 다른 CS(PROGA, PROGB, PROGC)에 속해있다. 각 섹션별로 LIST와 END symbol이 정의되어있고, 섹션에 상관없이 REF1~REF8은 모두 동일한 reference를 사용한다. 주목해야 할 것은 3개의 CS에서 동일한 reference가 적혀있지만 이들을 다루는 방법은 섹션마다 다르다는 것이다.</p>
<ul>
<li>각 CS에서 local reference의 주소 계산은 가능하니 object code에 잘 적으면 됨</li>
<li>external reference는 각 CS에서는 바로 알 수 없으니 format4 instruction으로 전환하여 0으로 채워둠<ul>
<li>그리고 modification record가 적힐 것이고 이걸 보고 loader가 수정해서 절대 주소를 찾아낼 것임</li>
</ul>
</li>
<li>모르는게 있더라도, 아는 것들은 다 적어야함 → 어셈블러는 자기가 아는거까지 처리!<img src="https://velog.velcdn.com/images/jungizz_/post/1005ee73-4572-4d19-8685-1a4695a05728/image.png" alt=""><br>
### 2. Object code → Object program
위에서 어셈블리 코드를 object code로 변환하였고, 이를 합쳐 만든 object program은 아래와 같다. modification record에 주의하여 살펴본다.</li>
<li>재배치 과정에서 외부 symbol들의 <strong>절대 주소</strong>로 수정하게 됨 → 그래서 local symbol의 상대 주소가 object code에 적혀있다면 <strong>해당 CS의 시작 주소를 더해서 절대주소로 통일해줘야함</strong></li>
<li>근데 “ENDA-LISTA”같은 식이면 시작주소 각각 더해줘봤자 다시 빠져서 의미가 없는 계산이 되므로 굳이 modification record에 적지 않음 (빨간 글씨!)<img src="https://velog.velcdn.com/images/jungizz_/post/60211a09-259e-4815-b0f7-5d1b53c27ed6/image.png" alt=""><br>
### 3. Relocation
Modification record에 따라 address field를 수정하여 재배치해주는 과정은 아래와 같다. 

</li>
</ul>
<p>ex) PROGA의 REF4<img src="https://velog.velcdn.com/images/jungizz_/post/04ce7a43-8ced-4b8a-87f3-6446cb7cb2cd/image.png" alt="">
<br></p>
<h3 id="4-load-memory">4. Load Memory</h3>
<p>Modification record까지 적용된 후, 이것들이 메모리에 올라갔을 때의 모습은 아래와 같다.</p>
<ul>
<li>REF4~8은 3개의 CS에서 동일한 값을 가짐</li>
<li>하지만, REF1<del>3처럼 instruction의 operand에 외부참조가 있는 경우에는 CS들이 어디에 로드되는지에 따라 다른 값이 나타남 ~</del>?정확히 이해 안됨..상대주소라서?~~<img src="https://velog.velcdn.com/images/jungizz_/post/b7d1b509-c99c-47b9-9902-eae00b858ea8/image.png" alt=""></li>
</ul>
<br>

<h1 id="🔸algorithm--data-structures-for-linking-loader">🔸Algorithm &amp; Data structures for Linking Loader</h1>
<p>Linking loader는 Modification Record들 사용하여 linking과 relocation까지 동시에 해결하기 때문에 더 복잡한 알고리즘을 가진다. </p>
<ul>
<li>Absolute loader와 다르게 input에 대해 <strong>2pass</strong>를 이용<ul>
<li>input: 여러 CS 집합</li>
</ul>
</li>
</ul>
<h4 id="pass1-모든-external-symbol에-주소-할당"><strong>Pass1</strong>: 모든 external symbol에 주소 할당</h4>
<ul>
<li>PROGADDR를 CSADDR에 넣어 각 CS(모듈)의 위치를 결정<ul>
<li><strong>PROGADDR</strong>: 링크된 프로그램이 메모리에 로드될 때의 시작주소<ul>
<li>OS가 메모리에서 어디가 비어있는지 확인하고 알려줌</li>
</ul>
</li>
<li><strong>CSADDR</strong>:  loader에 의해 스캔된 현재 각 CS에 할당된 시작 주소</li>
</ul>
</li>
<li><strong>external symbol table(ESTAB)</strong> 생성<ul>
<li>CS 시작 주소, symbol 이름, symbol의 절대 주소 저장<img src="https://velog.velcdn.com/images/jungizz_/post/c6bac063-6427-4eff-95d2-470cccecc7f1/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/0910eb7e-22f0-4683-93ec-61071bd7fba6/image.png" alt=""></li>
</ul>
</li>
</ul>
<h4 id="pass2-실제-loading-relocation-linking-수행"><strong>Pass2</strong>: 실제 loading, relocation, linking 수행</h4>
<ul>
<li>Modification record의경우, ESTAB를 이용하여  주소를 찾고 이를 통해 절대 주소 수정</li>
<li>로드된 프로그램에 control을 넘김<img src="https://velog.velcdn.com/images/jungizz_/post/f3f4b240-1f57-494b-8850-02ccae9d74fd/image.png" alt=""></li>
</ul>
<br>

<h1 id="🔸dynamic-linking">🔸Dynamic Linking</h1>
<p>지금까지 다룬 linking은 일반적으로 static linking이다. 이는 프로그램 실행 전에 모든 object code를 load&amp;linking하여 메모리에 올린다. <strong>Dynamic linking은 프로그램 실행 도중에 object code가 필요할 때, 그때 해당 코드를 load&amp;linking</strong>하는 것이다. 과정은 아래와 같다 (a-b-c-d-e)</p>
<ul>
<li>Dynamic linking에서 object code는 <strong>dynamic link library(DLL)</strong>에 저장됨<img src="https://velog.velcdn.com/images/jungizz_/post/229a24bf-137d-4f61-8d57-3df0c9157a08/image.png" alt=""></li>
</ul>
<ol>
<li>user program이 ‘ERRHANDL’ 서브루틴을 사용하고 싶은데, 아직 라이브러리에 있다. 그래서 OS의 Dynamic loader로 점프하여 요청한다.</li>
<li>Dynamic loader는 라이브러리에서 해당 서브루틴을 찾아 메모리에 load 해준다.</li>
<li>Dynamic loader는 load된 서브루틴으로 점프하여 실행한다.</li>
<li>return address에 따라 dynamic loader로 다시 돌아가고 user program으로 다시 돌아간다. (jump back 2번)</li>
</ol>
<ul>
<li>해당 서브루틴을 다시 호출하는 경우에는 load 과정이 필요 없다.</li>
</ul>
<blockquote>
<p>장점: 시간 및 메모리 공간 절약</p>
</blockquote>
<ul>
<li>사용되지 않을 라이브러리를 로드할 필요가 없으므로</li>
<li>여러 프로그램이 하나의 서브루틴/라이브러리의 복사본을 공유할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[시스템프로그래밍] 2. Assembler (3)]]></title>
            <link>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-2.-Assembler-3</link>
            <guid>https://velog.io/@jungizz_/%EC%8B%9C%EC%8A%A4%ED%85%9C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-2.-Assembler-3</guid>
            <pubDate>Fri, 25 Apr 2025 16:21:09 GMT</pubDate>
            <description><![CDATA[<p>지금까지는 machine을 위한 특징을 살펴보았고, 지금부터는 ‘사람’을 위한 Assembler에 대해 알아본다. </p>
<h1 id="🔸machine-independent-assembler-features">🔸Machine-Independent Assembler Features</h1>
<p>Machine architecture와 관련되지 않은 독립적인 몇 가지 특징은 프로그래머의 편의성 및 SW환경과 같은 문제와 밀접하게 관련되어 있다.</p>
<ul>
<li>Literal</li>
<li>Symbol definitions</li>
<li>Expressions</li>
<li>Program Blocks</li>
<li>Control sections<br>
# 🔸Literals</li>
<li>프로그래머가 상수 operand(피연산자)의 값을 instruction의 일부로 작성 → 즉, 명령에서 문자 그대로 명시<ul>
<li>프로그램 내의 다른 곳에서 상수를 정의할 필요가 없어짐 (상수에 대한 레이블 생성X)</li>
</ul>
</li>
<li>prefix ‘<strong>=</strong>’ 뒤에 BYTE 문에서와 동일한 표기법 사용<img src="https://velog.velcdn.com/images/jungizz_/post/7f083c9e-46e5-4323-a4b0-eacc69bf055e/image.png" alt="">    </li>
</ul>
<h2 id="literals-vs-immediate-operand">Literals vs Immediate Operand</h2>
<p>Literals은 immediate와 비슷하게 생겼지만, Literal은 메모리 공간을 사용하고, immediate는 사용하지 않는다.</p>
<ul>
<li><strong>immediate addressing</strong>에서 피연산자 값은 machine instruction의 일부로 조립 → immediate value 자체가 address field에 그대로 들어감<ul>
<li>address filed는 15bit이므로 그 이상의 값을 넣을 수 없음<img src="https://velog.velcdn.com/images/jungizz_/post/efea5210-41cf-46b0-aa48-ddd38ea51504/image.png" alt=""></li>
</ul>
</li>
<li><strong>Literals</strong>은 어셈블러가 다른 메모리 위치에서 지정된 값을 상수로 생성 → 상수의 주소가 address field에 들어감<ul>
<li>literal값을 메모리에서 가져옴</li>
<li>EOF와 관련된 레이블을 만들지 않았지만(공간 할당X), 어셈블러가 Literal인걸 보고 해당 공간을 만들고 참고하여 가져옴<img src="https://velog.velcdn.com/images/jungizz_/post/561bafa1-81f0-42b1-a63a-bc3d803ce274/image.png" alt="">(24bit의 ‘EOF’의 주소를 넣음)<br>

</li>
</ul>
</li>
</ul>
<h2 id="literal-pool">Literal Pool</h2>
<p>그럼 어셈블러는 Literal값의 메모리를 어떻게 잡을까?</p>
<ul>
<li>어셈블러는 프로그램에 사용되는 모든 Literal operand를 하나 이상의 <strong>Literal pool</strong>로 수집 (주소값 할당)<ul>
<li>기본 위치는 프로그램의 끝으로, END문 뒤에서부터 수집<img src="https://velog.velcdn.com/images/jungizz_/post/375aa0d6-3fd0-41f2-b094-73aa9e4bf81d/image.png" alt=""> </li>
</ul>
</li>
<li>만약, Literal pool을 프로그램의 다른 위치에 선언하고 싶다면, 어셈블러 directive인 <strong>LTORG</strong>를 사용<ul>
<li>어셈블러가 LTORG를 만나면, 이전 LTORG(또는 프로그램 시작) 이후 사용된 모든 Literal operand를 포함하는 풀을 생성<img src="https://velog.velcdn.com/images/jungizz_/post/24cc761c-7b57-4c06-a69e-0a6ef5303186/image.png" alt=""></li>
</ul>
</li>
<li>Literal Pool을 나누는 이유는?<ul>
<li>만약 모든 Literal operand가 END이후에 위치하는데 프로그램 초반에 Literal을 사용하면 disp를 12bit로 채우지 못할 수 있음</li>
<li>그래서 Literal 명령어와 변수는 가까이 위치시켜야 함</li>
</ul>
</li>
<li>Literal이 중복되는 경우<ul>
<li>대부분 어셈블러는 중복된 Literal을 인식하고 지정된 데이터 값의 복사본 하나만 저장</li>
<li>동일한 Literal operand ‘=X’05’’가 사용되지만, 이 값을 가지는 데이터 영역은 하나만 생성 → 두 명령어는 Literal pool의 동일한 주소 참조<img src="https://velog.velcdn.com/images/jungizz_/post/e6a6b5c2-5799-4ef4-9387-4e83a25585c8/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h2 id="assembler-handle">Assembler handle</h2>
<p>어셈블러가 literal operand를 어떻게 다룰까?</p>
<ul>
<li>LITAB(Literal Table)라는 자료구조를 사용<ul>
<li>테이블에는 사용된 각 literal에 대한 <strong>이름, 피연산자 값, 길이, 풀에서 할당된 주소</strong>를 포함</li>
</ul>
</li>
</ul>
<p><strong>Pass1</strong>. 각 Literal operand 인식</p>
<ul>
<li>어셈블러는 지정된 literal 이름을 LITAB에 검색하고, 만약 없다면 추가 (아직 주소 할당X)</li>
<li>만약 코드가 LTORG 또는 END이면, 각 literal에 주소를 할당</li>
</ul>
<p><strong>Pass2</strong>. 각 Literal operand를 해당 주소로 변환</p>
<ul>
<li>object code 생성에 사용할 operand 주소는 LITAB를 검색하여 얻음</li>
<li>그리고 literal의 데이터값이 object program에 들어감</li>
</ul>
<br>

<h1 id="🔸symbol-definitions-equ">🔸Symbol Definitions (EQU)</h1>
<ul>
<li><strong>EQU(equate)</strong>: Symbol을 정의하는 어셈블러 directive</li>
<li>#define과 유사<ul>
<li>(symbol) EQU (value) → #define (symbol) (value)<img src="https://velog.velcdn.com/images/jungizz_/post/a49df115-4a77-4d65-aa35-9f891f1515a4/image.png" alt=""></li>
</ul>
<ol>
<li>어셈블러가 EQU문을 만나면 MAXLEN을 SYMTAB에 입력</li>
<li>EQU로 선언한 Symbol을 만나면 SYMTAB에 검색하고, 이 값을 해당 instruction의 operand로 사용</li>
</ol>
</li>
<li>결과로 나오는 object code는 원본과 동일 → 즉, 기호 대신 값을 사용</li>
<li>숫자 값 대신 기호 이름을 설정하여 가독성 향상 (ex-PI)</li>
<li>이해하기 쉬울 뿐 아니라, Symbol의 값을 바꾸는 것이 더욱 간단<br></li>
<li>또 다른 용도로, 레지스터의 mnemonic 이름을 정의<ul>
<li>레지스터가 많은 경우, 레지스터에 대한 mnemonic 이름을 사용하는 것이 도움이 될 수 있음<img src="https://velog.velcdn.com/images/jungizz_/post/fea5213d-0dd9-4196-997b-cbf01aa53136/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h2 id="restrction-in-symbol-definitions">Restrction in Symbol definitions</h2>
<ul>
<li>EQU 오른쪽에 사용되는 모든 숫자 값(변수, 기호)은 프로그램에 미리 정의되어 있어야함<img src="https://velog.velcdn.com/images/jungizz_/post/7042dad5-a76b-4bf9-9639-9f05d7ede480/image.png" alt=""></li>
<li>위에 두 줄은 ALPHA가 미리 정의된 상태로, BETA를 ALPHA의 Symbol definition으로 사용 가능</li>
<li>반면에, 밑에 두 줄은 ALPHA가 정의되지 않았으므로 오류 발생 (모든 기호들은 PASS1에서 정의되야하는데, 어셈블러가 EQU문을 만나고 ALPHA를 BETA로 정의하려하는데 ALPHA가 아직 뭔지 몰라서 정의 불가능)</li>
</ul>
<br>

<h1 id="🔸expression">🔸Expression</h1>
<p>어셈블러에서는 Expression(수식)을 사용해서 주소나 값을 계산할 수 있다. 이는 메모리 주소를 유동적으로 관리하고, 코드의 재사용성을 높이는데 중요한 역할을 한다.<img src="https://velog.velcdn.com/images/jungizz_/post/1277df22-636c-4569-8c4c-0563d10750ca/image.png" alt=""></p>
<ul>
<li>어셈블러는 label, literal 등 단일 피연산자가 허용되는 곳에 Expression을 사용할 수 있음</li>
<li>각 expression은 어셈블러에 의해 평가(계산?)되며 주소나 값을 생성해야 함</li>
<li>위의 예시에서 EQU의 피연산자 위치에 BUFEND-BUFFER라는 수식이 들어감</li>
<li><strong>arithetic expressions</strong>(산술 표현식)<ul>
<li>어셈블러는  +, -, *, /등의 operator(연산자)를 사용하여 만들어진 산술 표현식을 허용</li>
<li>이러한 연산자는 상수, 사용자 정의 기호, <strong>특별한 항</strong>(아래 설명)을 포함할 수 있음</li>
<li>위의 예시에서 BUFEND-BUFFER수식은 ‘-’를 사용하여 만들어진 산술표현식</li>
</ul>
</li>
<li><strong>특별한 항</strong><ul>
<li>예를 들어, 현재의 위치 카운터를 나타내는 값을 ‘<em>’로 표시 (operand에 ‘</em>’가 단독으로 나오면 이 위치의 주소를 의미하는 것)</li>
<li>BUFEND에 현재 LOC값 저장<img src="https://velog.velcdn.com/images/jungizz_/post/0fcf6ccd-e652-4404-b041-2144649dc34b/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h3 id="absoluterelative-expression">Absolute/Relative expression</h3>
<p>Expression은 산출되는 값의 유형에 따라 분류할 수 있다.</p>
<ul>
<li>Absolute expressions: 프로그램 위치와 무관</li>
<li>Relative expressions:프로그램 시작 위치와 연관</li>
</ul>
<h4 id="example-equ에-의해-정의된-symbol은-절대적이거나-상대적일-수-있다">example (EQU에 의해 정의된 symbol은 절대적이거나 상대적일 수 있다)<img src="https://velog.velcdn.com/images/jungizz_/post/ff810a0e-a3f6-4d35-ba2e-58a07a3540f0/image.png" alt=""></h4>
<ul>
<li><strong>16진수 1000</strong>: Loc열의 다른 대부분 항목과 다르게 주소를 나타내는게 아니라 symbol값을 나타냄</li>
<li><strong>MAXLEN</strong>: 시작 주소가 바뀌어도 4096이라는 값을 유지 → <strong>Absolute expression</strong></li>
<li><strong>BUFEND, BUFFER</strong>: 시작 주소에 따라 값이 바뀜 → <strong>Relative expression</strong></li>
<li>근데 S를 프로그램의 시작 주소라고 한다면 (BUFEND+S)-(BUFFER+S)로 표현할 수 있어서 S가 식에서 전부 사라짐 → 시작 주소에 대한 정보 필요 X → <strong>Absolute expression</strong></li>
</ul>
<br>

<h3 id="determine-type-of-expression">Determine type of expression</h3>
<p>위처럼, expression의 유형을 결정하려면 프로그램에 정의된 모든 symbol의 유형을 추적해야한다.</p>
<ul>
<li>이를 위해 SYMTAB에 값 뿐만 아니라 값의 유형(절대/상대)를 나타내는 플래그가 필요 → <strong>type field</strong>에 표시<img src="https://velog.velcdn.com/images/jungizz_/post/41d2cbc6-ae3d-454d-9cb9-327567cfbddd/image.png" alt=""></li>
<li>format4 instruction의 operand는 relative value를 가질 수 있고, 이 값은 loader가 relocation할 수 있도록 수정되어야 함<img src="https://velog.velcdn.com/images/jungizz_/post/28d2a2a6-5210-4ba4-ae84-90e26c277d7a/image.png" alt=""></li>
</ul>
<br>

<h1 id="🔸program-blocks">🔸Program Blocks</h1>
<p>지금까지는 어셈블리되는 프로그램이 하나의 단일 단위로 처리되었다. 서브루틴, 데이터 영역등이 있긴하지만, 어셈블러에 의해 하나의 entity(독립체)로 처리되어 object code의 single block이 생성되었다.
많은 어셈블러는 source code와 object program을 유연하게 처리할 수 있는 기능을 제공한다. </p>
<ul>
<li><strong>Program Blocks</strong>: single object program 단위로 <strong>재배치</strong>된 code segments들<ul>
<li>machine insturctions과 데이터를 소스 코드의 순서와 다르게 메모리에 로드할 수 있음</li>
</ul>
</li>
<li><strong>Control sections</strong>: independent object program 단위로 <strong>번역</strong>되는 segment들 (뒤에 나옴)<ul>
<li>object program의 독립적인 부분들은 identity를 유지하고 lodader가 분리하여 처리<br></li>
</ul>
</li>
<li>어셈블러 directive <strong>USE</strong>는 소스 프로그램의 어떤 부분이 어떤 블록에 속하는지를 나타냄<ul>
<li>USE문이 없다면, 전체 프로그램은 단일 블록으로 간주<h4 id="example">example</h4>
</li>
</ul>
</li>
<li>이 예제에서는 세 가지 프로그램 블록으로 나눔(사용자 맘대로 기준)<ul>
<li>Excutable instructions (unnamed block) → 0</li>
<li>CDATA (길이가 짧은 데이터 영역) → 1</li>
<li>CBLKS (더 큰 메모리 블록으로 구성된 데이터 영역 → 2</li>
</ul>
</li>
<li>각 프로그램 블록은 <strong>각각 relative address 공간</strong>을 가짐<ul>
<li>각 블록마다 starting address가 다름<img src="https://velog.velcdn.com/images/jungizz_/post/2a8a9f44-ddde-424c-903d-ba517a3bf13f/image.png" alt=""></li>
</ul>
</li>
</ul>
<p><strong>Pass1:</strong></p>
<ul>
<li>각 프로그램에 대해 LOCCTR(location counter) 유지<ul>
<li>각 label은 자신이 해당한 블록의 시작 주소를 기준으로 상대 주소가 할당</li>
</ul>
</li>
<li>SYMTBL에는 각 symbol의 블록 번호 저장</li>
<li>Block table에는 각 블록의 시작 주소와 길이 저장<img src="https://velog.velcdn.com/images/jungizz_/post/eaf88bbc-c2ba-4236-bedc-ef793fa22619/image.png" alt=""></li>
</ul>
<p><strong>Pass2:</strong></p>
<ul>
<li>번역 과정에서 어셈블러는 각 symbol의 주소를 object프로그램의 시작 주소를 기준으로 계산<ul>
<li>symbol의 블록 내 상대주소 + 해당 블록의 시작 주소</li>
<li>ex) INPUT의 주소 = 6 + 66 = 6C</li>
</ul>
</li>
</ul>
<br>

<h3 id="object-code">Object code</h3>
<p>위와 같은 과정을 어셈블리 코드의 순서와 다르게 object code는 각 block의 순서로 배열된다. (default→CDATA→CBLKS)</p>
<ul>
<li>블록이 바뀔 때 새로운 text record를 출력</li>
<li>다른 기존 블록으로 돌아가는 경우, 기존 text record에 이어서 하거나 새로 만들어서 하거나 상관 없음<img src="https://velog.velcdn.com/images/jungizz_/post/2fe418d6-6557-4ed1-b507-63d4fcf17524/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/efbbd332-f611-4d4e-b5c3-02850e5e10ce/image.png" alt=""></li>
</ul>
<h4 id="text-record--load">Text record &amp; Load</h4>
<ul>
<li>object program의 text record가 주소 순서와 다르게 생성되더라도 문제X<ul>
<li>loader가 각 record에서 지정된 주소에 따라 올바르게 데이터를 배치하기 때문</li>
</ul>
</li>
<li>로딩이 완료되면, 각 블록에서 생성된 코드는 아래 범위의 상대 주소를 차지<ul>
<li>default: 0000~0065</li>
<li>CDATA: 0066~0070</li>
<li>CBLKS: 0071~1070<img src="https://velog.velcdn.com/images/jungizz_/post/e98c5302-5a77-47a2-9272-0841ee726586/image.png" alt="">즉, 어셈블리 코드에서 흩어져있던 각 블록들을 메모리 상에서 블록별로 모을 수 있음</li>
</ul>
</li>
</ul>
<br>

<h3 id="advantages">Advantages</h3>
<p>Program blocks은 모순되는 목표를 이룰 수 있다.</p>
<ol>
<li>특정 순서에 따라 프로그램을 블록으로 구분<ul>
<li>위 예시에서는 가장 큰 버퍼 영역이 object program의 가장 끝에 위치 → 프로그램 사이에 위치했다면 해당 영역 앞과 뒤에 위치한 구문들이 멀어짐 → 결국 extended format, base-relative를 더욱 빈번하게 사용</li>
<li>Literal Pool을 배치하기 쉬움 (literal을 CDATA 블럭에 넣기만 하면 됨)</li>
</ul>
</li>
<li>데이터 영역을 퍼트릴 수 있음<ul>
<li>가독성을 높이기 위해 각 블록에 사용되는 변수들을 근처에 배치 (데이터 영역이 참조되는 구문에 가까울수록 가독성 증가)</li>
</ul>
</li>
</ol>
<p>즉, 모순되는 목표란 “<strong>소스코드에서는 데이터 영역을 흩어지게 하여 가독성을 높이고, <del>Object code에서는</del> 메모리 상에서는 데이터 영역을 한 곳에 모아 프로그램의 성능을 높이기</strong>” 이다.
<br></p>
<h1 id="🔸control-section">🔸Control Section</h1>
<p>Control section(CS)은 어셈블리 이후 프로그램의 일부로, 각 CS는 다른 섹션과 독립적으로 로드되거나 재배치될 수 있다.</p>
<ul>
<li>프로그램의 서브루틴이나 하위 부문에 서로 다른 CS 사용</li>
<li>프로그래머는 각 CS를 개별적으로 assemble, load, manipulate할 수 있음</li>
<li>이를 통해 프로그램의 유연성 증가<img src="https://velog.velcdn.com/images/jungizz_/post/d5f1828c-4a95-48b1-9793-7bf08244f3bc/image.png" alt=""></li>
</ul>
<p>약간… 라이브러리 코드 링크하는 것 처럼… 헤더파일 include하는 것 처럼..
<br></p>
<h2 id="🔹program-linking">🔹Program Linking</h2>
<p>CS가 프로그램의 논리적으로 관련된 부분을 형성할 때, 그것들을 linking할 수 있어야한다.  즉, 한 CS의 instruction은 다른 섹션에 있는 insturction 및 데이터를 참조할 수 있어야한다. → <strong>external reference(외부 참조)</strong></p>
<ul>
<li>Loader가 external reference에 대한 정보를 생성하여 어셈블러에게 다른 CS가 어디에 있는지 알려주고 link를 할 수 있도록 함<br>
### External reference
어셈블러 directives에 의해 외부 참조가 처리된다. </li>
<li><strong>CSECT</strong>: 새로운 CS의 시작을 알림<ul>
<li>어셈블러는 program block과 마찬가지로 각 CS마다 독립적인 LOC을 설정 → <strong>CSECT</strong>하면 starting address 0000</li>
</ul>
</li>
<li><strong>EXTDEF</strong>: 현재 CS에 정의된 symbol, 다른 섹션에서 사용하기 위해</li>
<li><strong>EXTREF</strong>: 다른 곳에서 정의된 symbol을 사용하기 위해</li>
</ul>
<p>다른 섹션의 symbol을 사용하려면, 해당 symbol이 있는 섹션에서 EXTDEF를 사용해 외부에서 사용할 수 있게끔 알려야하고, 해당 symbol이 필요한 섹션에서는 EXTREF로 내가 다른 섹션에 있는 symbol을 여기서 사용하겠다라고 알려야한다.</p>
<h4 id="example-1">example</h4>
<ul>
<li><strong>CSECT</strong>를 이용하여 프로그램을 COPY, RDREC, WRREC 3개의 CS로 나눔</li>
<li>COPY SC에서 <strong>EXTDEF</strong>로 BUFFER, BUFEND, LENGTH를 다른 섹션에서 참조할 수 있게하고, <strong>EXTREF</strong>를 이용해 외부의 RDREC와 WRREC를 참조 가능</li>
<li>RDREC와 WRREC에서는 <strong>EXTREF</strong>를 이용해 COPY 섹션의 BUFFER BUFEND, LENGTH등을 참조<img src="https://velog.velcdn.com/images/jungizz_/post/564966af-1bdf-411a-93ae-aef6f27b206b/image.png" alt=""><br>
### Object code with External references
어셈블러는 EXTREF로 사용하는 symbol의 위치를 알 수 없어서 object code에 주소를 넣을 수 없다. 그래서 0주소를 넣고 loader에게 보내고, 이후 loader가 적절한 주소를 넣는다.![](https://velog.velcdn.com/images/jungizz_/post/924466d2-1743-4680-8a11-659c68cbcf36/image.png)이때, **format4 instruction**을 사용하여 실제 주소를 넣을 공간을 제공해야한다. 메모리 어디에 로드될지 모르기 때문에 메모리 전체를 표현할 수 있어야한다.
<br>
### New Record types
어셈블러는 loader가 필요한 위치에 적절한 값을 넣을 수 있도록 필요한 정보를 object program에 포함해야한다. 그래서 새로운 record가 필요하다.</li>
<li><strong>Define record</strong>: EXTDEF에 의해 CS에 정의된 symbol용으로, 해당 CS에 있는 symbol의 <strong>relative address</strong>를 나타냄<ul>
<li>Col. 1: D</li>
<li>Col. 2-7: 해당 CS에 정의된 symbol의 이름</li>
<li>Col. 8-13: 해당 CS에서의 상대 주소(16진수)</li>
<li>Col. 14-73: 다른 symbol에 대해 Col. 2-13의 정보 반복</li>
</ul>
</li>
<li><strong>Refer record</strong>: EXTREF에 의해 CS에서 사용되는 외부 symbol용으로, 주소 정보가 없음<ul>
<li>Col. 1: R</li>
<li>Col. 2-7: 해당 CS에서 언급한 symbol의 이름</li>
<li>Col. 8-73: 다른 external reference symbol 이름</li>
</ul>
</li>
<li><strong>Modification record</strong> (이전 Relocation때 나왔던 record로 Col.10-16이 추가)<ul>
<li>Col. 1: M</li>
<li>Col. 2-7: 수정할 address field의 시작 주소(16진수)</li>
<li>Col. 8-9: 수정할 address field의 길이, half-bytes(16진수)</li>
<li>Col. 10: 수정 플래그(+ or -)</li>
<li>Col. 11-16: 표시된 필드에 값을 추가하거나 뺀 external reference symbol</li>
</ul>
</li>
</ul>
<p>→ 이렇게 하면 프로그램이 여러개의 섹션으로 나눠져도 loader가 필요한 external section의 정보가 메모리 어디에 위치하는지 알 수 있게 됨</p>
<h4 id="example-2">example</h4>
<ul>
<li>3개의 CS로 나눠져서 object code가 3개의 프로그램으로 분리된 것처럼 보임</li>
<li>COPY섹션의 define record에는 BUFFER, BUFFEND, LENGTH와 각각의 상대주소가 존재하고, refer record에는 해당 섹션에서 참조할 외부 symbol의 이름이 존재<ul>
<li>loader는 modification record를 참고해서 00000으로 바꿔놨던 address field값들을 변경<img src="https://velog.velcdn.com/images/jungizz_/post/1c17fe56-1928-4809-a222-8e1a97cd7b66/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/a0252e58-7a53-49b2-978b-779d8c52cf37/image.png" alt=""><img src="https://velog.velcdn.com/images/jungizz_/post/9461c0ac-9b43-4504-be13-f61e0970e4a3/image.png" alt=""></li>
</ul>
</li>
</ul>
<br>

<h3 id="loader">Loader</h3>
<p>그래서~~ 로더가 모든 external symbol에 대해 해야할 일은</p>
<ul>
<li>정의된 레코드에서 상대주소 찾기</li>
<li>symbol이 정의된 CS의 시작 주소 추가하기</li>
<li>field 수정하기<br>
# 🔸1-pass Assembler
지금까지는 2-pass 어셈블러에 대해 다뤘다. 하지만 이것이 어셈블러를 설계하는 유일한 방법은 아니고, 1-pass나 multi-pass로도 설계할 수 있다. 
그 중 1-pass 어셈블러는 소스코드를 한 번만 스캔하기 때문에 오버헤드를 줄여주지만, 스캔 도중 생성된 object code를 patch(수정)해야하는 경우가 있다. 

</li>
</ul>
<h3 id="forward-reference">Forward reference</h3>
<ul>
<li>1-pass를 위해서는 <strong>Forward reference(전방 참조)</strong>에 대한 문제를 극복 필요<ul>
<li>이를 위해 모든 데이터 항목 정의는 참조 명령보다 먼저 배치</li>
<li>하지만 모두 이렇게 해결할 수 없음</li>
</ul>
</li>
<li>아래 예시의 빨간 점선처럼, label에 대한 전방 참조는 피할 수 없음<img src="https://velog.velcdn.com/images/jungizz_/post/59326969-e72d-4ea6-975f-ab4afffd1af9/image.png" alt=""></li>
</ul>
<p>그렇다면…… 1-pass 어셈블러는 이걸 어떻게 해결했을까?!!?!</p>
<p>1-pass 어셈블러에는 두 가지 타입이 존재한다. 각각 위 문제를 어떻게 해결하는지 살펴본다.</p>
<h2 id="type-1-load-and-go">Type 1: “Load-and-Go”</h2>
<ul>
<li>실행 중에 메모리에 object code를 직접 생성 (HDD, SSD와 같은 2차 저장소에 작성하지 않고)<ul>
<li>메모리의 속도가 2차 저장소의 속도보다 훨씬 빠르기 때문에 어셈블리 과정의 효율성을 높임</li>
<li>object program이 작성되지 않고, Loader가 필요 없음</li>
</ul>
</li>
<li>전방참조를 해결하기 위해<ul>
<li>정의되지 않은 symbol과 이 symbol을 참조하는 field 주소(<strong><em>이 symbol을 부른 instruction의 주소</em></strong>)와 함께 SYMTAB에 저장</li>
<li>나중에 symbol이 정의되면 SYMTAB에서 참조했던 주소를 찾아 올바른 주소로 field를 수정<ul>
<li>정의 전에 여러번 참조된다면, linked list를 통해 참조 주소를 이어서 연결</li>
</ul>
</li>
<li>즉, 한 번의 pass에서 SYMTAB를 만들어감</li>
</ul>
</li>
</ul>
<h4 id="메모리에-적힌-object-code와-symbol-table">메모리에 적힌 Object code와 Symbol table</h4>
<ul>
<li>Line 40까지 스캔한 상황<ul>
<li>RDREC, WRREC, ENDFIL이 정의되기 전에 참조했기 때문에 object code의 주소값 부분이 비어있음</li>
<li>symbol table에서는 해당 symbol을 참조하는 위치를 <strong>linked list</strong>형식으로 연결<img src="https://velog.velcdn.com/images/jungizz_/post/7509b9f1-36b3-4755-b53c-a231bbc4c259/image.png" alt=""></li>
</ul>
</li>
<li>Line 160까지 스캔한 상황<ul>
<li>ENDFIL과 RDREC가 선언되었고, linked list에 적혀있던 참조 위치로 가서 object code의 주소값 부분에 symbol의 주소를 넣어줌</li>
<li>아직까지 선언되지 않았는데 또 참조된 경우, linked list에 이어서 해당 위치를 연결<img src="https://velog.velcdn.com/images/jungizz_/post/3bc555c7-8c27-45ee-a18d-202f9dd4c84d/image.png" alt=""><h2 id="type-2">Type 2</h2>
</li>
</ul>
</li>
<li>메모리가 아닌 디스크에 object program을 작성<ul>
<li>나중에 실행 가능하도록 일반적인 object program 생성</li>
<li>Loader 필요</li>
</ul>
</li>
<li>전방 참조를 해결하기 위해<ul>
<li>우선 type1처럼 linked list에 참조 위치 기록</li>
<li>symbol이 정의된다면 새로운 text record를 생성 (정확한 피연산자 주소와 함께)</li>
<li>이후 로드될 때, loader에 의해 올바른 주소가 들어감</li>
</ul>
</li>
</ul>
<h4 id="디스크에-적힌-object-program">디스크에 적힌 Object program</h4>
<ul>
<li>두번째 text record은 line 10-40에서 생성된 object code<ul>
<li>전방참조로 주소를 모르는 경우는 0000으로 채워져 있음</li>
</ul>
</li>
<li>세번째 text record는 line 45에서 ENDFIL symbol의 정의가 발견돼서 어셈블러가 새로 만든 text record<ul>
<li><code>T.00201C.02.2024</code>→ine30의 JEQ의 피연산자 address field의 주소 201C(위치)에 / ENDFIL의 주소 2024(값)를 로드하도록 지정<img src="https://velog.velcdn.com/images/jungizz_/post/f69fe3fa-e8a6-4b7b-a96f-5b47efacd4df/image.png" alt=""></li>
</ul>
</li>
<li>symbol정의로 만들어진 새로운 text record는 어떻게 구분할까?<ul>
<li>symbol정의로 만들어진 text record의 주소는 이미 위에 있는 text record에 있는 주소이다. 그래서 알 수 있는 듯?</li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>