프로세스 제어블록(Process Control Block)의 약어로 프로세스를 관리하는데 사용하는 OS의 자료구조이다.

운영체제는 프로세스를 PCB 단위로 관리하며 프로세스 스케줄링을 위한 정보를 PCB 를 통해 관리한다.


프로세스가 생성될 때마다 고유의 PCB 가 생성되며, 프로세스가 완료되면 PCB 는 제거된다.

프로세스 간 Switching 이 발생할 때, 운영체제는 PCB 를 이용해서 상태를 전이시킨다. (State Transition)


프로세스는 CPU가 처리하던 작업 내용들을 PCB에 저장하고, 

다음에 다시 CPU 를 점유하여 작업할 때 PCB로부터 해당 정보들을 CPU 에 넘겨와서 하던 작업을 진행한다.


PCB는 다음과 같은 데이터 구성을 갖고 있다.


- Process Identification Data

- Process State Data

- Process Control Data


PCB 는 다음과 같은 정보들을 저장하고 있다.


(1) Process ID : 프로세스를 구분하는 ID


(2) Process State : 각 State 들의 상태를 저장한다.


(3) Program Counter : 다음 Instruction 의 주소를 저장하는 카운터. CPU는 이 값을 통해 Process 의 Instruction 을 수행한다.


(4) Register : Accumulator, CPU Register, General Register 등을 포함한다.


(5) CPU Scheduling Information : 우선 순위, 최종 실행시간, CPU 점유시간 등이 포함된다.


(6) Memory Information : 해당 프로세스 주소공간(lower bound ~ upper bound) 정보를 저장.


(7) Process Information(페이지 테이블, 스케줄링 큐 포인터, 소유자, 부모 등)


(8) Device I/O Status(프로세스에 할당된 입출력 장치 목록, 열린 팔린 목록 등)


(9) Pointer : 부모/자식 프로세스에 대한 포인터, 자원에 대한 포인터 등


(10) Open File List : 프로세스를 위해 열려있는 파일의 리스트






 가상메모리(Virtual Memory)는 컴퓨터가 사용하는 메모리 관리 테크닉이다.

실제메모리를 추상화하여 가상 메모리에 올림으로써 프로세스들이 연속된 물리적 메모리 공간처럼 여기게 만들 수 있을 뿐만 아니라 실제 메모리보다 많은 크기의 메모리도 운용이 가능하다.


가상메모리 기술이 동작하기 위해서는 2가지 이슈가 있다.

하나는 가상 메모리를 물리 메모리에 매핑시키는 Address Translation이고, 다른 하나는 이 가상 공간에 대한 관리 이슈이다.


이러한 Virtual Memory 와 Physical Memory 의 매핑을 위한 기술로 Paging 기법이 사용된다.


페이징 기법은 컴퓨터가 메인 메모리에서 사용하기 위해 데이터를 저장하고 검색하는 메모리 관리 기법이다.

페이징 기법을 통해 컴퓨터의 물리적 메모리는 연속적으로 할당되어 존재할 필요가 없으며, 반대로 연속적으로 존재하지 않는 물리적 메모리라도 페이징 기법을 통해 연속적으로 존재하는 것처럼 이용될 수 있다.


페이징 방식에서는 가상 메모리 상의 주소 공간을 일정한 크기로 분할한다. 

주소공간은 페이지 단위로 나뉘어져있으며 실제 기억공간은 페이지 크기와 같은 프레임으로 나누어 사용한다.


 - Frame : 물리 메모리를 일정된 한 크기로 나눈 블록

 - Page : 가상 메모리를 일정된 한 크기로 나눈 블록


이 때 Frame 과 Page 의 크기는 동일하게 관리된다.

페이지의 크기는 크기는 시스템에 따라 다르며 크기가 작으면 메모리를 단편화하기 쉬워지지만 페이지의 갯수가 많아지기 때문에 페이지 단위의 입출력이 자주 발생하게 된다.


페이지가 하나의 프레임을 할당받으면, 물리 메모리에 위치하게 된다.

