※  Linux 기본명령어

※  Linux  권한 관련 기본명령어 

 

 

 

 

 

 

 

※  Linux 기본명령어

 

※ Memory 참조 버그 예제

typedef struct {
    int a[2];
    double d;
} struct_t;

double func(int i) {
    volatile struct_t s;
    s.d = 3.14;
    s.a[i] = 107374824;
    return s.d;
}

 

 

cf. volatile 변수 소개

C/C++ 프로그래밍 언어에서 최적화 등 컴파일러의 재량을 제한하는 역할을 한다.
개발자가 설정한 개념을 구현하기 위해 코딩된 프로그램을 온전히 컴파일되도록 한다.
주로 최적화와 관련하여 volatile가 선언된 변수는 최적화에서 제외된다.
OS와 연관되어 장치제어를 위한 주소체계에서 지정한 주소를 직접 접근하는 방식을 지정할 수도 있다.
Linux Kernel 등의 OS에서 메모리 주소는 논리주소와 물리주소 간의 변환이 이루어진다.
경우에 따라 이런 변환을 제거하는 역할을 한다. 또한 원거리 메모리 점프 기계어 코드 등의 제한을 푼다.
C언어의 경우, 주로 메모리 맵 입출력(MMIO)을 제어할 때,
volatile을 선언한 변수를 사용하여 컴파일러의 최적화를 못하게 하는 역할을 한다.
static int foo;

void bar(void) {
    foo = 0;
    while (foo != 255);
}
foo의 값의 초기값이 0 이후, while 루프 안에서 foo의 값이 변하지 않기 때문에 while의 조건은 항상 true가 나온다. 따라서 컴파일러는 다음과 같이 최적화한다.
void bar_optimized(void){
    foo = 0;
    while (true);
}

이렇게 되면 while의 무한 루프에 빠지게 된다. 이런 최적화를 방지하기 위해 다음과 같이 volatile을 사용한다.

static volatile int foo;

void bar (void) {
    foo = 0;
    while (foo != 255);
}
이렇게 되면 개발자가 의도한 대로, 그리고 눈에 보이는 대로 기계어 코드가 생성된다.
이 프로그램만으로는 무한루프라고 생각할 수 있지만, 만약 foo가 하드웨어 장치의 레지스터라면 하드웨어에 의해 값이 변할 수 있다.
따라서 하드웨어 값을 폴링(polling)할 때 사용할 수 있다.

 

※ Memory 참조 버그 (= Buffer Overflow)의 심각성

배열에 할당된 크기 이상의 메모리를 접근할 때 주로 발생한다.

가장 빈번하게 발생하는 보안 취약성의 원인이 된다.

§ 가장 일반적인 형태로는 다음과 같다.
- string 입력의 길이를 check하지 않은 경우 
- stack에 생성되는 제한된 길이의 문자배열

 

§ UNIX의 gets 함수 (키보드를 관리해주는 라이브러리)

/* Get string from stdin */

char *gets (char *dest) {
	int c = getc();
    char *p = dest;
    while (c != EOF && c != '\n') {
    	*p++ = c;
        c = getc();
	}
    *p = '\0';
    return dest;
}
cf. 유사한 문제
- strcpy: 임의의 길이의 string을 복사
- scanf, fscanf, sscanf 함수를 %s와 함께 사용하는 경우

 

 

§ 위험한 buffer 코드

/* Echo Line */
void echo() {
	char buf[4];
    gets(buf);
    puts(buf);
}


int call_echo () {
	printf("Type a string: ");
    echo();
    return 0;
}

 

§ stack 상태

 

 

 

 

§ Buffer Overflow를 피하는 방법

※ string의 길이를 제한하는 라이브러리를 사용하면 된다! ( buffer 주소를 주면서 buffer크기를 check)

gets대신 fgets를 사용  
strcpy대신 strncpy를 사용
scanf를 %s와 함께 사용하지 않는다.  ( fgets를 사용한다. )
/* Echo Line */

void echo() {
	char buf[4];
    fgets(buf, 4, stdin);
    puts(buf);
}

※ procedure 목차

1. 스택의 구조

2. 호출의 관습

  - 제어의 전달

  - 데이터의 전달

  - 지역 데이터의 관리

3. 재귀실행

 

§ Procedure 소개

프로시저(procedure)는  특정 작업을 수행하는 서브 프로그램으로 자주 사용하기 위해 생성하는 메서드 같은 것이다.

