Programming/Computer Science Fundamentals

[프로그래밍 언어론] 2. 프로그램 언어의 발전 및 동작원리

Sujin Lee (Daisy) 2021. 9. 7. 01:58

 

* 프로그램 언어의 발전 

1. 컴퓨터 시스템의 발전

 - 아이디어 : 튜링기계 : 계산 자동화를 위한 상상 속의 기계 설계 

 - 전자식 컴퓨터 : 에니악, 콜로서스 : 전자 신호를 통한 계산기계 

 - 프로그램 저장 방식 컴퓨터 : 에드박 : 프로그램과 처리기를 분리 

 

2. 운영체제의 발전

 1) 일괄처리 운영체제 : 관리자(operator)를 대신할 프로그램 등장 

 2) 시분할 운영체제 : 한 컴퓨터를 여러 사람이 사용

     ㄴ 마치 컴퓨터에서 여러 프로그램이 동시에 진행되는 것 처럼 보이게 한 것 

         ex) 1초 동안은 A라는 프로그램, 그 다음 1초는 B프로그램, 그 다음 1초는 C 프로그램을 동작, 그 다음 1초는 A프로그램...

               이런식으로 나눠서 처리. 

 3) DOS : IBM 컴퓨터, Apple 등 개인용 컴퓨터(PC)의 등장

     ㄴ 이전까지는 서버 컴퓨터가 있어서 커다란 서버에 여러 사용자들이 붙어서 조금씩 컴퓨터를 나눠서 사용하는 형태였음

     ㄴ 이제는 개인용 컴퓨터가 등장함. 

     ㄴ 즉, 사용자가 오롯이 혼자서 컴퓨터를 다 사용할 수 있게됨. 

 4) PC 환경 운영체제 발전 : GUI 운영체제와 Linux의 발전 

 

3. 프로그래밍 언어의 발전

 1) 1950년대 : 초기 프로그래밍 언어

    (1) Fortran : 수식과 문장, 제어문의 등장.

      - IBM의 존 배커스에 의해 개발됨

      - 과학계산용 언어

    (2) Algol : 구조화 프로그래밍의 발전

      - 본래 이름 : IAL (International Algebraic Language)

      - 국제 위원회 ACM-GAMM을 통해 설계된 언어

      - 알고리즘 기술 언어 

    (3) LISP : 초기 함수형 언어

      - MIT의 존 매카시가 설계함

      - 최초의 함수형 언어 

 2) 1960년대 : 프로그래밍 언어의 발전 

     (1) Cobol : 레코드 타입의 소개 

       - 미 해군에서 그레이스 호퍼가 이끄는 팀에 의해 개발됨

       - 사무용 언어

     (2) PL/I : 너무 복잡. 

       - 모든 언어를 합쳐 보았으나 결과적으로 너무 복잡해짐. 

     (3) BASIC : 교육용 언어. 

       - Beginner's All-purpose Symbolic Instruction Code

     (4) Simula : 객체지향의 등장

       - 시뮬레이션 언어.

 3) 1970년대 : 프로그래밍 언어의 단순화. 

     (1) Pascal : 차세대 교육용 언어.

       - 구조화 프로그래밍 지원.

     (2) C : 진정한 시스템 프로그래밍 언어.

       - Unix 개발용 시스템 프로그래밍 언어.

       - Objective-C, C++, Java, C# 등 다양한 언어에 지대한 영향을 끼침.

     (3) Prolog : 선언적 논리 언어.

       - 최초의 논리 언어

     (4) Smalltalk : 객체지향 언어의 발전

       - 최초의 GUI, 마우스 등을 도입 

     (5) Ada : 안전성을 위한 대장정

       - 미 국방성(DoD) 공모

       - 매우 복잡한 언어로 1983년에 첫 컴파일러 등장

     (6) ML : 타입 시스템을 갖춘 현대 프로그래밍 언어

       - 강력한 정적 타입 검사, 타입 추론, 패턴 검사, 예외처리 등

     (7) Scheme : 간결한 LISP 

       - MIT 학생들의 기초 프로그래밍 언어  

 4) 1980년대 : 현대 프로그래밍 언어 등장 

    (1) Common Lisp : 방대한 LISP의 통합

       - 함수형 언어 패러다임과 객체지향 패러다임을 동시에 지원

    (2) Objective-C : C의 탈을 쓴 Smalltalk

       - C를 기초로 한 객체지향 언어의 신호탄

       - Apple의 애플리케이션 작성 언어로 발전

    (3) C++ : 객체지향으로 변신한 C

       - 클래스 개념을 C에 도입

    (4) Perl : 문자열 처리를 위한 언어

       - 정규식을 바탕으로 한 강력한 패턴 매칭 기능 포함

 5) 1990년대 이후 : 프로그래밍 언어의 대중화 

    (1) Java : 단순한 객체지향 언어

       - 원래 목적은 임베디드 컴퓨팅 분야였으나 웹 브라우저에 탑재되면서 인기

       - JVM (Java Virtual Machine)

          ㄴ 웹 브라우저 안에서 JVM이라고 하는 것을 지원하게 되면, 이걸 통해서 웹 브라우저상에서 Java로 짠 프로그램이 동작하게됨. 

          ㄴ 자바 애플릿(자바로 짠 프로그램을 컴파일 시키면 중간 코드 형태로등장하게 됨).

              이 자바 애플릿을 웹브라우저에 넣어주면 사용자 쪽에 넘어가서 동작함. 

    (2) JavaScript : 웹 프로그래밍 언어

       - Netscape

       - Elm, TypeScript 등 다양한 변종 언어로 발전

       - 스크립트 언어 --> 자바보다 좀 더 가볍게, 쉽게 웹상에서 동작 가능. 

    (3) Python : 빠른 프로토타이핑 언어

      - 스크립트 언어

      - 동적 언어를 추구

      - 다중 패러다임 언어  

    (4) Haskell : 순수 함수형 언어

      - 모나드(monad)가 탑재되면서 점차 인기

      - Scala에 영향을 줌 

 