프레임을 할당받지 못한 페이지들은 외부저장장치에 저장되며, 마찬가지로 프레임과 같은 크기 단위로 관리된다.


페이징 기법에서 페이지는 페이지테이블(Page Table) 이라는 자료구조 형태로 관리된다.

Page Table 은 프로세스의 페이지 정보를 저장하고 있으며, 하나의 프로세스는 하나의 페이지 테이블을 가진다.

Page table 은 Index 를 키로 해당 페이지에 할당된 메모리(Frame)의 시작 주소를 Value 로 저장하고 있다.


페이지 테이블 엔트리(Page Table Entry)는 페이지 테이블의 레코드를 말한다.

PTE 의 각 필드에는 다음 내용들이 기록된다.


 - 페이지 기본 주소(Page base address)


 - 플래그 비트(Flag bit)

  - Accessed bit : 페이지에 대한 접근이 있었는지를 나타낸다.

  - Dirty bit : 페이지의 내용에 변경이 있었는지를 나타낸다.

  - Present bit : 현재 페이지에 할당된 프레임이 있는지를 나타낸다.

  - Read/Write bit : 읽기/쓰기에 대한 권한을 표시한다.


페이지의 크기는 하드웨어에 의해 정의되며 대게 2의 제곱으로 증가한다.



페이징 기법에서 동적 주소 변환 과정은 다음과 같다.


(1) 수행 중인 프로세스가 가상주소를 참조한다.


(2) 페이징 기법을 통해 페이지 p가 페이지 프레임 f에 있음을 알아낸다.


(3) 실주소 r = f(페이지 프레임) + d(오프셋) 를 구해낸다.


위와 같은 방식으로 페이지 테이블은 Virtual Memory 에서 Physical Memory 를 참조가능하도록 지원한다.



페이지 테이블은 사용을 위해서 메모리에 존재해야 하지만, 그렇게 되면 메모리 접근에 있어서 중복 호출이 일어나므로(페이지 테이블에 한번, 페이지테이블을 통한 실제 메모리에 한번), 대게 MMU 의 지원을 받아 매핑시키게 된다. 



