체크포인트

설계 전 프로세스 관리 모델 학습

  • 스택 영역
  • 힙 영역
  • 텍스트 영역
  • 글로벌변수 GVAR

함수 설계 및 정리

  • 스택 영역
  • 힙 영역
  • 텍스트 영역
  • GVAR 영역

프로세스 메모리 구조

https://i.imgur.com/ZxcILSe.png

스택 영역

  • 프로그램이 자동으로 사용하는 영역으로, 호출된 함수의 수행을 마치고 복귀할 주소 및 데이터(지역변수, 매개변수, 리턴값 등)를 임시로 저장하는 공간
  • 프로세스가 메모리에 로드될 때 스택 사이즈는 고정되며 함수 호출 시 생성되고, 함수 반환 시 소멸
  • 스택사이즈가 고정이므로 재귀함수가 너무 깊거나, 함수 지역변수가 메모리를 초과하면 Stack Overflow가 발생
  • 영역이름대로 스택 자료구조를 사용하여 LIFO(후입선출)의 형식이다.
  • 명령 실행 시 자동으로 증가 또는 감소하기 때문에 보통 메모리의 마지막 번지를 지정한다
  • data, stack 영역의 크기를 계산해 메모리 영역을 결정

힙 영역

  • 프로그래머가 필요할 때마다 사용하는 동적인 영역
  • 런타입에 크기 결정
  • 메모리 주소 값에 의해서만 참조되고 사용됨
  • 위의 스택과 같은 공간을 공유하며, 힙이 낮은 주소부터 할당되는데 각 영역이 상대공간을 침범하면 Stack Overflow, Heap Overflow가 발생한다.
  • stack 에서 변수 할당 → pointer가 가리키는 heap 영역의 임의의 공간부터 원하는 크기만큼 할당해 사용
  • 힙 영역은 메모리의 낮은 주소에서 높은 주소의 방향으로 할당
  • 더 이상 해당 힙 영역을 참조하는 변수가 없을 경우 소멸되며, free() 함수로 나중에 할당했던 영역을 반납해야 한다.

데이터 영역

  • 전역변수, 정적변수, 배열과 같은 구조체 등이 저장되는 공간
  • BSS영역과 GVAR영역으로 나뉨
    • BSS영역
      • 초기화되지 않은 데이터 저장되는 영역
    • BVAR
      • 초기화된 데이터가 저장되는 영역

텍스트 영역

  • 코드 영역이라고도 불리며, 말그대로 코드가 저장되는 공간
  • 사용자가 작성한 프로그램 코드가 CPU에서 수행할 수 있는 기계어 명령 형태로 변환되어 저장되어 있음
  • 컴파일 타임에 결정되고 중간에 코드를 바꿀 수 없게 Read-only로 되어있음

프로그램 영역

  • STACK 영역 (512KB) : 어셈블리 코드를 실행하면서 호출할 때마다 필요한 값을 스택 방식으로 Push하거나 Pop하는 영역 ◦ 0x00000 부터 0x7FFFF 까지
    • 상수로 1024 * 512 = 524,288 을 할당해놓고 만약 스택의 length가 이를 넘으면 stackoverflow 에러 추가
  • HEAP 영역 (512KB) : 어셈블리 코드를 실행하면서 메모리 할당 요청이 있을 때 사용할 공간을 확보하는 영역 ◦ 0x80000 부터 0x100000
  • TEXT 영역 (문자열배열) : 입력으로 주어지는 어셈블리 코드가 저장되는 영역 ◦ 0x100000부터 명령어 한 줄(배열 인덱스 1개)이 4바이트씩이라고 가정한다.
    • int32Array가 각 요소를 4바이트로 잘라서 저장하므로 한 요소에 명령어 한줄이 있으면 된다.
    • 참조할 때도(포인터를 쓸 때도) 4바이트 기준으로 하면 된다.

