728x90
반응형

a : 커널 b : 디바이스 드라이브 c : 장치 제어기

1은 User Space와 Kernel Space를 제어하는 System call 부분이고, HW Interface가 아래에 있다. 

1번 화살표 : 그림은 부팅이 끝나 이미 각종 interrupt에 ISR의 시작주소가 interrupt vector table 또는 interrupt descriptor table인 IDT에 이미 등록되어 있는 상태. IDT가 있고 ISR의 시작주소가 초록색 박스에 쭉 등록되어 있다. 현재 이 상태에서 2층에 있는 응용 프로그램이(여기서는 read(buf) 파란색 박스) 1층에 있는 커널에게 read라는 시스템 콜을 호출하였다. 단 read라는 것은 데이터를 얻어오는 것이다. 사용자 공간내에 이를 위한 버퍼를 미리 잡아두어야한다. 이에 대한 포인트인 buf를 read호출 시 인자로 넣어주었다. 이 호출이 trap을 거쳐 커널내의 정보를 따라서 해당 디바이스 드라이브 내의 실제 구현 함수인 sys_read()까지 다다른 상태라고 보자. 

 

2번 : 디바이스 드라이버 내의 sys_read() 함수는 응용프로그램이 요청하는 데이터를 예를 들어서 하드웨어의 장치제어기로부터 받아와서 잠시 저장하기 위한 용도로 커널 내의 버퍼를 하나 할당받아야 한다. User Space의 메모리 공간하고 Kernel Space의 메모리 공간은 다르기 때문에 장치제어기가 바로 UserSpace의 버퍼에 copy하지는 못한다. 그 전에 kernel이 갖고 있는 space에 copy를 먼저 해놓은 다음에 kernel에 의해서 copy를 해줘야하는 구조이다. kernel내의 버퍼를 만들어 놓고, 그 버퍼를 pointing하는 변수의 이름이 ptr이라는 것이 된다. 그런 후, 2번 레지스터와 같이 장치 제어기의 명령 레지스터에 ptr이라는 값과 함께 하드디스크의 읽기 명령을 기록을 한다. ptr를 넘겨주는 이유는 장치 제어기가 버퍼에 써야하기 때문에 주소도 같이 넘겨주어야 하기 때문에 그렇다.
 명령 레지스터에 기록을 한다 라고하였는데 그 이유는 메모리 mapped I/O를 사용하고 있기 때문에 명령 레지스터에 상응하는 메모리 상의 주소에 read라는 시스템 콜에 해당하는 장치 제어기로의 명령 코드를 기록함으로써 명령을 내리기 때문에 그렇다. 명령을 내리는 것은 기록하는 것이다. 그럼 ptr값은 왜 같이 넘겨주었을까? 장치제어기가 DMA를 할 때, 그리로 직접 접근할 수 있도록 시작 주소를 알려놓게 위함. 그럼 이 시점에서 어떤 일이 벌어질까? 명령이 내려갔으니까, 즉 장치제어기가 명령 레지스터를 통해 명령을 받았으니까 곧바로 하드디스크의 해당 블록을 찾아서 읽어오도록 하는 물리적인 동작을 시작시키게 될 것이다. 장치제어기의 물리적인 하드디스크를 동작시키기 시작했다는 소리이다.

상태레지스터는 생략되어있다.

 

3번 : CPU는 더 이상 할 일이 없으므로 하드디스크 작업이 끝날때까지 기다려야 한다. 여기서 busy waiting으로 기다리면 성능이 떨어질 것이고, 따라서 디바이스 드라이버 상의 코드로 볼 때, 2번 화살표 작업을 수행하고 난 다음에는 3번과 같이 Sleep()을 하게 된다. Sleep함수의 내부에 들어가보면 결국에는 이 프로그램이 Sleep상태로 들어가면서 이제는 CPU가 수행시킬 다른 프로세스를 선정하기 위한 스케쥴링 함수를 불러오게 된다. 그럼 어떻게 될까? 스케쥴함수(Sched())에 의해 선정된 다른 프로세스가 CPU의 문맥교환이라는 것을 통해서 그 다른 프로세스를 수행하게 된다. 그 동안에 이 프로세스는 Sleep()상태에 있고 안 돌고 있는 것이다. 

 