(참고 : https://www.geeksforgeeks.org/operating-system-paging/

https://gabrieletolomei.wordpress.com/miscellanea/operating-systems/virtual-memory-paging-and-swapping/)


I/O Scheduling 은 운영체제가 여러 프로세스로부터 I/O 요청을 받았을 때 이에 대한 우선순위를 정해서 실행시켜주는 것을 말한다.

 

일반적으로 OS 내에 있는 스케줄러는 throughput 과 latency 를 고려하여 설계되며 하드디스크 검색으로 낭비되는 시간을 최소화하고, 우선적으로 필요한 프로세스들에게 I/O 요청을 할당하는 것이 주 목표이다.

 

I/O Scheduler 의 종류로는 다음과 같은 것들이 있다.

 

- Noop Scheduler : 커널이 스케줄링을 위해 별다른 작업을 하지 않는다. SSD 또는 RAID 디스크를 사용하는 경우 스케줄러의 동작이 오히려 디스크의 성능에 부하를 줄 수 있다는 점에 착안하였다.

 

- CFQ(Completely Fair Queueing) : 기본 스케줄러로, 각 프로세스마다 Round Robin 알고리즘에 의해 스케줄링이 수행된다. 각 작업을 마친 후 일정 time slice 만큼 기다린 후 다른 프로세스로 제어를 넘긴다. Synchronous 작업이 Asynchronous 작업보다 우선순위가 높다.

 

- Deadline : I/O 대기 시간의 한계를 정해놓고 데드라인이 가까운 작업부터 제어를 할당한다. 지연에 최적화되어있으며 I/O 가 몇몇 프로세스에 집중되어 있을 때 유용하기 때문에 Database 시스템에 주로 사용된다.

 

- Anticipatory : 연속된 read 요청의 경우 미리 처리량을 예측해서 요청을 스케줄링하는 방식으로 응답시간이 중요한 경우에 유용하다. 

 



Linux 계열의 운영체제는 EXT 라는 파일시스템 구조를 사용한다.


* EXT(Extended file system) : 리눅스 용으로 개발된 파일시스템으로 Minix 파일시스템의 한계를 극복하고자 만들어졌다.


* EXT2 

 : Linux 의 초창기 파일시스템 표준이다. TIMESTAMP 의 제약이 있으며 장애 발생시 파일 시스템 복구를 위한 fsck 도구를 갖고 있다.


- EXT2 inode : inode 는 파일 시스템의 가장 기본이 되는 단위이다. 

각각을 구분할 수 있는 고유번호를 가지고 파일의 데이터가 어느 블록의 어느 위치에 저장되어 있는지, 파일에 대한 접근권한, 파일의 최종 수정시간, 파일의 종류 등의 정보를 inode 테이블에 저장한다.

저장되는 정보는 모드, 소유자 정보, 크기, Timestamp, 데이터블록이다. 


- EXT2 super block : 슈퍼블록은 해당 파일 시스템의 기본적인 크기나 형태에 대한 정보를 저장한다. 슈퍼블록에 저장되는 항목은 다음과 같다.


 (1) Magic number : 마운트하는 소프트웨어에게 EXT2 파일 시스템의 슈퍼블록임을 확인하게하는 값이다.


 (2) Revision level : major level과 minor level 로 구성되어 있으며, mount 시스템이 file system 에서 지원되는지에 대한 버전 정보 확인을 위해 사용된다. 

 또한 이 정보를 이용해 호환성을 판단할 수 있다.


 (3) Mount count : 마운트 횟수와 최대마운트 횟수를 저장한다. 마운트가 실행될 때마다 카운트가 증가하며 이 값을 이용해 시스템 전체를 검사할 필요가 있는지 확인한다.


 (4) Block group number : 블록 그룹번호는 슈퍼 블록 복제본을 갖고 있는 블록 그룹의 번호를 나타낸다. 


 (5) Block size : 블록 크기는 File System 의 블록크기를 바이트 단위로 표시한다.


 (6) Blocks per group : 그룹에 속한 블록 수를 저장한다.


 (7) Free block : 파일 시스템 내부적으로 존재하는 프리블록 수를 나타낸다.


 (8) Free inode : 파일 시스템 내부적으로 존재하는 free inode 수를 나타낸다.


 (9) First inode : 리눅스 시스템의 첫번째 inode는 "/" 디렉토리 엔트리를 나타낸다.


- EXT2 group descriptor : 블록그룹에서 블록의 할당 상태를 나타내주는 비트맵이다. 블록을 할당하거나 해제할 경우 참조되는 정보로, block bitmap, inode bitmap, inode table 을 구성요소로 갖는다.

inode 비트맵은 블록 그룹에서 inode 할당상태를 나타내주는 비트맵으로 그 수는 블록 비트맵과 같은 block 의 수와 동일하다.


- EXT2 directory : EXT2 파일 시스템에서 디렉토리는 파일에 대한 접근 경로를 생성하고 저장한다.

directory 는 entry 의 list로 나타내어 지며, 엔트리 리스트에는 inode, 이름길이, 이름 정보가 저장된다.



* EXT3

 : 저널링을 지원하는 호환성 높은 파일시스템 구조이다. 온라인 상태에서 파일시스템을 확장 가능하며, directory 에 대한 tree 기반 indexing 을 제공한다.

쓰기 실행중 fsck 가 불가능한 단점이 있다.


 - 저널링(Journaling) 기술

기존의 EXT2는 시스템에 문제가 생기는 경우 파일시스템이 손상될 수 있는 문제점을 갖고 있었고, 이를 해결하기 위해 fsck(File System Check) 라는 도구를 사용하였다.

하지만 복구에는 시간이 오래걸리고, 복구하는 동안 시스템의 사용으 불가능한 단점이 있었다. 

EXT3는 이 단점을 해결하기 위한 저널링(Journaling) 기능을 사용하며, 이를 통해 파일 시스템의 무결성과 복구 기능을 갖추게 되었다.

저널링 기술은 파일 수정 이전에 로그에 내용을 저장하고, 로그를 바탕으로 수정 내용을 처리하는 Replay 과정을 거치며, 이런 파일 시스템의 특성을

Logging File System 이라고 한다.


 - 저널링(Journaling) 파일 시스템의 구조

저널링을 수행하기 위한 로그영역과 일반 파일 시스템 영역으로 나뉜다. Log는 Rolling File 형태의 구조로 관리되며, 이는 전용 스레드가 처리한다. 

로그에 대한 트랜잭션이 별도로 관리되며, 트랜잭션 내의 레코드 I/O 를 바탕으로 파일시스템이 관리된다.



* EXT4

 : Linux 파일시스템 표준으로, Timestamp 가 나노초단위로 개선되어 있다. 

 ext2, ext3 를 ext4 로 마운팅 가능하며 호환성이 뛰어나다. 다음과 같은 특징들을 지닌다.


 (1) 대형 파일 시스템 : 최대 1 Exabyte 까지의 볼륨을 지원한다.


 (2) Extent : 전통적인 Block mapping 을 대체하기 위한 extent 방식을 사용한다. inode 에 최대 4개의 extent를 할당해서 물리적으로 인접한 블록들을 묶어서 관리한다.


 (3) Allocate on flush : 데이터가 디스크에 실제로 쓰여지기까지 블록 할당을 지연시키는 기술이다. 하나의 파일에 대해 블록의 분산을 막을 수 있고 디스크 이동을 최소화시킬 수 있다. 다만 장애 시 충돌 가능성이 존재한다.


 (4) 디렉터리 제한 변경 : 하위 Directory 에 대한 제한이 32000개에서 64000개까지 늘어났다. 




참조 : http://egloos.zum.com/nix102guri/v/392605




단일 프로세스 & 쓰레드를 갖는 프로그램을 개발할 경우에는 신경쓸 일이 거의 없지만 여러 작업루틴이 동시에 수행되며, 공유 리소스에 접근하게 되는 동시성 프로그래밍을 할 경우에는 반드시 신경써주어야 할 부분이 바로


Mutual Exclusion (상호배제) 문제이다. 이는 공유 불가능한 자원을 동시에 사용하게 될 경우 발생할 수 있는 충돌을 방지하기 위해서 Critical Section 을 만들고, 해당 영역에서 데이터를 사용하게끔 하는 방법을 사용한다.


(1) 세마포어(Semaphore)


이렇게 공유자원에 대한 동시성 문제가 발생하였을 때, 즉 여러 개의 프로세스 또는 쓰레드가 동시접근하는 문제를 방지하기 위해 고안된 것이 바로 세마포어이다. 


세마포어란 리소스의 상태를 나타내는 카운터를 지정하여 다중 프로세스에서 행동을 조정 및 동기화 시킬 수 있는 기술이다. 

여러 프로세스가 접근 시 여러 개의 Lock 을 할당하여 동시에 허용 가능한 Counter 의 제한을 둔다. 카운터가 1개로 0 / 1의 값을 가질 때 Binary Semaphore 라하고 이는 Mutex와 동작이 같다.


(2) 뮤텍스(Mutex)


뮤텍스는 Lock 을 가지고 있을 때에만 공유자원에 접근이 가능하게끔 하는 로직이다. 

즉, 세마포어가 여러 개의 락을 두어 제한된 리소스 접근을 허용하는데 반해 뮤텍스는 오로지 한 개의 쓰레드/프로세스만 할당한다.



* 세마포어(Semaphore) 와 뮤텍스(Mutex) 의 차이점


- 세마포어는 주로 시스템적 범위에 적용이 되며 뮤텍스는 프로세스 내에서 적용이 된다. 

(물론 세마포어와 뮤텍스는 매커니즘의 개념이기 때문에 국한된다고 할 수는 없다.) 주로 뮤텍스는 프로세스 내 쓰레드간 자원 접근에 대하여 적용이 되며 Lock 한 쓰레드가 Unlock 도 해주어야 한다. 

반면 세마포어는 Lock 을 건 소유주가 아니더라도 Unlock 이 가능하다.


- 세마포어는 동기화 대상이 하나 이상일 때, 뮤텍스는 하나일 때 사용된다.


동기화 처리 로직은 Lock 을 수반하며 이 Lock 이 여러 개의 작업 큐 내에서 걸릴 때 DeadLock 이 생길 우려가 있다.


데드락은 임계 영역 내의 전역 리소스에 대해 복수 개의 Lock에 의해 처리가 지연되는 현상이다. 

대표적으로 Critical Section 에 접근하여 공유 자원을 처리하는 여러개의 로직이 서로에 대한 의존도(Dependency)를 가질 때 발생한다.


다음과 같은 상황을 가정해보자.




위의 그림에서 프로세스1 은 Resource 1을 선점하고 있으며, Resource 2에 대한 작업처리를 요구한다.

Resource 2에 대한 처리를 완료하면 Resource 1을 사용할 수 있도록 Critical Section 바깥으로 반환할 것이다.

반대로 프로세스2 는 Resouce 2를 선점하고 있으며 Resource 1에 대한 작업 처리를 마친 후 Resource 2를 반환할 것이다.


위의 상황에서 두 프로세스는 서로 선점하는 Resource가 다르므로 동시에 Critical Section 진입이 가능하지만, 서로의 자원을 요구하는 탓에 탈출은 불가능하다. 이 상황을 Deadlock이라고 한다.


Deadlock 이 발생하면 자원의 누수 및 동작의 교착상태가 계속되기 때문에 어플리케이션 또는 시스템에 치명적이며 따라서 문제 해결을 위해 다음과 같이 관리 한다.


- 교착 상태의 예방

(1) Mutual Exclusion 조건 제거

(2) 사용할 때에만 해당 자원을 점유하고 사용하지 않을 때에는 해당 자원을 다른 프로세스가 사용할 수 있도록 양도

(3) 선점 가능한 프로토콜 제작

(4) 자원 접근에 대한 순차적 처리 


- 교착 상태의 회피

 : 자원 요청에 대해 Circular Wait를 방지하기 위한 할당 상태를 검사한다.


- 교착 상태의 무시

 : 확률이 낮은 경우 별도의 처리를 하지 않는다.




 Linux 서버 관리를 하다가 자주 마주치는 것 중 하나가 서버의 포트 및 방화벽 문제라고 할 수 있는데 삽질하면서 알아낸 바를 정리해두었다.


CentOS, Fedora 등의 리눅스에서 포트 방화벽을 확인 하는 방법

- iptables –-list

- iptables -L(리스팅) -v(자세히)


Ubuntu 에서 포트 방화벽을 확인하는 방법

- sudo ufw status


Ubuntu 에서 포트 방화벽을 활성화 / 비활성화 하는 방법

- sudo ufw enable

- sudo ufw disable


CentOS, Fedora 등의 리눅스에서 포트를 추가하는 방법 (WhiteList)

- iptables -I INPUT 1 -p tcp –-dport 80 -j ACCEPT (80 번 Input 포트를 1번으로 추가)

- iptables -I OUTPUT 2 -p tcp –-dport 8080 -j ACCEPT (8080번 Output 포트를 2번으로 추가)


Ubuntu 에서 포트를 추가하는 방법 (WhiteList)

- sudo ufw allow <port>/<optional: protocol> (sudo ufw allow 443/tcp)


CentOS, Fedora 등의 리눅스에서 포트 WhiteList를 제거하는 방법

- iptables -D INPUT 2 (INPUT 2번째 규칙 제거)


Ubuntu 에서 포트 WhiteList를 제거하는 방법

- sudo ufw deny <port>/<optional: protocol>


Iptables 를 이용한 포트포워딩

- iptables -t nat -A PREROUTING -p tcp -m tcp –dport 80 -j REDIRECT –to-ports 8080

- iptables -t filter -A FORWARD -p tcp -m tcp –dport 80 -j ACCEPT


정확히는 삽질한 부분은 클라우드 환경에서 인스턴스의 외부 Security Group 보안설정을 고려하지 않은 채, 인스턴스의 방화벽을 직접 건드린 부분이었으나,

외부 방화벽 제어 로직 역시 다르지 않으므로 기본적인 내용은 익혀두자.




정리하기에 앞서 알아두어야할 점은 Synchronous, Asynchronous 와 Blocking, Non-Blocking 은 서로 비교 가능한 개념들이 아니다. 많이들 기술 먼저 습득하다보니(특히 NodeJS), 개념을 먼저 습득하기보다는, 특징을 먼저 배우는 터라 개념이 혼용되어서 쓰이는 경우가 많을 뿐이다.


동기(Sync) 와 비동기(Async)


<Synchronous I/O Model>


Sync와 Async 를 구분하는 기준은 작업 순서이다.

동기식 모델은 모든 작업들이 일련의 순서를 따르며 그 순서에 맞게 동작한다. 즉, A,B,C 순서대로 작업이 시작되었다면 A,B,C 순서로 작업이 끝나야 한다. 설령 여러 작업이 동시에 처리되고 있다고 해도, 작업이 처리되는 모델의 순서가 보장된다면 이는 동기식 처리 모델이라고 할 수 있다. 

많은 자료들이 동기식 처리 모델을 설명할 때, 작업이 실행되는 동안 다음 처리를 기다리는 것이(Wait) Sync 모델이라고 하지만, 이는 잘 알려진 오해이다. 이 Wait Process 때문에 Blocking 과 개념의 혼동이 생기는 경우가 흔하다. 동기 처리 모델에서 알아두어야할 점은 작업의 순서가 보장된다는 점 뿐이다.

동기식 처리 모델은 우리가 만드는 대부분의 프로세스의 로직이며, 특히 Pipeline 을 준수하는 Working Process 에서 매우 훌륭한 모델이다.


<Asynchronous I/O Model>


반면 비동기식 모델은 작업의 순서가 보장되지 않는다. 말그대로 비동기(Asynchronous) 처리 모델로, A,B,C 순서로 작업이 시작되어도 A,B,C 순서로 작업이 끝난다고 보장할 수 없다.

비동기식 처리 모델이 이득을 보는 경우는 각 작업이 분리될 수 있으며, Latency 가 큰 경우이다. 예를들어 각 클라이언트 또는 작업 별로 Latency 가 발생하는 네트워크 처리나 File I/O 등이 훌륭한 적용 예시이다.



블로킹(Blocking) 과 넌블로킹(Non-Blocking)


Blocking 과 Non-Blocking 을 구분하는 기준은 통지 이다.

Blocking 이란 말그대로 작업의 멈춤, 대기(Wait) 을 말한다. 즉, 작업을 시작하고 작업이 끝날때까지 대기하다가 즉석에서 완료 통지를 받는다.

이 때 작업이 멈추는 동안 다른작업이 끼어들수 있는지 없는지는 다른 얘기이다. 이부분이 중요하면서도 헷갈린데, 많은 Blocking 방식의 사례에서 다른 작업의 Interrupt 를 방지하기 때문에, Blocking 은 곧 "순차처리" 로 생각하는 오류가 생긴다. (이렇게 생각해버리면 Blocking 과 Synchronous 모델은 같은 개념이 된다.)

단순히 생각해서 Blocking 은 그저 작업을 수행하는 데 있어서 대기 시간을 갖는다는 의미일 뿐이다.


Non Blocking 이란 작업의 완료를 나중에 통지받는 개념이다. 작업의 시작 이후 완료시까지 대기하지 않고, 완료시킨다. 

즉, 내부 동작에 무관하게 작업에 대한 완료를 처리받는 걸 말한다.

(작업의 예약 부분이 오히려 비동기 설명과 오해를 불러오는 듯 하여 수정했습니다.)

효과적인 작업 상태의 처리를 위해 Non-blocking 에서는 성공, 실패, 일부 성공(partial success) 라는 3가지 패턴이 존재한다.

한 작업에 대해 대기 Queue 와 무관하게 다른 작업을 처리하는 효율적인 알고리즘 처리 시, 각 작업의 완료들의 순서가 보장되지 않는 경우가 많기 때문에 Async 와 헷갈릴 수 있지만, 이들은 앞서 언급했듯이 비교할 수 없는 다른 개념이다.



오해


많은 종류의 소프트웨어에서 동기 처리 방식이 Blocking 이고, 비동기 처리 방식이 Non-Blocking 인 이유는 익숙한 구조이기 때문이다.

일련의 작업들에 대해 순차적으로 하나씩 처리하고 완료하는 방식은 매 작업의 수행마다 Blocking 하는게 작업의 순서를 보장하기 쉬우며,

여러 작업들이 동시에 일어나는 구조에서는 한 작업을 수행하는 동시에 Non-Blocking으로 다른 작업을 받아와서 처리하는 구조가 효율적이다.

그렇기 때문에 같은 개념으로 혼동하기 쉽지만, 동기/비동기와 블로킹/논블로킹은 서로 양립할 수 있는 개념이란걸 기억하자.



정리한 개념을 통해 System I/O 의 모델들에 대해 이해해보자.


- Synchronous Blocking I/O : Application layer 의 계층이 Block을 일으키는 System Call을 호출하고, 이에 따라 User Layer와 Kernel Layer 간의 Context Switching 이 발생한다. 

이 때, Application 은 CPU 를 사용하지 않고 Kernel 의 응답을 기다리게 된다.


- Synchronous Non-blocking I/O : Nonblock Kernel Systemcall 을 사용하기 때문에 더 향상된 것처럼 보이지만, 

User Layer 가 Synchronous 이므로 응답을 기다리는 동안 Kernel 의 System Call을 Polling 하게 된다. 당연히 Context Switching 빈도수가 늘어나기 때문에 더 I/O에 지연이 발생하게 된다.


- Asynchronous Blocking I/O : User Layer의 I/O 가 Non-blocking 이고 Kernel 에서 알림이 블로킹 방식이다. Select System call 이 이 방식의 대표적이며 여러 I/O 를 한번에 수행할 수 있는 모델이다.


- Asynchronous Non-blocking I/O : Kernel I/O 의 개시와 알림 두 차례만 Context Switching 이 발생하고, Kernel 작업이 Non-block 이므로 select() 와 같은 멀티플렉싱 뿐 아니라 다른 프로세싱 자체가 가능하다. 

Kernel level 에서의 응답은 Signal 이나 Thread 기반 Callback 을 통해 user level 로 마치 이벤트처럼 전달된다.


OS 레벨에서 프로세스들의 동작 처리는 비동기 처리 모델 방식으로 진행된다. 각 프로세스 내에서 처리는 작업의 순서가 보장되는 동기 처리 방식이지만, 커널 레벨의 관점에서는 커널의 리소스를 사용하는 모든 작업들의

순서를 지켜줄 수 없으며, 커널 동작의 관점에서 보면 비동기 처리 모델을 사용한다. 

즉, 시스템 프로그래밍 윗 레벨에서의 대부분의 작업 모델은 동기 처리이지만, 커널 레벨에서 프로세스의 운용은 비동기 처리로 볼 수 있다.

물론 각 프로그램들이 동기 / 비동기 중 어떤 방식으로 내부 로직을 처리하는 가는 별도의 문제이다.



추가로 NodeJS 포스팅에서 언급할 예정이지만, NodeJS 는 Async I/O 처리를 위해 libuv 를 사용하는데, 이 엔진은 default thread를 4개 사용하여 Async I/O 를 구현한다. 결국 Async 라 해도 마법은 아니며, 구현의 레벨로 보자면 Scheduling 과 Loop 의 문제이다.


참조 : https://docs.microsoft.com/en-us/windows/desktop/FileIO/synchronous-and-asynchronous-i-o




+ Recent posts