이런 프로시저의 실행에는 3가지 유형이 있다.

 

§ 프로시저 실행의 유형 (모든 동작은 기계어로 구현된다.)

1. 제어의 전달

  ▷ 프로시저 코드의 시작부분으로 리턴지점으로 돌아간다.

2. 데이터의 전달

  ▷ 프로시저 인자

  ▷ return 값

3. 메모리 관리

  ▷ 프로시저 실행 중 할당하며 return시 반환한다.

 

 

 

 

 

 

 

※ procedure _ 2. 호출의 관습

§ x86-64 스택

- 메모리 영역으로 register %rsp는 가장 작은 스택주소를 저장.

 

 

 

 

§ procedure 제어흐름

▶ procedure 호출

stack frame을 이용해 procedure호출과 return을 지원하며 아래와 같은 호출 방법을 사용한다.

call Label

- Step 1: return 주소를 stack에 push

- Step 2: Label로 jump

cf) return 주소란?
call 바로 다음 줄의 명령어 주소로 아래 예시를 들자면, 400549가 바로 return 주소이다.

400544:  callq  400550  <mult2>
400549:  mov  %rax,  (%rbx)

 

▶ procedure 반환

stack frame을 이용해 procedure반환을 진행하는데, stack에서 return주소의 pop을 진행하며 아래와 같은 방법 사용하며, 이 return 주소로 jump한다는 의미이다.

ret

 

 

 

 

 

§ procedure 데이터의 흐름

이때, Stack Frame은 필요할 때에만 할당한다.

 

 

 

§ procedure 지역 데이터의 관리

▶ Stack Frame 기반 언어 = 재귀호출을 지원하는 언어로 C, Java와 같은 언어들을 예시로 들 수 있다.

- "Reentrant", 즉 재진입해야하는 코드로 각 실행개체의 상태를 저장할 장소가 필요하기 때문이다.

 

Ex. Recursion의 Stack Frame

 

 

▶ x86-64 / Linux 의 Stack Frame 

 

Ex-1).  incr 함수

 

Ex-2).  incr 함수

- incr #1

subq	$16,  %rsp         #rsp-16 = stack이 커진다 = stack frame할당으로 공간확보
movq	$15213,  8(%rsp)   #15213을 v1에 copy

- incr #2

movl	$3000,  %esi         # rsi에 3000을 넣음
leaq	8(%rsp), %rdi        # rdi에 주소 할당

- incr #3  (15213 + 3000을 더해 18213값이 들어감)

 

- incr #4

addq	8(%rsp),  %rax         # 현재 incr의 return값 rax는 v2에 저장되어 있어서 v1+v2를 다음과 같이 진행
addq	$16, %rsp              # pop을 진행

 

 

 

 

▶ register 보관관습 => stack frame에 필요한 register를 push해 저장함

- 똑같은 register를 다른 함수에서 사용가능해서 return 후 다시 register를 복원한다.

- 아래 예시를 보면 두 개 모두 %rdx라는 중복된 register를 사용한다.

 

때문에 compiler에 따라 다음 2가지 방식으로 구현할 수 있다.

 

 

 

 

 

 

§ 재귀 실행

Ex)

/*-----------Recursive popcount-----------*/
long pcount_r (unsigned long x) {
    if (x == 0)
        return 0;
    else
        return (x & 1) + pcount_r(x >> 1);
}

 

 

 

 

 

 

 

 

 

※ processor 상태

실행되는 cpu의 상태정보를 register에 저장한다.

이때, test 결과를 의미하는 code를 Condition code라 부른다.

 

§ Condition Codes

- 모두 1bit로 0이나 1만 저장되며 flag register라고도 불린다.

- Condition code를 setting, 즉 flag register 값을 바꾸는 방법은 다음과 같다.

 

1. 산술연산 [간접 setting] (단, 이때 leaq명령어로는 값이 바뀌지는 않는다.)

- ex)  addq  Src, Dest  ↔  t = a + b

 

 

2. 비교 명령어 [직접 setting]

cmpq  Src2,  Src1   로 표기하며 이때, Src1 - Src2를 계산하여 비교한다.

(단, Src1 - Src2의 결과를 저장하지는 않는다.)

 

 

3. test 명령어 [직접 setting]

testq  Src2,  Src1   로 표기하며 이때, Src1 & Src2 연산을 진행하여 비교한다.