4번 : 그렇게 CPU가 다른 프로세스를 진행하고 있는 동안에 장치 제어기는 하드 디스크를 물리적으로 구동해서 결국 응용프로세스가 read해 오고자 하는 블록을 찾아내서 자료 레지스터로 읽어들일 것이다. 여기서 자료레지스터는 장치 제어기 내의 저장 공간으로 보아도 된다. 이렇게 데이터가 준비가 되면, 2번 작업을 통해 전달받은 ptr 주소값에 해당하는 메인 메모리의 주소로부터 시작해서 그 데이터를 전달하게 된다. 이 전달하는 과정에서 바로 DMA가 사용된다는 것인데, 블록 데이터이기 때문에 블록을 옮기기 위해서 CPU가 간섭할 필요가 없고, 블록 단위로 바로 장치제어기에서 Device Driver의 버퍼로 copy되게 하기 위해서 DMA를 사용한다. 

 

5번 : 4번이 끝나고 나면 device driver내의 버퍼에 해당 자료가 와 있는 상태가 되는 것이다. 이제 device driver가 깨어날 시점이 된 것이다. 그럼 device driver가 ptr저기 다음 라인을 실행 시키게 되는 것이다. 그런데 현재 sleep상태라는 것이다. 그러면 어떻게 속개될 수 있도록 장치제어기가 작동하느냐, 바로 장치 제어기가 DMA 끝났읍니다 하고 interrupt를 CPU에게 걸어준다. interrupt는 다수준 인터럽트로 인터럽트 번호나 masking이 동반되어 결국 IDT에 기록된 해당 디바이스 드라이브 내의 ISR 처리 함수로 jump가 되는 것이다. 

 

6번: 하드웨어의 도움을 받아서 IDT에서 해당 Device Driver내에 위치한 ISR의 시작주소를 알아낼 수가 있고, 그 시작주소를 PC(Program Counter)에 얹으면 해당하는 ISR로 jump하게 된다. 이렇게 호출된 ISR은 interrupt처리에 필요한 작업과 아울러서 궁극적으로 아까 sleep상태로 들어간 프로세스(아까 read프로세스)를 깨우는 작업을 하게 된다.

 

7번 : 이를 위해서 프로세스를 스케쥴링 queue에 넣게 된다. 이렇게 스케쥴링 queue에 들어가면 시점이 문제이지 언젠가는 프로그램이 속개된다. 결국 sleep상태로 들어간 프로세스가 wake-up되는 것이다. 이렇게 wakeup이 되고나면 시간이 얼마 지났는지 모르지만 결국 device driver코드 상으로 볼 때  아까 3번에서 호출한 sleep함수가 그제서야 return이 되는 것이고, 그 다음 라인을 수행하게 될 것이다. 거기가 바로, ptr 다음 라인부분이 된다. 

그럼 sleep전과 sleep후 달라진 것은 무엇일까? : 시간이 얼마나 지났는지는 모르지만 전에는 버퍼에 데이터가 없었는데 sleep후 꺠어나 보니까 버퍼에 데이터가 생긴 것이다. 

 

8번 : 그럼 곧바로 ptr이 포인팅하는 버퍼 내의 데이터를 사용자 공간으로 copy해준다. (커널 공간과 사용자 공간이 부리되어 있기 때문에 그렇게 해야 넘어간다)

 

9번 : sys_read()함수는 역할을 다했기 때문에 return과정을 거친다. 리턴과정을 거쳐서 응용 프로그램으로 돌아가는 것이다. 그렇게 되면 응용프로그램 입장에서 보면은 read함수가 끝나고 다음라인으로 속개되는 것이다. 

 

CPU와 장치드라이버(I/O device)만 두고 상태변화 보기

CPU는 그림과 같이 2가지 상태를 반복한다. 즉, 사용자 프로세스를 수행하다가 인터럽트가 들어오면 ISR을 수행하다가 두 상태를 번갈아가면서 계속 진행한다. 

반면에 I/O 장치는 쉬다가(idle 상태이다가) 입출력 작업을 받아서 입출력 작업 수행(transferring)하다가 두개의 상태를 반복한다.

1~9를 표현한다면 그림처럼 된다.

2번(I/O 장치로 데이터 입출력시키기), 5번(interrupt 시키기), 7번(해당 ISR로 점프해서 interrupt 처리작업을 한다)

8번,9번(read함수 다음줄 속개)

 

보호(Protection을 의미)