다음과 같이 동작하는 함수를 포함하는 Simulator 객체를 구현한다.

  • TEXT, STACK, HEAP 영역은 크기에 맞게 미리 할당한다.
  • TEXT 영역에서 몇 번째 명령을 실행중인지 PC(Program Counter) 변수를 사용한다.
  • STACK 영역을 위해서 Stack Pointer변수를 두고 관리한다.HEAP 영역을 관리하는 방식은 스스로 판단한다.
  • 이번 미션에서는 일반적인 프로세스 메모리 모델(배경 지식 참조)중에서 텍스트, 스택과 힙 영역을 위주로 구현한다. 다른 영역은 무시한다.

Int32Array - JavaScript | MDN

예시 분석을 통한 이해

setSize("INT", 4)
setSize("BOOL", 1)
locate("main", ["VAR A: BOOL[4]", "VAR B: INT", "CALL foo()", "SET B=$RETURN"])
locate("foo", ["VAR K: INT", "RETURN 10"])
step()
usage()
step()
step()
heapdump()
step()
callstack()
step()
gabageCollect()
reset()
  • setSIze(”INT”,4)

    • simulator.types[’INT’] =4
  • setSize(“BOOL”, 1)

    • simulator.types[’BOOL’] = 1
  • locate(“main”, [“VAR A: BOOL[4]”, “VAR B: INT”, “CALL foo()”, “SET B=$RETURN”])

    let Text = {
       codes: ["VAR A: BOOL[4]", "VAR B: INT", "CALL foo()", "SET B=$RETURN"],
       functions: [
            {
            name: main,
            variant:{
    	        A : INT,
    	        B: INT
            }
            최종위치: 4
            }
         }
      ]
    }
  • locate(“foo”, [“VAR K: INT”, “RETURN 10”])

let Text = {
   codes: ["VAR A: BOOL[4]", "VAR B: INT", "CALL foo()", "SET B=$RETURN",
   "VAR K: INT", "RETURN 10" ],
   pc: 0, //몇번째 명령 실행중인지
   functions: [
        {
        name: main,
        variant:{
	        A: BOOL,
	        B: INT
        }
        최종위치: 4
        }, {
	      name: foo,
	      variant : {
		      K: INT
	      }
	     }
     }
  ]
}
  • step()

    • pc주소에 있는 코드 실행(PC 초기값인 0) → Text.codes[pc]를 실행

    • VAR A: BOOL[4] 파싱해서 → malloc(BOOL, 4)를 실행

    • BOOL 1 → 패딩 붙여서 8로 바꾸고 → 8*4 = 32 → HeapLoc + 32 = 32

      let info = {
      	type: BOOL, // 타입
      	address: HeapLoc(= 0), // 힙에서의 주소
      	allocatedLength:32, // 할당된 길이
      	sp: 0 // 연결된 stackPointer
      }
    • heap.heapMemory.push(info)

    • stack.locMemory.push(info.address)

    • stack.stackPointer += 1

    • text.px++

  • usage()

    • 스택 영역 전체크기, 사용중인 용량, 남은 용량과 힙 영역 전체크기, 사용중인 용량, 남은 용량을 순서대로 배열로 리턴
    • [stack.max, stack.locMemory.length *4, stack.remain, heap.max, heap.heapLoc, heap.remain]
  • step()

    • Text.codes[pc]를 실행 → VAR B: INT

    • malloc(INT, 1)

    • INT 4 ⇒ 패딩 붙여서 8로 바꿈 → HeapLoc + 8 = 40

      let info = {
      	type: INT, // 타입
      	address: HeapLoc, // 힙에서의 주소
      	allocatedLength: 40, // 할당된 길이
      	sp: 1 // 연결된 stackPointer
      }
    • stack.locMemory.push(40) ⇒ stack = [34, 40]

    • stack.stackPointer += 1 ⇒ 2

    • text.px ++

  • step()

    • Text.codes[pc]를 실행 → CALL foo()
    • CALL ⇒ 마지막 PC를 스택에 push
    • 마지막 pc = 2를 스택에 push → stack = [34, 40, 2], stackPointer = 3
    • FuncName에 해당하는 함수 주소로 PC를 옮기고 실행
      • PC = foo의 주소로
      • VAR K: INT → malloc(INT, 1) → Heaploc + 8 = 48 → stack = [34, 40, 48] → stackPointer = 4
    • 호출에 대해 기록하고 callstack()에서 활용한다.
      • 호출에 대해 기록?????????
  • heapdump()

    • 힙에 있는 상태를 문자열 배열로 표현해서 return

    • 힙 영역 정보는 타입과 주소, 할당한 길이, 해당 주소를 참조하는 스택 포인터 변수 정보도 포함한다.

    • 힙 영역 정보?

      • 타입과 주소
      • 할당한 길이
      • 해당 주소를 참조하는 스택 포인터 변수 정보

      스택은 메서드와 함수 프레임, 원시 값, 객체 포인터를 포함한 정적 데이터가 저장되는 곳이다

