관리 메뉴

개발자비행일지

응용프로그램과 프로그램 실행원리 본문

▶ Computer Science

응용프로그램과 프로그램 실행원리

Cyber0946 2018. 11. 29. 22:27

애플리케이션

개요

프로그램언어를 컴파일로의 종류 관점에서 분류 할 때 두 가지 종류가 있다. 하나는 컴파일러 언어, 다른 하나는 인터프리터 언어이다. 먼저 컴파일러 언어는 컴파일 과정을 통해서 한 번에 목적 코드로 변환하는 방식이다. , 컴파일 과정에서 분석과 최적화가 동시에 일어난다. 이렇기 때문에 컴파일 언어는 실행 속도가 빠르다는 장점이 있다.

인터프리터 언어는 한줄 씩 실행 시점(runtime)에 해석하고 실행한다. 해석과 실행이 동시에 일어나야 되기 때문에 컴파일 언어에 비해서 속도가 느리다. 그렇지만 디버깅이 쉽고, 운영체제에 국한되지 않고 독립적으로 개발 하 수 있다는 장점을 가진다. 이 두 가지 외에도 프로그래밍 언어는 자바와 같이 하이브리드 방식의 언어도 존재한다. 하이브리드 방식은 분석과 최적화를 동시에 진행해서 중간 언어 형태인 바이너리 형태로 컴파일하고 실행시점에 한줄 씩 해석하는 방식을 사용한다.

세 가지 언어 모두 결국에는 바이너리 형태로 변환되고 소스코드가 아닌 바이너리가 동작한다는 공통점을 가진다. 따라서 해커는 컴퓨터는 소스코드에 의해서 동작하는 것이 아닌 바이너리에 의해서 동작한다는 것과 이 바이너리를 같은 소스코드여도 컴파일 환경, 운영체제의 종류에 따라서 다른 양상을 보인다는 것을 인지하고 있어야 한다.

프로그래밍 언어의 종류에 따라서 프로그램이 실행되는 방식이 달라지고, 이에 따라 해킹의 방식이 달라진다. 왜냐하면 해킹이란 해커가 원하는 목적을 달성하기 위해 프로그램 취약점을 파악하여 그 명령을 전달하는 것이기 때문이다. 소스 코드를 역컴파일러해서 동작을 분석하는 것은 프로그램의 규모가 커질수록 어렵기 때문에 입력 값에 대한 프로세스의 동작을 관찰하면서 해킹을 진행하게 된다. 인터프리터 언어의 경우에는 한 줄 한 줄 진행 과정에서 해석되기 때문에 소스코드를 직접 분석하면서 찾아 낼 수 있다. 취약점 분석 기법에는 화이트 박스 기법과 블랙 박스 기법이 있는데 이 둘의 차이는 전자는 소스코드를 알고 있는 경우이며, 후자는 입력과 출력만으로 분석을 하는 경우이다.

<!--[if !supportEmptyParas]--> <!--[endif]-->

[참고] 컴파일 언어와 인터프리터 언어의 차이

컴파일 언어와 인터프리터 언어의 가장 큰 차이점은 컴파일 하는 시점이다. (런타임 전에 컴파일을 하는지 안하는지)가 가장 큰 차이를 가지며, 이에 따라 실행 속도의 차이가 존재하게 된다.

자바의 경우는 하이브리드 방식의 언어지만 런타임 전에 컴파일을 통해 바이트 언어로 변경된다는 차이점을 가지지만, 컴파일의 시점이 실행 전이기 때문에 엄격하게 볼 때 컴파일 언어라고 할 수 있다.

컴파일 언어와 인터프리터 언어의 실행 과정의 차이

컴파일 언어(C)

C언어를 예로 실행 과정을 살펴보도록 한다. 먼저 실행은 아래 표와 같은 순서로 진행된다.

`C 코드` --(컴파일러)--> `어셈블리어` --(어셈블러)--> `기계어` --(링커)--> `실행파일` --> `런타임` 시작

자 각 과정을 살펴 보면 아래와 같다. 컴파일러와 어셈블러 과정이 합쳐져서 컴파일 과정이 되며, 이 때, 코드가 기계어로 변환된다. 이 과정을 컴파일 타임이라고 부른다. 어셈블리어와 기계어는 1:1 매칭 되는데 기계어에서 어셈블리어로 변환시키는 것을 disassemble이라고 한다. 기계어 명령을 각각 instruction이라고 부른다. (instruction : 명령, 지시) 레지스터 단위로 들어가 성능테스트를 할 때 어셈블리어가 유용하다.

링커는 운영체제에서 개발자에게 제공하는 개발을 위한 라이브러리를 가져다가 연결해 주는 소프트웨어를 말하며, 운영체제마다 라이브러리가 다르기 때문에 실행 파일은 응용프로그램에 따라 다를 수 밖에 없다.

인터프린터 언어는 런타임 중에 컴파일(해석)을 한다. 따라서 컴파일 언어에 비해서 비교적 느린 편이다.

인터프리터 언어(파이썬)

인터프린터 언어 (Interpreted Language)는 런타임 중에 프로그램에 존재하는 명령문을 한줄 한줄 해석하며 실행한다. 파이썬을 예로 들 때, 이는 컴파일 과정에 렉서(lexer), 파서(parser) 를 거쳐서 바이트 코드를 만들어낸다. (렉서 : 요소를 하나하나 쪼갠다. / 파서 : abstract syntax tree, 구문분석)

python 코드 --> 실행 & 런타임 시작 --> (컴파일) --> 바이트코드 --> VM --> 기계어

 

어셈블리어는 CPU에서 레지스터 단위로 동작하기 때문에 CPU 의존적이나, (: 인텔 계열의 어셈블리어) 바이트코드는 그렇지 않다. 바이트코드는 이를 지원하는 가상머신(virtual machine) 위에서 돌아가기 때문에 어떤 cpu 위에서도 가상머신이 동작하고 그 속에서 동작하기 때문에 호환성이 좋다. 즉 운영체제에 독립적이다. 파이썬 초기 설치 시 window, mac, linux용을 따로 받는 이유는 바이트코드에서 어떤 기계어로 바꿀지를 결정하기 위해서이다. Java, python 모두 바이트코드를 사용하기 때문에 vm(virtual machine)이 필요하다.

인터프리티드 언어는 특별한 플랫폼 의존성을 가질 필요가 없다. 실행중에 ‘interpreted’ 되므로, 적절한 interpreter만 있다면 어떤 기종에서도 잘 실행될 수 있는 것이다. 이런 이유로 인터프리티드 언어는 보다 더 크로스 플랫폼적 성격을 갖게 된다.


프로그램 실행

우리가 작성한 프로그램은 어떻게 실행하게 될까? 이 과정에 대해서 컴파일 언어를 기준으로 알아보자. 먼저 프로그래머는 프로그래밍 언어에 따라서 문법에 맞게 소스코드를 개발한다. 이 소스코드를 컴파일러라는 소프트웨어가 실행환경에 최적화 될 수 있도록 CPU 하드웨어가 이해할 수 있는 목적코드(어셈블리어)로 변환해 준다. 컴파일이 완료된 결과물을 운영체제에 맞게 링킹, 로딩, 실행 과정을 통해서 프로그램이 실행되게 되고 우리가 그 결과를 입·출력장치를 사용해서 프로그램과 소통하게 된다.<!--[endif]-->