소프트웨어로 만들어진 커널이 응용 프로그램에 의하여 문제가 발생하는 경우가 생길 수 있다. 어떻게 이를 하드웨어의 도움을 받아서 protect할 수 있는지에 대하여.

보호(Protect) 대상 : 전부를 보호하는 것이 아니라 근원적인 부분 보호함으로써 직간접적으로 보호

불법(Illegal) I/O : 응용 프로그램이 저지를 수 있는 잘못된 입출력에 대한 보호. 특히 장치제어기를 임의로 접근함으로써 시스템이 오작동하는 방법에 대해서 설명. 
불법 메모리 접근 : 응용 프로그램이 다른 응용프로그램이나 커널 프로그램이 탑재된 메모리 영역을 임의로 접근하거나 내용을 변경해서는 안된다. 이를 방지하기 위한 방법에 대해서 설명. 

무한 루프(Infinite Loop) : 응용 프로그램이 무한 루프를 돌 경우 CPU가 그 루프만 돌리도록 놓아두면 시스템이 마치 정지한 것과 같을 것이다. 무한 루프를 방지하는 방법에 대해서 설명하겠다. 

 

이중모드와 모드비트

이중모드

커널이랑 커널 모드의 차이점? 

커널 : 하드웨어에 접근하기 위한 프로그램

커널 모드 : OS의 함수들

  • 사용자 모드 : 사용자 공간 상의 코드만 실행 가능
    • 인터럽트나 입출력 제어와 관련된 특권명령어(privileged instruction) 수행 불가
    • 특권명령어 수행 시도 시 트랩 발생
    • 메모리 참조 영역도 제한
  • 커널 모드 : 커널 공간 상의 코드만 실행 가능하며 특권명령 사용 가능
    • 사용자 프로그램의 시스템 호출(트랩), 인터럽트 처리, 명령어 수행 오류 발생 시 발생하는 트랩 처리
    • 하드웨어적인 제한이나 보호를 수행치 않음 -> 커널 모드로 수행할 코드를 작성할 경우 매우 조심해야한다.

그전에 이러한 보호 방법의 근본적인 수단인 이중모드에 대해서 먼저 설명하도록 하겠다. 방금 전 3가지 보호대상이 있다고 하였는데, 사실 이 세가지 대상말고도 보호대상은 많을 것이다. 하나하나에 대해 방법 강구하는 것은 소모적이다. 그보다는 근본적이고 이를 통해 여러 문제가 해소되는 방법을 찾아야 한다. 그러한 것으로 이중 모드라는 것이 있다. 즉, 이중모드란 커널모드나 사용자 모드를 분리하는 것을 이야기 하는데, 기본적으로 메인메모리를 사용자 프로그램이 탑재한 사용자 프로그램과 커널 프로그램이 탑재된 커널 프로그램으로 나누어서 CPU가 사용자 프로그램을 수행할 때는 사용자 모드로 수행하도록 하고, 커널 공간의 프로그램을 수행하도록 할 때는 반드시 커널 모드로만 수행하도록 하는 것이다. 왜 이렇게 할까? 

- 기본적으로 자원 공유 환경에서는 한 응용 프로그램의 오동작이 다른 프로그램의 오동작을 야기시킬 수 있다. 그러한 오동작에는 잘못된 명령어 사용, 타 영역 접근, 임의로 입출력 장치 제어기에 접근 등, 다양한 것들이 있을 수 있다. 이러한 오동작은 결국 CPU가 프로그램, 즉 코드를 수행할 수 밖에 없기 때문에 벌어지는 건데, 그렇다면 코드 중, 커널이나 다른 프로그램의 오동작을 일으킬 소지가 있는 코드는 커널 내에서만 수행하도록 하면 될 것이다. 따라서, 커널의 그러한 코드들을 함수로 만들어 모아놓고, 응용 프로그램은 필요시에 그 함수를 불러서 쓰면 되는 것이다. 문제가 있을만한 코드는 커널내에 집어넣어놓고, 그것을 사용할 떄는 커널모드에서만 수행될 수 있도록 통제를 한다. 모아만 놓고 응용 프로그램들이 아무렇게 불러쓰도록 하면 서로 여전히 간섭이 생겨서 마찬가지로 문제가 되고, 이렇게 모아놓은 것은 별 소용이 없게 된다. 따라서 이러한 커널 내 함수는 CPU로 하여금 별도에 모드인 커널 모드에서만 수행되도록하고, 응용 프로그램이 그 함수를 호출할 필요가 생기면 커널 모드로의 진입을 허가받아서 수행을 하도록 한다. 그것이 바로 이중 모드(dual mode)를 두는 이유인 것이고, 문제가 될 만한 코드는 커널 내에다 놓고, 그것을 응용프로그램은 호출을 통해서, 여기서 호출은 시스템 콜이 되는 것이다. 트랩이라는 시스템 콜을 통해서 커널 모드로 진입해서야만 수행할 수 있도록 하겠다는 것이다. 

