프로세스의 메모리 구조
프로세스에 대한 이해는 프로그래밍의 핵심이라고 할 수 있다. 프로세스는 간단하게 '수행중인 프로그램'이라고 정의할 수 있다(참고로 프로그램은 수행 가능한 디스크상의 이미지라고 정의할 수 있다). 다시 말해, 프로세스는 디스크에 저장돼 있던 실행 가능한 프로그램이 메모리에 적재돼 운영체제의 제어를 받는 상태를 말한다.
프로그램을 작성해 컴파일하고 링크하면 실행 가능한 파일이 생성된다(윈도우라면 *.exe라는 파일이, 유닉스라면 기본적으로 a.out이라는 파일이 생성된다). 쉘을 통해 사용자가 프로그램을 수행시키면, 커널은 이 프로그램을 제어에 적합한 자료구조로 만들어 메모리로 읽어낸 후, 커널의 프로세스 테이블에 등록하고, 메모리, 파일, 입출력 장치 같은 자원을 할당하는데, 이때부터 프로그램은 커널의 한 프로세스로서 실행 상태가 된다.
프로세스는 Win32의 경우에 CreateProcess(), 유닉스의 경우에는 fork() 시스템 콜을 사용해 새로운 프로세스를 생성하고, 프로세스간에는 다양한 IPC(Inter-Process Communication) 방법을 통해 데이터를 교환하거나 프로세스간의 동기화를 수행한다.
<그림 1>은 프로그램이 메모리에 적재된 모습을 나타낸다. 이 구조는 유닉스와 윈도우가 크게 다르지 않다 (JVM에서 돌아가는 자바 프로그램도 비슷한 구조를 갖는다). 그림에서 위쪽이 낮은 주소번지가 된다. 그림에서 보이는 네 개의 범위(텍스트, 데이터, 힙, 스택)를 각각 세그먼트라고 한다.
각 세그먼트에 대해 좀더 자세하게 알아보자(여기서는 유닉스와 C를 기준으로 서술하지만, 윈도우에서도 공통적인 특성을 갖는다).
◆ 텍스트 : 프로그램의 실행 코드인 기계어 코드와 읽기 전용 데이터 등을 가진다(CPU가 읽어들여 수행한다고 해서 텍스트라고 부르며, 코드 영역이라고도 한다).
◆ 데이터 : C언어에서 전역 변수, 정적 변수 등으로 선언된 변수 영역(읽기/쓰기 가능)
◆ 힙 : 프로그램 수행 중 malloc(), free() 등의 시스템 콜로 할당되고, 해지되는 메모리 영역
◆ 스택 : C언어의 함수 호출시 지역 변수와 인수, 함수의 수행이 끝났을 때 리턴할 주소(return address)를 푸시한다(함수가 끝나면 이 값을 팝하고 리턴하게 된다).
운영체제는 프로그램의 텍스트 부분에 메모리를 읽기 전용으로만 사용하고, 프로세스간에는 메모리 영역을 침범하지 못하도록 한다. 따라서, 프로그램의 텍스트로 되어 있는 메모리 영역을 침범해 기록하면 버스 에러나 세그먼트 결함이 일어나서 프로그램이 종료된다. 여러분은 윈도우에서 '잘못된 연산을 수행해 프로그램을 종료합니다'란 메시지를 본적이 있을 것이다. 이 에러(잘못된 연산)의 95% 이상이 바로 앞서 이런 연유에서 발생한다.
C/C++와 같이 포인터(주소를 갖는 변수)를 사용해 메모리를 직접 액세스하는 언어로 개발할 때도 흔히 이와 같은 에러가 발생한다. 따라서 프로그램을 개발할 때 가장 중요한 점이 바로 메모리 관리다. 프로그램 오류의 80% 이상을 차지하는 이유를 살펴보면 다음과 같다.
◆ 초기화
◆ 해지
자바는 가비지 컬렉션을 통해 사용하지 않는 메모리를 자동으로 해지하지만, 필요에 따라 명시해 주는 것이 좋다. C/C++에서는 쓰지 않는 메모리에 대해 반드시 명시적으로 delete를 써줘야 한다.
C/C++는 할당된 메모리에 대한 포인터를 잃어버리는 경우가 생기는데, 이를 메모리 누수라고 한다.
메모리 누수는 프로그래머를 괴롭히는 가장 큰 골칫거리 중 하나다. 꼼꼼히 확인하는 습관을 들이는 것이 좋다.
멀티로 시작하는 단어의 사용
멀티 프로그래밍, 멀티 프로세싱, 멀티 태스킹, 멀티 쓰레드, 멀티 유저는 비슷비슷한 용어이기 때문에 혼돈해 사용하지만 의미가 모두 다르다.
◆ 멀티 프로그래밍 : 여러 프로그램이 메모리에 적재돼 수행되는 것. 하나의 프로그램이 수행되다가 입출력을 하면 CPU는 다른 프로그램으로 제어권을 넘긴다. 즉, 프로그램의 입출력을 기준으로 프로그램 수행의 스위칭이 일어난다(요즘은 좀처럼 쓰이지 않는 말이다).
◆ 멀티 프로세싱 : 다수의 프로세서가 작업을 처리하는 것. 운영체제가 멀티 프로세싱을 지원한다는 것은 다수의 CPU를 지원할 수 있다는 뜻이다. 여러 CPU가 운영체제와 메모리를 공유해 프로그램을 수행하는 방식을 SMP(대칭형 멀티 프로세싱)라고 한다(윈도우 NT가 이런 방식이다).
◆ 멀티 태스킹(시분할 시스템) : 태스크(Task, 운영체제가 수행하는 기본 단위)가 여럿인 것. 운영체제가 강제로 CPU 시간을 프로세스에 할당한다.
◆ 멀티 유저 : 멀티 유저는 기본적으로 멀티 태스킹이 되는 시스템에 다중 사용자 파일 시스템 등 다중 사용자를 지원하기 위한 기능을 추가한 것이다.
◆ 멀티 쓰레드 : 하나의 프로세스 내에 여러 개의 수행경로가 존재하고, 이를 동시에 수행하는 것. 하나의 프로세스가 동시에 두 가지 이상의 작업을 수행하는데 활용된다(쉽게 말해 프로세스 내에서 멀티 태스킹을 하는 것이라고 볼 수 있다).
여기서 멀티 쓰레드에 대해 좀더 구체적으로 알아보자. 쓰레드는 '수행 경로'라고 정의할 수 있다.
CPU가 텍스트(코드)를 읽어 수행할 때 수행되는 절차를 하나의 수행 경로라고 한다. 멀티 쓰레드란 결국 이런 수행 경로가 여러 개 있다는 뜻이다. 멀티 쓰레드도 멀티 태스킹과 마찬가지로 기본적으로 여러 쓰레드를 번갈아 수행한다. 텍스트, 데이터, 힙, 스택의 각 세그먼트에 대한 주소를 CPU 레지스터에 담고 있는데, 이 레지스터의 내용을 바꿔(컨텍스트 스위칭) 다른 쓰레드를 수행하도록 한다. 쓰레드 간에 프로세스가 갖고 있는 메모리와 코드 영역을 공유하므로, 컨텍스트 스위칭할 때 레지스터의 내용을 조금만 바꿔도 멀티 태스킹에 비해 컨텍스트 스위칭 하는 속도가 빠르다.
멀티 태스킹이나 멀티 쓰레드는 두 개 이상의 작업이 동시에 수행되므로, 동시에 수행되는 작업에 대해 동기화가 필연적으로 필요하다.
자바는 언어 차원에서 동기화를 지원한다. synchronized 키워드를 이용해 손쉽게 동기화를 구현할 수 있지만, 꼭 필요할 때만 사용하는 것이 좋다. synchronized를 사용한 것이 사용하지 않은 코드에 비해 약 3∼4배정도 느려지기 때문이다. 대부분의 유닉스와 윈도우는 운영체제 차원에서 Event, Critical Section, Mutex, Semaphore 등의 동기화 메커니즘을 제공한다.
객체지향 방법론과 UML
요즘 소프트웨어 전반에 '객체지향'이란 말이 붙지 않은 분야가 없다. 객체지향 데이터베이스, 객체지향 프로그래밍 언어뿐만 아니라 소프트웨어 공학에도 객체지향 분석/설계, 방법론이 널리 사용되고 있다. 이제 UML(Unified Modeling Language)은 정착 단계에 들어섰다. 어느 글을 보든지, 표기법은 기본적으로 모두 UML을 사용하고 있다.
하지만 많은 사람들이 UML을 오해하고 있는데, 그 중 가장 대표적인 두 가지만 살펴보겠다.
1. UML은 객체지향 방법론이다.
2. UML은 프로젝트를 진행하는 데 생기는 많은 기술적인 문제를 푸는 데 도움이 된다.
UML은 방법론(methodology)이 아니다. UML은 표기법(notation)이다. 많은 사람들이 UML을 일종의 방법론처럼 생각하고 있는데, UML은 객체지향 분석/설계의 결과를 표시하는 표준적인 표기법을 제공할 뿐이지 UML 자체가 무엇인가를 해주는 것은 아니다. 방법론이라고 얘기하려면 표기법과 함께 공정을 갖고 있어야 한다.
방법론 = 표기법 + 공정
하지만 UML은 공정을 포함하고 있지 않다. 따라서 RUP(Rational Unified Process)를 쓰거나 자체의 고유한 공정을 써도 좋다는 얘기가 된다.
또 한 가지 사실은 UML을 쓰더라도 여전히 기술적인 문제들은 그대로 남아 있다는 점이다. 기술적인 문제는 어떤 방법론을 쓰건 방법론과는 별개로 풀어야 할 과제로 남게 된다.
그렇다면 왜 UML을 사용하는가. 그 답은 '남들이 모두 쓰니까'다. 우스개 소리 같지만 사실이자 곧 현실이다. 좀더 고상하게 말해 표준이기 때문이다. UML은 개발자간 혹은 개발자와 고객간의 의사소통에 있어 표준적인 형태를 제공한다는 데 가장 큰 의의가 있다. 사실 시스템을 분석/설계하는 것은 표기법이나 공정보다 '경험'이 훨씬 중요하다. 필자는 '경험'을 앞서는 것은 아무 것도 없다고 본다.
<그림 2> UML의 클래스 다이어그램

