mmap(2)에 있는 2의 의미 : 시스템 콜이라는 뜻
Virtual Memory : 물리 메모리 공간의 제약, 메모리 단편화, 보안을 위해 OS가 프로세스에게 제공하는 주소. 이를 통해 프로세스에게 자신이 메모리를 독점하고 있다는 환상을 제공한다.
Page Table : 각 가상 메모리 주소가 어떤 물리 메모리 주소에 매핑돼있는지 적혀있는 테이블. 그 밖에도 읽기 권한 등 해당 페이지에 대한 정보가 적혀있다.
Translation Lookaside Buffer(TLB) : MMU(Memory Management Unit)가 가상 주소를 물리 주소로 변환할 때 TLB라는 캐시에서 변환 정보를 읽어온다.
Page Fault : 메모리는 페이지 단위로 나뉜다. 프로세스가 메모리에 접근하면 가상 주소가 물리 주소로 변환된다. 주소는 페이지 단위로 요청한다. 이때 요청한 페이지가 메모리의 물리 메모리에 없고 디스크(스왑 영역)에 있다면 페이지 폴트가 발생한다. 페이지 폴트 발생하면 Trap 발생함.
Trap : 오류 발생하거나 시스템 콜 요청하면 일어남. 커널 모드로 전환돼서 트랩 핸들러 실행.
스왑 공간 : 물리 메모리 꽉 찼을 때 메모리 데이터를 디스크에 임시로 저장하는 공간
https://www.geeksforgeeks.org/swap-space-in-operating-system/
Major Page Fault: 페이지가 물리 메모리에 없고 디스크의 스왑 영역에 있는 경우. Disk I/O가 발생하여 성능에 영향을 미침.
Minor Page Fault: 페이지가 물리 메모리에 있으나, 페이지 테이블에 반영되지 않은 경우. Disk I/O 없이 페이지 테이블만 업데이트함.
Invalid Page Fault: 잘못된 메모리 참조 시 발생하며, 세그멘테이션 폴트가 발생할 수 있음.
Lazy Loading : 웹 개념인듯. 처음 웹페이지 로딩할 때 유저가 필요한 것만 로딩하고, 나중에 요청하면 해당하는 부분을 마저 로딩하는 거.
Demand Paging : 시작할 때 물리 메모리에 프로그램을 전부 다 로드하는게 아니라 요청할 때 필요한 페이지만 로드하는거. 그래서 처음 로딩할 땐 페이지 폴트난다. 물리 메모리 주소 공간 아낄 순 있는데 disk I/O 때문에 성능이 느려진다.
https://www.geeksforgeeks.org/what-is-demand-paging-in-operating-system/
페이지 교체 알고리즘
Belady’s Anomaly : 페이지 수 늘어나면 페이지 폴트 횟수도 늘어난다
Anonymous page : 파일로 백업되지 않아서 파일과 연결되지 않고 순수하게 메모리만 사용하는 페이지. 힙과 스택 같은 동적 메모리 할당에서 사용됨. 파일과 관련이 없기 때문에 물리 메모리가 꽉 차면 익명 페이지는 스왑 공간으로 간다. (파일 매핑된 이미지는 파일에서 다시 읽어오면 됨)
https://stackoverflow.com/questions/13024087/what-are-memory-mapped-page-and-anonymous-page
DMA (Direct Memory Access) : 입출력 장치가 CPU를 거치지 않고 메모리에 직접 접근하여 데이터를 전송하는 기능. 사실 CPU가 아예 개입 안 하는건 아니고 DMA 컨트롤러한테 어디 가서 얼마만큼 전송하라고 명령은 내린다.
초기 컴퓨터는 물리 메모리 주소 일정 부분은 운영체제, 일정 부분은 현재 프로그램을 담음.
나중에 시분할 구현하긴 했는데 하나의 프로세스를 짧게 실행시킨 다음 죄다 디스크(물리 메모리 포함)에 적재하고 또 다음 거 실행하고 죄다 적재해서 느렸다. (요즘은 필요한 것만 왔다갔다)
대화식 이용(interactivity) : 현재 실행 중인 작업으로부터 즉시 응답
프로세스가 다른 프로세스 안 터트리게 하려고 주소 공간 만들었다.
프로그램 코드는 반드시 메모리에 있어야 되고
스택은 함수 호출 체인 상의 현재 위치, 지역 변수, 함수 인자와 반환 값 등을 저장. 높은 주소에서 낮은 주소로 자란다.
힙은 동적으로 할당되는 메모리. 낮은 주소에서 높은 주소로 자란다.
(근데 이것들은 가상 주소고 실제로는 임의의 물리 주소에 적재됨)
물리 메모리를 공유하는 다수의 프로세스에게 프로세스 전용의 커다란 주소 공간이라는 개념을 제공하는 방법 : 야 이거 네꺼야 하고 가상 메모리 주소 공간 준 다음에 실제로는 그렇게 예쁘게 안 주고 맘대로 물리 메모리 할당했다가 할당 취소했다가 함.
격리 : 한 개체가 실패하더라도 상대 개체에 아무 영향을 안 준다
가상 메모리 주요 목표
UNIX 메모리 관리 인터페이스 알아볼 것
C 프로그램이 실행되면 두 가지 유형의 메모리 공간이 할당됨.
첫번째는 스택임. 함수 안에서 int x;
선언하면 정수 x를 위한 메모리 선언됨. 함수가 리턴하면 컴파일러가 메모리 반환해줌. 함수 리턴 이후에도 유지돼야 하는 정보는 스택에 저장하지 않는게 좋음.
두번째는 힙임. 오랫동안 값이 유지돼야 하는 변수를 위한 공간임. 모든 할당과 반환이 프로그래머에 의해 명시적으로 처리됨.
malloc() : 바이트 단위로 필요 공간 크기 적어줘야 됨. sizeof() 함수 써라. 문자열을 위한 공간 선언할 땐 +1 해줘야 문자열 끝 나타내는 문자도 저장됨. void * 타입을 반환하므로 프로그래머가 필요한 타입으로 변환해줘야됨.
자주 저지르는 실수
malloc()이랑 free()는 시스템 콜은 아니고 라이브러리 함수임.
시스템 콜은 brk 같은 거임. 프로그램의 break 위치를 변경하는 데 사용됨.
break는 힙의 마지막 위치를 나타냄.
brk는 새로운 break 주소를 나타내는 한 개의 인자를 받고 그거대로 힙 크기를 변경함.
sbrk는 증가량만 받아들임.
brk나 sbrk 직접 호출하지는 마라. 큰일난다.
mmap() 함수로 운영체제로부터 메모리를 얻을 수도 있다.
프로그램에 anonymous 메모리 영역을 만든다.
anonymous 영역은 특정 파일과 연결되어 있지 않고 스왑 공간에 연결된 영역을 말한다.
base and bound
1950년대 후반 첫 번째 시분할 컴퓨터에서 사용됨
물리 주소 = 가상 주소 + 베이스 주소
프로세스가 실행을 시작한 이후에도 주소 공간을 이동하는 동적 재배치(dynamic relocation) 사용함.
바운드 레지스터는 메모리 참조가 합법적인가를 확인한다.
프로세스 구조체(process structure), 프로세스 제어 블럭(process control block, PCB) : 베이스와 바운드 레지스터 값 저장
내부 단편화 : 남아있는 메모리가 충분한데 연속적으로 할당 안돼있어서 할당 못해서 공간 낭비되는거
base and bound 일반화시킨게 세그멘테이션
# 각 자리수를 순회하며
# 1. 1이라면 나머지 자리수에 2023 꽂아넣는 경우의 수
# 2. 2라면 (나머지 자리수에 023 꽂아넣는 경우의 수) + (나머지 자리수에 2023 꽂아넣는 경우의 수)
# 3. 3이상이라면 (나머지 자리수에 023 꽂아넣는 경우의 수) + (자기 자신 - 1) (나머지 자리수에 2023 꽂아넣는 경우의 수)
# 다 더한게 경우의 수
# 숫자가 들어갈 자리 3개만 고르면 순서는 알아서 정해지므로 cntC3 구하면 됨
# cntC3 = cnt(cnt-1)(cnt-2)/6
# 4개면 cntC4 = cnt(cnt-1)(cnt-2)(cnt-3)/12
# (나머지 자리수에 023 꽂아넣는 경우의 수)에 9**(cnt-4) 곱해줘야됨
# (나머지 자리수에 2023 꽂아넣는 경우의 수)에 9**(cnt-5) 곱해줘야됨
N=input()
L=len(N)
cnt=len(N)
res=0
while cnt>3:
now=L-cnt
case1=(cnt-1)*(cnt-2)*(cnt-3)*(cnt-4)/12 * (9**(cnt-4)) # 나머지에 2023
case2=(cnt-1)*(cnt-2)*(cnt-3)/6 # 나머지에 023
if not (cnt == 4):
case2*=9**(cnt-5)
if N[now]=='1':
res+=case1
elif N[now]=='2':
res+=case1
res+=case2
else:
res+=case1
res+=(int(N[now])-1)*case2
cnt-=1
print(int(res))
1번째 시도
# 각 자리수를 순회하며
# 1. 1이라면 나머지 자리수에 2023 꽂아넣는 경우의 수
# 2. 2라면 (나머지 자리수에 023 꽂아넣는 경우의 수) + (나머지 자리수에 2023 꽂아넣는 경우의 수)
# 3. 3이상이라면 (나머지 자리수에 023 꽂아넣는 경우의 수) + (자기 자신 - 1) (나머지 자리수에 2023 꽂아넣는 경우의 수)
# 다 더한게 경우의 수
# 숫자가 들어갈 자리 3개만 고르면 순서는 알아서 정해지므로 cntC3 구하면 됨
# cntC3 = cnt(cnt-1)(cnt-2)/6
# 4개면 cntC4 = cnt(cnt-1)(cnt-2)(cnt-3)/12
# (나머지 자리수에 023 꽂아넣는 경우의 수)에 9**(cnt-4) 곱해줘야됨
# (나머지 자리수에 2023 꽂아넣는 경우의 수)에 9**(cnt-5) 곱해줘야됨
N=input()
L=len(N)
cnt=len(N)
res=0
while cnt>3:
case=(cnt-1)*(cnt-2)*(cnt-3)/6 # 나머지에 023
case*=(9**(cnt-4))
temp = (int(N[L-cnt])-1)*case
res+=(int(N[L-cnt])-1)*case
cnt-=1
print(int(res))
2번째 시도
1시간 가까이 지나서 일단 마무리. 핀토스 해야됨.
내일 마저 풀어볼 것.