이중 모드는 어떻게 실현시킬 수 있을까? 코드를 그렇게 모아놓고 커널모드에서만 실행시키는 것은 좋은 아이디어인데 어떻게 실현??? 이를 위해서 CPU내에 특수 레지스터라는 것들이 있는데, 그 특수 레지스터라는 것들 중에 상태 레지스터라는 것이 있다. 상태 레지스에서 1비트를 모드비트(mode bit)로 사용한다. 모드비트가 0이면 커널 모드이고, 1이면 사용자 모드인 것인데 CPU는 이 모드 비트를 보고 커널모드인 경우에만 소위 말하는 특권 명령어(priviledge Instruction)를 수행한다. 특권 명령어는 다양한데, 입출력 명령이라든지 interrupt관련 명령어, 특히 이 모드 비트 자체를 변경시키는 명령어 등이 특권 명령어에 속한다. 

시스템 콜이 호출되면 트랩과정을 거치게 되는데 이 과정에서 모드비트가 커널모드인 0으로 설정이 되는 것이다.(이 부분이 중요) 해당 시스템 콜에서 return을 할 때는 모드 비트를 다시 1로 두는 것이다. 그렇게 해서 return을 하고나면, 응용 프로그램에서는 다시 시스템 콜을 하지 않고는 특권 명령어를 사용할 수 없게 되는 것이다. 

 

이러한 이중모드를 잘 사용하면 몇가지 보호 수단을 잘 사용할 수 있다.

  • Illegal I/O 차단 (불법 I/O 차단)
    • 모든 I/O 관련 명령을 특권명령어로 함 -> 시스템 호출 즉, 트랩을 통해서만 I/O가 가능하도록 해놓으면 된다. 
      • 명령어만 방어해서는 안되고 메모리 영역도 방어해야 한다.
    • 메모리의 커널 영역 보호 수단과 함께 사용
      • 인터럽트 처리 루틴(ISR) 및 장치 구동기 영역
      • 시스템 버퍼 영역
      • 인터럽트 벡터 영역
  • 불법 메모리 접근 차단 : 이를 위해 특권 명령어로만 접근할 수 있는 2개의 레지스터를 사용하는 것이다.
    (Base register와 Limit register)
    • Base와 Limit 레지스터로 프로그램 공간 정의 - 레지스터의 적재 명령은 커널 모드 명령(특권명령어)
    • 사용자 모드에서 주소 생성시에 범위를 확인, 벗어나면 커널로 트랩함

가상메모리의 경우 메모리 보호가 페이지 단위로 제공됨(맵에 정의된 주소공간 초과 여부를 하드웨어가 차단)
ex> UNIX나 LINUX의 경우 "Segmentation Fault"

job2가 메모리 어딘가를 접근하려고 할 때마다 그 대상주소가 이 범위를 벗어나는지를 메모리 관리 유닛이라는 것이 이 두 레지스터를 활용해서 항상 체크하도록 하는 것이다. 그래서 거기서 벗어나게 되면 오류가 발생했고 그 오류가 발생해도 트랩이 걸리는데 오류 발생에 의한 트랩이 걸림으로써 그 오류를 커널이 처리하도록 그렇게 하는 것이다.

 가상 메모리의 경우 메모리 보호가 페이지 단위가 되고 이를 보호하기 위한 메커니즘이 하드웨어적으로도 지원이 된다. 

