리눅스 Tips, 리눅스 C/C++ 프로그래밍, 모바일 클라우드 동향 및 테스트 등

  Welcome to my Blog!

 °Name : KWON HAN SEUNG
 °Interested in
   - Linux
   - Mobile Cloud
   - Image Processing
   - Signal Processing

 Facebook Link...
 Univ. Team Link...

2012년 10월 30일 화요일

SDN 동향 : SDN과 Open Flow의 관계


SDN을 명확히!


(출처 : http://cafe.naver.com/openflow/275)

뚱딴지같이 이제와서 < SDN이란? >제목의 글을 쓰려고 합니다.
왜 그럴까요??

최근 SDN.. OpenFlow가 화제가 되면서.. 아주 이상한 형태의 말들과 플레이들이 감지됩니다.
따라서.. 정확한 정의가 필요할 듯 합니다.

SDN과 OpenFlow는 동급이 절대 아닙니다.
SDN은 오히려 하나의 아키텍쳐로 이해하시는 것이 좋습니다.

그럼.. 무슨 아키텍쳐냐?? 면...
Control Plane과 Data Plane이 물리적으로 완전 분리된 형태의 아키텍쳐입니다.

최근에 SDN이 Software Defined라는 것에 착안해서
Software적으로 무엇인가를 하면 SDN인 것으로 둔갑하는 아주 잘못된 정보들이 흘러다니고 있는데..
절대 그렇지 않습니다.

가령 대표적으로 잘못 인식되는 것이 API입니다.
API를 이용하면 네트워크 운용에 매우 큰 도움을 줍니다. 그렇다고 API 그 자체가 SDN일 수는 없습니다.

SDN은 Software적으로 뭔가 끼적끼적되면 SDN이 되는 것이 절대 아니라...
반드시 Control Plane과 Data Plane이 분리되어져 있는 아키텍쳐이여야만 SDN인 것입니다.

그렇다면 SDN은 오직 Control Plane과 Data Plane 영역에만 관심이 있을까요?
아니죠!!!! 절대 아니죠!!!




SDN의 영역은 Control Plane과 Data Plane을 넘어서 네트워크 전체 영역을 관장합니다.
실제 SDN 개발 프로젝트를 진행하게 되면 Application 단의 네트워크 운용과 관련된 부분을 매우 많이 고민합니다.

국내에서 OpenFlow가 Hot 이슈이다 보니까
OpenFlow = SDN으로 오해하게 되어서
SDN은 오직 Control Plane과 Data Plane 만을 다루는 것으로 오해하시는데.. 절대 그렇지 않습니다.
단.. OpenFlow는 Control Plane과 Data Plane에만 관심이 있습니다. (태생이 그런 프로토콜입니다!!!)

상용 SDN 제품들의 예를 들면..
SDN의 대표 주자인 NICIRA의 경우는 자사 솔루션과 다른 Application 간 연동을 통한 자동화 및 오케스트레이션에 매우 적극적으로 지원을 하고 있습니다. 또한 NEC 및 NTT도 SDN의 중요한 요소로서 정확한 네트워크 관리 및 운용에 초점을 두고 있습니다. 이를 위해 다양한 SDN Application을 내놓고 있습니다.

반드시 SDN과 관련하여 기억하실 내용은...
- SDN은 아키텍쳐로서 반드시 Control Plane과 Data Plane이 분리되어야 한다.
SDN은 Control Plane과 Data Plane의 울타리를 넘어서 네트워크 전체 영역에 초점을 두고 있다.

위은 내용은 매우 중요합니다.
이를 정확히 인지해야 SDN이 나온 배경 및 네트워크 변혁의 정당성 그리고.. 그를 통한 정확한 기대효과를 이해할 수 있게됩니다.

리눅스 C/C++ : 10장 포인터(1)

출처 http://coolprogramming.springnote.com    저작자 NetGong


1, 포인터란

 포인터는 C언어에서 '가리킨다', '지시한다'라는 의미로 사용됩니다. 포인터는 어떤 곳의 메모리 주소를 가리킵니다. 가리킨다는 말은 메모리의 주소를 알고 있다는 말입니다. 어떤 변수나 함수 등의 주소를 가리키는 개념을 포인터라합니다. 정리하면,

  • 포인터는 주소를 저장하는 변수(메모리)를 의미합니다.(단, 그 변수가 주소를 가지고 있어야 합니다.)
  • 주소를 저장하고 있기 때문에 언제든지 그 주소의 메모리에 방문?!할 수 있고 접근할 수 있습니다.
  • 그래서 포인터는 '주소를 가리킨다'고 말하는 것입니다. '주소를 참조한다'고도 합니다.

 포인터를 만드는 방법은 간단합니다. 포인터 변수를 만들고 그 포인터 변수가 주소를 가지면 포인터(참조)가 되는 것이지요. 문법은 간단합니다.

    int     n     =   10;
    int*   pn    =  &n;

  n 은 정수를 저장하기 위한 변수(메모리공간의 이름)를 의미합니다. pn은 주소(int형주소)를 저장하기 위한 변수(메모리공간의 이름)를 의미합니다. 그리고 n에는 정수 10을 저장했습니다. pn에는 주소 &n을 저장했습니다. 그러므로 pn은 주소를 가리킵니다. 그래서 pn을 포인터라 합니다. 정리하면 앞으로 포인터와 포인터변수는 같은 용어로 사용하겠습니다. pn을 포인터라합니다.

2, 포인터의 사용


void main( )
{    int n = 10;
     int * pn = &n;
     printf("%d\n", n);
     printf("%d\n", *pn);
}
  1. 10
  2. 10

 포인터 pn은 int형 시작주소 &n(12ff60)을 저장하고 있고 pn이 int형 주소이므로 pn 앞에 * 연산자를 붙인 *pn은 n과 같은 메모리가 된다. 아래 그림은 위 예제의 간단한 메모리표현이다.


 아래는 주소를 확인하는 예제입니다.

void main( )
{    int n = 10;
     int * pn = &n;
     printf("%x\n", &n);
     printf("%x\n", pn);
}
  1. 12ff60
    12ff60

 &n과 pn 모두 12ff60입니다. 당연한 결과죠. 그러면 pn과 &n의 차이는? pn은 변수이며 &n은 주소자체(상수)이다. 변수와 주소를 참고 하세요. 위쪽 그림을 보면 포인터 pn 도 4byte 메모리 공간을 갖습니다. 모든 포인터는 4byte 크기입니다. 자세한 내용은 아래에서 다시 하도록 하겠습니다. 정리하면,

  • n은 int형 변수이며 정수를 저장한다.
  • pn은 int*형 변수이며 주소(int형주소)를 저장한다.

 char형 포인터의 예제입니다.

void main( )
{    char c = 'A';
     char * pc = &c;
     printf("%c %c\n", c, *pc);
}
  1. A A

 c는 char형 변수이며 문자 'A' (정수 65)를 저장합니다. 'A'가 정수 65인지 다음에 설명합니다.
pc는 char*형 변수이며 시작주소 &c(주소 12ff60)를 저장합니다. *pc는 pc의 주소 메모리인 c 입니다. 아래 그림을 참고 하세요.


 포인터 변수 pc도  4byte 메모리 공간을 사용합니다. 모든 포인터 변수의 크기는 4byte입니다.
또 한가지 발견하셨나요?  int형 변수 주소는 int형 포인터에 char형 변수 주소는 char형 포인터에 저장해야 합니다. 이유는 다음에 설명하겠습니다. 지금은 무조건 같은 자료형에 저장해야 한다는 것만 알아 두세요.

 여기까지 잘 오셨습니다. 이제 여러분은 포인터가 무엇인지 어떻게 사용하는 지 확실히 알았습니다. 하지만 어디에 왜? 사용하는지는? 공부하지 않았습니다. 곧 공부하게 됩니다. 조금 기다리세요.

3, 포인터의 크기

 모든 포인터의 크기는 4byte라고 했죠? 그래서 준비했습니다. 확인을 해보도록 하죠. (단, 우리는 다른 언급이 없으면 32bit OS를 기준으로 설명한다고 했습니다. 그래서 포인터의 크기가 모두 4byte입니다. 만약 64bit OS라면 포인터의 크기는 64bit가 됩니다.)

 자 연산자 하나를 더 배우도록 하겠습니다. 연산자 sizeof()입니다. 이 연산자는 함수와 비슷하게 동작하는 연산자입니다. 이 연산자는 변수, 자료형등의 byte수를 구할 때 사용합니다.

void main( )
{    printf("%d\n", sizeof( char ) );
     printf("%d\n", sizeof( int ) );
     printf("%d\n", sizeof( double ) );
}
  1. 1
  2. 4
  3. 8

 간단하게 자료형의 크기를 구할 수 있습니다.

void main( )
{    char c=0;
     int n=0;
     double d=0;
     printf("%d\n", sizeof( c ) );
     printf("%d\n", sizeof( n ) );
     printf("%d\n", sizeof( d ) );
}
  1. 1
  2. 4
  3. 8

 변수의 크기는 당연히 자료형의 크기와 같습니다.

void main( )
{    printf("%d\n", sizeof( char* ) );
     printf("%d\n", sizeof( int* ) );
     printf("%d\n", sizeof( double* ) );
}
  1. 4
  2. 4
  3. 4
 포인터의 크기는 모두 4byte입니다.

void main( )
{    char* pc=0;
     int* pn=0;
     double* pd=0;
     printf("%d\n", sizeof( pc ) );
     printf("%d\n", sizeof( pn ) );
     printf("%d\n", sizeof( pd ) );
}
  1. 4
  2. 4
  3. 4
포인터 변수의 크기도 모두 4byte입니다.

 자 그럼 포인터의 크기는 왜? 4byte일까요? 32bit OS에서 주소의 크기는 4byte를 넘지 않기 때문입니다. 포인터란 주소를 저장하는 변수고 주소가 4byte를 넘지 않기 때문에 4byte이면 모든 어떤 주소든 저장이 가능하겠죠. 그래서 포인터는 모두 4byte의 크기를 갖는 겁니다. 아셨죠?

오늘은 여기까지... 수고 하셨습니다.
오늘은 반복을 강조하고 싶네요. 반복학습은 이해와 암기에 정말 효과적인 학습 방법입니다.

2012년 10월 26일 금요일

SDN 동향 : SDN이란 무엇인가?


SDN은 무엇인가? 그리고 왜 대두되었는가?

(출처 : http://katesfam.blogspot.kr/2012/01/sdn.html)

 수년 동안 컴퓨터 과학자들은 네트워크의 속도와 안정성, 에너지 효율, 보안 등을 획기적으로 개선시킬 수 있는 방법을 꿈꿔왔다. 그러나 그 방법을 설계하거나 고안하더라도, 실제로 대규모(large-scale)로 실험하거나 검증하는 것은 불가능했다. 인터넷의 코어(core)를 구성하는 라우터나 스위치들이 이른바 완전히 닫혀 있어서 그 위에서 새로운 소프트웨어나 프로그램을 실험하는 것이 원천적으로 봉쇄되었기 때문이다.

 이러한 연유로 연구되어온 많은 기술 중 SDNSoftware Defined Networking을 의미하며 우리말로 소프트웨어 정의 네트워킹이라 부른다. SDN은 OpenFlow라는 기술 혹은 소프트웨어를 통하여 널리 알려졌다. OpenFlowSDN은 뗄레야 뗄 수 없는 관계이다. SDN이 물론 더 큰 개념으로 네트워크 구조 혹은 새로운 패러다임이며, OpenFlowSDN을 위한 인터페이스 표준 기술로 정의된다. SDN을 지원하는 기술 중에서 학교, 연구소, 기업 등으로부터 가장 관심을 받는  OpenFlow는 별도로 설명하기로 하고 (이미 많은 참고자료가 나와 있기도 하다), 여기서는 먼저 SDN이 무엇인지, 그리고 왜 필요성이 부각되었는지에 대하여 몇 가지 레퍼런스를 바탕으로 다루어 보려 한다.

 먼저 위키피디어의 정의를 살펴보자. Kate Greene 2009년도3/4월 판 MIT 테크니컬 리뷰에서 소개한 용어로 알려져 있는 SDN은 네트워크 제어 기능(control plane)이 물리적 네트워크와 분리되어 있는 네트워크 구조를 말한다. 위키피디어에 따르면  SDN을 특징짓는 두 가지 중요한 포인트는 다음과 같다. 첫째, 네트워크 제어 기능을 데이터 전달 기능(data plane)과 분리하여 구현해야 한다. 둘째, 네트워크 제어 기능이 개발되고 실행될 수 있는 환경을 분리하여 전형적인 낮은 성능의 CPU가 장착된 하드웨어 스위치에 더 이상 위치시키지 않는다. 다시 말해서 SDN이라면 기본적으로 네트워크 제어 기능이 기존의 스위치나 라우터 등의 하드웨어와 별도로 분리되어야 하고, 데이터 전달 기능과도 역시 분리되어 개발 및 실행될 수 있는 네트워크 구조를 가져야 한다.

 분리된 SDN의 제어 기능은 필연적으로 네트워크 스위치(하드웨어) 상의 데이터 경로와 상호작용할 수 있는 기능을 가져야만 한다. 이러한 상호작용 혹은 통신 메커니즘 중의 하나가 바로 OpenFlow 기술이다. OpenFlow 흔히 SDN과 동일한 것으로 혼동되기도 하지만, 사실 SDN을 구성하는 하나의 요소로 제어 기능을 가진 머쉰과 네트워킹 스위치간의 통신을 담당하는 표준 인터페이스이다. 그리고, SDN의 범주 안에서 OpenFlow를 반드시 사용해야 한다는 아무런 제약이나 요구사항도 없다. 현재 SDNOpenFlow의 정의, 마켓팅 등의 이슈는 개방형 네트워킹 재단(Open Networking Foundation; ONF)에서 관리되고 있다

 그렇다면 ONF에서는 SDN을 어떻게 바라보고 있을까? 일단 ONF가 무엇인지부터 살펴보자. ONF (미연방세법을 따르며) 비영리, 상호 이익을 바탕으로 하는 국제 기구로 SDN의 개발과 활용을 촉진하는 것을 목표로 삼고 있다. ONF의 이사회는 여덟 명의 멤버로 구성되는데 여섯 개의 설립 회사가 각각 한 명씩 지정한 여섯 명의 이사와 두 명의 창립자이다. 여섯 개의 설립 회사는 대규모 네트워크 운영자 및 (잠재) 사용자 그룹을 대표하는 도이치 텔레콤(Deutsche Telecom), 페이스북(Facebook), 구글(Google), 마이크로소프트(Microsoft), 버라이즌(Verizon)과 야후(Yahoo)이며, 두 명의 창립자는 UC 버클리의 Scott Shenker와 스탠포드 대학의 Nick Mckeown이다. 그리고 이외에 사무총장(Executive Director) Dan PittONF를 총괄 관리한다.

 ONFSDN을 바라보는 관점은 크게 두 가지의 기본적인 원칙을 바탕으로 하고 있다.

 먼저 SDN은 소프트웨어 정의 포워딩(Software Defined Forwarding)을 해야 한다. 이것은 스위치와 같은 하드웨어가 수행하는 데이터 포워딩 기능이 반드시 개방형 인터페이스와 소프트웨어를 통해서 제어되어야만 한다는 것을 의미한다. 하드웨어는 소프트웨어로부터 [헤더 템플릿, 포워딩 액션셋을 받아 특정한 액션(action)을 실행한다. 예를 들면 어떤 네트워크 포트로 패킷을 전달(forwarding)”하거나 혹은 폐기(drop)”할 수 있다. 다만 해당 특정 액션은 [헤더 템플릿, 포워딩 액션]헤더 템플릿에 상응하는 패킷에 대해서만 실행된다. 여기서 헤더 템플릿은 모든 패킷혹은 어떤 패킷의 그룹등을 의미하는 와일드 카드를 포함할 수 도 있다. 앞서 언급되었듯이 SDN의 소프트웨어 정의 포워딩은 반드시 개방형 인터페이스와 소프트웨어를 포함하는데, OpenFlow 기술이 개방형 인터페이스에 해당된다.

 그리고, 두 번째 원칙은 SDN이 추상화된 글로벌 관리 혹은 글로벌 관리 추상화(Global Management Abstraction)를 목표로 한다는 것이다. SDN은 기본적인 글로벌 관리 추상화를 지원함으로서 보다 선도적인 네트워크 관리 툴이 개발될 수 있도록 해야 한다. 예를 들면 이런 추상화 도구들은 네트워크의 글로벌 뷰, 네트워크 이벤트(토폴로지 변화나 새로운 플로우 생성 등)에 따른 반응, 그리고 네트워크 요소를 제어할 수 있는 기능 등을 포함할 수 있다. (네트워크 요소 제어는 해당 엔트리를 하드웨어의 포워딩 테이블에 넣는 방법을 사용한다.)

 따라서, ONF 가 바라보는 SDN은 두 가지, ,  소프트웨어 정의 포워딩과 글로벌 관리 추상화가 핵심이다. 그리고, 이를 위해서 개방형 인터페이스(: OpenFlow), 제어 소프트웨어, 글로벌 네트워크 관리 툴 등의 세부적인 기능이 언급되었다.

 여기서 잠시 위키피디어로 돌아가보자. 위키피디어에서 언급한 Kate Greene (과학기술 저널리스트)이 작성한 테크니컬 리뷰의 내용을 보면 SDN이 무엇인지 개괄적으로 잘 정리되어 있다. 이 기사에 따르면 ONF의 창립자 중 하나이자 이사회 멤버이며, OpenFlow 기술과 표준을 개발한 Nick McKeown이 다음과 같이 말한다. “오늘날 보안, 라우팅, 에너지 효율 관리 등은 단지 기계덩어리인 네트워크 장비에 의해 좌지우지됩니다. 그건 정말 바꾸기 힘들지요. 이것이 바로 인터넷 인프라가 40년 동안이나 변하지 않은 이유입니다.” 일반적으로 데이터 패킷이 스위치 (혹은 라우터)에 도착하면 스위치의 펌웨어가 해당 패킷의 목적지 주소를 보고 그 패킷을 이미 정해진 규칙에 따라 포워딩(forwarding)한다. 정해진 규칙은 네트워크 운영자도 제어하기 어렵다. 같은 목적지를 갖는 모든 패킷은 같은 경로를 이용하고 언제나 같은 방식으로 다루어진다. 이것이 현대 인터넷의 일반적인 패킷 포워딩 방식이다.

 SDN은 무엇인가? 라는 질문의 답은 바로 현재 인터넷이 가지고 있는 항상 같은 방식이며 제어가 어려운패킷 포워딩 방식을 바꾸는 것으로 부터 시작한다. OpenFlow의 예를 들면, 그 전에는 거의 아무도 손대기 어려웠던 종단간 네트워크 경로를 컴퓨터 과학자들이 쉽게 변경할 수 있도록 지원하여 e-mail보다 비디오 어플리케이션이 우선 데이터를 받을 수 있도록 하거나 다양한 트래픽을 각자 다른 경로로 보낼 수도 있고, 어떤 트래픽은 보안 목적으로 격리할 수 있도록 해준다.

 그렇다면 패킷 포워딩 방식을 바꾸는 것이 바로 SDN인가? 그렇지 않다. 이것은 SDN이라는 큰 구조를 구성하는 하나의 요소일 뿐이다. OpenFlow가 바로 패킷 포워딩 방식을 표준화된 방법으로 바꿀 수 있는 하나의 콤포넌트이다. SDN은 아키텍쳐 혹은 프레임을 제공하는 큰 개념이자 구조 혹은 패러다임으로, 하드웨어와 어플리케이션, 하드웨어 추상화 계층, 하드웨어와 분리된 제어 기능(control plane, controller 등으로 불리운다.), 하드웨어 추상화 계층과 통신하는 표준 기능 등을 모두 포함한다.

 SDN 구조에서, 하드웨어는 스위치나 라우터 등이며, 시스코, 쥬니퍼, HP, NEC 등이 개발하고 현재 인터넷 공급자들이 서비스를 제공하기 위하여 설치하고 운영하는 하드웨어 박스(box)를 의미한다. Nick이 말했듯이 오늘날의 인터넷이 거의 변화하지 못한 가장 큰 원인을 제공하는 주범들이다. SDN은 이 기계덩어리에 하드웨어 추상화를 위한 계층을 더해준다. , OpenFlow와 같이 표준화된 인터페이스를 통하여 하드웨어에 접근하고, 소프트웨어에 기반하여 하드웨어를 통제할 수 있는 기반을 제공하는 것이다. 이 추상화 계층은 OpenFlow의 플로우 테이블과 같은 형태로 하드웨어에 구현되어야 한다. 따라서 하드웨어 벤더들의 지원과 협력이 필수적이다. (현재 약 16개의 주요 네트워크 벤더들이 구현했거나 구현중이다. SDN을 지향하는 OpenFlow가 가장 선두에서 탄력받는 기술로 주목받고 있는 배경이기도 하다.) 추상화 계층을 구현함에 있어서 가장 중요한 것은 표준화된 인터페이스를 지원하는 것이고, 두 번째는 각 개별 벤더가 원하는 독립성을 보장해 주는 것이다. 개별 벤더의 고유 기술이나 고유 기능은 자치적으로 보장되면서 SDN을 위한 표준화된 통로를 제공하는 것은 벤더 고유의 기술을 보호하면서도 호환성을 유지하는데 있어서 필수적이며, OpenFlow의 경우 이를 매우 잘 준수하고 있다.

 다음으로 무엇보다 중요한 요소는 바로 하드웨어와 분리된 제어 기능이다. Controller로 불리기도 하는 이 제어 기능은 스위치나 라우터가 아닌 별도의 머쉰 상에서 구현된다. 머쉰은 PC가 될 수도 있고 성능 좋은 서버가 될 수도 있다. 이 제어 기능은 두 가지 SDN의 다른 두 가지 콤포넌트와 상호작용한다. 한 가지가 어플리케이션이고 다른 한 가지는 하드웨어, 좀 더 정확히는 하드웨어에 구현된 추상화 계층이다. 따라서 제어 기능을 통해서 어플리케이션은 네트워크의 다양한 정보를 얻을 수 있고, 반대로 네트워크 역시 어플리케이션 요구 사항 등의 정보를 얻을 수 있다. 이러한 핵심적인 역할 때문에, 제어 기능은 종종 네트워크 운영 체제(Network OS)라고 불리우기도 한다.

 제어 기능은 주로 API와 같은 방법을 통해서 어플리케이션이 원하는 기능을 제공한다. 반대의 경우도 거의 같다. 어플리케이션과 제어 기능 간의 통로인 셈이다. 그렇다면 제어 기능과 하드웨어 추상화 계층의 통신은 어떻게 이루어질까? OpenFlow가 이 질문에 대한 해답이며, 이미 많은 연구가 진행되어 있다.

 지금까지 위키피디어, ONF, 테크니컬 리뷰 등을 인용하고 몇 가지 살을 붙여 SDN이 무엇인지 정리해 보았다. SDN은 새로운 네트워크 구조이며 패러다임이다. 그럼 이제부터 SDN이 왜 대두되었는지 알아보기로 하자.

 가장 최근에 ONF가 개최한 컨퍼런스인 Open Networking Summit(2011 10)에서 ONF의 또 다른 창립자인 Scott Shenker가 발표한 내용을 보면, 인터넷이 이렇게 크게 성공한 가장 큰 이유가 바로 계층화(layering)”에 있다는 것을 알 수 있다. 어플리케이션(WWW, e-mail )은 신뢰성/비신뢰성 트랜스포트 계층(TCP/UDP)위에서 돌아가고, 트랜스포트 계층은 최선형 글로벌 패킷 전달 계층(IP)위에서 동작되며, IP는 최선형 로컬 패킷 전달 계층(Ethernet, PPP )위에서, 다시 로컬 패킷 전달 계층은 물리 계층(copper, fiber, radio) 위에서 돌아가는 것이 바로 계층화이다. , 서로 다른 계층이 독립적으로 동작하되 상/하위 계층과 상호 호환되는 방식으로 일종의 혁신을 이룬 것이다. 덕분에 인터넷은 교육/연구망으로 시작되었으나, 상업적으로도 엄청난 성공을 거두었고 지금까지 가장 널리 사용되고 있다. 그러나, 초창기 개발된 인터넷 구조나 모델이 아직도 거의 그대로 사용되고 있는 형편으로, 상업적인 성공이 또 다른 인터넷의 혁신으로 이어지지 못했다. 왜 그럴까?

 이 질문에 답하기 전에 먼저 다른 분야를 한 번 살펴보자. 예를 들어 컴퓨터 운영체제(OS), 데이터베이스(DB), 분산 시스템 등은 소프트웨어의 연구 개발을 통해 발전해 왔다. 학교에서 배우는 기본적 원리들을 바탕으로 소프트웨어를 개발하고 진화시킨 것이다. 이러한 소프트웨어는 우리가 흔히 알고 있는 고급 프로그래밍 언어로 작성할 수 있으며, 새로운 기능이 필요한 경우 기존에 개발된 소프트웨어를 바탕으로 새로운 버젼으로 계속해서 발전할 수 있다. LINUX, Windows, Mac OS 등이 이런 방식으로 진화해온 대표적인 OS 이다. 데이터베이스나 분산 시스템도 마찬가지이다. 그리고 이러한 소프트웨어 기반의 시스템들은 대부분 편리하고 쉬운 유저 인터페이스를 통해 쉽게 관리 가능한 환경을 가지고 있다.

 그렇다면 네트워크는 어떤가? 일단 학교에서 OSI 7 계층 부터 시작하여 TCP, IP 등등의 여러 프로토콜에 대해서 배운다. 이들의 기본적 원리나 알고리즘에 대해서도 배우지만 대부분 동작 원리 등 실용적인 부분에 집중된다. 물론 이른바 단말 시스템(end-system)에서 돌아가는 TCP 등의 일부 프로토콜은 기능이 향상된 버젼이 개발되었거나 개발이 진행 중이다. 하지만, 단말과 단말 사이에서 데이터 전송과 전달을 담당하는 액세스/코어 네트워크의 프로토콜이나 기타 기능들은 이미 대부분 개발이 끝나서 적용 및 서비스 되고 있는 단계이기 때문에 새롭게 수정하거나 개발하기 어렵다. 물론 네트워크 벤더의 경우 추가적인 기술 개발과 적용이 가능하지만 DB나 운영체제 등과 비교할 때 많은 진보가 일어나지는 않았다. 네트워크 분야의 경우, 소프트웨어와 프로그래밍을 바탕으로 한 새로운 기술의 개발과 진화가 다른 컴퓨터 과학 분야와 비교할 때 그 발전이 더디게 진행되어 온 것은 분명한 사실이다. 네트워크 관리 환경은 어떠한가? 일부 운영자나 엔지니어에게 종속되어 있는데다가 그 마저도 사용하기가 쉽지 않다. 전문적인 지식이나 기술을 요구하는 경우도 많다. 최근 몇 몇 연구들이 이러한 관리 환경을 개선하는데 그 촛점을 맞추고 있긴 하지만 다른 분야에 비해서 부족한 상태로, 매우 불편한 사용자 인터페이스를 가지고 있다.

 구조는 단순하지만 관리가 복잡하고 인터페이스는 어렵다.” 이것이 현재의 인터넷이 가지고 있는 가장 큰 문제로 귀결된다. SDN이 대두된 이유는 바로 이 화두를 타파하기 위해서이다.

 단순한 구조는 물론 장점이다. 덕분에 인터넷이 오늘날의 압도적 지위를 누리게 되었다. 하지만 단순성을 유지하기 위하여 새로운 기술을 적용하거나 소프트웨어를 개발하는 측면에서 다른 분야에 비해 큰 제약을 가져야만 했다. 이와 같은 단점을 보완하기 위하여 SDN은 제어 프레임워크, 혹은 제어 기능을 분리하여 소프트웨어의 개발을 촉진시키고자 한다. , 소프트웨어를 기반으로 인터넷이 보다 빠른 속도로 진화할 수 있도록 하자는 것이다. 그리고, SDN은 관리의 복잡성을 해소하기 위하여 제어 기능을 기존 하드웨어에서 분리시키고, 사용자 인터페이스를 매우 단순하고 편리하게 만듦으로써 엔지니어, 운영자 뿐만 아니라 일반 사용자도 쉽게 네트워크를 관리하고, 가상 네트워크를 생성하여 이용할 수 있도록 한다.

 참고로, 미래인터넷 포럼(FIF)의 테스트베드 워킹그룹 이슈 분석서 #2에서 Nick의 발표 내용을 정리한 부분을 보면, SDN이 어떻게 인터넷이 가지고 있는 문제를 해결하고 혁신을 가속화할 수 있는지에 대하여 잘 알 수 있다.

Nick SDN을 기반으로 한 새로운 패러다임을 만들어 내어서 아래와 같은 혜택들을 누리는 네트워크 장치 생태계를 만드는 것이 필요하다고 역설한다.
1.     데이터 전달(Forwarding) 추상화에 따라서 OpenFlow 표준에 의해 검증된   하드웨어를 이용하고, 또한 이에 연동하여 소프트웨어 기반으로 제공되는 네트워킹 특성이 요구되는 모든 절차들에 대해서 각각의 절차마다 충분하게 증빙되어 있는 견실한 네트워킹을 실현할 수 있는 토대를 구축할 수 있다.
2.     장비 사용자들이 자신의 필요에 따라 네트워킹을 유연하게 구성(customize)하고 불필요한 구성요소들은 과감하게 제거하면서 자신만을 위해서 가상화된 네트워크를 생성하기 쉽도록 지원한다.
3.     하드웨어 추상화(abstraction)에 따라 확장성을 고려한 상태에서 공통화된( commodity 형식으로) 하드웨어를 구입하고, 또한 소프트웨어도 분리해서 구입하도록 하여 기존의 폐쇄적인 네트워크 장비 공급자 체인을 벗어나서 자체 개발, 외주 개발, 오픈 소스 형식을 모두 포함하는 다변화된 공급자 체인으로 체질 개선을 유도할 수 있다.
4.     소프트웨어를 개발하는 속도로 혁신이 일어나도록 하고, 표준은 구현된 소프트웨어의 확산을 위해 뒤따라가는 방식을 취하고, 소프트웨어적인 개방성에 근간하여 기술의 공유 협력을 쉽도록 함으로써 혁신의 속도를 가속하도록 지원한다.

 다른 내용도 모두 중요하지만, 특히 4번에 주목하자. 소프트웨어를 개발하는 속도로 혁신이 일어나게 하고 표준은 이를 뒤따르게 하자는 것이야 말로 SDN이 왜 대두되었는지 설명하는 핵심 중의 핵심이라 할 수 있다.

 내용이 두서없이 길어진 듯 하다. 이제 정리해보자. SDN은 아직도 정의되고 있는 단계이다. 본문에서 언급한 여러 레퍼런스를 보면 분명히 주된 맥락은 있지만 모호하고 추상적인 부분도 있는 것이 사실이다. 따라서 앞으로 SDN의 모습은 계속해서 변화될 가능성이 틀림없이 존재한다. 그렇지만 주된 맥락을 고려할 때 SDN은 현재 시점에서 다음과 같이 정의될 수 있을 것이라 생각한다. 

 "SDN은 이른바 소프트웨어를 통해서 현재의 인터넷이 가지는 구조적 문제를 근본적으로 해결하고 혁신할 수 있도록 대두된 새로운 네트워크 구조 혹은 패러다임으로써, 어플리케이션, 네트워크 OS, 하드웨어 추상화, 표준화된 인터페이스 및 하드웨어를 모두 아우르는 개념"이다.

Mobile Cloud 동향 : 모바일 클라우드의 변화


진화하는 클라우드 모바일의 변화를 이끈다 

(김종대 책임연구원)



멀티 디바이스 시대의 도래

 지난 7월, 이미 국내 스마트폰 가입자가 1,500만명을 돌파하였고, 2012년말 경에는 약 4,000만명에 달하여 거의 모든 이동통신 가입자가 스마트폰을 이용하게 될 것으로 예상되고 있다. 뿐만 아니라 스마트패드 역시 아이패드를 중심으로 활발히 판매되고 있으며, 스마트TV도 꾸준히 보급되고 있다. 자동차도 점차 스마트카로 진화하고 있다.대부분의 사람들이 PC와 스마트폰 나아가서는 스마트패드와 스마트TV, 스마트카를 동시에 보유하는, 즉 한 명이 최소 2개 이상의 단말기를 이용하는 멀티 디바이스 시대가 열리고 있는 것이다.

 1인당 여러 대의 단말 보유, 이동성 향상 요구, 4G 확산 등 모바일 시장을 중심으로 한 환경 변화로 인해 이전보다 한층 진화된 클라우드 기반의 서비스가 다양하게 등장할 것으로 예상된다. 활용 범위 측면에서는 스토리지 서비스가 보다 다양한 모바일 서비스와 결합되고 있으며, 이용 자원 측면에서는 복잡한 연산을 위해 단말기의 CPU 뿐만 아니라 인터넷 상의 IT 자원까지 활용하는 클라우드 프로세싱이 확산되고 있다. 그리고 구현 방식 측면에서는 모바일 네트워크 성능이 향상되면서 모든 것을 인터넷 상의 서버에서 처리하고 단말은 단순히 디스플레이 역할만 하는 클라우드 스트리밍 방식까지 등장하고 있다.

 이러한 클라우드의 진화는 클라우드 시장 뿐만 아니라 모바일 시장 전반에 걸쳐 영향을 끼칠 것이다.
클라우드와 OS, 하드웨어를 결합하여 최적의 사용자 경험을 제공하는 사업자가 모바일 시장의 변화를 이끌 것으로 보인다.


모바일 클라우드 시장 전망 및 성장 동인 분석

□ 모바일 클라우드 시장 폭발적 성장
  ◦ 2015년 전 세계에서 2억 4,000만 명에 달하는 비즈니스 사용자들이 클라우드 컴퓨팅을 이용하고 이를 통해 52억 달러의 매출이 발생할 것으로 전망 (ABI Research, 2010)
   - 모바일 클라우드 기반 애플리케이션 시장은 2009년 4억 달러에서 2014년 95억 달러로 연평균 88%의 성장률을 기록할 전망
  ◦ 국내 모바일 클라우드 응용시장 규모도 2009년 212억 원에서 연평균 107.1%씩 성장, 2013년 8,100억 원에 이를 전망 (Juniper Research, 2010)

□ 차세대 이동통신망의 등장 
  ◦ 기존 3G 대비 통신 속도 5배 증가, 지연 시간 1/10 수준인 4G 시대 개막
  ◦ 실시간 스트리밍 서비스 확산, N스크린 기반의 개인 맞춤형 컨텐츠 서비스의 활성화

□ 클라우드 컴퓨팅 기반의 스마트워크 수요 증가
  ◦ 그룹웨어 중심의 모바일 오피스에서 UC&협업 중심의 업무환경으로 고도화
  ◦ 모바일 상에서의 ERP, CRM, BI 등과 같은 미션크리티컬 업무 지원에 대한 조직원들의 요구 증가

□ 스마트폰․태블릿 PC․컨넥티드 단말 시장 고속 성장
  ◦ 2011년 스마트폰 판매량 4억 2천만 대, 2016년 10억 대 상회 전망 (IMS리서치, 2011)
  ◦ 2015년 美 태블릿 판매량은 4천 4백만 대로 노트북 판매량 3,980 만 대보다 500만 대 가량 추월(포레스터리서치, 2011)
   - 데스크톱PC 판매량은 1,820만 대에 불과


                [ 미국내 데스크톱PC, 노트북, 태블릿PC 판매량 변화]

자료 : 포레스터리서치(2011)


□ 모바일 OS 종속성 탈피, 콘텐츠 및 서비스 이용 편의성 증대 요구 증가
  ◦ 모바일 OS에 종속되지 않은 앱 및 애플리케이션 개발 요구 증가
    - 오픈소스 기반의 개방형 모바일OS 플랫폼 강조
  ◦ 모바일 단말 간 데이터 및 콘텐츠의 자유로운 이용에 대한 소비자 요구 증가(N-Screen 서비스)


모바일 서비스의 한계

 2011년 9월말 현재, 애플 앱스토어에는 약 47만개, 구글 안드로이드 마켓에는 약 29만개의 앱이 등록되어 있다.
언뜻 보기에 이미 세상의 모든 서비스가 등록되어 있는 것처럼 생각될 수 있다.
그러나, 대용량 통계 데이터 처리, ▶고화질 동영상 인코딩, ▶3D 그래픽 편집, ▶P2P 파일 전송 등 이미 PC 환경에서는 구현되어 있지만, 아직도 스마트폰용 어플리케이션 프로세서(AP) 성능 상의 문제 또는 배터리의 제약 등으로 인해 모바일 환경에서는 이용할 수 없는 서비스가 다수 존재한다.

 이러한 서비스 중 매우 높은 빈도의 키보드 입력을 필요로 하는 문서 작업 등을 제외하면 대부분 모바일 환경에서도 유용한 서비스들이지만, 모바일 단말기만으로 구현하는 것은 기술적으로 수년 내에 현실화되기 어려울 것으로 예상된다.

 현존하는 모바일 최고 성능의 어플리케이션 프로세서라고 해도 가장 저렴한 넷북용 CPU의 성능에 미치지 못하고 있으며, 배터리도 다양한 차세대 배터리들이 연구 단계에 있지만 실제 상용화 가능한 기술만 감안할 경우 3~5년 내에 20% 이상의 밀도 향상을 기대하기는 어려울 것이기 때문이다.





클라우드 프로세싱을 통한 모바일 서비스의 한계 극복

 따라서, 대용량 통계 처리, 3D 그래픽 편집과 같이 매우 높은 성능의 CPU를 필요로 하는 앱, 또는 P2P 파일 전송과 같이 장시간 동안 실행시켜 놓고 통신을 해야 해서 많은 양의 전력을 소모하는 앱을 포함하여 모든 모바일 서비스를 제약 없이 구현하기 위해서는 스토리지 뿐만 아니라 프로세싱까지도 인터넷 상의 IT 자원을 활용하는 고도화된 클라우드가 필수적일 것이다.

 예를 들어, 3D 그래픽 편집은 사용자가 모델링한 물체에 표면과 배경, 광원, 시야 각도 등을 종합적으로 고려하여 최종적인 화면을 만드는 렌더링 과정에서 가장 많은 프로세싱이 요구되는데, 오토데스크(Autodesk)의 네온(Neon)이라는 클라우드 렌더링 서비스는 최고 성능의 PC에서도 80분이나 소요되는 3D 렌더링 작업을 단 8분만에 완료해 준다.

 이러한 렌더링 서비스를 모바일에 접목한다면 현 수준의 스마트폰 어플리케이션 프로세서로도 PC와 유사한 수준의 3D 그래픽 편집이 가능해질 수 있는 것이다. 설혹 스마트폰 어플리케이션 프로세서가 비약적인 성능 발전을 이룬다고 해도 3D 렌더링과 같이 많은 연산이 필요한 작업의 경우, 클라우드 프로세싱을 이용하는 것이 단말기의 전력을 적게 사용하기 때문에 보다 효과적인 대안이라 할 수 있다.

 또한, P2P 파일 전송의 경우도 클라우드 프로세싱을 이용하여 전력 효율적으로 구현할 수 있다.
전송 속도가 느리면 오랫동안 앱을 실행시켜두어야 하는데, 전송 속도와 관계 없이 배터리 소모는 지속적으로 일어나게 된다. 따라서 외부의 서버에 파일을 전송 받고, 일정량 이상 다운로드 받으면 스마트폰에 전송하는 방식으로 비효율적인 배터리 소모를 예방할 수 있는 것이다.

 따라서, 모빌리티에 대한 요구가 지속될수록, 단말기의 크기와 무게의 변화 없이도 모든 서비스를 제약 없이 모바일로 구현할 수 있고 전력을 효율적으로 사용하여 단말기를 보다 오래 사용할 수 있도록 하는 클라우드 프로세싱은 앞으로 더욱 확산될 전망이다.


4G 시대, 클라우드 스트리밍의 등장

 인터넷의 IT 자원을 단말기와 함께 묶어서 데이터 스토리지 또는 프로세싱에 사용하는 클라우드는 구현하는 방식에 따라 여러 가지 유형이 존재할 수 있는데, 현재 크게 3가지가 존재하고 있다.

 단말기의 OS 위에 설치하여 이용하는 네이티브 앱(Native App) 방식과 웹브라우저를 통해 웹사이트에 접속하여 이용하는 웹 앱(Web App) 방식, 그리고 네이티브 앱 방식과 웹 앱 방식을 적절히 혼합한 하이브리드 앱 방식이 그것이다.

 그런데 4G 시대가 본격적으로 개막되면서 3가지 유형 외에 클라우드 스트리밍(Cloud Streaming)이라는 궁극적인 클라우드 방식이 추가적으로 등장하고 있다.





네이티브 앱 방식의 장단점

 네이티브 앱은 4가지 방식 중 클라우드를 가장 적게 이용하는 방식이다. 대부분은 단말기에 설치해두고, 멀티 디바이스 간 공유가 필요한 데이터, 매우 복잡한 연산 등에 한해서 클라우드 스토리지 또는 클라우드 프로세싱을 적용하게 된다. 그래서 UI를 자유롭게 구성할 수 있고, 빠르게 동작할 수 있으며, 네트워크에 일시적인 단절이 있어도 큰 불편 없이 이용할 수 있다는 장점이 있다. 그러나, OS에 의존적이기 때문에 동일한 서비스를 위한 앱이라고 해도 OS별로 각각 개발해야 한다는 단점이 있다. 

 이미 개발된 앱이라도 타 OS로 추가 개발할 경우, 최초 개발비의 약 30% 내외가 소요된다는 점을 감안하면, iOS, 안드로이드, 윈도우폰 등 다수의 모바일 단말용 OS와 PC용 OS, TV용 OS가 병존하는 현 상황은 개발자 입장에서 상당한 비용 부담이 될 것이다. 이는 모바일 서비스 개발을 위축시키고 결국 사용자가 보다 다양한 서비스를 이용하는 데에 부정적 요인으로 작용할 것이다.



                        [ 전세계 스마트폰 운영체제 시장점유율 및 연평균성장률 전망(2011~2015년) ]



웹 앱 방식의 장단점

 반면, 웹 앱은 모든 데이터와 앱이 인터넷 상의 서버에 저장되어 있고, 사용자는 단말기의 웹브라우저를 통해 서버의 데이터와 앱을 HTML이라는 표준 프로토콜로 받아오는 방식이다. 프로세싱은 단말기만으로 이루어질 수도, 인터넷의 IT 자원을 함께 이용할 수도 있다. 이러한 과정으로 실행되는 웹 앱의 특성상 OS가 아무리 많아도 개발자는 하나의 웹 앱만 개발하면 되므로 개발비와 이후의 업그레이드 비용이 대폭 절감된다는 장점이 있다.

 그러나, 웹 앱은 속도가 느리고 단말기의 변화를 빠르게 수용하지 못한다는 치명적인 단점이 있다.
과거 HTML4까지는 속도 외에도 주소록, 통화기록, 메시지 등 단말기의 주요 리소스에 접근하기 어렵고, GPS, 카메라, 가속도계 등 일부 인터페이스를 이용할 수 없으며, UI의 자유도가 현저히 떨어진다는 단점도 있었으나, 최근 HTML5가 보급되면서 이러한 문제들은 대부분 사라졌다.

 그러나 C언어 대비 Java의 느린 처리 속도, HTML의 해석(Parsing) 과정으로 인한 지연, 웹페이지 전환 시마다 네트워크를 연결, 종료하면서 발생하는 지연 등으로 인해 네이티브 앱 대비 느린 속도는 구조적으로 해결하기 매우 어렵다. 따라서, 네트워크 액션 게임과 같이 빠른 조작과 빠른 응답 속도가 필요한 서비스의 경우 웹 앱 방식으로 구현하기가 거의 불가능하며, 구현하더라도 실제 이용에 있어서 지연으로 인한 불편함이 다소 발생할 수 밖에 없다. 또한 웹 앱의 경우 설치는 불필요하다고 하지만, 그렇기 때문에 최초에 필요한 최소의 데이터와 앱을 다운로드 받는 로딩 시간이 소요된다. 따라서, 간단한 서비스는 아무 문제가 없겠지만, 복잡한 서비스의 경우 웹 앱으로 구현한다면 긴 로딩 시간으로 인해 사용자에게 불편함을 야기할 수도 있다.


하이브리드 앱 방식의 장단점

 하이브리드 앱은 네이티브 앱의 개발 비용에 대한 부담과 웹 앱의 느린 속도를 보완하고자 두 가지를 적절히 혼합하여 서비스를 구현하는 방식이다. UI부분은 네이티브 앱의 방식을, 서비스가 동작하는 부분은 웹 앱의 방식을 적용하는 것이다. 물론 두 방식의 단점을 일부 보완할 수는 있겠지만, 하이브리드 앱의 경우도 한계는 존재한다. 두 방식의 단점을 모두 가지고 있게 되는 것이다. 네이티브 앱의 개발 비용보다는 낮겠지만 OS별로 각각 개발해야 한다는 점은 동일하며, 웹 앱 보다 UI 조작 속도는 월등히 빠르겠지만, 빠른 조작이 필요한 서비스는 웹 앱과 마찬가지로 구현하기 어렵다는 단점이 있다.


궁극의 클라우드, 클라우드 스트리밍

 이러한 네이티브 앱, 웹 앱, 하이브리드 앱 등 여러 방식에 존재할 수 밖에 없는 단점을 모두 극복할 수 있는 대안이 클라우드 스트리밍이라 할 수 있다. 클라우드 스트리밍은 데이터 및 앱을 저장하는 것뿐만 아니라 앱을 실행하는 프로세싱까지도 모두 인터넷 상의 서버에서 이루어지는 방식을 의미한다. 단말기에서는 단지 서버에서 생성된 화면 이미지만을 전송 받아 디스플레이하는 역할만을 수행한다. 단말기의 성능 자체가 무의미해지는, 클라우드의 가장 궁극적인 형태라고 할 수 있다.

 클라우드 스트리밍을 이용할 경우, 서버의 무제한에 가까운 처리 성능을 이용하기 때문에 모바일 단말에서 네이티브 앱을 실행하는 것보다 빠르게 동작할 수 있으며, 서버에 여러 종류의 OS를 동시에 설치하여 이용할 수 있기 때문에 개발자는 하나의 OS만 대응하여 앱을 개발해도 클라우드 스트리밍을 이용하는 모든 사용자가 앱을 이용할 수 있게 되는 것이다.

 또한, 수GB에 달하는 대용량 앱이라고 해도 이미 서버에 설치되어 있고, 사용자는 실행만 하여 화면 이미지만을 전송 받으면 되기 때문에, 네이티브 앱과 같이 다운로드 받고 설치하는 과정 또는 웹 앱과 같이 로딩하는 과정이 전혀 없이 바로 이용할 수 있다는 장점도 있다.

 예를 들어, 클라우드 게임 업체인 온라이브(OnLive)의 경우, 클라우드 스트리밍 방식을 이용하여 아이패드에서 플레이스테이션용 3D게임을 구현한 바가 있다. 플레이스테이션에 비해 매우 낮은 성능의 단말기로도 고품질의 3D게임을 무리 없이 이용할 수 있는 것이다.

 이처럼 클라우드 스트리밍이 완벽하게 구현될 수 있다면, 단말기에 설치되어 있는 OS와 무관하게 윈도우XP용 어플리케이션도, iOS 또는 맥OS용 어플리케이션도, 플레이스테이션 또는 Xbox와 같은 게임기용 어플리케이션도 모두 하나의 단말기로 이용할 수 있게 될 것이다.

 게임 콘텐츠 또한 모두 구입할 필요도 설치할 필요도 없이 원하는 때 콘텐츠를 이용하고, 이용한 시간만큼 금액을 지불하면 되는 것이다. 그렇게 된다면 훨씬 저렴한 비용에 다양한 콘텐츠 및 서비스를 이용할 수 있는 환경이 구현되는 것이다.


클라우드로 인한 모바일 시장의 변화

 클라우드 스트리밍까지 포함하여 클라우드의 4가지 구현 방식은 각기 장단점이 있으며 상당기간 하나의 방식으로 통합될 수는 없을 것이다. 오히려 각 구현 방식은 상호 보완적인 관계에 있을 것으로 전망된다. 물론 치열한 경쟁이 존재하겠지만, 클라우드 서비스 업체끼리 또는 클라우드 구현 방식끼리의 경쟁이 아니라 각 모바일 서비스별로 어떤 클라우드 구현 방식이 가장 적합하며, 이를 위해 OS와 하드웨어는 어떻게 구성되어야 하는지를 찾기 위한 경쟁이 이루어질 것으로 예상된다.

 어떤 클라우드 방식을 이용하느냐에 따라 단말과 서비스를 포함한 모바일 전반의 사용자 경험이 판이하게 달라지기 때문이다. 클라우드 서비스 업체뿐만 아니라 OS 업체, 제조사, 통신업체 등 다양한 업체가 복잡한 관계를 맺고 경쟁을 하게 될 것이며, 그 경쟁의 결과는 단순히 클라우드 시장이 아니라 모바일 시장 전반에 영향을 끼칠 것이다.

 과거 피처폰 시절 하드웨어를 중심으로 경쟁을 했다면, 스마트폰 시대에는 OS와 하드웨어 조합의 최적화를 통해 경쟁을 했다고 할 수 있다. 하드웨어 스펙이 다소 낮더라도 OS가 효율적이고 사용하기 편리하면 전반적인 사용자 경험은 오히려 우수할 수 있기 때문이다. 마찬가지로 앞으로 다가올 클라우드 시대에는 클라우드와 OS, 하드웨어를 결합하여 속도, 편의성, 가격, 서비스 다양성 등이 최적화된 사용자 경험을 제공하는 사업자 또는 사업자들의 연합이 전체 모바일 생태계를 주도할 가능성이 높을 것으로 전망된다.




2012년 10월 25일 목요일

리눅스 C/C++ : 9장 배열과 문자열

 

배열과 문자열

8장의 1번 문제를 풀어보도록 하자. hello world를 찍는 문제였다. char 하나에는 하나의 문자만 들어갈 수 있으므로 공백문자까지 포함 11개의 char를 선언해서 사용해야 한다.

          #include <stdio.h>

int main(int argc, char **argv)
{
char h='h';
char e='e';
char l='l';
char o='o';
char w='w';
char d='d';
char r='r';
char space=' ';

printf("%c%c%c%c%c%c%c%c%c%c%c\n",
h,e,l,l,o,space,w,o,r,l,d);
return 0;
}

 

배열

그럭저럭 hello world를 출력하긴 했지만, 지나치게 복잡하고 비효율적이라는 느낌이 들 것이다. C 에서 제공하는 원시 데이터 타입이란 것은 말그대로 최소한의 데이터 타입일 뿐으로, 좀 복잡한 프로그램을 만들기 위해서는 이것을 구조화 할 필요가 있다.

이것은 서류철을 만드는 것과 비슷한 과정이라고 보면 될것이다. 서류 하나하나를 일일이 관리하는건 상당히 짜증나는 일일 것이다. 그래서 우리는 (이름 등으로 정리된) 서류철을 만들고 다시 서류철을 보관하기 위한 캐비넷을 사용한다. 자료구조화 하는 것이다.

프로그램에서 사용되는 데이터(자료)들도 이렇게 구조화할 필요가 있다. 이것을 자료구조라고 한다. 이러한 자료구조들 중에서 가장 기본이 되는게, 배열이다.

배열은 서류철로 볼 수 있다. 서류는 최소단위가 되고, 이것을 나란히 쭉 정리해서 하나로 묶은게 서류철이다. 배열은 최소단위의 데이터를 일렬로 나열한 자료구조다. 서류철 같은 것을 보면 서류를 쉽게 찾기 위해서 인덱스를 한다. 보통은 이름으로 인덱스를 하는데, 배열역시 데이터를 쉽게 찾기 위해서 인덱스를 가지고 있다. 서류철과 다른점은 인덱스가 숫자로 되어 있다는 것 정도가 될 것이다. 이 숫자는 0 부터 시작한다.

다시 문제로 되돌아가보자. 문자열 hello world을 구조적으로 쉽게 표현할 수 있는 방법은 바로 배열을 이용하는 것이다. 이 배열의 최소단위는 문자 즉 char 데이터가 될 것이다. 이것을 위해서 아래와 같이 12 개의 char 형 데이터를 저장할 수 있는 공간을 만들어서 각각의 문자들을 집어 넣으면 된다. 이렇게 자료를 인덱스를 key로 해서 구조화 시킨것을 배열이라고 한다. 인덱스를 통해서 서류철에서 서류를 꺼낼 수 있듯이, 이 인덱스를 이용해서 (value)를 가져올 수 있다.

array

C 뿐만 아니라, 거의 모든 프로그래밍 언어가 배열을 지원하고 있다. 그리고 배열의 첫번째 인덱스는 1이 아닌 0부터 시작한다. 인덱스가 0부터 시작하는 것은 우리의 일반적인 직관과는 거리가 있어서, 초기에 혼동하기 쉬우니 주의하기 바란다. 배열의 인덱스 뿐만 아니라, 컴퓨터는 모든 것의 첫번째는 0부터 시작한다. 예를 들어 메모리의 첫번째 주소는 0이다.

 

  • 배열 : 테이터 저장 장소의 집합으로 각각의 배열은 동일한 데이터 유형과 동일한 이름을 가진다
  • 배열 요소 : 배열에 있는 각 저장 위치

 

C에서 배열

배열은 자료구조이므로 구조화가 가능한 모든 데이터들은 배열로 만들어서 선언할 수 있다. 선언방법은 간단하다.

          데이터타입  변수명 [크기];
          


예를들어 정수 데이터 10개를 저장하기 위한 배열은 아래와 같이 선언할 수 있다.

          int  mydata[10];
          

매우 간단하다. 이제 우리가 목적으로 하는 hello world를 저장하기 위한 배열은 다음과 같이 만들면 될 것이다. 변수명은 hello 로 하도록 하겠다.

          char hello[12];
          

여기에서 약간 의문시 되는 점이 있을 것이다. 우리가 저장하고자 하는 hello world는 11자인데, 12개의 공간을 가지도록 선언했기 때문이다. 이는 \0을 저장하기 위한 공간을 하나더 필요로 하기 때문이다. \0을 만나면 프로그램은 문자열이 여기에서 끝났다고 판단을 하게 된다. 만약 \0을 만나지 못한다면, 저장공간을 초과해서 \0을 만날때까지 계속 데이터를 읽을 려고 할 것이다. 프로그램은 바보라는 것을 명심해야 한다. 어디가 시작이고 어디가 끝인지를 명확하게 해주어야만 한다.

자 그럼 만들어진 배열 hello 에 hello world를 저장해 보도록 하자.

- 배열 번호는 0 부터 항상 시작한다

- 동일한 것을 저장하고 여러 개의 변수를 만드는 대신 사용한다

- 배열 이름 붙이기와 선언하기

: #define 지시자를 이용하여 배열을 선언한다

ex) #define month 12

int array[month] 와 int array[12]는 동일한 코드 이다

 

배열 선언과 정의

배열에 값을 저장하는 데에는 두가지 방법이 있다. 하나는 선언과 동시에 값을 대입하는 것이다.

          #include <stdio.h>

int main()
{
char hello[12] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
printf("%s\n", hello);
}

중괄호 "{","}" 사이에 각각의 원소 데이터를 넣으면 된다. 데이터와의 구분은 ,을 통해서 이루어진다. 1부터 5까지 저장하는 int 형 배열은 다음과 같이 선언할 수 있을 것이다.

          int data[5] = {0, 1, 2, 3, 4};
          


값이 여러개가 중복이 될 경우가 있을 수 있다. 예를 들어 배열의 크기가 100인데, 모든 값을 1로 해서 선언하고 싶을 때가 있을 것이다. 이경우 ,뒤에 데이터를 쓰지 않으면 된다. 그럼 가장 마지막 데이터로 배열의 끝까지 채워지게 된다.

          int data[100]={0,};
          


아래의 프로그램을 실행시키고 결과를 확인해 보도록 하자.

          
          #include <stdio.h>

int main()
{
char hello[12] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
int data[100] = {0,};
printf("%s\n", hello);
printf("%d\n", data[2]);
}


단 문자열의 경우 예외적으로 괄호 {}를 사용하지 않고, 쌍따움표를 이용해서 직접 선언할 수 있도록 지원하고 있다. 위의 코드는 아래와 같이 좀더 간단하게 표현할 수 있다.

              char hello[12] = "hello world\0";
          

 

배열의 데이터에 접근

서류철에서 원하는 서류에 접근하기 위해서 인덱스를 사용하듯이, 배열역시 인덱스를 통해서 접근할 수 있다. 이 인덱스는 0부터 시작되는 정수로 배열첨자라고 부르기도 한다. 접근 방법은 간단하다. 괄호 [] 안에 꺼내오기 원하는 데이터의 배열첨자를 넣어주기만 하면 된다.

          char hello[12] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
printf("%c", hello[4]);

hello[4] 가 o를 가져오리란걸 쉽게 예상할 수 있을 것이다.

대입연산자를 이용하면 배열의 원하는 위치에 데이터를 쓰는 것도 가능하다. hello[4]의 값을 'w'로 바꾸어보자.

          hello[4] = 'w';
          


이제 이전에 배웠던 루프문을 이용해서, 변수 hello 의 값을 출력하는 프로그램을 만들어 보자.

          #include <stdio.h>

int main()
{
char hello[12] = "hello world\0";
int i = 0;
for (i = 0; i < 12; i++)
{
printf("%c", hello[i]);
}
printf("\n");
}

 

잘못된 배열첨자의 사용

배열의 크기가 12 인데, 이를 초과해서 데이터를 집어 넣거나, 배열첨자를 초과해서 데이터를 가져오는 경우를 생각해 보자.

유닉스는 다중 사용자, 다중 프로세스를 지원하고 있다. 이말은 동시에 여러개의 프로그램들이 돌아갈 수 있음을 의미한다. ps는 현재 실행중인 프로세스의 목록을 보여주는 유닉스 프로그램이다. ps 를 이용해서 현재 떠있는 프로세스의 목록을 알아보도록 하자.

프로세스란
프로세스는 프로그램이 실행된 이미지다. 여러분이 어떤 프로그램을 실행시키면, 하드디스크에 있는 프로그램이 직접 수행되는게 아니고, 해당 프로그램의 복사본이 메인메모리에 올라가서 수행이 된다. 그러므로 하나의 프로그램은 여러개의 프로세스로 생성될 수 있다.
          ]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 Jan30 ? 00:00:35 init [3]
root 2 1 0 Jan30 ? 00:00:00 [keventd]
root 3 1 0 Jan30 ? 00:00:00 [kapmd]
root 4 1 0 Jan30 ? 00:00:00 [ksoftirqd_CPU0]
root 5 1 0 Jan30 ? 00:04:44 [kswapd]
root 6 1 0 Jan30 ? 00:00:00 [bdflush]
root 7 1 0 Jan30 ? 00:00:04 [kupdated]
root 8 1 0 Jan30 ? 00:00:00 [mdrecoveryd]
root 12 1 0 Jan30 ? 00:08:54 [kjournald]
root 71 1 0 Jan30 ? 00:00:00 [khubd]
root 1181 1 0 Jan30 ? 00:00:00 [kjournald]
root 1480 1 0 Jan30 ? 00:01:11 syslogd -m 0
root 1484 1 0 Jan30 ? 00:00:00 klogd -x
rpcuser 1521 1 0 Jan30 ? 00:00:00 rpc.statd
root 1607 1 0 Jan30 ? 00:00:00 /usr/sbin/apmd -p 10 -w 5 -W -P
root 1644 1 0 Jan30 ? 00:05:22 /usr/sbin/sshd
root 1658 1 0 Jan30 ? 00:00:00 xinetd -stayalive -reuse -pidfil
root 1671 1 0 Jan30 ? 00:00:16 crond
daemon 1693 1 0 Jan30 ? 00:00:00 /usr/sbin/atd
root 1711 1 0 Jan30 tty1 00:00:00 /sbin/mingetty tty1
root 1712 1 0 Jan30 tty2 00:00:00 /sbin/mingetty tty2
... ...

아무리 간단한 유닉스 시스템이라고 하더라도 최소한 30개 이상의 프로세스가 떠있을 것이다. 그렇다면 해결해줘야 할 문제가 있다. 프로세스는 메인메모리 상에서 실행되고, 자신이 사용하는 데이터를 역시 메모리에 올려놓고 읽거나 쓰게 된다. 그런데 다른 프로세스가 자신의 메모리 영역을 침범하면 안될 것이다. 유닉스 운영체제는 프로세스를 관리하면서, 동시에 각 프로세스가 다른 프로세스의 메모리영역을 침범하는지를 감시를 한다. 만약 다른 프로세스의 메모리 영역을 침범했다면, 운영체제는 프로세스를 강제로 종료시켜버리게 된다.

우리는 아주 간단하게 이런 프로그램을 작성할 수 있다.

          #include <stdio.h>

int main()
{
char hello[12] = "hello world!! My site is Joinc";
int i = 0;
printf("%s", hello[i]);
printf("bye bye\n");
}


이 프로그램은 char 형의 배열인 hello 를 위해서 12의 크기를 할당했다. 그러나 12byte를 초과하는 문자열을 집어 넣고 있음을 알 수 있다. 이 프로그램을 컴파일 해서 실행시키면 다음과 같은 에러메시지가 떨어질 것이다. 프로그램의 이름은 array_test 로 하겠다.

          # ./array_test
Segmentation fault

printf("%s", hello[i]); 를 하면서, 자신에게 할당되지 않은 영역을 읽어 들이려고 시도를 하는 중에, 다른 프로세스가 사용하는 메모리 영역을 침범했고 이 때문에 Segmentation fault메시지를 출력하면서 강제 종료되어 버렸다. bye bye\n은 출력조차 되지 못했다.

앞으로 많은 프로그램을 작성할 건데, 가장 흔하게 볼 수 있는 에러메시지가 Segmentation fault가 될 것이니, 지금 부터 익숙해지기를 바란다. 인간은 서류철의 용량을 초과해서 서류가 들어오면, 억지로 용량을 늘리던지, 안되면 풀로붙이던지 하는 등의 유도리를 발휘해서 업무를 처리할 수 있지만, 우리의 컴퓨터는 그렇게 똑똑하지 못한점 이해를 해주어야 한다. 프로그램을 작성할 때는 할당한 공간을 초과했는지, 제대로 사용했는지를 엄격하게 검사해야 한다.

위의 프로그램은 제대로 수행되는 경우도 있을 것이다. 왜냐면 쓸려고 하는 메모리 영역이 다른 프로세스에 의해서 사용되지 않는 자유상태일 수도 있기 때문이다. 이 경우에는 다른 프로세스 메모리 영역을 침범한 행위는 아니므로, 에러가 발생하지는 않을 것이다. 물론.. 제대로된 결과가 나올 것이라는건 보장할 수가 없다. 자유상태이기 때문에, 프로세스가 실행되고 있는 도중에, 다른 프로세스에게 해당 메모리 영역이 할당되어버릴 수 있기 때문이다. 이럴 경우 Segmentation fault 를 출력하면서 종료되어 버릴 것이다.

 

문자열의 출력

다시 문제로 되돌아가 보자. for 문과 배열을 이용해서 좀 더 쉽게 문제를 풀기는 했지만, 문자열 하나 출력할려고 루프문까지 사용하는건 합리적이지 못하다. printf 함수를 이용하면 쉽게 이 문제를 풀 수 있다. 이 함수는 주어진 변수를 형식화해서 출력할 수 있도록 도와준다.

          #include <stdio.h>

int main()
{
char hello[12] = "hello world\0";
printf("%s", hello);
}

이걸로 문제는 간단히 해결됐다. "%s" 는 주어진 인자를 문자열로 인식해서 출력하는 포맷 옵션이다.

 

문자열 복사

문자열을 배열에 복사하는 간단한 방법에 대해서 생각해보자. 다음과 같이 코드를 작성하면 어떻게 될까.

          int main()
{
char hello[12];
hello = "hello world\0";
}

문제 없을 거라고 생각할 수도 있지만, 컴파일 하면 아래와 같은 에러메시지와 함께 컴파일 실패하게 된다.

          # gcc -o array array.c
array.c: In function `main':
array.c:6: incompatible types in assignment

대입연산자는 같은 타입에 대해서만 허용되는 연산자이기 때문에, 배열에 문자열을 대입하는건 C 문법에 어긋나기 때문이다. 결국 문자열을 처리하기 위해서는 별도의 함수를 만들어서 사용하는 수 밖에 없다. 다행히 C 는 표준 라이브러리 형식으로 몇개의 유용한 문자열 처리 함수를 제공한다. strcat(3), strcpy(3)와 같은 함수가 문자열 처리를 위한 대표적인 함수다. 다음은 strcpy 함수를 이용해서 문자열을 복사한 프로그램이다.

          #include <stdio.h>

int main()
{
char hello[12];
strcpy(hello,"hello world\0");
printf("%s\n", hello);
}
  • 문자열 저장요견 변경

: 저장공간을 실행 중에 할당할 수 있도록 하는 함수로는 "malloc()" 함수가 있다

ex) 1. 100개의 문자로 이루어진 문자열 메모리 할당

char *str;

if ((str = (chr*) malloc(100) ==NULL))

{ printf("*****");

}

2. 50개의 정수 배열 메모리 할당

int *number;

numbers = (int*) malloc(50 * sizeof(int));

2012년 10월 22일 월요일

리눅스 C/C++ : 8장 데이터 타입

 

1 Data Types

우리가 사용하는 데이터는 컴퓨터의 메모리에 연속된 비트의 나열로 저장이 된다. 이러한 비트의 나열은 컴퓨터입장에서는 문제가 안되겠지만 인간의 입장에서는 알아보기 힘들다는 문제가 발생한다. 그래서 데이터 타입을 두어서 인간이 좀더 쉽게 사용할 수 있도록 하고 있다.

데이터 타입은 다음과 같은 특징을 가지고 있다.

  • 데이터가 어떻게 표현되고 사용될지를 결정한다.
  • 데이터 타입에 따라 컴퓨터가 어떻게 데이터를 다룰지를 알려주다. (숫자로 다룰지 아니면 문자로 다룰지 등..)
  • 모든 값은 데이터 타입에 의해서 표현될 수 있다.

예를 들어서 메모리에 다음과 같은 비트패턴이 저장되어 있다고 가정해보자.

0000 0000 0110 0111

이 값이 어떻게 표현될까 ? 이것은 데이터 타입을 어떻게 정의하느냐에 따라 달라진다. 만약 데이터 타입을 'int로 하기로 했다면 숫자 103으로 표현될 것이다. 그러나 문자를 저장하는 char로 하기로 했다면 영문자 g로 표현이 된다.

데이터 타입은 이렇게 인간의 입장에서 컴퓨터 메모리에 저장된 데이터를 어떻게 다룰 것인지를 결정하기 위해서 사용이 된다.

아래의 코드를 실행시켜보기 바란다.

          #include <stdio.h>

int main(int argc, char **argv)
{
int a = 103;
char b = 103;

printf("%d\n", a);
printf("%c\n", b);
}

똑같은 103인데, 서로 다르게 출력되는걸 확인할 수 있을 것이다.

 

2 Primitive Data Types

인간은 매우 다양한 형태의 데이터를 다루기를 원하고, 그런일을 할 수 있는 프로그램을 만들어낼 수 있어야 한다. 다루는 데이터의 형태가 다양하니, 데이터 타입역시 다양하면 좋을 것이다. 그러나 컴퓨터는 매우 단순한 기계다. 쓸데없이 데이터 타입을 이것 저것 만들면, 이것을 다루는 컴퓨터 역시 달가워하지 않을 것이다. 프로그래머 역시 각 데이터 타입에 따른 고려사항이 늘어나니 권장할만한 사항이 아니다.

그래서 데이터타입을 다루어야 하는 프로그래밍 언어는 Primitive Data Type이라고 불리우는 최소한의 반드시 필요한 데이터 타입을 지원하고 있다. Primitive Data Type은 원시 데이터 타입 이라고 부르기도 한다.

각 원시 데이터 타입은 고유의 크기를 가지고 있으며, 표현할 수 있는 데이터의 한계가 정의되어 있다. 다음은 C 언어에서 지원하는 원시 데이터 타입과 크기, 데이터 범위를 보여주는 표다.

  2.1숫자형 원시 데이터 타입

숫자를 표현하기 위해서 사용되는 데이터 타입이다. 크게 정수형 데이터를 표현하기 위한 정수형 원시 데이터 타입부동소숫점형 원시 데이터 타입으로 나눌 수 있다.

  • 정수형 원시 데이터 타입
    char 1byte -128 127
    short 2byte -32768 32767
    long int 4byte 2,147,483,648 2,147,483,647
    long long int 8byte 9,223,372,036,854,775,808 9,223,372,036,854,775,807

이들 정수형 데이터 타입들은 signed비트라는 것을 가지고 있어서, 음수까지 표현할것인지를 정의할 수 있다. 음수까지 표현할 거라면 signed를 양수만 표현할거라면 unsigned를 타입의 앞에 붙여준다. 따로 붙여주지 않았다면 signed 가 붙은걸로 해석을 한다. 즉 위의 데이터 타입은 실제로는 signed char, signed short, signed long int, signed long long int와 동일하다.

unsigned를 명시하게 되면 양의 정수만 표현하게 된다. 음의 정수를 표현할 필요가 없으니 그만큼 양수 쪽으로 표현범위가 늘어날 것이다. unsigned char 이라면 255, unsigned long int 라면 4,294,967,295 가 된다.

  • 부동소숫점형 원시 데이터 타입
    float 4byte +/- 10E-37 +/- 10E38
    double 8byte +/- 10E-307 +/- 10E308

  2.2enumerated type

일반적으로 enumerated 타입은 숫자로 나열된 카테고리 같은 데이터를 만들기 위해서 사용한다. C에서는 enum을 이용해서 enumerated 타입의 데이터를 정의할 수 있다.

          enum cardsuit {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
};

이제 enum 을 이루는 각각의 요소들은 숫자 0,1,2... 로 차례대로 대응되게 된다. 다음의 프로그램을 실행시켜 보자.

          #include <stdio.h>
enum cardsuit {
CLUBS,
DIAMONDS,
HEARTS,
SPACES
};
int main()
{
printf("Card CLUBS is %d\n", CLUBS);
printf("Card DIAMONDS is %d\n", DIAMONDS);
printf("Card HEARTS is %d\n", HEARTS);
printf("Card SPACES is %d\n", SPACES);
}


다음과 같은 결과를 확인할 수 있을 것이다.

          # ./enum
Card CLUBS is 0
Card DIAMONDS is 1
Card HEARTS is 2
Card SPACES is 3


그렇지만 CLUBS 가 0이 아닌 다른 수로 대응되어야 할 경우도 생길 것이다. 그럴 땐, 필요한 값을 대입시켜 주면 된다. 즉 값을 명시하지 않으면 0부터 시작해서 1씩 증가하고, 값을 명시하면 명시된 값을 시작으로 1씩 증가하는 것으로 보면 된다.

cardsuit를 다음과 같이 정의 했을 때, 어떤 값이 출력될지 생각해 보라.

          enum cardsuit {
CLUBS = 1,
DIAMONDS,
HEARTS = 100,
SPACES
};

- 즉 enum이라는 함수안의 변수들이 차례대로 1씩증가한다고 생각하면 된다

  2.3Pointer type

컴퓨터는 계산을 하기 위한 기계다. 이때 계산에 사용될 모든 데이터는 일단 메모리로 읽혀져서 필요한 계산을 하게 된다. 예를 들어 하드디스크에 A 라는 문서가 있다고 가정해 보자. 이 문서를 편집하기 위한 프로그램을 가동시키면, 프로그램은 하드디스크에 있는 문서를 모두 읽어서 컴퓨터의 메모리로 불러들인 다음 필요한 일을 하게 된다. 다른 모든 연산들역시 마찬가지다.

그렇다면 문제가 발생한다. 컴퓨터에는 여러개의 프로그램이 떠 있을테니, 각각의 프로그램이 사용하는 데이터가 메모리의 여기저기 저장되어 있을 것이다. 이때 프로그램은 자신이 사용할 데이터가 메모리상의 어느 위치에 있는지 알고 있어야 한다.

이를 위해서 메모리에는 아래 그림과 같이 주소 값이 부여되어 있다.



프로그램은 이 기억값을 기억해서 데이터의 위치를 정확히 찾아내어서 읽어오게 되는 것이다. Pointer는 이러한 주소값을 저장하는 데이터 타입이다.

다른 데이터 타입들이 그렇듯이 pointer도 고유의 크기를 가지고 있다. pointer 데이터 타입의 크기는 4byte이다. 메모리상의 주소에는 마이너스 값이 필요가 없으므로 0 - 4,294,967,295의 숫자가 저장된다. 32bit 컴퓨터에 사용가능한 메모리의 총크기가 4Giga다 라는 얘기가 여기에서 나온다. 가리킬수 있는 숫자의 범위가 4,294,967,295 (약 4giga)이므로, 이를 초과한 영역에 저장된 데이터는 읽어올 수가 없기 때문이다.

이는 C 와 같은 프로그래밍 언어에도 그대로 적용된다. 포인터의 크기가 4byte 이니, 최대다룰 수 있는 메모리의 크기가 4Giga로 제한이 된다. 물론 여러분이 64bit 컴퓨터운영체제 그리고 컴파일러를 사용한다면 테라 byte급의 데이터를 다룰 수 있다. 64bit 컴퓨터가 대용량 데이터 처리에 유리하다는 얘기가 나오는 이유다.

포인터는 이쯤에서 끝내도록 하겠다. 포인터에 대한 자세한 내용은 따로 한장 정도를 할애해서 자세히 다루도록 하겠다.

 

3 type casting - 형변환

아래 프로그램을 컴파일 후 실행시켜 보자.

          #include <stdio.h>

int main(int argc, char **argv)
{
unsigned char ch = 'c';

printf("%c\n", ch);
ch = ch+1;
printf("%c\n", ch);
ch = ch+1;
printf("%c\n", ch);
}

결과로 c d e 가 출력될 것이다.

이상하군. 분명히 두개는 서로 다른 데이터 타입인데, 더하기가 되는군 ?

앞서 언급했지만, 데이터형이란 표현방식에 따른 것일 뿐이다. 컴퓨터 입장에서는 모두가 비트일 뿐이다. 즉 ch+1은 컴퓨터 입장에서 다음과 같이 계산이 된다.

                                         0110 0011   = 'a'
0000 0000 0000 0000 0000 0000 0000 0001 = '1'
==========================
0000 0000 0110 0100 = 'b'

만약 printf("%d\n" ch+1) 을 한다면, 100 이 출력될 것이다. 비트패턴을 숫자로 표현하도록 표현방식을 바꿨기 때문이다.

이렇게 모두 동일한 비트일 뿐임으로 서로 다른 타입간의 계산이 가능해진다. 그러나 이건 어디까지나 가능하다일뿐 실제로는 의도하지 않는 다양한 문제가 발생할 수 있다.

  • 데이터타입의 크기에 따른 문제
    데이터 타입은 서로 다른 크기를 가지고 있다. 위의 코드에서 ch 에 1000을 더하면 어떻게 될까. 1099가 나오길 예상할 수 있겠지만 ch의 테이터 타입인 char는 1byte의 크기로 255까지만 표현이 가능하다. 때문에 데이터 저장공간을 초과하게 될 것이다. 실제로는 1byte의 상위 비트는 버려지게 된다. 고로 0-255까지의 값이 출력이 될것이다. 뭐.. 값이 255를 초과하지 않는 범위에서 연산을 한다면 문제가 없기는 하겠지만 실수로 문제가 발생할 소지가 다분하다.

  • 다른 데이터 타입끼리라도 주의해서 프로그래밍을 하면 문제 없겠지만 가능하면, 타입을 맞추어서 계산을 하는게 좋다.
  • signed bit 문제

    정수형 데이터 타입은 signed bit 를 가지고 있어서 이걸로 양수인지 음수인지를 판단하게 된다. 다음의 코드를 확인해 보자.

              #include <stdio.h>

    int main(int argc, char **argv)
    {
    unsigned int i = 100;

    if (i < -10)
    {
    printf("Large\n");
    }
    }

    상식적으로 100 은 -10보다 크기 때문에 if문의 블럭은 실행되지 않아야 겠지만, 컴파일해서 실행시켜 보면 블럭문이 실행이 되는걸 알 수 있다. 이는 i 가 unsigned 형으로 -10 을 unsigned 형으로 보고 비교를 하기 때문이다. -10이 unsigned 형이 되면 4,294,967,285 으로 표현이 된다. 이를 이해하기 위해서는 2의 보수를 통한 singed 데이터 처리에 대해서 알고 있어야 하는데, 5장 데이터와 비트문서를 읽어보기 바란다.

    이 문제를 해결하기 위해서 형변환을 수행한다. 위의 프로그램의 경우 문제를 피해가는 가장 좋은 방법은 i를 signed 형으로 선언하는게 될것이다. 그러나 불가피 하게 unsigned 형을 고집해야 할 경우가 발생한다. 그때는 casting(형변환)연산자를 통해서 형변환을 하도록 한다.

  •           if ((signed)i < -10)
    {
    ...
    }
    이제 제대로 되는걸 확인할 수 있을 것이다.

프로그램을 작성할 때는 데이터가 어디에 쓰일 것인지를 명확히 해서, 그에 맞는 데이터 타입을 지정해 줘야 한다. 그렇지만 사람이다 보니, 위에서 처럼 사소한 실수를 하기도 한다. 문제는 이런 프로그램도 문제 없이 컴파일이 된다는 것이다. 결국 프로그램이 실행하는 도중에 문제를 일으키게 될 것이다. 다행히 gcc 컴파일러는 컴파일 옵션을 통해서 저러한 문제를 사전에 잡아낼 수 있게 하고 있다. 위 프로그램을 type.c로 저장하고 아래와 같은 옵션을 주고 컴파일 해보자.

- 즉 casting은 전역변수를 함수안에서의 형 변환을 시키는 것이다

          # gcc -W -Wall -o type type.c
...
type.c:7: warning: comparison between signed and unsigned
...

comparison between signed and unsigned와 같은 경고메시지를 출력함을 알 수 있다. 자세한 내용은 리팩토링 : 모든 경고메시지를 제거하라 문서를 읽어보기 바란다. 아직 읽기 버겁다면, 그냥 대충 저런게 있나보다 하는 수준에서 읽어두어도 도움이 될 것이다.

 

4 문자와 문자열 표현

데이터는 크게 문자와 숫자로 이루어져 있음을 알고 있다. 그런데 정작 문자문자열을 표현하기 위한 데이터 타입을 다룬거 같지를 않다. 이에 대해서 얘기해 보고자 한다.

C 언어에서 문자를 위한 데이터 타입으로는 char 데이터 타입을 사용한다. char 는 1byte 256의 크기를 가지는데, 1byte 문자권의 영어와 수십개의 특수문자를 충분히 표한할 수 있는바 char를 문자를 저장하기 위한 데이터 타입으로 사용하고 있다.

컴퓨터에서 표현되는 문자는 0에서 255까지의 각 크기에 대응되는 문자들이 표준으로 정의되어 있다. 각 값에 대응되는 문자는 다음의 링크에 ASCII 테이블로 정리되어 있다.

ASCII 테이블 link...

그러나 ASCII 테이블만 가지고는 일본어, 한글, 중국어와 같은 2byte 문자는 표현할 수 없다. 2byte 문자는 char를 2개 이상 사용해서 저장해야 한다.

이제 마지막으로 문자열이 남았다. C는 문자열을 위한 데이터 타입을 가지고 있지 않다. C에서 문자열을 처리하기 위해서는 배열을 사용해야만 한다. 이것은 원시 데이터 타입을 여러개를 포함하고 있는 데이터 구조다. 예를 들어 문자열은 char를 여러개 포함할 수 있는 데이터 구조를 이용하면 표현할 수 있을 것이다. 배열은 다음장에서 자세히 다루도록 하겠다.