(단, Src1 & Src2의 결과를 저장하지는 않는다.)

 

 

§ Condition Codes값 읽어오기  (feat.  SetX 명령)

§  SetX  ByteReg 

- cmp 명령 실행 후에 적용된다.

- ByteReg의 하위 byte를  condition code (flag register)의  조합 X에 따라  0이나 1로 설정

- 이때, quad와 같은 경우 ByteReg의 나머지 7 byte는 변경이 없다.

즉, %rsp를 예로 들면 최하위 1byte인 %spl을 이용해 0이나 1로 설정한다.

Q. quad를 예로 들면, 그렇다면 1byte를 제외한 나머지 7byte는 어떻게 해야할까?
A. movzbl을 이용해 mov를 이용해 copy 후 나머지 나머지 7byte는 0으로 padding 해준다.

 

Ex. C code  ->  assembly

int gt (long x, long y) { 
    return x > y;
}

 

 

 

 

 

 

 

※ 점프 (jump)

§  jX  Label 

이때, Label은 assembly에서 위치를 나타내며 특정 label로 jump해서 fetch하게 해주는 것이다.

- 조건코드에 따라서 코드의 실행위치를 이동한다.

 

§ 조건부 분기예제  (Old Style)

long absdiff (long x, long y) {
    long result;
    if (x > y)
        result = x - y;
    else
        result = y - x;
    return result;
}

 

 

§ 조건부 분기예제  (Go to 코드 Style)

 

 

§ 조건부 이동명령  (cmovX)

3항 연산자와 같은 한문장으로 표현 가능한 if-else문에서 사용하는데, 위의 go-to보다 더욱 효율적이다.

- 분기문은 pipe라인의 instruction흐름을 매우 방해하지

- 조건부 이동명령은 제어의 이동이 필요가 없기 때문이다.

 

 

§ 조건문 컴파일  (Do-While  loop)

- 바꾸는 방법: do-while => Go to => assembly

이때, x>>=1의 경우, 등호를 통해 shift결과를 x에 save할 수 있게 된 것이다.

 

 

§ 조건문 컴파일 (While  loop)

- 바꾸는 방법: 조건문 => do-while => Go to => assembly

§ 일반적인 While문의 번역- 1

 

 

§ 일반적인 While문의 번역- 2

 

 

 

 

§ 조건문 컴파일  (for  loop)

- 바꾸는 방법: for => while => do-while => go to => assembly

for (init; test; update)
    Body
    

init;
while (test) {
    Body
    update;
}

 

 

 

 

§ 조건문 컴파일  (switch 문)

long switch_eg (long x, long y, long z) {
    long w = 1;
    switch (x) {
        case 1:
            w = y * z;
            break;
        case 2:
            w = y / z;
            /* Fall through */
        case 3:
            w += z;
            break;
        case 5:
        case 6:
            w -= z;
            break;
        default:
            w = 2;
    }
    return w;
}

§ jump table 구조

 

▶ table 구조

- 각 타겟은 8 byte를 필요로 하며  시작주소는 .L4 이다.

 

▶ jump 하기

▷ 직접 점프: jmp  .L8

- 점프 대상은 레이블 .L8로 표시한다.

 

▷ 간접 점프: jmp  *.L4(, %rdi, 8)

- 점프 테이블의 시작: .L4

- 8 byte 주소이기에 8의 배수로 증가해야 함

- 점프 target 유효주소는 .L4 + x * 8 로부터 얻어진다.  (0 ≤ x ≤ 6일 때, 성립)

 

 

(cf. intel의 x86-64 cpu의 assembly를 기준으로 진행.)

※  Assembly란? 

기계어와 1:1 대응관계를 갖는 명령어의 집합체로 low-level 프로그래밍 언어이다.

- 물론 고급언어의 경우, 안전하고 편리하며 컴파일러를 통해 더 훌륭한 어셈블리 프로그램생성이 가능할지도 모른다.

assembly High-level Language
-대형 프로그램 개발이 불편

-속도가 중요한 응용프로그램, 하드웨어 직접 제어 시 이용

-임베디드 시스템의 초기코드 개발 시 이용

-플랫폼에 따라 새롭게 작성해야 해 이식성이 매우 낮음

-하지만 많은 간접적인 응용이 있음

- 배열이나 구조체 없이 메모리에서 연속적 byte들로 표시
-대형 프로그램 개발하기 매우 편함