Segmentation Fault는 코드에서 포인터를 잘못 사용할 경우, 해당 프로세스에 할당된 메모리 영역을 벗어날 때 발생하는 오류 메시지

  • Infinite Loop 방지(CPU 보호)
    • 시분할에서는 타이머 인터럽트를 이용하여 타임슬라이스를 구현함으로써 CPU 공유 실형
      • 타이머 또는 클럭으로 하여금 고정된 빈도로 인터럽트를 발생토록 함(예: 1/100초)
      • 타임 슬라이스 실현을 위해 필요한 최고 우선순위 인터럽트 설정, 인터럽트 인터벌 설정 등 모드 특권 명령어로만 수행하도록 함 -> 응용프로그램이 타임슬라이스 인터벌을 악의적으로 수정할 수 없게 되고, 결국 매 타임슬라이스마다 스케쥴링이 이루어짐으로써 무한 루프도는 프로그램만 도는것을 방지
    • 프로그램의 실행 시간을 제야함으로써 한 프로그램의 무한루프 수행(CPU 독점)을 방지

 

 

 

기타 : 몇가지 주제를 두서없이 집고 가겠다

 

캐시(Cache) : 캐시와 레지스터는 CPU 내에 있고 특히 레지스터는 Core내에 있다. 이 둘은 용량이나 속도면에서 반비례적인 특징이 있다. 레지스터가 가장빠르지만 용량은 제일 적다. 메인 메모리는 용량은 크지만 속도는 느리다. 두 가지 장점을 절충해서 속도도 레지스터에 버금가도록 빠르고 용량도 메인메모리처럼 큰 장치가 있는것같은 착각을 줄 수 있는 방법 -> 캐시가 이것을 위해서 고안됨.

  • 캐시
    • 레지스터와 메모리 사이에 접근시간 또는 데이터 이동속도의 심한 격차가 있을 때에 이의 완화를 위하여 사용
    • CPU에서 메인메모리에 접근 시에 그 자료를 메모리보다 속도가 빠른 캐시에 복사해놓고 다음번 접근시에 캐시를 방문하여 찾는 자료가 다행히 거기에 있으면 캐시로부터 바로 갖고 가고, 없으면 그때서야 메인메모리에서 가져온다. 
    • CPU가 갖고있는 값이 항상 캐시에 복사되어 있다면 메인메모리의 속도가 캐시만큼 빨라지게 되는 것이고, 용량은 메인메모리의 용량과 다름없어진다.
    • 알고리즘 여부에 따라서는 캐시에서 데이터를 갖고 있을 확률(Hit Ratio)을 90%이상 개선 가능
  • Hit Ratio가 높아서 100%에 근접할수록 사용하는 효과 극대화 : 가격이 비싸고 용량은 적지만 빠른 속도로 메인메모리의 용량을 사용할 수 있는 효과를 만들어 냄.

캐시 - 메모리 계층구조

-> 개념을 확장하면 시스템 전체의 기억 장치의 성능이 획기적으로 개선될 것이다. 이러한 발상으로 고안된 거시 메모리 계층구조이다. 맨 위에 레지스터, 그리고 메인 메모리 사이에 캐쉬, 그러면 캐쉬와 하드디스크 사이에서 메인 메모리는 캐쉬의 역할을 하지 않을까? 이러한 관점을 확대하서 운영하면 전체적으로 용량은 최하위 기억장치의 용량으로 확대가 되고, 접근 속도는 최상위 기억장치 속도처럼 제공될 수 있다. 그래서 컴퓨터 시스템은 메모리 계층 구조를 갖게 되는 것이다. 

 