* 프로그램 동작 원리 

1. 컴퓨터 구조 

 - CPU와 메모리, 저장장치 등이 BUS로 연결.

 - 다양한 입출력 장치도 BUS에 연결될 수 있음. 

2. 컴퓨터 동작 원리

 - 전원 : 운영체제 적재(저장장치 --> 메모리) --> 수행 

 - CPU는 인출-해석-실행 주기를 반복하여 메모리의 명령어를 실행. 

 - 상세설명) 

   ㄴ 전원을 넣으면, 저장장치에 미리 저장되어 있던 운영체제(OS)가, BUS를 통해서 메모리에 적재가 됨. 

       전원을 넣으면, 저장장치에 있던 운영체제가 메모리로 적재됨. 

       적재가 되면, 전원이 켜진 컴퓨터는, 메모리에 적재된 운영체제(OS)가 알아서 그 컴퓨터를 관장하게 됨. 

   ㄴ 그렇다면 운영체제는 어떻게 동작하는가? 

       운영체제가 메모리에만 가만히 있으면, 아무 일도 하지 않음

       운영체제가 일을 하기 위해서는 CPU가 작업을 해야함.

   ㄴ CPU가 하는 일은 인출,해석,실행이라고 하는 기본 단위를 가지고 명령어를 처리함.

       1) 인출 : 메모리에 들어있던 운영체제의 특정 명령어 하나를 뽑아서 CPU로 가지고 오는 것. 

       2) 해석 : CPU로 가져온 명령어를, CPU가 보고서 '아! 뭐를 하라는 명령이구나! 내가 3번 명령을 해야하는구나!'

                      이런 식으로 해석하게됨.

       3) 실행 

   ㄴ ex) move라는 명령어가 들어왔다 (인출) 

             --> CPU는 그 move 명령어에 맞춰서 '몇 번지에 있는 값을 다른 레지스터로 값을 옮기면 되는구나.'하고 해석함 (해석) 

             --> CPU가 이것을 처리하게됨. (실행) 

 

3. 프로그램 동작 원리

 - CPU는 인출-해석-실행 주기를 반복하여 메모리에 적재된 프로그램의 명령어를 실행. 

 