-이식성이 높음 (high portability)

-비효율적 실행파일의 생성가능성이 존재

-대형 실용 응용프로그램 개발 시 이용

 

 

※  x86 processor

1978년부터 시작되어 점점 새로운 기능들을 추가하였지만, 예전의 기능들을 그대로 유지해 접근이 가능하다.

이런 intel의 철학은 바로 CICS (Complex Instruction Set Computer)로 다양한 형태의 명령어를 갖는다.

- EIP: 다음 명령어를 fetch해오는 주소 program counter

- Register File: Register의 집합

- Condition Code: 가장 최근 연산의 결과의 상태정보를 저장

- Memory: byte주소 지정가능한 배열로 명령어, data가 저장되며 stack이 존재한다.

 

 

※  x86-64의 정수 register

x86-64 processor(CPU)는 64-bit 값을 저장할 수 있는 16개의 register가 존재한다.

이들은 정수 데이터와 포인터를 저장하는 데, 포인터의 경우 주소를 저장하는데 사용합니다.

rsp, rbp와 같이 p로 끝나는 register의 경우가 바로 포인터를 사용해 주소를 저장하는 register이다.

이 16개의 레지스터는 모두 %r로 시작하는 이름을 갖는다.

위 사진을 보면 %rxx 레지스터의 절반의 크기에 %exx라는이름이 붙어있는 것을 확인할 수 있다.

%exx%rxx의 하위 4바이트에 해당하며 이와 같은 방식으로 아래 사진처럼  2바이트, 1바이트에 각각 접근가능!

이때, esp와 ebp는 모두 pointer로 주소를 저장하는 register이다.

이는 예전에 1바이트 단위 접근방식을 남겨두어 예전 의 기능을 그대로 유지해 하위 호완성을 유지하기 위해 과거의 레지스터들의 이름이 남아있는 것이다.

전체 16개의 레지스터의 하위 바이트들은 바이트, 워드(16비트), 더블워드(32비트), 쿼드워드(64비트)씩 접근할 수 있습니다.

 

 

※  데이터의 이동 (Moving Data):

§  movq  Source, Dest

위의 뜻은 Source를 읽어 Destination에 복사를 한다는 의미이다.

 

§  Operand의 유형 

 

▶ Immediate : $로 시작하는 형태,  상수 정수 data

ex) $0x400,  $-503Register : %로 시작하는 형태,  16개 register지만 %rsp는 stack접근에만 이용한다.

ex) %rax,  %r13

Memory : (%register)의 형태, register로 지정되는 주소에 저장된 8개의 연속적 메모리 byte

  - 이때, Memory주소지정을 다음과 같은 방식으로 할 수 있다.

    i) 일반형 (R)  =  Mem[Reg[R]]                  ex) movq (%rcx) , %rax

   ii) 변위형 D(R)  =  Mem[Reg[R] + D]         ex) movq  8(%rbp) , %rdx 

  iii) 가장 일반적인 형태  D(Rb, Ri, S)  =  Mem[Reg[Rb] + S*Reg[Ri] + D] 

       - D: 상수 변위 (1, 2 or 4 bytes)

       - Rb: base register : 16개 register 모두 가능

       - Ri: index register : %rsp를 제외한 모두 가능

       - S: 배율 (1, 2, 4  or 8 bytes)

이때, Dest에는 Immediate 즉, 상수값은 올 수 없다! (상수값에 대입할 수 없어서)

또한, Source와 Dest 모두 Memory인 memory-memory 데이터 이동은 한개의 명령으로 불가능하다.

 

Ex.  아래 함수에 대해 Assembly과정을 이해해보자.

<C code>

void swap (long *xp, long *yp) {
    long t0 = *xp;
    long t1 = *yp;
    *xp = t1;
    *yp = t0;
}

 

<Assembly>

swap:	
    movq	(%rdi), %rax	# t0 = *xp
    movq	(%rsi), %rdx	# t1 = *yp
    movq	%rdx, (%rdi)	# *xp = t1
    movq	%rax, (%rsi)	# *yp = t0
    ret

 

 

 

 

 

※  주소 계산 명령어 (Load Effective Address):

§  leaq  Src, Dst

- Src: 주소 모드 식 (Memory)

- Dst: 수식으로 표현된 주소값 저장 (Register)

 

용도: 메모리를 참조하지 않고 "주소만 계산"할 때  (C에서 &연산자, 곱셈과 같은 경우)