부트스트래핑(bootstrapping) : 흔히 부르는 부팅의 풀네임

  • Bootstrap의 어원적 의미 : 장화 뒤에 고리인데 장화를 신을때 부트스트랩을 혼자 신을 수 있도록 도와주는 것으로 나중에 개념이 확장되어 남의 도움 없이 자기가 스스로 수행한다는 의미. 장화에 달려있다는 점이 중요한데 이래서 운영체제의 관점에서 볼 때 운영체제의 시동을 운영체제 일부 기능이 스스로 수행한다는 과정을 뜻하는 용도. 
  • 부팅 시퀀스 - 시스템에 따라 차이가 크겠지만 원론적에서 아래와 같다. : "process of chain loading"
    • 전원이 처음들어오면 메인 메모리에는 아무것도 없다.(volatile memory이기 때문에) 부팅을 시작시키는 조그마한 프로그램이라도 어딘가에 있어야 그것으로부터 부팅과정을 시작할 수 있게 된다. 그 조그마한 프로그램은 volatile 메모리에 있으면 안되고, 비휘발성 메모리에 있어야 한다. 그것의 예가 ROM이다. ROM에 들어있는 20~30바이트의 간단한 프로그램(롬 로더)를 제일 먼저 수행한다.
    • 롬 로더는 DMA를 이용해서 하드디스크 0번 섹터(부트섹터)에 존재하는 마스터 부트 레코드로부터 부트스트랩 로더의 첫번째 블록을 읽어, 메인 메모리에 적재한 후 이 첫번째 블록으로 점프(첫번째 블록의 시작주소)하여 역할을 끝냄 -> 롬로더의 역할은 끝남 -> 부트스트랩 로더의 첫번째 블록이 시작됨.
    • 첫번째 블록이 수행되면 그 첫번째 블록은 부트스트랩 로더의 나머지 부분을 순차적으로 끌어서 적재하게 된다. 
    • 그렇게 나머지 부분의 적재가 끝나면 자신을 메모리의 상위 장소로 옮긴 후 전체를 실행한다. 그렇게되면 커널 프로그램을 읽어들여서 하위 기억장소에 다시 적재하게 되는 것이다. 
      부트스트랩의 역할은 커널 프로그램을 하위장소로 읽어들여서 적재하는 것이다. 
    • 부트스트랩은 이제 커널 적재가 끝나면 약속된 장소로 점프하여 커널을 시작시킨다.
      ---> chain loading 방식(아주 작은 프로그램에서 실행하여 다음 것을 로딩하고 그리고 그리로 점프하는 과정을 연쇄적으로 실행해서 운영체제 전체를 실행하게 되는 것이다.)으로 이루어진다. 

모노리틱 커널 vs 마이크로 커널 : 커널의 구성상의 특징을 지칭하는 것이다.

모노리틱 커널 : 커널이 하나로 되어있다.

  • 프로세스 관리, 메모리 관리, 파일시스템, 입출력 관리 및 네트워크 관리 등 모든 기능을 커널 내부에 포함 - 효율성을 높임
  • 다른 플랫폼으로의 이식성이나 확장성의 한계를 하드웨어나 환경에 종속적인 부분을 따로 분리한 계층구조로 극복
  • 예 : 리눅스 - 모듈 사용으로 확장성 문제 해결 // 새로운 장치 모듈을 사용해서 장치 하나만 컴파일하면 됨.
  • 장점 : 일반적으로 시스템 호출 서비스가 빠름
  • 단점 
    • 전체적으로 볼 때 새로운 하드웨어 플랫폼에 대한 이식성은 떨어짐
    • 한 부분에서 발생한 문제가 시스템 전체에 영향을 줄 수있음
    • 구성요소들 간의 의존성이 높아져 디버깅이 어려워짐

마이크로 커널

  • 프로세스간 통신, 메모리 관리, 클럭 인터럽트 처리와 CPU 스캐쥴링 등 아주 핵심기능만을 커널에 포함시키고 나머지는 서버 형태로 두어 사용자 모드의 프로세스로 수행 

  • 시스템 콜이 들어오면 file system이나 device driver로 메시지를 전달하는 구조이다. 전달하고 결과를 받아서 다시 전달하고 하다보니까 user와 kernel 모드를 스위칭하는 것이 빈번해짐.
  • 통신 프로토콜, 디바이스 드라이버 등의 수행은 사용자 프로세스로 존재한다
  • 파일 시스템도 일종의 서버 프로세스로 수행한다
  • 인터럽트도 인터럽트 서비스 프로세스에 메시지를 보내 처리한다
  • 예 : CMU의 Mach, Windows-NT(마이크로 커널과 계층구조를 겸비한 독특한 구조)
  • 장점
    • 유연성이 좋다 : 커널의 핵심부분을 제외하고 나머지 User Application으로 실행하기 때문에
    • 한 부분에서 발생한 오류가 시스템 전체로 확산되지 않음
    • 소형의 내장형 시스템에 활용하기에 유리
  • 단점

파일서비스 등에서 프로세스의 문맥교환과 메시지 전송이 빈번해져서 속도가 느려질 수 있음

728x90
반응형

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

Race condition / Critical Section  (0) 2022.06.07
[5] 프로세스  (0) 2022.03.24
[3] 컴퓨터 구조와 OS 연계  (0) 2022.03.14
[2] 시분할 시스템, 실시간 시스템  (0) 2022.03.09
[1] 운영체제의 발전  (0) 2022.03.07

+ Recent posts