4. 프로그래밍 언어 구현이 필요한 이유 

 1) 기계어

    - 기계, 즉 CPU가 이해하고 수행할 수 있는 명령어. 

      ㄴ 컴퓨터는, 최종적으로는, 기계어로 된 프로그램만 수행할 수 있음. 

    - 이진수 형태의 명령어를 사람이 이해하는 것은 매우 난해함. 

 2) 어셈블리어

    - 기계어에 거의 1:1 대응하는 형태의 기호 언어

      ㄴ 사람이 직접 기계어로 코딩하는 것이 힘들므로, 2진수 명령어들 중에서 명령어 몇 개를 사람이 보기 좋게 기호로 변환한 것. 

          ex) 기계어로 10001000이라고 하면 사람이 보기 좋게 MOV라는 명령어로 표현해보자. 

    - CPU에 종속적 --> 이식성이 거의 0 

       ㄴ 기계어보다는 사람이 이해하기 좋기는 하지만, 여전히 이 명령어 자체는 CPU가 바로 이해할 수 있는 수준의 언어이기 때문에 이 어셈블리어도 역시 CPU에 종속적임. 

       ㄴ CPU에 종속적이라는 말은, 특별한 A라고 하는 CPU에서만 동작하는 프로그램이라는 말. 

            즉, 이 어셈블리어는 C라고 하는 다른 회사에서 만들어낸 CPU에서는 동작하지 못함. 

            회사가 다르고 CPU 종류가 달라지면 그 CPU들이 처리할 수 있는 명령어들이 달라지기 때문에,

            A회사의 CPU에 맞춰진 프로그램은 C회사에서 만든 CPU에서는 동작하지 못함. 

 3) 고급 프로그래밍 언어

    - 사람에 가까운 표현으로 프로그램을 나타냄

    - 특정 기계에 종속적이지 않음

    - 고급 프로그래밍 언어로 작성한 것은, 최종적으로는 CPU(기계)가 알아듣는 기계어로 표현을 바꿔줘야함

       ㄴ 소스 프로그램 : 프로그래머가 작성한 프로그램. 보통 고급 언어. 

                                   CPU는 소스 프로그램을 바로 실행시킬 수는 없다. 

       ㄴ 목적 프로그램 : 프로그래머가 작성한 것을 컴파일러가 변환해서, 기계어, 즉 CPU가 알아들을 수 있는 형태로,

                                   즉 컴퓨터 하드웨어가 수행할 수 있는 형태로 바뀐 프로그램. 

       ㄴ 소스 프로그램 --- 컴파일러 ----> 목적 프로그램 

           아래의 이미지 참고! :) 

           ㄴ 인터프리터 : 소스 프로그램을, 명령어 하나하나 직접 그때그때마다 해석하고 실행하고 하게 해주는 것. 

 

5. 프로그래밍 언어의 구현 방법 

 1) 인터프리터 

   - 프로그래밍 언어로 작성된 고수준의 명령을 해석하여 수행하는 프로그램

   - 인터프리터는 CPU의 인출-해석-실행 주기를 흉내냄. 

      ㄴ CPU가 기계어 프로그램을 보고 명령어 하나를 가지고 인출,해석,실행하는 것처럼 인터프리터가 활동함.

      ㄴ 즉, 인터프리터가 고수준의 소스 프로그램 명령어 하나를 가지고 와서, 그것을 해석해서, 거기서 나오는 실행에 필요한 여러가지 기계어들을 하드웨어에게 던져줌. 그러면 그 순간에 CPU가 그 명령어 몇개를 처리함.

        이게 끝나고 나면, 인터프리터가 이제 고수준 언어의 두번째 명령어를 가지고 옴. 이걸 또 인터프리터가 두번째 명령어를 해석해서 기계어 명령어로 변환되고 하드웨어가 처리하는....

     ㄴ 매번 고급 언어의 명령어 하나를 가지고 와서 해석하고 실행함. 

     ㄴ 소스 프로그램을, 명령어 하나하나 직접 그때그때마다 해석하고 실행하고 하게 해주는 것. 

 

 

 2) 컴파일러

   - 프로그램을 CPU가 수행할 수 있는 형태로 바꾸어서 CPU가 실행

   - 소스 프로그램을 목적 프로그램으로 바꿔줌. 

   - 인터프리터가 하는 해석 과정을 미리 모두 수행 (효율적) 

     ㄴ 소스 프로그램을 컴파일러가 최초 한 번에 전체 통 번역을 해서 목적 프로그램(기계어)로 바꿔서 효율적, 빠르게 동작 가능.

     ㄴ 즉, 인터프리터는 해석 과정이 실행 시간에 계속 이루어지지만, 컴파일러 방식은 해석까지 미리 다 끝내버렸기 때문에 실제 동작하는 실행 시간에는 실행하는 역할만 해주면 됨. 복잡한 고급 언어를 번역할 필요 X. 따라서 효율적. 

   - 상용 프로그램은 컴파일 방식으로 번역된 후 판매 

      ㄴ 일반적으로 우리가 쓰는 프로그램들은 다 컴파일러 방식을 사용함! :) 

 

