Zookeeper 설치

zookeeper회사 서버시스템에 적용 할 수 있나 알아보기 위해 Zookeeper를 테스트하기로 한다.

일단 아파치-주키퍼 사이트(http://zookeeper.apache.org/)에 들어가 다운로드 링크를 찾아 wget 으로 다운로드한다. 지금 이 포스팅을 하는 시점에서 가장 최신 버전은 3.4.5 버전이다.

압축을 풀면 나오는 디렉토리가 주키퍼의 프로그램 디렉토리이다.

일단 conf 디렉토리로 가서 zoo_sample.cfg 파일을 zoo.cfg 파일로 복사한다.

그리고 bin 디렉토리로 가서 ./zkServer.sh 명령으로 주키퍼를 실행한다.

실행하면 다음과 같은 화면이 출력된다.

JMX enabled by default
Using config: /backup/program/zookeeper-3.4.5/bin/../conf/zoo.cfg
Usage: ./zkServer.sh {start|start-foreground|stop|restart|status|upgrade|print-cmd}

CentOS의 service 와 마찬가지로 실행옵션을 주면서 실행할 수 있다.

./zkServer.sh start 를 입력했다.

JMX enabled by default
Using config: /backup/program/zookeeper-3.4.5/bin/../conf/zoo.cfg
Starting zookeeper … STARTED

시작되었다고 나왔지만 실제로 프로세스는 떠있지 않았다.

그래서 뭘까 생각해보다가 bin 디렉토리를 보았더니 zookeeper.out 파일이 생성되어 있었다. 이 파일의 내용에는,

nohup: failed to run command `/usr/share/java/bin/java’: 그런 파일이나 디렉터리가 없습니다

라고 되어있다.

아마 환경설정은 zkServer.sh 파일 안에 있을 것이므로 이 파일을 편집에서 java 라는 단어로 검색했으나 문제가 될만한 부분이 없었다. 하지만 보다보니 zkEnv.sh 를 실행하는 부분이 보인다. 파일명으로 보아 환경설정을 파는 파일이 이 파일 같으므로 다시 이 파일을 vi로 열어본다.

중간쯤 가다보면

if [ “$JAVA_HOME” != “” ]; then
JAVA=”$JAVA_HOME/bin/java”
else
JAVA=java
fi

이런 내용이 있다.

자바를 실행할 홈디렉토리를 설정하는 것 같다.

쉘로 나와서 echo $JAVA_HOME 을 입력해보니 /usr/share/java 이라고 뜬다. 그래서 결국은 zookeeper.out 파일에 /usr/share/java/bin/java 이런 경로가 없다고 나온 것이었다.

CentOS 6.4 에서는 java 명령은 사실 경로 설정이 없어도 상관이 없으며 굳이 경로를 넣고 싶다면 /usr/bin/java 로 실행하면 된다. (이 파일은 /etc/alternatives/java 의 심볼링링크이다.)

if [ “$JAVA_HOME” != “” ]; then
JAVA=”$JAVA_HOME/bin/java
else
JAVA=java
fi

이 부분을

if [ “$JAVA_HOME” != “” ]; then
# JAVA=”$JAVA_HOME/bin/java”
JAVA=/usr/bin/java
else
JAVA=java
fi

처럼 바꾸고 다시 ./zkServer.sh start 를 실행한다. 아무 문제 없이 STARTED 되었다고 나오고, 다시 ./zkServer.sh status 를 입력해보면

JMX enabled by default
Using config: /backup/program/zookeeper-3.4.5/bin/../conf/zoo.cfg
Mode: standalone

라고 나오면 실행 성공.

이제부터는 주키퍼를 실제로 사용해보도록 한다.

전자계산기조직응용기사 실기시험 문제

지난번 본 전자계산기조직응용기사 실기시험 필답형 문제.

  1. 높은 순위의 인터럽트를 먼저 처리하기 위한 소프트웨어 우선순위 인터럽트 처리방식은?
  2. FIFO 교체 알고리즘에서 페이지 부재 참조요구 페이지 순서는?
  3. 서브루틴이 끝나면 첫번째 서브루틴에서 콜 다음 명령부터 수행하는 것은?
  4. 어셈블러가 원시 프로그램을 번역할 때 어셈블러에게 필요한 작업을 지시하는 명령은?
  5. A X B + C를 Post Order로 변경하면?
  6. 다음의 설명에 맞는 것은? 오퍼랜드부 없이 op-code만 있다. 모든 연산은 스택메모리의 스택포인터가 가리키는 오퍼랜드를 이용하여 수행한다.
  7. 다음에 들어갈 것은? 제어장치는 고정배전 제어장치와 소프트웨어적으로 구현하는 (          ) 기법이 있다.
  8. 베타적 OR회로(EX-OR Gate) 1개와 AND 회로 1개로 반가산기 회로를 작성하라.
  9. 병렬 컴퓨터 구조 중 한개의 명령으로 여러 Data를 처리하는 것은?
  10. 산술 및 논리 연산시 연산결과를 일시적으로 저장하는 레지스터는?
  11. 스케쥴링 기법 중 SJF 기법을 선점 형태로 변경한 기법은?
  12. 임베디드 운영체제 두가지는 무엇인가?
  13. 다음에 들어갈 것은? 제어신호의 교환 방법에는 (          ) 펄스에 의한 방법과 핸드 쉐이킹에 의한 방법이 있다.
  14. 이 코드는? BCD 코드의 인접하는 비트를 X-OR 연산하여 만든 코드이다.
  15. 마이크로프로세서 버스의 종류를 크게 3가지로 분류하여 기술하시오.

이번엔 떨어졌다. 다음번엔 꼭 합격하도록 공부 열심히 해야지.

PSIAndroid Korean translation

I use PSIAndroid for my server monitoring. When I see PSIAndroid’s twitter, Erkan Valentin was recieved good idea. I sent mention that I want to multi-language support and translate system. (Like phpMyAdmin) After some minute, he sent message that it’s good idea. I was downloaded ‘string.xml’ file and translate by Korean and sent to he. It is second participation to open-source software translation.

I could help by participating translate open-source softwere, so I feel good.

If you want download PSIAndroid, Click https://play.google.com/store/apps/details?id=com.phpsysinfo.

BlackBerry Playbook은 왜 한국개발자들에게 외면 받았는가

작년초에 열린 Adobe Refresh 2011 에서는 BlackBerry사의 Playbook이 소개되었다.

한국 시장에는 아직 팔고 있지 않다. 아마 여러분이 한국에서 이 제품을 제일 처음 보게 될 것이다. 라는 소개로 시작된 플레이북 소개는 꽤 흥미로웠다. 커널 수준에서의 Adobe AIR의 지원, RIM과 Adobe의 협력, 커널 수준에서 AIR의 지원으로 인해 상당한 속도를 구현할 수 있다는 점 등.

AIR는 느려터졌다는 느낌을 지울 수 없던 나도 플레이북 시연을 보고는 정말 놀랐다. AIR가 저렇게 부드럽게 돌아가다니. 아마 나뿐만이 아니라 그 자리에 있던 다른 플래시 개발자들도 마찬가지였을 것이다.

그날 이후 일주일 정도가 지난 후 난 Playbook Developer 사이트에 접속해보았다. 이미 깨끗하게 준비가 되어있었다. (안타깝게도 한국개발자들을 위한 준비는 전혀 없었던 것 같다.) 이 웹사이트에서는 플레이북 SDK와 시뮬레이터 등에 대한 안내를 하고 있었고 다운로드 받을 수 있게 친절하게 준비되어있었다. 허나 안타깝게도 한국어로는 전혀 준비되어있지 않았다. RIM의 한국지사가 있는지도 모르겠지만 한국지사가 있었다면 개발자들을 조금이라도 도와주려는 노력이 있었다면 어땠을까?

플레이북의 시뮬레이터는 VMWare를 이용하여 사용할 수 있는 ISO 형식의 가상이미지 파일이었는데 맥에서 자기네 기기 테스트를 위하여 가상머신을 이용한 시뮬레이터를 제공하는 것은 상당히 괜찮은 생각 같았다. VMWare라는 시스템에 대해서 난 거부감이 거의 없었고 사용방법도 매우 편리했다. 적어도 아이폰의 SDK와 시뮬레이터에 비해서는 말이다.

어찌됐던 그렇게 Playbook에서 돌아갈만한 간단한 프로그램을 만들었고 블랙베리에 앱을 등록하러 갔다. 그런데 등록과정이 너무 복잡했다.

한국어 페이지까지는 기대하지 않았지만 인증서를 발급받고 등록하고… 프로그램을 업로드하고… 하는 과정을 수십번 반복하면서 난 지쳐갔다. 프로그램 하나 만드는게 왜 이렇게 어렵던지…

결국에는 수십번의 시도와 수십일간의 시간낭비 끝에 그만두었다. 차라리 아이폰SDK 같은 경우는 도움말이라도 많이 찾을 수 있어서 다행인데 플레이북쪽에는 아무도 아는 사람도 없고 인터넷에서 정보를 찾기도 어려웠다. 물론 플레이북의 고객지원도 그다지 좋지 못했다.

QNX 운영체제(일부 사람들은 플레이북인 안드로이드를 사용하는줄 아나보다.)의 장점을 제대로 살리지 못한 것 같기도하고… 아무래도 우리나라에는 조금 생소한 QNX라는 운영체제에 대해서도 더 많이 홍보했어야하지 않나 싶다.

플레이북 개발을 포기하게 만들었던 또 하나의 이유는 RIM에서 우리나라에 블랙베리 정식유통을 포기했다. 따라서 내가 어플리케이션을 만든다하더래도 어차피 외국인만을 대상으로 해야할 것이고 내가 만들 수 있는, 만들어야 할 어플리케이션은 결국 계속 줄어든다. 그럴바에는 차라리 사람들이 많이 쓰는 아이폰쪽으로 가는게 낫겠지?

개발자들도 기기를 구할 수 없는… 그리고 언제 우리나라에 정식발매될지도 모를 이런 상황들이 한국개발자들에게 외면 받은 것 같다.

지금 네이버를 살펴보니 플레이북 16기가가 이 대략 15만원~25만원 선에 팔리고 있다. 25만원 정도면 미개봉도 구할 수 있다. 이제 플레이북은 결국 ‘싸구려 잉여타블렛’ 정도의 이미지 밖에 되지 않을 것이고 아이패드나 안드로이드 타블렛들과는 경쟁도 안될 것 같다. 따라서 개발자들도 더이상 이 플랫폼에는 관심을 가질 이유가 없어졌다.

우리나라가 아이패드에 대부분 점령 당했지만 그래도 꾸준히 진출해봤으면 어떨까 싶었다. 그래도 내가 보기엔 플레이북도 나쁘지 않았고 가격적인 메리트도 있었으니.

blackberry developer

지금 BlackBerry Developer 사이트를 가보니 내가 이 사이트를 처음 만났던 1년전에 비해 많이 바뀌었네. C++, 웹워크, AIR, 자바 모든 환경을 다 지원한다. 과연 이런 플랫폼 위에서 개발하던 사람들이 얼마나 블랙베리쪽으로 올지는 모르겠지만…

여튼 플레이북이 우리나라에서 잘 되길 바라던 마음이 있었는데 이제 망해버려서 아쉬운 마음도 있다. RIM은 그냥 우리나라에서 철수해도 될듯…

IOCP에 대한 좋은 설명 문서

IOCP에 대해 검색하다가 발견하게 된 좋은 문서. 데브피아의 이기탁님이 쓰신 문서라고 한다. IOCP에 대하여 자세하게 잘 설명하고 있는듯하다. 나중에 다시 생각날 때마다 보기 위해 여기에 글을 쓰고 해당문서를 저장해놓는다.

이렇게 퍼와도 되는지 모르겠다. 내가 가져온 곳은 http://sweeper.egloos.com/2811340 (당연히 저작권은 이기탁님에게 있다.)

쓰레드 처리를 위한 Auto Lock/Unlock 클래스

오늘 공부의 목표는 Lock/Unlock의 자동화였다.

CRITICAL_SECTION을 사용할 때 이것을 초기화, 삭제해주는 것뿐만 아니라 EnterCriticalSection/LeaveCriticalSection을 일일히 수동으로 해준다는 것은 귀찮을 뿐만 아니라 사람의 실수에 의하여 문제가 생길 소지가 있는 부분이기 때문. 만약 언락을 안하면 데드락 상황에 빠질 수 있기 때문에 반드시 언락을 해줘야하는데 어떤 프로그램 로직상 혹은 작성자의 실수로 이런 경우가 생길 수 있다. 그래서 그걸 아예 자동화해주는게 Auto Lock/Unlock 클래스.

두개의 헤더파일로 구성된다.

lock.h 파일

#include <iostream>
#include <Windows.h>
#include <process.h>

class Lock
{
public :
	Lock()
	{
		// std::cout << "INIT" << std::endl;
		InitializeCriticalSection(&cs_);
	}

	~Lock()
	{
		// std::cout << "DELETE" << std::endl;
		DeleteCriticalSection(&cs_);
	}

	void lock()
	{
		// std::cout << "LOCK" << std::endl;
		EnterCriticalSection(&cs_);
	}

	void unlock()
	{
		// std::cout << "UNLOCK" << std::endl;
		LeaveCriticalSection(&cs_);
	}
private :
	CRITICAL_SECTION cs_;
};

크리티컬 섹션을 생성하고 초기화와 삭제, 락과 언락을 해주는 메서드로 이루어져있다.

그다음은 AutoLockUnlock.h 파일

#include <iostream>

#include "lock.h"

class AutoLockUnlock
{
private :
	static Lock lock_;
public :
	AutoLockUnlock()
	{
		// std::cout << "AutoLockUnlock() 생성자" << std::endl;
		lock_.lock();
	}

	~AutoLockUnlock()
	{
		lock_.unlock();
		//std::cout << "AutoLockUnlock() 소멸자" << std::endl;
	}
protected :
private :
};

Lock AutoLockUnlock::lock_;

실제사용은 autolockunlock.h 파일을 인클루드하고 스코프를 만들어주기 위해 락이 필요한 부분을 {, }로 감싼 다음 해당 블록 안 제일 첫줄에 AutoLockUnlock autolockunlock; 처럼 하나 선언해준다. 블록이 시작되면 AutoLockUnlock의 생성자에 의해 크리티컬섹션의 초기화와 락이 시작되고 코드 블록이 종료되면 AutoLockUnlock의 소멸자에 의하여 언락이 되고 크리티컬섹션 삭제까지 된다.

아래와 같은 쓰레드 함수가 있다면 맨처음에 AutoLockUnlock 타입을 같은 변수를 생성함으로써 초기화/락/언락/삭제를 한번에 할 수 있다.

unsigned int WINAPI ThreadFunc(void * arg)
{
	AutoLockUnlock autolock; // 여기서 오토락을 건다. 생성만 해주면 알아서 만들어지고 알아서 삭제된다.
	std::cout << "쓰레드 시작 :: " << target << std::endl;

	for (int i = 0; i < 50000; i++)
	{
		target = target + 1;
	}

	for (int i = 50000; i < 100000; i++)
	{
		target = target - 1;
	}
	std::cout << "쓰레드 종료 :: " << target << std::endl;
	return 0;
}

중요한건 {, }에 의한 코드블록에 의해서 AutoLockUnlock 클래스의 생성자와 소멸자가 호출된다는 점. 이것을 이용하여 자동화된 Lock/Unlock을 구현한다.

쓰레드를 쓸 때는 항상 이러한 자동화된 락과 언락을 사용함으로써 데드락에 빠지는 사태를 막을 수 있다. 쓰레드를 쓸 때는 무조건 사용할 것.

cin을 쓸 때 입력을 한번 지나가는 현상을 해결하는 방법

오늘 공부하던 중 발견한 부분.

cin을 통해 문자 하나를 입력 받은 후 getline으로 다른 문자열을 입력 받으려고 할 때 문제가 생긴다. 입력을 한번 건너뛰는 현상.

cin으로 입력 받은 다음 다른 이벤트(?)가 더 오는 것 때문이라고 한다. 이 문제를 해결하기 위해서 std::fflush(stdin);을 이용하면 이 현상이 사라진다. cin을 이용할 때는 반드시 기억해놔야할듯.

std::string temp;
std::cin >> temp;
std::cout << "start" << std::endl;
int rtn = std::fflush(stdin);

std::string msg;
std::getline(std::cin, msg);
std::cout << "end" << std::endl;

C++로 만든 계산기

회사 스터디 커리큘럼 중 만들어 본 C++로 구현된 사칙연산 계산기이다.

사실은 TCP 서버/클라이언트를 공부하는 와중에 책에 나온 예제가 너무 쉽다하여 계산기 기능을 가진 서버와 클라이언트로 바꾸기로 해서 만들게 되었다. 책에는 클라이언트에서 입력값 3개를 받도록 한다. 첫번째로는 상수, 두번째는 연산자, 세번째로는 상수를 받아서 서버로 보내면 서버는 두 상수를 연산자에 따라 계산하고 클라이언트로 다시 전송하는 방식이다. 이거야 뭐….;;

이 방법도 너무 쉽기 때문에 아예 수식으로 입력 받도록 만들었다.

최소한 인터넷에 떠도는 대부분의 사칙연산 계산기보다는 내가 만든게 훨씬 더 정교하고 정확하다고 말할 수 있을 것 같다.

만드는 과정에서 어려웠던 점과 해결하는 알고리즘 몇가지에 대해 기억해두려고 여기에 적어둔다. 혹시 누가 이 글을 보게 된다면, 내가 생각했던 부분이나 내 코드에 문제가 있다면 지적해주면 좋겠다.

 

일단 수식을 입력 받은 후 제일 처음 할일은 수식의 빈칸을 모두 제거하는 일이다. 수식의 처음부터 끝까지 검색하며 빈칸을 모두 제거하는 함수를 하나 만들어 적용한다.

1. 일단 괄호 문제

사칙연산에서 괄호가 들어가면 계산의 우선순위가 바뀐다는 것이 문제인데 이에 대한 해결책은 후위 연산과 스택을 응용하는 방법이다. 여기에 대해서는 인터넷에 이미 많은 글들이 있어서 따로 여기에 적을 필요는 없는 것 같다. 내 경우에는 vector와 stack을 둘다 응용하며 썼다.

보통 인터넷에서 찾은 사칙연산 계산기는 딱 여기까지만 해법이 나와있는 경우가 대부분이다. 난 여기에 보완하여 아래의 문제사항들을 대응할 수 있도록 바꾼다.

2. 음수 연산자 문제

위에 괄호 문제를 해결하고 나니 음수가 문제가 되었다. 예를 들면, -2+3 같은 식은 2 앞에 붙은 -를 연산자가 아니라 해당 상수와 같이 묶어서 봐야한다는 것. 그리고 여기에는 괄호에 마이너스가 붙는 경우까지 생겨난다. 예를 들면, -(2+3) 같은 수식이다. 위에 1번에서 괄호문제를 풀기 위해 후위 연산으로 변경하고 상수 스택과 연산자 스택으로 만들었다면 – 기호를 하나의 연산자로 취급하기 때문에 같이 계산할 대상 상수가 없어서 오류가 나게 된다. 이 문제를 해결하기 위해서는 음수를 0-x와 같은 식으로 바꿔줘야한다. 예를 들면 -2+3은 0-2+3으로, -(2+3)과 같은 경우는 0-(2+3)과 같이 바꿔줘야한다. 이렇게 하면 후위연산으로 전환 후에 스택으로 계산할 때 같이 비교할 대상이 있기 때문에 정상적으로 계산이 된다.

하나 주의할 것은, 만약 음수 기호 앞에 0이 아닌 다른 상수가 왔다면 이 경우에는 정상적으로 계산이 되므로 더이상 어떤 처리를 하면 안된다. 아래에 나오는 이중연산자 문제를 미리 처리하고 이 음수 연산자 문제를 처리하도록 만들었기 때문에 아래와 같은 정리할 수 있다.

  1. 괄호 앞에 -가 있는 경우 [ex : -(3+4) 라면]
  2. 0 – x로 변경한다. [ex : 0-(3+4)]
  3. 하지만 해당 부분 앞에 0이 아닌 상수가 있는 경우에는 [ex : …3-(3+4)] 변경하지 않는다.
  4. 그러므로 n번째에 ‘(‘가 오고 n-1번째에 -가 왔다면 n-2번째에는 반드시 0이 아닌 상수이어야하며 그렇지 않은 경우에는 0-x로 변경한다.
  5. 그런데 이미 이중연산자는 이전에 걸러져서 올 것이므로 위와 같은 상황이 오는 경우는 n-2번째가 ‘)’이거나 ‘(‘인 경우 밖에 없다.
  6. 따라서 이러한 경우는 해당 부분이 ‘)-(‘이거나 혹은 ‘(-(‘인 경우이므로, 이런 경우에는 각각 ‘)+0-(‘와 ‘(0-(‘으로 변경한다.

위의 6가지 규칙을 차례대로 따라 수식을 처리하면 된다. string의 find와 replace 메서드를 가지고 위 처리를 할 수 있다.

정말 사람 애먹인 작업이었다. 인터넷으로 찾아봐도 이 문제를 해결하는 방법에 대해서는 아무 도움을 받을 수 없어서 혼자 생각했다. 한참 골치아팠던 문제.

3. 소수점 문제

위에 문제들을 해결하고나니 이번엔 정수형 상수만 처리가 되었다. 소수점 처리도 하기 위해 후위연산 변환 후에는 double 형으로 처리할 수 있도록 변경한다. double 형으로 하면 소수점 이하 6자리까지는 나온다. 만약 이보다 더 강력한 정밀도를 원한다면 char 배열을 응용하여 계산하면 가능하다. (…지만 너무 귀찮아서 내가 해보진 않았다.) 소수점이 들어가게 되면 처리하고 가공할 때 숫자, 사칙연산자 뿐만 아니라 . 까지도 처리한다는 것에 유의해야한다.

4-1. 잘못된 수식에 대한 문제 – 수식의 맨마지막에 연산자가 오는 경우

수식계산 중 처음부터 아예 잘못된 수식이 입력될 가능성이 있다. 예를 들면 수식의 맨마지막에 연산자가 붙는 경우, 예를 들면, ‘2+3-‘와 같은 경우. 이 역시도 후위연산 전환후 계산과정에서 계산할 대상이 없어지게 되므로 프로그램이 죽는 문제가 발생한다. 수식의 맨마지막에 숫자가 아닌 다른 기호가 왔는지 찾아서 처리한다.

4-2. 잘못된 수식에 대한 문제 – 수식의 괄호의 갯수가 맞지 않거나 짝이 맞지 않는 경우

정상적인 수식이라면 여는 괄호와 닫는 괄호의 갯수가 맞아야하며 서로 짝이 맞아야한다. 이 상황을 만족시키기 위해서 난 다음과 같이 생각했다.

  1. 수식에 맨처음부터 검색했을 시 나오는 괄호가 닫는 괄호이거나, 수식의 맨뒤에서부터 검색했을 시 나오는 괄호가 여는 괄호라면 이 수식은 잘못되었다. 따라서 find나 find_first_of 등의 메서드를 이용해서 여는 괄호와 닫는 괄호가 처음 나오는 인덱스를 구한 다음 두 인덱스를 검사해보면 된다.
  2. 수식의 괄호의 갯수를 맞춰보기 위해서는 flag를 하나 설정하고 초기값을 0으로 둔다. 앞에서부터 검색해가며 여는 괄호 ‘(‘가 나왔을 시 flag를 1 더하고, 닫는 괄호가 ‘)’가 나왔을 시 flag를 1 뺀다. 수식의 끝까지 다 돌았을 때 여는 괄호와 닫는 괄호의 갯수가 같았다면 flag는 0이 되어야한다. 만약 0보다 크다면 여는 괄호가 더 많은 것이고 0보다 작다면 닫는 괄호가 더 많은 수식이다.

4-3. 잘못된 수식에 대한 문제 – 수식에 문자가 들어온 경우

간단하게 하나씩 검색해가며 문자가 들어있는지 판단한다. 문자열 검색은 너무 간단해서 적어둘 필요가 없을 것 같다. C/C++을 해본 사람이라면 누구나 다 간단하게 할 수 있다.

4-4. 잘못된 수식에 대한 문제 – 이중연산자가 있는 경우

한줄로 수식을 입력받는 경우 연산자가 이중으로 입력될 수 없다. 예를 들면 3+-2, 3*-2 등의 경우에는 처리할 수 없다. 한줄로 입력받는 사칙연산의 경우 연산자가 겹칠 경우에는 항상 괄호로 묶어야하며 그렇지 않고 연산자가 이중으로 온 경우에는 계산할 수 없다. 수식이 입력되었을 때 현재 글자와 이전 글자를 비교함으로써 이중연산자가 있는지 검색하고 있다면 오류로 판단한다.

 

위 프로그램을 클라이언트와 서버로 나누었다. TCP로 통신하게 되며 여기에 스레드를 통한 코드는 없다. 그냥 한번 1:1 대응 통신이다.

서버 프로그램 다운로드 :: CalcServer

클라이언트 프로그램 다운로드 :: CalcClient

여기까지 하면 대부분의 계산을 다 처리할 수 있다. 남은건 서버에서 계산한 결과를 클라이언트로 보내는 것. 나같은 경우는 클라이언트로 패킷을 보낼 때 처음 한바이트를 에러코드로 넣었다. 때문에 클라이언트에서 잘못된 수식을 입력하면 서버가 무슨 이유로 계산이 안됐는지 알려주고 클라이언트는 그 이유를 화면에 보여준다.

여기까지 하고 테스트 해보니 한줄로 입력가능한 사칙연산은 대부분 다 처리할 수 있었다. 거의 한 98%? 후…

간단한 사칙연산 계산기 하나 만드는게 얼마나 어려운지 몸소 체험했다. 이 문제의 핵심은 첫째로 중위연산을 후위연산으로 전환하고 스택을 이용하여 계산해가는 것. 그리고 둘째로 후위연산으로 전환할 때 나오는 문제점을 해결해나가는 것이다. 후위연산과 스택을 응용한 계산을 처리하지 못한다면 아무 진행도 할 수가 없다. 이 원리를 꼭 이해해야만 다음 과정들을 해결해나갈 수 있다.

네트워크 공부하다가 어쩌다보니 문자열과 수식연산에 대해 많이 공부하게 된것 같다.

 

위에 98%는 해결했다고 한 이유… 그 2%. 아직 해결하지 못한 문제가 남아있어서 이것도 적어둔다.

1. 수식에 0/x가 입력되거나 x/0이 입력되는 경우 혹은 계산과정 중 이런 경우가 나오는 경우

일단 수학에서는 0 나누기 x는 0이고 x 나누기 0은 무한대다. 컴퓨터에서는 0 나누기 x를 0, x 나누기 0은 오류로 판단한다.

입력된 수식 중 0 / x가 있는지 검색하거나 x / 0이 있는지 검색하는건 그리 어렵지 않다. 문자열에서 해당하는 부분을 검색하면 되니까.

문제는 입력된 수식을 계산하는 과정중 이러한 부분이 나오면 문제가 된다. 예를 들면, 100+3/(3-3)*(3-3) 이런 수식의 경우에는 처음부터 0 / x나 x / 0이라는 부분은 없기 때문에 단순히 문자열검색으로는 문제가 없다. 하지만 계산하는 과정 중에 이런 부분이 나오기 때문에 계산 과정 중 오류가 나고 프로그램이 죽게 된다. 이 문제를 해결하려면 후위연산으로 전환한 후에 스택을 처리하며 연산자 스택에 나누기가 있을 때 계산 스택에서 0이 있는지 확인해보는 수 밖에 없다.

이 문제는 스택 구조만 좀더 생각해보면 충분히 해결할 수 있을 것으로 보이는데 현재는 지쳐서 중지.

2. 괄호 앞에 상수가 나오는 경우

이건 만들다가 깜빡 잊고 안 넣은건데 나중에서야 생각났다. 수학의 규칙에서는 괄호 앞에 상수와 상수끼리는 연산자를 생략할 수 없다. 예를 들면, 2*3을 23이라고 쓸 수 없는 것과 마찬가지다. 연산자를 생략가능한 경우는 문자 즉, 변수와 같이 쓸 경우네는 2*x를 2x라고 쓸 수 있다. 지금 내가 만든 것은 상수항만 입력 받은 수식이므로 당연히 연산자는 생략될 수 없다.

중요한건 괄호가 있는 경우에도 연산자는 생략할 수 없다. 따라서 2(3+4)라는 수식은 실제로는 이렇게 써서는 안되는 수식이다.

이 문제에 대해서는 예전에 이게 되는거다 안되는거다 한번 인터넷에서 논란이 되었었는데, 난 이 경우에 대해서는 상수끼리는 연산자를 생략할 수 없다.라고 생각하고 그렇게 규정하기 때문에 잘못된 수식으로 간주한다. 그리고 C++ 컴파일러도 이런식의 수식은 잘못되었다고 판단하기 때문이다. 2(3+4)가 2*(3+4)라는 것은 인간이 쓰기 편하게 하려고 그렇게 약속한 것일뿐 엄밀히 기계적으로 따졌을 때는 틀렸다고 생각한다.

어찌되었던 괄호앞에 상수가 나오는 경우에는 잘못된 수식으로 봐야하는데 이 경우에 대한 처리를 넣지 못했다. 이것도 스택 구조만 조금 생각하면 충분히 해결할 수 있을 것 같다.

3. 불완전한 소수로 입력된 경우

소수를 입력할 때 0.1, 3.0처럼 입력하지 않고 .1, 3. 처럼 불완전하게 입력하는 경우에 대한 문제다. 테스트 해본 결과 double 형으로 변경하는 과정에서 이러한 숫자들은 전부 완전한 소수형태로 자동전환이 되었다. 하지만 수학에서 이렇게 쓰는 경우는 없으므로 체크하는게 좋을 것 같다. 이건 구현하기 쉬운 부분이라 일단 패쓰.

PF_INET과 AF_INET의 차이

네트워크 프로그래밍 책을 둘러보던 중 소켓 생성할 때 PF_INET으로 생성하는 것을 보고 이게 뭔가 싶었다. 내가 배울 때는 항상 AF_INET으로 배웠기 때문. 책에도 이에 대한 내용이 부족해서 검색해보니 여기에 대한 정보가 많았다.

굳이 정리하자면 PF_INET과 AF_INET은 둘다 상수값 2로 같은 값을 나타내지만 PF_INET은 프로토콜 설계에, AF_INET은 주소체계에 쓰이는 것이 바람직하다고 한다. 그래서 socket() 함수에서는 PF_INET을 sockaddr_in 구조체의 sin_family에는 AF_INET을 쓰는 것이 좋다고 한다.

예전에 socket() 함수를 설계할 때 전문가들은 하나의 프로토콜 안에서 서로 다른 주소체계를 쓸 수 있게 될것이라 생각하여 PF_INET을 만들었는데 현재의 IPV4에서도 아직까지 바뀐게 없어 그냥 그대로 사용되고 있다고 한다.

socket() 함수에서도 AF_INET을 써도 잘 작동되긴 하는데 어쨌튼 의미적으로는 다른 것이라고 하니 왠만하면 지켜가야할 듯.

참고자료 :: 네이버에 검색하면 부지기수로 있다;;;

힙과 스택

내가 면접 때 많이 들어봤던 질문 중의 하나였다.

“힙과 스택에 대하여 설명해보세요.”

C/C++을 공부하면서 당연히 배우는 과정 중의 하나인데 문제는 이런 내용은 C 처음 배울 때 나온 내용이라 기억의 저편으로 멀리 가버렸다는 점. 기억을 되살리기 위해 다시 정리해본다.

스택의 특징은…

  • 프로그램이 자동적으로 할당한 메모리 영역이다.
  • 자동변수와 파라미터들이 생성되고 스코프가 끝나면 사라지는 메모리 영역.
  • 함수가 실행된 뒤 원래 실행위치로 돌아오는 리턴값 역시도 스택에 저장된다.
  • 함수 안에서 선언된 변수는 모두 스택으로 들어간다.
  • 함수를 호출했을 때 현재 상태에 대한 값도 스택으로 들어간다.
  • LIFO 구조를 가진다. 먼저 들어간 자료가 가장 나중에 나오게 되며, 가장 나중에 들어간 자료가 가장 처음에 나오게 된다.
  • 윈도우 프로그램의 경우 기본 스택메모리의 크기는 1MByte이다.
  • 스택의 크기를 넘어가게 되면 Page Fault 인터럽트(즉, 스택오버플로우)가 발생하게 된다.
  • 모든 포인터 역시도 스택에 올라간다. 포인터 역시도 하나의 변수라는 것을 잊지말 것.
  • 사용할 스택의 크기는 컴파일 하는 순간 모두 정해진다. 따라서 실행 도중 메모리의 크기를 변경할 수 없다.
  • 스레드가 여러개라면 각 스레드마다 고유한 스택영역이 준비된다.
  • 하나의 메소드를 실행하기 위한 스택메모리의 묶음을 스택프레임이라고 부른다.
  • 어떤 변수를 선언했다면 변수명 자체는 스택에 변수 내용은 힙에 저장된다.

힙의 특징은…

  • 프로그래머가 스스로 할당한 메모리 영역이다.
  • 힙은 프로그램이 동적으로 할당받아 사용되는 메모리 영역. C에서의 malloc을 통해 생성하고 free를 통해 해제하며 C++에서는 new를 통해 생성하고 delete를 통해 메모리를 해제한다.
  • 힙 메모리가 부족하게 되면 메모리 이상현상으로 프로그램은 종료되게 된다.
  • 사용할 힙의 크기는 프로그램 실행 중 정해진다.
  • 동적 메모리할당은 프로그램 사용자들이 얼마나 데이터를 입력할지 예측할 수 없는 경우에 사용한다.
  • 여러 스레드가 있는 경우 힙 영역은 각 스레드가 공통으로 사용할 수도 있다.

이외에도 데이터영역과 텍스트영역(코드영역)이 있다.

데이터영역의 특징은…

  • 프로그램이 실행되고 난 후 종료될 때 까지 지워지지 않을 메모리 공간이다.
  • 따라서, 전역변수, Static 변수 등이 저장된다.
  • 프로그램당 한개의 영역이 할당된다.

텍스트영역(코드영역)의 특징은…

  • 프로그램의 실행코드를 저장한다. 프로그래머가 만든 코드, 함수는 모두 여기에 저장된다.
  • 역시 프로그램당 한개의 영역이 할당된다.

 

위의 내용들을 찾을 수 있었다. 다시금 찾아보니 생각보다 내용도 많고 차이점도 여러가지인 것 같다. 앞으로 더 찾아보며 여기에 추가해야겠다.

 

<참고자료>

  • http://blog.naver.com/brosvaby?Redirect=Log&logNo=165225008
  • http://panic.kldp.org/node/199
  • http://blog.naver.com/ysjin1212?Redirect=Log&logNo=110128934379
  • http://blog.naver.com/0bloodwind0?Redirect=Log&logNo=20127908176
  • http://blog.naver.com/zelet7?Redirect=Log&logNo=140015294688