함수 및 데이터구조 설계

Simulator 객체

let Simulator = {
		pointer : 0,
		types : {
			boolean: 4
		},
		pc:,
    text : {
	    codes: [코드들],
	    functions: [
		    {
			    이름: 함수이름,
			    최종위치: codes에서의 최종위치
		    }
	    ]
    }
};
 
const setSize = (type, length) => {
    Simulator[type] = length;
}
 

Text 객체

let Text = {
   codes: ["VAR A: INT", "VAR B: INT", "CALL foo()", "SET B=$RETURN"]
    functions: [
        {
        name: main,
        address: 0x100000
        } , {
       name: ‘foo’,
       address : 0x100000 + (4 * 4)
     }
  ]
}
    
 

Heap 객체

let heap = {
	max: 1024 * 512,
	heapLoc: 0,
	get remain() {
		return this.max - this.locMemory.length
	},
	heapMemory: [{
	{ 
		할당된 길이: 32
		type: BOOL
		address: heapLoc + mallocByte (힙 주소)
		sp: 0
		value: null
		}
},]
}

Stack 객체

let stack = {
	max: 1024 * 512,
	locMemory : Int32Array(),
	stackPointer = 0,
	get remain() {
		return this.max - this.locMemory.length
	}
}

개발자 원칙 P38 “동기를 관리할 때 또 신경써야 하는 것은 에너지입니다.” → 우리가 미션을 해결하기 위해 열심히 노력하는 것도 좋지만, 계속해서 밤을 새어 가며 하는 것은 몸도 망치고 나중에 가서 힘이 빠질 가능성이 있다. → 미션을 해결하는데 힘을 두기 보단 집중과 휴식의 밸런스를 맞춰야 한다고 생각한다. ✔️ 퀘스트 : 미션을 해결하면서 중간중간 쉬면서 생각을 정리하는 시간 가지기 ✔️ 수행 기준

  • 산책하기 : 하루종일 앉아서 코딩만 하면 힘들테니 4시간 당 최소 30분 산책하기
  • 인증하기 : 산책하고 인증샷 찍기
  • 생각 기록하기 : 오늘 하루는 어땠는지 다시금 생각하며 쌓인 생각들 정리하고 기록하기

✔️  퀘스트 : 과제를 진행하기 전에 과제를 세분화 하여 시간계획을 세운 뒤 과제 진행하기

✔️ 의도 : 계획을 짜는 것 또한 개발자의 중요한 능력이다. 계획을 짜며 본인의 능력치를 파악해보자

✔️ 수행 기준

  • 계획 하기 : 체크리스트 작성, 사전 지식 학습 , 구현, readme작성 등 세부 Task를 정하여 Task 당 할당 시간을 정한다.
  • 수행 하기 : 각 Task들에 얼마나 시간이 소요되었는지 측정한다. (최대한 계획한것과 비슷하게 수행한다.
  • 분석하기 : 계획과 수행 사이에서 발생한 시간 차이가 왜 났는지 분석한다.