
### Memory Management 1
메모리 관리
메모리: 주소를 통해 접근하는 매체
- 논리적인 주소(Logical address) : 프로세스마다 독립적으로 가지는 주소 공간 / 프로세스마다 0번지부터 시작하는 논리주소를 갖고 있음
- 물리적인 주소(Physical address) : 물리적 메모리에 프로그램이 실제로 올라가는 위치
주소 바인딩: 물리적인 주소를 결정하는 것
논리주소 -> 물리주소로 변환
물리적인 메모리 어디에 올라갈지 결정되는 것..
Symbolic Address
숫자로 된 주소 사용x 심볼을 이용함
프로그래머 입장에선 심볼로된 address를 사용함
변수명, 함수명으로 호출하고 선언하는데
컴파일하면서 심볼릭이였던게 숫자로 된 주소(logical address)로 변환이 되는 것임
< 물리적인 주소가 언제 정해지는지에 따라 구분 >
1) 컴파일 타임 바인딩 - 컴파일시 바인딩됨 (컴파일시점에 물리적인 주소가 정해짐)
2) 로드 타임 바인딩 - 실행시 바인딩됨 / 컴파일타임에선 논리적 주소만 정해지고, 실행시킬때 물리적 주소가 정해진다
3) 익스큐션 타임 바인딩(런타임 바인딩) - 실행하면서 바뀜 / 로드타임 바인딩처럼 실행시킬때 물리적 주소가 정해지는 것은 똑같은데 실행도중에 물리주소가 이동할 수 있다.
- 절대코드 (absolute code) : 컴파일 타임 바인딩에서 물리적 주소를 바꾸고 싶다면 컴파일을 새로해야 바꿀 수 있다
- relocated code : 반면에 로드 타임 바인딩과 런타임 바인딩의 컴파일시 정해지는 논리주소가 relocated code이다.
CPU가 메모리 주소를 요청할때마디 주소변환을 그때그때 해야 한다. -> 런타임 바인딩은 하드웨어적인(MMU의) 지원이 필요하다
CPU는 논리적 주소를 바라보고 있다
물리적 메모리에 올라갈때 시작위치는 바뀌지만 논리적 주소는 똑같이 0번지부터 시작하기 때문..
따라서 CPU가 보는 주소도 논리적 메모리다
CPU가 논리적 주소로 요청을 하면 그때 물리적 주소로 주소 변환을 해서 처리를 해준다
MMU
MMU: 주소 변환을 위한 하드웨어
MMU -> 레지스터 2개를 통해서 주소변환을 함
relocation register(=base register), limit register를 이용
relocation register(=base register) : 물리적 메모리에서의 시작위치 / 이 값을 논리주소와 더하면 물리주소가 됨.
limit register : 프로그램의 최대 크기 / 프로그램크기보다 더 큰 크기의 논리주소를 달라고 하면 막아준다. 17000번지가 젤 끝인데 18000번지를 요청하게 되면, 다른 프로그램의 메모리이기 때문에 막아주는 것임
=> 트랩(소프트웨어 인터럽트)이 걸리고 운영체제가 확인하게 됨..
사용자 프로그램은 logical address만 다룬다
CPU도 logical address를 바라봄
물리주소는 요청이 왔을때 MMU를 통해 주소변환해서 얻게 된다.
용어
- Dynamic Loading(동적 할당)
메모리에 동적으로 올린다
프로그램 전체를 메모리에 올리는게 아니라 어떠한 루틴이 필요할때마다 메모리에 올리는 것
라이브러리를 써서 다이나믹 로딩을 쓴다.
원랜 다이나믹 로딩은 라이브러리를 이용해서 프로그래머가 직접 구현하는거다
다이나믹 로딩은 운영체제 페이징 기법도 다이나믹 로딩이라고 섞어 쓰기도 한다
운영체제가 관리하는 페이징 시스템
오버레이 (Overlays) : 예ㅔ전에 프로그램 1개도 메모리에 올리기 어려울때 쪼개서 메모리에 올린 것, 프로그래머가 수작업으로 했었다
=> 메뉴얼 오버레이라고도 부름. 운영체제의 지원x, 프로그래머가 코딩으로 구현함, 라이브러리x
스와핑(Swapping)
프로세스를 메모리에서 통째로 하드디스크로 쫓아냄 (=backing store, swap area)
메모리에 있는걸 통째로 하드디스크로 내리는거 : swap out
하드디스크로 내려간게 메모리로 올라온거 : swap in
중기스케줄러가 스왑아웃으로 할 것을 정함.
중기 스케줄러를 스와퍼라고도 부름
쫓아내는 기준: cpu 우선순위가 낮은거(cpu 수행할 확률이 낮은...)
스와핑 시스템이 지원되기 위해선
컴파일 타임 바인딩과 로드타임 바인딩은
원래 위치로 올라와야한다
런타임바인딩은 스왑아웃되서 나중에 스왑인될때 다른위치로도 올라갈 수 있게 지원해줌
=> 효율적으로 되려면 런타임 바인딩이 지원되야 할 것..
=============== ???
용량이 방대해서 transfer time은 스왑되는 데이터 양에 비례한다
디스크에선 디스크 헤더가 이동하는 시간 - seak time 이 대부분을 이룬다(transfer time보단..)
transfer time : 데이터를 읽고 쓰는 시간
seak time : 데이터가 있는 실린더로 이동하는 시간
===============
페이징에서 일부페이지가 쫓겨나는 것도 스왑아웃이라고 함
=> 전통적인 의미는 전체가 쫓겨나는 것이 스왑아웃.
Dynamic Linking
36분 49초
프로그램을 작성한다음에 링크해서 실행파일을 만듦
- 링크: 여러곳에 있는 컴파일된걸 묶어서 실행파일로 만드는거 => 라이브러리들도 링킹되서 실행파일로 만들어짐!!
- static 링킹: 라이브러리가 내 실행파일에 포함되는게
- 다이나믹 링킹 : 라이브러리 코드가 컴파일할때 실행파일에 포함되지 않음 -> 실행할때 라이브러리가 별도의 파일 형태로 있고 위치의 포인터만 존재, 그 라이브러리 파일이 어디있는지 찾아서 필요하면 메모리에 올리고 라이브러리를 실행
Shared 라이브러리 : DLL
물리적인 메모리 관리
연속할당 : 프로그램을 연속적으로 할당
불연속할당 : 프로그램을 구성하는 것을 잘게 쪼개서 부분 부분을 올림 (페이징, 세그멘테이션 기법)
연속할당
고정분할 방식, 가변분할 방식
고정분할 방식 : 사용자 프로그램이 들어갈 메모리 영역을 미리 분할해둠 / 파티션을 미리 분할함
가변분할 방식 : 사용자 프로그램이 들어갈 메모리 영역을 미리 분할하지 않음.
외부조각 - 올릴려는 프로그램보다 조각이 너무 작아서 사용이 안됨
내부조각 - 프로그램의 크기가 분할조각보다 작아서 조각에 프로그램을 할당했지만, 남아서 사용이 안되는 공간
가변분할 방식이더라고 프로그램이 실행되고 끝나고 하다보면 메모리에 구멍(홀)이 뚫리게 되고 새로 프로그램이 실행되면서 외부조각이 생길수있다
홀 : 가용 메모리 공간 (쓸 수 있는 메모리 공간)
Dynamic Storage-Allocation Problem
53분쯔음
가변 분할 방식에서 가장 적절한 홀을 찾는 문제
- First-fit
- Best-fit
- Worst-fit
컴펙션 : 외부조각으로 생기는 홀을 한쪽으로 밀어서 모아서 큰 홀을 만드는거 -> 실행중인 프로그램을 한군데로 미는 방법이라.. 바인딩을 다 점검해야하기 때문에 생각보단 비용이 많이드는 복잡한 방법
런타임 바인딩이 지원되야지만 컴펙션을 할 수 있다
불연속할당 방법
- 페이징 기법
- 세그멘테이션 기법
페이징 기법 : 프로그램을 구성하는 주소공간을 같은 크기의 페이지로 자름
페이지 단위로 물리적인 메모리에 올려놓거나 백킹스토어(swap area)에 내려놓는다.
페이지 프레임 : 물리적 메모리에 페이징 크기만큼 미리 잘라놓음
=> 어차피 비어있는 위치는 페이지 프레임 크기만큼 비어있기 때문에 연속할당에서 생긴 크기가 균일하지 않아서 생겼던 문제에 대한 것은 해결이된다..
MMU 주소변환이 복잡해진다.. 페이지별로 해야하기 때문
세그멘테이션 기법: 의미단위로 자름
코드 데이터 스택로 자를 수도 있고
프로그램의 함수마다 세그먼트로 자를수도 있음
의미단위로 잘라서 크기가 균일하진 않음 => 세그멘테이션 기법을 사용하더라도 세그먼트 크기가 균일하지 않기 때문에
다이나믹 Storage-Allocation Problem 이 발생한다
하지만 연속할당처럼 통째로 메모리에 올리진 않고 짤라서 올린다
페이징
프로세스를 구성하는 주소공간을 동일한 공간의 페이지로 나눔
페이지 일부는 물리적메모리 에 올라가고
일부는 백킹스토어에 있게되고
(backing store는 swap device로 하드웨어의 부분인데 페이지를 임시로 보관하는 공간)
불연속적으로 메모리에 올라갈 수 있게 된다
물리적 메모리도 페이지 크기로 나누는데
페이지가 들어갈 수 있는 위치, 틀이라고 해서 페이지 프레임 이라고 부른다
비어있는 페이지 프레임이 어딘지를 관리하게 되고
주소변환이 페이지 단위로 이뤄져야하기 때문에
단지 레지스터 2개(베이스 레지스터, limit 레지스터)만 이용한 주소변환이 안되고
페이지 테이블을 써서
페이징 기법이 주소변환을 하게 된다
페이징 기법은 내부조각이 생길수있다
프로그램 크기가 페이지 크기로 딱 안떨어지면 남는 공간이 있으니깐...
### Memory Management 2
물리적 메모리를 관리하는 기법
메모리에는 운영체제 커널이 항상 상주하고 있고, 나머지 공간에 사용자 프로그램이 있을 수 있다
연속할당과 비연속할당
주소변환
페이징
페이지 별로 주소변환 해야함
주소변환을 위해 페이지 테이블을 사용함
보통 페이지의 크기는 4kb이다
.
메모리 접근을 위해선 2번의 접근이 필요하다
-> 주소변환 하기 위함
-> 데이터 접근하기 위함
메모리를 2번 접근해야하다보니 속도향상을 위해서 TLB라는 별도의 캐시 하드웨어를 사용. (= 캐시 메모리)
메인 메모리와 CPU 사이에 있는 주소변환을 위한 계층
빈번하게 사용하는 데이터를 보관하기 위한 캐시메모리: 메인메모리에서 많이 사용하는걸 캐시메모리에 올려서 cpu에서 좀 더 빨리 접근할 수 있게함..
TLB
논리적인 페이지번호와 물리적인 페이지번호 쌍을 갖고 있어야한다
TLB에 페이지번호가 있는지 전체를 검색해봐야 한다
TLB 어디에도 없으면 페이지테이블을 통해서 주소변환을 해야한다 (없는걸 TLB 미싱 이라함)
TLB는 병렬적으로 서치가 가능한 것을 지원함
페이지테이블과.TLB는 각 프로세스마다 있다
CONTEXT SWITCH가 일어날때 TLB를 플러시 해서 비워줘야 한다.
=> 프로세스마다 논리주소가 다르기 때문
2단계 페이지 테이블
23분 30초
페이지 테이블이 2단계로 이루어짐..
2단계 페이지 테이블을 써서 속도는 줄어들지 않음, 페이지 테이블을 사용하기 위한 공간이 줄어든다.
~ 단위 ~
2^10 k 킬로. 1024
2^20 m 메가. 백만보다 좀 큰
2^30 g 기가
32비트 주소체계를 사용함
2^32 비트 => 4기가바이트
31분 49초 설명
프로그램마다 페이지 테이블을 위해서 사용하는 공간이 크고
공간 낭비가 심하기 때문에 2단계 페이지 테이블을 사용한다
프로그램의 주소공간은 주로 코드, 데이터, 스택으로 구성이 되는데
사용안되는 논리적 주소가 상당부분을 차지한다
하지만 사용하지 않는 논리주소라고 해서 페이지테이블을 만들 때 빼고 만들 수는 없다
페이지 테이블은 배열 자료구조이고, 배열은 index번호를 통해 접근하기 때문이다.
(안쓴다고 해서 중간을 빼버리면 인덱스 번호도 달라질 것임, n번째를 접속하고 싶을테니 .. )
=> 프로그램이 사용하는 주소공간은 상당히 일부분임에도 페이지 테이블 엔트리는 다 만들어져야 한다
( 페이지 테이블 엔트리: 페이지 테이블의 각각의 행들을 의미 )
(사용하지 않는 논리주소까지 다 만들어야 해서) 1단계 페이지 테이블을 사용하면 공간 낭비가 심하다
=> 이를 해결: 2단계 페이지 테이블
사용하지 않는 부분의 안쪽 테이블 공간을 만들지 않아서 공간 낭비를 없앤다.
Two-Level Page Table
논리적인 주소에서 P1, P2...가 바깥쪽 테이블에서 인덱스를 찾을 페이지 번호이다.
바깥쪽 테이블에선 안쪽 페이지 테이블이 어디인지를 가리킨다.
그렇게 찾다보면... 물리적인 페이지 프레임 번호를 얻게된다.
물리적 페이지 프레임에서 d번째 인덱스에서 원하는 정보를 찾을 수 있다.
안쪽 페이지 테이블의 크기가 물리적 페이지 크기와 똑같다
안쪽 페이지 테이블은 테이블 자체가 페이지화되서 페이지 어딘가에 들어가 있게 된다
페이지 크기가 4kb이기 때문에 안쪽 페이지 테이블의 크기는 하나가 4kb가 된다
보통 엔트리 하나당 4B(바이트)이다
페이지( 물리적 페이지 프레임 ) 하나의 크기가 4kb이기 때문에 (4kb라고 크기를 가정함)
4kb = 2^12b
=>> 12비트로 ,, offset을 구분 가능 (물리적 페이지 프레임에서 얼마나 떨어졌는지 12비트로 구분하여 표현할 수 있음) -> ?? 2의 12b승인데 비트는 2개(0,1)를 나타낼 수 있어서 12비트 있으면 구분할 수 있단건가?
=> 서로 다른 4k 군데를 구분
메모리는 바이트 단위로 주소가 매겨짐
page of page table(안쪽 페이지 테이블)은 1k개이다.
1k = 2^10
1k개의 엔트리를 구분하기 위해서 10비트가 필요하다.
=> 서로 다른 1k군데를 구분
32비트 주소 체계를 사용해서 page offset을 위한 12비트와 안쪽 페이지 테이블을 위한 10비트를 빼면
바깥쪽 페이지 테이블을 위해선 10비트가 남게 됨
(제일 바깥쪽 페이지는 다른 것들 빼줌으로써 자동으로 몇비트를 줄지가 계산이 된다)
64비트 주소체계를 사용할 경우 페이지 크기는 똑같이 4kb이고, 2단계 페이지 테이블을 사용할 때 p1, p2, d는 각각 몇비트가 필요할까? -> 43분 질문..🤔
서로다른 n개의 정보를 구분하기 위해선 몇비트가 필요한가?
n비트로 구분 가능한 서로 다른 위치가 몇군데인가?
를 알면 주소에서 몇비트가 필요하고 페이지 테이블에서 몇군데를 가리켜야 하는지를 생각할 수 있다.
2단계 페이지 테이블을 사용하는 이유: 시간은 더 걸리지만 페이지 테이블을 위한 공간을 줄인다
=> 안쪽 페이지 테이블이 만들어져서 바깥쪽 페이지 테이블에선 안쪽 페이지 테이블의 주소를 가리키고, 실제로 사용이 안되는 페이지 테이블은 안쪽 페이지 테이블이 만들어지지 않고 NULL상태로 주소공간을 가리키지 않는다.
==> 상당 부분이 실제로 사용을 안하기 떄문에 주소공간이 줄여진다.
(도저히 이해가 잘 안되서 챗지피티 사용해서 물어봄..
https://chatgpt.com/share/e096daab-f681-4499-850a-8ff48a25a8d5)
### Memory Management 3
페이지 테이블은 2단계만 사용하는게 아니라 다단계 페이지 테이블을 쓴다.
Address space가 더 커지면서 다단계 페이지 테이블 필요
=> 테이블을 위한 주소공간을 줄일 수 있지만, 주소 변환을 위한 메모리 접근 시간이 오래 걸리고 여러번의 메모리 접근이 필요하게 된다
=> 시간 오버헤드가 크다.
=> TLB를 통해 메모리 접근 시간을 줄일 수 있음
TLB: 주소변환을 위한 캐시메모리
TLB를 사용하면 TLB를 통해서 바로 주소 변환이 되는 경우가 있으므로 주소변환에 필요한 시간이 지나치게 오래 걸리진 않는다
TLB에 없어서 페이지 테이블을 접근해야 하는 경우에는 시간이 오래 걸리지만
hit ratio 비율로 계산해서 평균을 내보면 됨
평균내서 계산해보면 다단계 페이지 테이블이 크게 오버헤드가 있진 않다는 것을 알 수 있다..
페이지 테이블 엔트리(PTE)
logical 메모리에 있는 페이지 개수만큼 페이지 테이블의 엔트리가 존재
엔트리에는 물리적 메모리에 어떤 페이지 프레임에 올라가있는지 주소 변환 정보가 들어가 있다.
엔트리에는 주소 변환 정보뿐만 아니라 부가적인 비트도 저장되어있다..
부가적인 비트에는 valid-invalid bit, Protection bit가 있다.
페이지 테이블의 엔트리는 인덱스로 접근해야 하기 때문에 사용하지 않는 부분까지도 페이지 테이블이 만들어져야 하고, 사용하지 않는 경우엔 valid-invalid bit 가 invalid로 표시가 된다
valid-invalid bit
valid: 해당 주소 프레임에 그 프로세스를 구성하는 유효한 내용이 있다 (접근 허용)
invalid: 해당 주소 프레임에 그 프로세스를 구성하는 유효한 내용이 없다 (접근 불허)
- 프로세스가 그 주소 부분을 사용하지 않는 경우
- 해당 페이지가 메모리에 올라와 있지 않고 backing store(swap area)에 있는 경우
Protection bit
page에 대한 접근 권한 (read / write / read-only)
=> 코드 영역같은 경우엔 read-only이다 실행하다가 중간에 바뀔 일 없이 실행만 되기 때문....
=> 데이터 영역은 중간에 값을 쓰고 지울 수 있기 때문에 read write 권한이 있을 것임~
페이지 테이블은 프로세스마다 독립적으로 존재
따라서 다른 프로세스가 내 페이지 테이블을 접근하는건 애초에 불가능함.
자신의 페이지 테이블만 접근 가능함
Inverted Page Table
페이지 테이블이 메모리에서 많은 용량을 차지한다는 것이 문제다
공간 오버헤드가 컸다..
페이지 테이블 엔트리가 필요했기 때문
=> Inverted Page Table가 나오게 됨.
시스템 안에 페이지 테이블이 딱 1개가 존재한다.
페이지 테이블 엔트리가 프로세스의 페이지 개수만큼 존재하는 것이 아니라
물리적인 메모리의 페이지 프레임 개수만큼 엔트리가 존재한다
역방향 페이지 테이블
페이지 프레임에 f번쨰 엔트리를 가면 논리적인 페이지 번호가 들어있는 형태
주소변환이란 logical address -> physical address로 바꾸는 것임
역방향 페이지 테이블( Inverted Page Table )은 physical address을 보고 logical address 를 얻을 수 있는 테이블이다.
페이지 테이블이 주어지면 엔트리를 전부 검색해야지만 주소 변환을 할 수 있다
=> 페이지 테이블을 위한 공간을 줄이고자 하는 것.. 문제는 시간적인 오버헤드가 있다
한번에 n번째로 찾아가는게 아니라 전부 검색해야지만 페이지테이블의 어디에 위치하는지를 알 수 있기 때문..
페이지테이블엔 어떤 프로세스인지 모르기 때문에 pid도 같이 저장된다
associative register(별도의 하드웨어)를 사용해서 병렬적으로 동시에 검색할 수 있게 해서 탐색하는데 발생하는 시간 오버헤드를 줄일 수 있다..
associative register 찾아보는데 어렵다
Shared Page
다른 프로세스들과 공유할 수 있는 페이지
Shared Code - 같은 코드를 쓸 경우엔 같은 물리적인 메모리의 프레임으로 매핑을 시켜서 메모리에 하나만 올릴 수 있음
Re-entract Code (재 진입 가능 코드, Pure code)
read-only로 해서 하나의 code만 메모리에 올림
Shared code는 모든 프로세스의 logical address space에서 동일한 위치에 있어야 함
(동일한 물리주소를 갖는건 당연한거고 동일한 논리주소를 가져야 함, 논리공간에서 1번째에 위치한 코드면 다른 프로세스에서도 1번째에 코드가 위치해야 한다는 것)
물리적 메모리 안에 있는 코드에는 논리주소가 적혀있다
따라서 shared code는 동일한 논리주소를 가져야 한다
shared memory는 프로세스간 통신을 목적으로 (IPC) 커뮤니케이션을 목적으로 하는 것( shared memory 는 뭐였더라 )
shared code는 read-only로 해서 다른 프로세스에게 영향을 미치는 문제를 배제한 코드이다.(코드 공유가 목표)
페이징 기법: 프로그램을 구성하는 주소공간을 같은 크기의 페이지 단위로 쪼갬
Segmentation 기법: 프로그램을 구성하는 주소공간을 의미단위로 쪼갬 (예시 - 코드, 데이터, 스택)
Segmentation
세그먼트 번호, 오프셋(세그먼트 안에서 얼마나 떨어져있는지 나타냄)
각 세그먼트 별로 서로 다른 물리적인 메모리 주소에 위치할 수 있기 때문에 세그먼트별로 주소변환을 해야함
=> Segment table
● Segment-table base register(STBR): 세그먼트 테이블의 시작위치
● Segment-table length register(STLR): 세그먼트 테이블의 길이(=엔트리의 수 )
세그먼트 테이블의 페이지 엔트리
세그먼트 테이블의 길이(=엔트리의 수)를 나타내는 이유는 페이징 기법에서는 페이지 크기가 다 동일했기 때문에 필요없었음
Segmentation 기법은 의미 단위로 자르는 것이기 때문에 세그먼트의 길이가 균일하지 않을 수 있기 때문!!
크기가 균일하지 않기 때문에 가변분할 방식에서와 동일한 문제점들이 발생함,
외부 조각이 발생하는 단점이 있다..
세그먼트는 의미단위로 쪼개기 때문에 의미단위로 일을 할 때는 효과적이다.
세그멘테이션은 의미단위로 쪼개기 때문에 의미 단위로 처리해야 하는 일인 Protection과 Sharing 면에서는 페이징보다 더 좋지만
크기가 균일하지 않기 때문에 Allocation 면에서는 페이징보다 좋지 않고 외부 조각이 발생한다.
### Memory Management 4
세그먼테이션에선 길이와 비교해서 잘못된 메모리 접근을 하는 것인지 확인해서 정상적일 때만 주소변환을 해주고 비정상적인 접근이면 트랩을 건다
=> 페이지처럼 동일한 크기가 아니라 STLR 레지스터의 길이와 비교함
페이지는 개수가 많다
세그먼트는 개수가 페이지에 비하면 적다
테이블에 따른 메모리 낭비를 따지면 페이징 쪽이 낭비가 크다..
Paged Segmentation
세그먼테이션과 페이징을 혼합한 방법
세그먼트를 여러개의 페이지로 구성하는 기법
세그먼트 하나가 여러개의 페이지로 구성됨..
세그먼트당 페이지 테이블이 존재함
메모리에 올릴땐 페이징 기법을 써서 올라감
프로세스가 논리적인 주소를 갖고 있고
CPU가 논리주소를 주면 물리적인 메모리 주소로 변환해서
메모리 참조를 하게 된다
주소변환에 있어서 운영체제의 역할? => X,, 운영체제가 하는 일은 한개도 없다
다 하드웨어가 해줘야 하는 역할이다.
I/O장치를 접근할 때는 운영체제가 필요함.. 사용자 프로세스가 직접 IO접근을 못하기 때문
메모리를 접근할 때는 운영체제가 필요하지 않음
운영체제도 하나의 프로그램이기 때문에 CPU를 잡았을 때 처리하는 것은 다른 프로그램과 다르지 않다.
사실상 이 챕터에선 운영체제의 역할은 없었다!
다음챕터인 Virtual Memory 에선 운영체제가 중요한 역할을 하게 된다
<용어> 서브루틴함수, 심볼테이블
강의: http://www.kocw.net/home/cview.do?mty=p&kemId=1046323
Two-Level Page Table 에서 계산 나오는게 헷갈린다*
'운영체제' 카테고리의 다른 글
윈도우 자바 환경변수 설정 방법과 이유 (Feat.리눅스) (0) | 2024.10.06 |
---|---|
[OS 7주 스터디🍀] 9. Virtual Memory (0) | 2024.08.18 |
[OS 7주 스터디🍀] 7. Deadlock (0) | 2024.08.04 |
[OS 7주 스터디🍀] 6. Process Synchronization (0) | 2024.08.04 |
[OS 7주 스터디🍀] 5. CPU Scheduling (1) | 2024.07.28 |