Ex.  아래 함수에 대해 Assembly과정을 이해해보자.

<C code>

long m12 (long x) {
    return x * 12;
}

<Assembly>

leaq (%rdi, %rdi, 2) , %rax		# t <- x + x*2
salq $2,  %rax				# return t << 2

leaq (%rdi, %rdi, 2) , %rax 부분을 통해 %rdi * 3과 같은 결과를 얻고

salq  $2  (shift  arithmetic  left  2자리)연산을 이용해 (3 * %rdi) * 4로 최종적으로 12가 곱해지는 C code와 동일한 결과를 얻는다.

 

 

 

 

 

 

 

※  Floating Point

실수(floating point)연산을 위한 단일 표준.

 

§ 2진 소수표현

위의 예시를 통해 유추할 수 있는 점은 

- 우측으로 shift하면 2로 나눈 효과를,

- 좌측으로 shift하면 2를 곱한 효과를 얻는다.

 

☆나만의 계산법

 

 

 

※  Patriot Missile Defense:

Software Problem Led to System Failure at Dhahran, Saudi Arabia

1991.2.25일 1차 이라크 전쟁인 걸프전당시 이라크는 미국을 포함한 연합군에 스커드 미사일을 날렸고

미국은 기존의 지령유도 방식, 즉 미사일과 적 미사일을 별도의 레이더로 각각 추적하는 것에 비해 첨단 방공무기 중 하나인 패트리어트 미사일로 요격미사일을 발사하였다.

 

패트리어트 미사일은 미사일과 표적사이의 상대적인 속도나 방향을 더 확실히 알 수 있다는 점과

미사일 자체에 자신이 표적을 향해 어떻게 날아가야 할지 계산하는 고성능 CPU를 박아 넣을 필요는 없으므로 결국 소모품인 미사일 자체의 단가는 싸진다는 장점이 있다.

 

그러나 미사일이 자체적으로 표적에 어떻게 날아갈지 계산하는 유도기능은 없기 때문에 상대적으로 시간지연문제나 전파방해에 취약하며, 동시 교전능력을 늘리는데 있어 불리하였고, 결과적으로 요격에 실패해 미군기지 다란은 98명의 사상자가 발생하였다.

(미군이 자랑하는 첨단방공시스템 패트리어트가 구형 구소련제 스커트 미사일 요격에 실패한 것이다.)

 

후일 밝혀진 사실은 수치오차에 의한 것인데, 이에 대해 살펴보려 한다.

 

∴ 100h동안 0.1초씩 증가를 표현하면 (100*3600s * 0.1) * 근사값 = 약 0.34344초이므로

초속 2km의 스커드 미사일의 경우, 대략 700m정도의 오차가 발생해버리게 되는 것이다. 

 

 

 

 

 

 

※   IEEE 표준 Floating Point

§ 소수 표현방법

즉, s는 0이나 1 둘 중 하나이다.

 

이때, 인코딩은 exp에 따라 다음 3가지로 나뉜다.

- Normalized values: exp ≠ 00...0  && exp ≠ 11...1인 경우

- Denormalized values: exp = 00...0인 경우

- Special values: exp = 11...1인 경우

 

 

§ Normalized values (exp ≠ 000 . . . 0  && exp ≠ 111 . . . 1인 경우)

# 지수(E)로 exp 구하기.

- exp = E + bias(조정값)

- bias로 +, - 범위의 E값을 unsigned로 전환한다.

 

# 유효숫자(M)은 묵시적으로 1로 시작.

 

§ Denormalized values (exp = 000 . . . 0인 경우)

=> E = 1 - bias인 constant로 고정!

즉, 값을 결정하는 것은 M이 된다.

§ Special values (exp = 111 . . . 1인 경우)

 

 

 

 

 

※   IEEE 표준 Floating Point의 값의 분포와 Round-To-Even

 

§ 인접 짝수 모드 (Round-To-Even) _ 10진수

▶ 컴퓨터에서 사용하는 모드: Default 근사 모드

3.0 ---------------- 3.5 ---------------- 4.0 의 경우, 보통의 반올림을 진행하면 항상 값이 크게 오차가 발생한다.

default 근사모드로 인접한 짝수에 근사하게 하면, (3.5를 4.0에 근사, 4.5는 4에 근사하게 함) random하게 오차가 발생해