많은 사람이 재사용을 부르짖고 객체지향의 가장 큰 장점을 재사용에서 찾고 있지만, 실제 여러분의 프로젝트에서 얼마나 많은 코드가 재사용되고 있는지 돌이켜 보라.
재사용이 가장 잘 적용되는 분야가 바로 컴포넌트 기반의 개발(CBD; Component Based Development)이다. 그렇지만 실제 프로젝트에서 컴포넌트를 만들고 조립하는 역할과 배분이 그리 쉽게 이뤄지지는 않는다. 결국 객체지향론이나 재사용, CBD 등은 프로젝트를 진행하는 데 좀더 도움이 되도록 하는 독려의 대상이지 그 자체가 목적이 될 수는 없다. 소프트웨어 분야에서는 아무리 좋은 개념에 아무리 좋은 제품이라도 실제 활용되지 않으면 아무 소용이 없다.
컴퓨터와 수학
컴퓨터의 구조 자체가 수학이라는 것을 아는 사람은 생각보다 그리 많지 않은 것 같다.
사실 컴퓨터의 아키텍처는 부울 대수이고, 관계형 데이터베이스는 수학의 집합론을 그대로 옮겨 놓은 것이다. 관계형 데이터베이스가 계층형 데이터베이스나 네트워크 데이터베이스를 제치고 70년대부터 지금까지 널리 쓰이는 까닭은 수학적 이론을 바탕으로 했기 때문에 가능했다는 말도 있다.
물론 수학에 대한 이해와 지식이 있는 경우 프로그래밍을 하는 데 많은 도움을 받는 것은 사실이지만 절대 필수적인 것은 아니다. 어쩌면 중학교 3학년 정도의 수학이면 충분하다고 볼 수도 있다.
고등학교 이상의 수학 실력이 필요한 분야는 컴퓨터 그래픽스다(컴퓨터 그래픽스를 프로그래밍으로 접근하는 경우). 만일 보다 심도 깊은 연구가 필요하다면, 이산 수학(Discrete Mathematics)이라는 수학분야를 공부할 것을 권한다. 이산 수학은 전산학의 기초 이론을 수학적인 입장에서 다룬다. 수학이라는 학문에서 가장 중요하다는 무한(Infinity)와 연속(Continuity)을 뺀 수학이라고 보면 틀리지 않는다.
미래를 좌우하는 것
인터넷의 확산은 엔터프라이즈 애플리케이션과 데스크톱 애플리케이션의 경계를 허물었고, 소프트웨어 산업전체를 뒤집어 놓았다. 대부분의 애플리케이션 개발은 웹 기반으로 전환됐으며, 인터넷을 빼놓고선 소프트웨어를 말하기가 힘들어졌다.
그 다음은 무엇인가. 그것은 바로 '모바일'일 것이라는 점에 많은 사람이 공감을 표시하고 있다.
모바일 시장의 활성화에 따라 특수 목적의 운영체제나 하드웨어가 각광받을 것이고 더욱 더 많은 기술이 발표될 것이다. 현재도 기술은 약어집이 필요할 정도로 쏟아져 나오고 있다. 따라서 점차 코딩은 기술과 기술을 묶어내는 도구로 사용되고 있다.
많은 플랫폼과 언어가 흥망성쇠를 거듭해 가면서 발전하고 있지만, 기본과 기반기술은 크게 변하지 않았다. 하드웨어 구조 자체가 '폰 노이만 구조(stored program)'를 벗어나지 않고 있고, 소프트웨어의 모든 기술이 이전 기술을 바탕에 두고 시작하기 때문이다.
UML을 예로 들어보자. UML은 사실 전혀 새로운 것이 아니다. 이전의 여러 기술과 개념을 종합하고 표준화했을 뿐이다. C#도 C++, 스몰토크, 자바와 같은 조상 언어와 COM/DCOM에 기반해 만들어졌다.
필자는 개인적으로 향후 대중적인 프로그래밍 언어의 대표 주자는 자바와 C#이 될 것으로 점치고 있다. C#은 클래스 라이브러리 전체가 COM 객체로 구성돼 있고, 자바는 EJB를 통해 CBD를 지원하고 있다. JVM과 .NET 플랫폼을 보면 재사용 가능한 컴포넌트가 플랫폼에 흡수되는 것을 볼 수 있다.
이런 얘기가 사뭇 다르게 들릴지 몰라도, 조금만 깊게 살펴보면 실은 비슷한 개념의 기반기술을 배경으로 하고 있다는 것을 알 수 있다.
웹뿐만 아니라 다른 서비스에도 XML과 SOAP는 더욱 더 많이 쓰이게 될 것이다(XML과 SOAP는 여러 가지 기능을 묶어내고 연결하는 기본적인 방법이 될 것이다). 이제 개발자들에게는 네트워크를 기반으로 다양한 지식을 자신이 선호하는 언어로 요리해 엮어내고 연결할 수 있는 자질이 요구되고 있다. @
참고 문헌
Operating System Concepts 3/e, A. Silberschatz, J. Peterson, P. Galvin, Addison-Wesley, 1992
Digital Logic and Computer Design, M. Morris Mano, Prentice-Hall, 1979
Computer System Architecture 2/e, M. Morris Mano, Prentice-Hall, 1982
Computer Organization and Architecture: Principles of Structure and Function, William Stallings, Macmillan Publishing Company, 1987
Discrete Mathematics with Computer Science Applications, Skvarcius, Robinson, The Benjamin/Cummings Publishing Company, 1986
Fundamentals of Data Structures in C++, Ellis Horowitz, Sartaj Sahni and Dinesh Mehta, Computer Science Press, 1995
Introduction to Algorithms: A Creative Approach, Udi Manber, Addison-Wesley Publishing Company, 1989
Object Lifecycles, Sally Shlaer, Stephen J. Mellor, Prentice-Hall, New Jersey, 1992
Object-Oriented Modeling and Design, J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, W. Lorensen, Prentice-Hall, 1991
출처 : 마이크로 소프트 웨어
엠파스 블로그 '내일을 헤쳐가는 그대'에서 발췌..