3) 하이브리드 구현

  - 인터프리터 방식과 컴파일러 방식을 조합한 방식.

  - 중간코드까지 컴파일한 후 인터프리터를 통해 해석

     ㄴ 소스 프로그램을, 최종 목적지인 기계어까지 해석하는게 아니라, 중간 코드까지 컴파일한 후 인터프리터를 통해서 해석함. 

     ㄴ 컴파일러 방식으로 일단 기본적으로 많은 해석을 해두긴함. 일단은 기본적으로 변환을 하기 때문에, 고급 언어를 직접 인터프리터로 돌리는 것보다 효율적이긴함. 근데 왜 이렇게 했느냐? 이 중간 코드라고 하는 것은 하드웨어와 무관하게 만들어낼 수 있다. 그래서 인터프리터에 적합한 코드 형태로 바꾸기 때문에, 특정 A회사의 CPU, B회사의 CPU가 아닌, 이 인터프리터가 해석할 수 있는 그 특별한 중간코드로 만들어졌기 때문에, A라는 CPU에 가서도 이 인터프리터만 있으면 바로 이 프로그램 중간 코드를 실행할 수 있고, C라는 CPU를 쓰더라도 역시 이 동일한 인터프리터만 있으면 이 중간코드를 가지고 역시 C라는 CPU에서도 동작시킬 수 있다. 

  - 이 인터프리터를 가상기계(VM)라고 부름. 

    ex) Java : 하이브리드 방식으로 구현된 언어. 

         ㄴ 최초에 컴파일을 하고 나면 중간 코드, '바이트 코드'라고 부르는 프로그램이 됨. 

             그러면 어디든지간에 JVM만 있으면, 한 번 컴팡링르 시켰던 그 자바 프로그램(바이트 코드)를 던져만 주면 어느곳에서든 다 실행이 되더라.  

  - 유연성을 늘리면서도 (초반에 한 번 번역을 했기 때문에) 실행 효율성을 높인 것. 

 

 

6. 프로그래밍 언어의 요구사항

 1) 표현 풍부성(expressiveness) : 프로그래머의 아이디어를 표현할 수 있어야 함

 2) 유지 보수성(maintainability): 변화에 쉽게 대처할 수 있어야함

 3) 실행 가능성(executability) : 컴퓨터에서 실행할 수 있어야 함 

 

7. 프로그래밍 언어의 설계 원칙

 1) 규칙성(regularity) : 언어의 기능이 잘 조합될 수 있어야함 

     ㄴ 일반성, 직교성, 일관성 

 2) 추상화 지원(support of abstraction)

    : 실세계의 대상을 추상화하여 나타낼 수 있고, 이를 대상으로 어떤 연산을 수행할 수 있어야함 

     ㄴ 데이터 추상화, 제어 추상화, 추상 데이터 타입 정의 

 3) 복잡도 제어(complexity control) : 복잡한 대상 및 처리 방법을 제어할 수 있어야함 

     ㄴ 캡슐화, 모듈화 

 

8. 프로그래밍 언어의 평가 기준

 1) 작성력 : 프로그램 수식이나 문장, 기능을 쉽게 표현할 수 있는가

 2) 가독성 : 작성된 프로그램을 보고 쉽게 이해할 수 있도록 하는가

 3) 신뢰성 : 작성된 프로그램이 오류에 빠지는 가능성을 줄이는가 

 4) 직교성 : 언어 기능이 서로 간섭하지 않고 자유롭게 조합될 수 있는가

 5) 일관성 : 유사한 기능을 같은 형태로 나타낼 수 있는가

 6) 확장성 : 사용자가 원하는 새로운 기능을 추가할 수 있는가

 7) 효율성 : 작성도니 프로그램이 효율적으로 수행될 수 있도록 하는가

 8) 유연성 : 프로그래머가 표현하고 싶은 내용을 유연하게 수용하는가

 9) 이식성 : 프로그램을 다른 실행 환경으로 이전할 수 있는가 

반응형