최종적으로 오차누적가능성이 확률적으로 줄어든다.

 

§ 인접 짝수 모드 (Round-To-Even) _ 2진수

 

※ 컴퓨터에서의 데이터 표현

컴퓨터 내부 (memory, register)에 데이터가 표현되는 방식을 이해하는 것은 정말 중요!

- 바이트 저장순서(Byte Ordering)

- program의 소스코드와 실행코드

- 정수의 type casting과 연산

 

 

※ 1 Byte = 8 bits 이다!

32-bit 운영체제 컴퓨터의 주소범위는 2^32 즉, 4GB인데, 요즘 사용되는 64-bit는 가용 주소공간이 약 1.8*10^19 Byte정도.

cf. x86-64 컴퓨터는 48-bit주소를 지원하는데, 이는 256TB정도이다.

 

 

 

※ Byte Ordering (바이트 저장순서)

크게 2가지가 있는데, 다음과 같다.

1. Big Endian: Least significant byte (최하위 byte)가 최대주소에 저장된다.

2. Little Endian: Least significant byte (최하위 byte)가 최소주소에 저장된다.

 

예를들어, 0x01234567의 경우, 7이 최하위 bit로 각각의 0, 1, 2, 3, 4, 5, 6, 7은 4-bit크기를 갖는데, 

아래의 주소 한칸은 1byte(=8-bits)이므로 2개의 숫자값이 들어가는 것을 알 수 있다.

 

 

※ 비트 연산자

메모리에 할당된 정수값을 bit단위로 논리연산을 실행하기 위해 사용한다.

 

※ shift 연산

Left Shift  [x << y]: x를 y만큼 왼쪽으로 shift

 

Right Shift  [x >> y]: x를 y만큼 오른쪽으로 shift

- 논리 shift [Log. >>]: 밀어낸 후 값을 0으로 채움

- 산술 shift [Arith. >>]: 밀어낸 후 값을 원래값의 처음숫자로 채움

 

 

 

 

 

※ 정수의 표현방법

- unsigned int

- BCD code: 4-bits로 십진수 0~9를 표시 (2351 => 0010 0011 0101 0001)

- MSB에 의한 부호표시: (-22 => 110110, 이때 맨 앞자리 1은 부호를 의미, 1은 음수, 0은 양수)

- 부호를 갖는 signed int

 

컴퓨터에서는 2의 보수로 부호형 정수를 표시!

- 0을 1로 바꾸고 1을 0으로 바꾸는 shifting을 진행, 그 후 가장 마지막 자리에 1을 더하면 된다.

 

이를 통해 양수부분에 있어서는 signed와 unsigned의 표현은 동일하다.

 

 

 

※ Data type casting 

C언어에서는 signed로부터 unsigned로의 변환을 허용한다. (다만 아래와 같은 이유로 인해 주의해서 사용해야 한다.)

 

§  C언어에서 signed와 unsigned의 변환

- 상수는 아무런 명시가 없으면 signed integer이다.

- 명시적으로 U를 숫자 끝에 붙이면 "수식 전체"Unsigned이다!

 

 

가장 중요한 점은 signed => unsigned로 바꾸면 음수가 엄청 큰 양수로 바뀔 수 있다는 점을 유의해야 한다!

 

 

 

 

 

 

 

본질! 컴퓨터의 모든 정보는 bit와 byte로 저장된다!

※ Hardware 구성요소

- Bus: 구성요소간 정보교환 통로. 입출력버스, 시스템버스가 존재.

- I/O devices

- Main memory

- Cache memory

- CPU (processor): 기억장치로부터 명령/데이터 호출, 제어장치로 명령의 해석, ALU로 명령의 실행, I/O로 입출력 진행

cf. 명령 주기: fetch(인출), decode(해독), execution(실행), store(저장)의 cycle로 진행됨.

 

Ex. hello.c 의 컴파일 과정  (feat. gcc-o hello hello.c )

 

 

 

 

 

※ 컴퓨터 구조

 

Step1. hello 명령이 인식된다면?

 

Step 2. 그렇다면 hello program이 로딩되는 경우는 어떻게 될까?

 

Step 3. hello program의 실행

 

 

 

 

 

 

 

 

 

 

 

Week 9.

 

Week 10.

 

Week 11.

 

Week 12.

 

Week 13.

 

 

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

this->OS.code(mid)  (0) 2022.10.30

+ Recent posts