Category Archives: 프로그래밍

Varnish Cache 소스로부터 배우기

요즘 Varnish Cache를 본격적으로 도입하기 위해서 노력중입니다. 설계 문서를 보면 많이 사용하는 Squid는 70/80년대 하드웨어에 적합하게 설계되었기 때문에 요즘 하드웨어에서는 퍼포먼스가 아주 많이 떨어진다는 흥미로운 얘기가 있습니다.

Squid에서는 소프트웨어적으로 캐시를 따로 구현해서, 커널에서 메모리 관리하는 것과 같이 잘 동작하지 않는다고 합니다. 소프트웨어적으로 캐시를 따로 구현하면 필요하지 않은 메모리 영역을 swap out할때, 똑똑한 커널에서 이미 swap out됐던 데이터를 메모리 영역으로 불러왔다가 다시 swap out 하는 과정이 일어난다고 합니다. 옛날 OS에서는 메모리 관리가 최적화가 안되어있었지만 요즘은 메모리 관리를 커널에서 알아서 하게 하면 퍼포먼스가 많이 빨라진다고 하네요.

Varnish Cache 최적화는 크게 3가지 정도로 요약할수 있을거 같습니다.

1. 메모리 관리를 커널에 위임
2. 메모리 할당/해제 부분을 최소화 (workspace 단위 할당, 한번에 해제)
3. 설정 파일 컴파일러로 컴파일

메모리 관리는 mmap을 통해서 관리합니다. 파일 시스템 영역과 메모리 영역을 연결하여 사용하고, storage_file.c에서 직접 메모리 쪼개서 관리합니다. 메모리 영역을 쪼개서 free list(VTAILQ)에 넣고, 할당할때 free list에서 꺼내서 쓰는 방식입니다. 프로그램에서는 무엇이 메모리에 있고, 파일로 swap 할지 결정하지 않고 커널이 하도록 내버려둡니다.

메모리 할당은 workspace 단위로 하고 그 안에서 쪼개서 쓰는 거 같습니다. 이 부분은 좀더 분석이 필요할거 같습니다.

설정 파일은 c로 변경한 다음 저장하고, 직접 cc를 호출해서 오브젝트 파일을 생성하며, dlopen으로 로드하게 되어있습니다.  mgt_vcc.c의 mgt_CallCc 함수를 참고하세요. dlopen을 사용하기 위해서 컴파일러에 -fpic -shared 등의 옵션을 넘기게 되어있습니다.

Varnish의 개발자가 FreeBSD 커널 개발자 출신입니다. 지금도 개발하는지는 모르겠네요..Varnish는 정말 BSD커널 소스처럼 짜놨네요. BSD 커널 소스보면 데이타 구조가 대부분 queue.h로 되어있는데, 여기서도 VTAILQ 등으로 이름만 조금 바꿔서 사용하고 있습니다. queue.h는 여러가지 데이타 구조를 C 매크로만으로 구현한 헤더파일 라이브러리입니다. 구현할때 struct 안에 포인터가 내장되서 여러가지 방법으로 메모리 사용을 최적화할수 있습니다. 커널에서는 대부분 블럭단위로 메모리 할당하여, 그걸 쪼개서 free 리스트에 넣어놓고 그걸 꺼내써서 사용하는 방식으로 메모리를 관리합니다. 메모리 할당과 해제가 간단한 포인터 연산으로 끝나기 때문에 매우 효율적이고, 메모리 fragmentation도 많이 줄여줍니다. 메모리 할당과 해제가 자주 일어나는 경우 아주 큰 도움이 되죠.

2000년에 FreeBSD에서 방화벽 커널 모듈을 개발한 적이 있었는데, 메모리 관리를 queue.h와 “Redesigning the BSD Callout and Timer Facilities (1995)”를 참고해서 구현했었습니다. 퍼포먼스 최적화에 아주 큰 도움이 됐었습니다. Redesigning the BSD Callout… 논문을 어떻게 알게 되었는지는 기억은 안나지만, 정말 많은 도움을 받았던 논문이네요. 지금 찾아보니 첫번째 저자는 구글에 있고, 두번째 저자는 벤처 만들었다가 시스코에 인수됐다고 하네요. 지금은 UCSD 교수로 있네요. 이 논문에 대해서는 블로그에 글을 한번 정리해서 올리도록 노력하겠습니다.

2008년 1월 OCaml Meeting 프리젠테이션 & mp3

2008년 1월 26일 파리에서 OCaml 미팅이 있었나봅니다.

프리젠테이션과 mp3 파일이 올라왔네요. 평소에 관심만 있고 실제적으로는 활용을 아직 못하고 있는 언어인데… (LISP도 마찬가지) 여러가지로 관심이 가는 언어입니다. 요즘은 다이나믹 언어가 추세이긴 하지만, static type checking도 프로그램 안정성에 많은 기여를 하죠. Ocaml에서는 type inference 지원으로 자바처럼 열심히 type 선언을 안해줘도 됩니다. static type checking을 위해서 프로그래머가 노가다할 필요도 없고, 안전하면서도 유연한 소스가 나오죠… 언어적인 측면도 좋고, 컴파일러도 구현도 아주 잘 되어있지만, 라이브러리(특히 윈도우 관련 라이브러리)가 풍성하지 못해서 아직 활용을 못하고 있습니다.

아래 페이지에서 프리젠테이션과 mp3를 구할수 있습니다.
http://wiki.cocan.org/events/europe/ocamlmeetingparis2008

프랑스 파리에서 했는데, 모두 영어로 되어있는거 같긴 하네요. 다만 mp3가 너무 작게 녹음됐네요. mp3gain으로 볼륨 조절하시면 됩니다.

제가 요즘 실제로 사용하는 언어는:
   C++
   Python
   PHP
   Javascript
   Actionscript

관심있는 언어는:
   LISP
   Objective Caml
   D언어
   Smalltalk

입니다.

Incredibuild 분산 컴파일

전에 Visual Studio에서 precompiled header 설정을 바꿔서 컴파일 시간을 대폭 줄인 글을 올렸었습니다. 그때 여러가지 컴파일 속도를 향상할수 있는 방법을 찾다가 발견한게 Incredibuild인데 평가판(30일)을 설치해서 회사에서 적용해 보았습니다.

최근 접한 프로그램 중에 개념도 신선하고, 구현도 아주 잘 된 소프트웨어네요.

Coordinator와 Agent로 설치할수 있는데, Coordinator는 빌드를 전체적으로 관리해주는 서버 프로그램으로 네트워크에 하나만 설치하면 되고, Agent는 Coordinator 서버에 연결해 있다가 작업을 할당 받아 처리하는 프로그램입니다. Agent를 많이 깔아주면 속도 향상이 그만큼 되죠.

신기한거는 Agent 설치하는 피씨에 개발툴이 없어도 된다는 겁니다. 컴파일러까지 전송해서 실행하는 방법으로, 처음 빌드를 요청한 컴퓨터의 빌드시스템을 그대로 받아서 실행하기 때문에 시스템간의 버전 차이, 헤더파일 차이 문제 때문에 빌드가 이상하게 되는거를 방지할수 있습니다. PC에 Agent만 설치하면 되기 때문에, 개발툴을 설치하지않고 쉽게 여러 컴퓨터에 설치할수 있는것도 큰 장점입니다.

또한, Visual Studio 6.0이 Multi CPU를 전혀 활용하지 않기 때문에 하나의 피씨에만 깔아도 속도 향상을 시킬수 있습니다.

아래는 coordinator 상황을 볼수 있는 모니터 프로그램입니다. 8개의 PC가 등록되어 있으며, 현재 온라인이 4개입니다. 요즘은 거의 Dual Core이기 때문에 CPU는 16개 입니다.

아래는 빌드했을때 모습입니다. 빌드는 Visual Studio안에 Incredibuild 메뉴에서 Rebuild로 할수 있습니다. 6개의 PC가 사용되었고, 사용한 CPU는 12개 입니다.

아주 비주얼하게 보여주기 때문에 눈도 즐겁죠^^ 설치하고 몇번을 rebuild all하면서 빌드 속도에 만족하고 있습니다. Visual Studio안에 창이 나타나며, 별도로 창을 띄워서 볼수도 있습니다. 원래 네모 상자안에 소스 파일명이 나왔지만 회사 보안상(?) 지웠습니다. 초록색이 이상없이 컴파일 된거고, 오류가 나면 빨간색, 경고가 있으면 노란색으로 표시됩니다. 하늘색은 초기에 종속성 분석과정과 링크 과정입니다. 위에 그래프는 여러가지 중에 선택해서 보여줄수 있도록 되어있는데, 파란색이 실제로 사용한 CPU 사용량(GHz 단위), 빨간색이 유효하게 사용한 CPU 사용량입니다. 차이가 나는 이유는 일을 시키고 늦게 처리되는거나 종속성 때문에 따로 돌릴수 있는 작업이 없을때는 중복적으로 일을 시키고 그중 빨리 처리되는 결과를 받아서 처리하기 때문입니다. 위 그래프 상에서 최대값이 27.5GHz입니다 ^^.

원래 5:21.9 초 걸리던 빌드 시간이, precompiled header 설정 오류를 고치고 1:32.8 초로 incredibuild를 도입하고 나서는 23.5 초로 줄었습니다. 원래보다 13.7배, precompiled header 설정 오류를 고치고 나서 기준으로 보면 3.9배 빨라졌습니다. 사용한 CPU에 비해서 아주 많이 빨라진건 아니지만 컴파일하고 기다리는 시간이 즐겁고 짧아졌네요.

하지만, 실제적으로 개발할때 rebuild all하지 않고 바뀐 부분만 컴파일하기 때문에 incredibuild 도입효과는 생각보다 크지 않습니다. 링크과정 자체는 분산해서 처리하기가 애매하기도 하고, 컴파일된 결과를 모두 받아와서 링크하는 과정을 거쳐야하기 때문에 그런거 같습니다. 그리고 Agent를 추가한다고 지속적으로 속도가 비례해서 증가하지 않습니다. 네트워크 Bandwidth의 영향도 있고, 소스간 종속성 때문에 분산해서 한꺼번에 처리할수 있는 일의 수가 어느정도 정해져 있기 때문입니다. 소스간 종속성을 줄이면 개선이 가능하겠지만, 중소규모의 소프트웨어 프로젝트에서는 도입 효과가 크지 않을거라 생각합니다. 단, 빌드 서버를 구축한다면 아주 좋은 방법이 될거 같네요. 그런데, 가격이 문제입니다! 카피당 $350.

30일간 평가판을 사용해볼수 있으니, 빌드시간이 문제가 된다면 한번 사용해보시기 바랍니다.
http://www.xoreax.com/download.htm

Trac report

2008년 들어서 회사 개발팀에서 모든 일을 Trac의 티켓으로 관리하기로 했습니다.

Trac이 한글화 되어있지 않은거 같아서 몇가지 간단한거는 한글로 바꿨습니다.

현재 0.10.4 버전을 사용중인데, 설정 변경은 TracWebAdmin 플러그인을 통해서 했습니다. 새버전에는 Admin 기능이 Trac에 통합된다고 하는데, 정말 없으면 안되는 필수 기능입니다.

Priority를 영어에서 1,2,3,4,5로 바꾸고,
Type은 버그수정, 기능개선, 새로운기능으로 변경했습니다.

개발팀 밖에서 티켓 등록 및 모니터링을 할수 있도록, Permission을 바꿨습니다.
먼저 개발자(developers) 그룹을 생성하고, TIMELINE_VIEW와 BROWSER_VIEW 액션을 anonymous에서 developers로 옮겼습니다. (삭제하고 다시 등록)

Trac의 사용자 인증은 htpasswd를 사용하며, 웹서버는 lighttpd를 사용하고 있습니다.

View Tickets 페이지 보면 report를 편집할수 있는데,  편집해서 TODO와 최근변경사항을 만들었습니다. TODO는 아직 완료되지 않은 일(티켓)을 내거와 다른사람거로 나누어 보여주며, 우선도에 따라 정렬해 보여줍니다. 바탕색도 우선도에 따라서 보여줍니다. 아래는 report를 위한 SQL Query입니다.

[CODE]
SELECT p.value AS __color__,
  (CASE owner
    WHEN ‘$USER’ THEN ‘My Tickets’
    ELSE ‘Active Tickets’
   END) AS __group__,
  id AS ticket,
  (CASE status WHEN ‘assigned’ THEN owner||’ *’ ELSE owner END) AS owner,
  summary, component, priority, t.type AS type,
  time AS created,
  changetime AS _changetime, description AS _description,
  reporter AS _reporter
  FROM ticket t, enum p
  WHERE status IN (‘new’, ‘assigned’, ‘reopened’)
AND p.name = t.priority AND p.type = ‘priority’
  ORDER BY (owner = ‘$USER’) DESC, p.value, milestone, t.type, time
[/CODE]

최근변경사항은 티켓에 일어난 최근 변경 사항을 20개 보여줍니다. 물론 Timeline에서도 볼수 있지만, Timeline을 개발자만 볼수 있고, 소스 커밋과는 별도로 볼수 있도록 하는게 편리할것 같아서 따로 report를 생성했습니다.

[CODE]
SELECT
  id AS ticket,
  summary,
  changetime AS modified,
  status,
  component,
  t.type AS type,
  (CASE status WHEN ‘assigned’ THEN owner||’ *’ ELSE owner END) AS owner,
  time AS created,
  description AS description,
  reporter AS _reporter
  FROM ticket t
  ORDER BY changetime desc limit 0,20
[/CODE]

trac report 만드는데 참고할 문서가 별로 없어서 좀 헤맸는데… 위에서 reporter as _reporter식으로 “as _*”로 하면 테이블에서 항목이 표시가 안됩니다. 기본 예제들을 자세히 보면 항목이 뭐가 있는지 볼수 있으므로 기본 예제들을 참고하시고 조금씩 바꾸면 원하는 출력을 얻을수 있습니다.

TODO와 최근변경사항 링크를 메뉴로 쉽게 접근할 수 있도록, http://trac-hacks.org/wiki/NavAddPlugin를 설치했습니다.

회사 SVN 통계 그래프

얼마전(12월6일)에 TortoiseSVN에서 Merge기능을 사용하다가 새로운 기능을 찾았습니다.
Commit 통계를 그래프로 그려주는 기능이 있더군요! 이제 회사 생긴지도 6년이 넘었고, 연말이고 해서 지난 Commit 통계를 그래프로 그려봤습니다.

회사는 2001년 말에 만들어졌고, APP 개발만 SVN으로 관리하고 있습니다. 원래 CVS로 관리했었는데 작년에 SVN으로 갈아탔습니다. CVS에서 파일단위로 커밋이 관리되는데, SVN으로 가져올때 커밋단위로 변경해서 가져와서 통계내는데도 전혀 문제가 없네요.

먼저 회사의 SVN은 세가지로 관리됩니다. 내부 개발이 Develop, 외부 프로젝트가 Project, 실험 프로젝트가 Test. Test에서 어느정도 쓸만하게 되면 Develop으로 옮겨갑니다. 외부 프로젝트의 경우 외부 저장소에서 관리되는 경우도 있어서 이런 경우는 포함되어 있지 않습니다.

예상외로 그래프가 들쑥날쑥하지는 않네요. 2002년까지는 3명정도, 그 이후로는 4명 정도가 개발인원으로 유지되고 있는거 같네요. 예전엔 외부 프로젝트가 많아서 인당 프로젝트가 할당되서 스트레스 받았었는데, 요즘은 외부 프로젝트를 안해서 그래도 살만합니다.

현재 revision 10948으로 대략 한달에 20일 일한다고 하면 하루 8회 정도 commit이 일어났네요..

아래는 올해 Develop commit 그래프 입니다.

새로 들어온 사람들이 잘 적응하고 있는거 같아서 다행이네요.

아래는 사용자별 그래프입니다. 제가 제일 commit은 많이 하는데… 다른 사람들은 commit하는 주기가 저에 비해서 좀 길어서 그런거 같습니다.


TortoiseCVS에도 이런 기능이 있는지는 모르겠지만 CVS에서 SVN으로 바꾼 이후 후회한적이 거의 없습니다. Trac의 소스보기가 CVSWeb하고 인터페이스가 좀 틀려서 Revision보느거하고 Diff보는게 헷갈렸던거 빼고는 정말 장점이 많네요.

위 그래프를 개발자들에게 보여주니 commit 자주해야겠다고 하네요. commit 횟수가 아닌 commit 라인수 단위로 그래프를 그려볼수 있는 방법이 없나 찾아봐야겠네요.

Visual C++ 컴파일 속도 향상

간만에 글 올리네요…

회사 프로젝트 빌드 시간이 갈수록 길어지고 있었는데, 빌드시간을 아주 많이 줄일수 있는 좋은 글을 찾았습니다. precompiled header를 사용하고 있었지만, 제대로 설정이 안되어있었나봅니다.

제가 VC++ 6.0을 사용하기 때문에 6.0 기준으로 설명합니다.

일단 라이브러리를 제외하고 메인 exe 빌드하는데 시간을 측정했습니다. 측정 방법은 msdev.exe 실행할때 /y3 옵션을 주면 빌드시간이 표시됩니다.

변경하기전 출력입니다.
Build Time 5:21.9

변경후 출력입니다.
Build Time 1:32.8

대략 3.4배 빨라졌습니다.
근데, 팀 개발자들의 반응은 의외였습니다! 빌드시키고 놀시간이 없어졌다고 부정적인 반응을 —

변경 내용은 프로젝트 전체에서 precompiled header 세팅을 새로 했습니다. 원래 Automatic use of precompiled header에서 Use precompiled header file (stdafx.h)로 지정했습니다. 그리고 예외적으로 precompiled header를 안쓰던 몇개의 C(++) 소스들은 파일별로 Not using precompiled header로 설정했습니다.

다음 stdafx.h에 편의상 resource.h와 로그 관련 헤더를 하나 포함했었는데, 프로젝트 관련 헤더는 모두 뺐습니다. 대신 자주 사용하는 STL 헤더들을 stdafx.h에서 include 했습니다.

10분 정도 간단한 변경으로 빌드 속도가 3배 이상 빨라졌습니다. ^^v

boost::format

이번에 프로그램을 중국어로 포팅하면서 죽는 버그가 생겼는데 이 버그 잡느라 많이 삽질했습니다. XP에서 죽을 때 보면 모두 MFC 쪽 스택만 표시되서 UI 쪽이라는 의심은 했었는데 도저히 어딘지는 못찾겠더군요. 바꾼거도 별로 없는데, 이상하게 로그인만 하면 죽는겁니다 TT

Windows 2000에서 디버깅하면 뭔가 나타날줄 알았는데 스택이 똑같더군요 –;

아주 우연히 버그를 발견했는데…. 버그는

[CODE type=C]CString x;
string val = “haha”;
x.Format(“%s”, val);[/HTML][/CODE]

이런 거 였습니다. 뭐 위에 코드가 실행될때 바로 죽는거도 아니고, 몇번 실행되다 보면 이상해지는거죠. printf나 Format 자체가 뒤에 오는 인자를 컴파일러에서 체크할 방법이 없기 때문에 이런 오류는 컴파일러에서 워닝도 표시안해줍니다. 다만 gcc에서는 printf 등의 ANSI 함수에 대해서는 경고 표시는 해주죠.

하여간 아주 간단한 실수 하나로 정말 어의없게 디버깅해도 거의 찾을 수 없는 수준의 버그가 만들어지네요.

소스에서 한글로 되어 있는 부분을 문자열 리소스로 빼는 작업인데, 이를 위해서 GS() 함수와 GSTR() 함수를 만들었습니다. GS는 STL string을 리턴하고, GSTR은 C string을 리턴하도록 정의했습니다. 문자열 리소스 빼고나니 300개 정도 되는데 그중에 하나 실수한거죠.

[CODE type=C]extern CLanguage *gLanguage;

#define GSTR(x) gLanguage->GetString(x).c_str()
#define GS(x) gLanguage->GetString(x)[/HTML][/CODE]

이런 오류는 언제든지 다시 발생할수 있다는 생각에 구글에서 “type safe format”으로 검색해보니, boost에 format 라이브러리가 있더군요. 예전에 서브버전에 등록한 boost 버전에서는 없었던거 같은데… 하여간 라이브러리를 boost 1.34.1로 업데이트했습니다.

boost는 기본적으로 header 파일로만 구성된 template 라이브러리로 따로 컴파일을 할 필요없고, 몇가지 기능(예: regex, python, thread)이 필요하면 라이브러리를 컴파일해야합니다. 기본적인 기능은 거의 헤더 파일로만 구현되어 있어서 라이브러리 컴파일은 하지 않았습니다.

boost::format은 CString::Format이나 printf와 약간 사용법이 틀립니다. Format 등은 떨거지 인자들을 함수 인자로 쭈루루 전달하는데, 이렇게 전달하면 컴파일러에서 type checking을 할수 없기 때문에 문법이 약간 달라졌습니다. 처음에는 생소하지만, 조금만 익숙해지면 불편하지 않게 사용할수 있습니다.

[CODE type=C]#include <boost/format.hpp>

void test_format()
{
   string str1 = (boost::format(“%02d:%s”) % 30 % “hello”).str();
   CString str2;
   str2.Format(“%02d:%s”, 30, “hello”);
   CString str3 = str1.c_str();
}[/HTML][/CODE]

일단, 인자를 잘못 전달한다던가 해서 테스트해봤는데 죽는 경우는 없었습니다.

인자를 잘못 전달하더라도 컴파일러에서는 경고나 오류가 발생하지 않습니다. (VC 6.0 이라 그런지도 모르겠습니다.)

제일 큰 차이점은 인자들을 인자로 넘기는 것이 아니라 % 로 stream 형태로 넘기는 것입니다.
boost::format(“…”)으로 개체가 만들어지고 개체에 % 함수로 하나씩 인자를 넘기는 거죠. format의 인자(%d %s… 등등등)는 printf나 Format과 차이가 거의 없고, 그대로 사용하면 되고, 몇가지 추가 기능도 있습니다.

앞에 format의 타입과 뒤에 %로 전달되는 인자가 틀려도 알아서 잘(?) 표시 해줍니다. 내부적으로 C++ stream (<<)을 이용해서 변환하기 때문에 type이 안맞아도 그냥 동작하는거 같네요. %03d 등은 C++ stream manipulator 등을 이용할듯 하네요. 그래서 manipulator로 스트림 속성을 바꾸는 거기 때문에, 숫자 전달하는곳에 문자열을 전달해도 문제가 되지 않는듯합니다. stream manipulator는 C++ 책이나 http://www.deitel.com/articles/cplusplus_tutorials/20060218/index.html 를 참고하세요
.

시험 삼아 CString을 직접 전달해봤습니다. 죽지는 않고, 포인터 값을 출력하네요.

CString 넘길때마다 GetBuffer(0) 호출하기는 귀찮으니 operator <<를 만들어주면 CString을 직접 넘겨도 됩니다.

[CODE type=C]std::ostream& operator<<(std::ostream& os, const CString &x);[/HTML][/CODE]

구현은 쉬울거 같았는데, casting 때문에 좀 헤맸습니다.

[CODE type=C]std::ostream& operator<<(std::ostream& os, const CString &x)
{
   os << const_cast<CString&>(x).GetBuffer(0);
   return os;
}[/HTML][/CODE]

이렇게 하면 boost::format에 직접 CString을 넘겨도 문자열이 잘 출력됩니다.

사용하기 편리하도록 stdafx.h에 다음 라인들을 추가했습니다.

[CODE type=C]std::ostream& operator<<(std::ostream& os, const CString &x);
#define FORMAT_STRING(fmt, args) (boost::format(fmt) % args).str()
#define FORMAT_CSTRING(fmt, args) (FORMAT_STRING(fmt, args).c_str())
[/HTML][/CODE]

원래 소스를 boost::format으로 바꾸기 위해서는 일단 boost/format.hpp를 include 하고
아래처럼 바꾸면 됩니다.

       CString x;
       x.Format(“%02d 시”, cnt);
       m_hourCtrl.AddString(x);
    →
       m_hourCtrl.AddString(FORMAT_CSTRING(“%02d 시”, cnt));

       SendRequestFormat(“user %s|%s”, userid.c_str(), version.c_str());
    →
       SendRequest(FORMAT_STRING(“user %s|%s”, m_userid % version));

Resource Hacker: 리소스 편집 툴

외국 프로그램 한글화 할때 사용하는 툴인데, 이번에 회사에서 응용프로그램을 중국어로 바꾸는데 사용하고 있습니다. 원래는 언어별로 리소스 DLL 파일을 만들어서 작업했었는데 이번에 새로운 버그(?)가 들어가서 DLL에 있는 리소스를 잘(!–) 못 읽어오는 문제가 발생했는데, 생각처럼 쉽게 해결이 안되더군요.

시간은 없고 해서 다른 방법을 찾아봤는데, Resource Hacker(reshack.exe)에서 일괄 작업등을 지원하여 불편없이 localization에 사용할수 있더군요. 또한 DLL 처럼 컴파일 할 필요도 없고, 번역하는 사람한테 넘겨서 직접 실행해보면서 미비한 점들을 고칠수 있고, 작업 완료후 exe나 res를 넘겨받은다음 필요한 부분만 res 파일로 CVS나 SVN에 저장해두면 나중에 업그레이드 할때도 큰 문제없이 적용할수 있습니다. 아니면 인스톨러 생성에 필요한 모든 파일을 넘기고 번역후 직접 로컬화된 설치 프로그램을 작성하도록 할수도 있습니다.

Resource Hacker는 델파이로 만들어졌고, 개발이 중단된 상태지만, 사용하기에는 큰 불편함이 없습니다. 소스가 공개된 XN Resource Editor도 있지만 문서화도 잘 안되어있고, 완성도가 떨어집니다. XN Resource Editor는 오픈소스이기 때문에 발전가능성이 있긴하죠…

사용자 인터페이스는 그리 복잡하지 않습니다. 다이알로그나 문자열 편집은 오른쪽에 편집창을 이용해서 텍스트로 편집할수 있기 때문에 워드 문서 등에서 직접 Copy&Paste를 할수 있어 편합니다.

exe파일이 바뀔대마다 노가다를 할수 없으므로, 자동화는 반드시 필요합니다. reshack에서는 커맨드라인을 통해서 작업을 시킬수도 있고, -script 옵션으로 스크립트로 여러가지 작업을 한번에 돌릴수 있는 기능도 제공합니다.

한가지 사소한 버그를 찾았는데 파일명이 숫자로 시작하면 오류도 없이 작업하지 않고 종료되더군요… –; 파일명만 바꿔주면 되더군요.

배치파일과 script 파일을 만들었습니다:

ResHacker.exe -script apply_cn.txt
// apply_cn.txt
[FILENAMES]
Exe=      appname.exe
SaveAs=   appname_cn.exe
Log=      appname_cn.log

[COMMANDS]
//-delete   MENU,,0
//-delete   DIALOG,,0
//-delete   STRINGTABLE,,1042
-delete   STRINGTABLE,,1033
-addoverwrite      cn.res, MENU,,2052
-addoverwrite      cn.res, DIALOG,,2052
-addoverwrite      cn.res, STRINGTABLE,,2052

리소스 지정은, 타입(MENU,DIALOG,STRINGTABLE), 아이디(100,101..), 언어(1042,1033) 형태로 나오는데 모든걸 지정하려면 해당 항목을 비워주면 됩니다.

언어는 1033은 영어(US), 1042(한국어) 2052(중국어-simplified).

위에서는 appname.exe에 있는 영문 문자열 리소스를 빼고, cn.res에 있는 메뉴,다이알로그,문자열 리소스를 추가하여 appname_cn.exe로 저장합니다.

XPath & libxml2

XML은 꽤 오래전부터 사용했지만, 여러가지 복잡한 용어들이 나오면서 좀 멀어졌던 느낌이었는데, XPath는 정말 프로그래머에게 유용한 툴인것 같네요.

XPath는 XML 문서에서 쉽게 element를 찾는 API로 쿼리를 문자열로 넘기면 조건에 맞는 element나 element 리스트를 반환하게 됩니다. 1.0 버전이 있고 2.0 버전이 최근에 나왔습니다. 아직까지는 라이브러리들이 1.0 기반이 대부분입니다.

쿼리는 예제로 살펴보는것이 빠른듯하네요.

“A/B/C” : A element 밑에 B element 밑에 C element들은 찾을때
“/A/B/C” : 위와 같지만 A가 최상위 element.
“/A/B/C[1]” : C element중 첫번째
“/A/B/C[2]” : C element중 두번째
“//C” : 모든 C element
“B//C” : B 하위에 있는 C element
“A/B/*” : A element 밑에 B element 바로 밑의 모든 element
“A/B//*” : A element 밑에 B element 밑의 모든 element (하위 element 포함)
“//*” : 문서의 모든 element

libxml2라는 C 라이브러리가 있지만 python wrapper를 이용하여 python에서 위의 쿼리들을 돌려봤습니다. 테스트해본 결과 /로 시작하지 않는 쿼리들은 제대로 동작하지 않더군요. (검색되는 결과가 없음) 이런 쿼리들은 앞에 //를 붙여주면 제대로 동작합니다.

아래는 python 소스입니다. 중간에 예제 XML을 보기 좋게(?) 들여쓰기했놨지만 출력할때 한줄로 볼수 있도록 xml에서 공백과 newline을 제거합니다.

[CODE type=python]
import libxml2

def xpathElements(ctxt, query):
   if query[0] == ‘/’:
       print “\”%s\”” % query
   else:
       print “\”%s\” -> \”//%s\”” % (query, query)
       query = “//” + query
   res = ctxt.xpathEval(query)
   for e in res:
       print ”    %s (%s)” % (e.name, e)

xml = “””
<A>
   <B>
       <C id=’c1’/>
       <C id=’c2′>
           <D/>
       </C>
       <E>
           <F/>
           <A>
               <B>
                   <C/>
               </B>
           </A>
       </E>
   </B>
</A>”””

xml = ”.join([l.strip() for l in xml.splitlines()])

doc = libxml2.parseDoc(xml)

ctxt = doc.xpathNewContext()

xpathElements(ctxt, “A/B/C”)
xpathElements(ctxt, “/A/B/C”)
xpathElements(ctxt, “/A/B/C[1]”)
xpathElements(ctxt, “/A/B/C[2]”)
xpathElements(ctxt, “/A/B/C[3]”)
xpathElements(ctxt, “//C”)
xpathElements(ctxt, “//B//C”)
xpathElements(ctxt, “A/B/*”)
xpathElements(ctxt, “A/B//*”)
xpathElements(ctxt, “//*”)
[/HTML][/CODE]

다음은 실행결과입니다.

“A/B/C” -> “//A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“/A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
“/A/B/C[1]”
   C (<C id=”c1″/>)
“/A/B/C[2]”
   C (<C id=”c2″><D/></C>)
“/A/B/C[3]”
“//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“//B//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“A/B/*” -> “//A/B/*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   E (<E><F/><A><B><C/></B></A></E>)
   C (<C/>)
“A/B//*” -> “//A/B//*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)
“//*”
   A (<A><B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B></A>)
   B (<B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B>)
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)

컴퓨터 프로그래밍(CS) 인터넷 강좌

MIT OpenCourseWare 비디오와 오디오 강좌 – 컴퓨터 외에도 여러 공학 강좌가 있습니다. MIT OCW가 매스컴을 통해서 많이 알려졌지만, 비디오 강좌는 아직 많은 자료가 없는거 같네요. 이중 컴퓨터 강좌는:

  • 6.033 Computer Systems Engineering, Spring 2005
  • 6.046J / 18.410J Introduction to Algorithms (SMA 5503), Fall 2005

UC Berkeley 강좌 : CS뿐만 아니라 공학 강좌가 많이 있으며, 오른쪽에서 semester 선택하면 예전 강좌들을 들을수 있습니다. 현재 진행되고 있는 강좌도 들을수 있어서 좋네요.

  2007 Spring

  • CS 61A     The Structure and Interpretation of Computer Programs
  • CS 61BL    Data Structures and Programming Methodology
  • CS 61C     Machine Structures

  2006 Fall

  • CS 162     Operating Systems and Systems Programming
  • CS 61A     The Structure and Interpretation of Computer Programs
  • CS 61B     Data Structures
  • CS 61C     Machine Structures

  2006 Spring

  • CS 162     Operating Systems and System Programming
  • CS 252     Graduate Computer Architecture
  • CS 61A     The Structure and Interpretation of Computer Programs
  • CS 61B     Data Structures
  • CS 61C     Machine Structures

ArsDigita University CS 강좌 : 영국에 있는 대학인가봅니다. 저도 직접은 안봐서 모르겠지만… 다음 강좌들이 있네요.

  • Maths for Computer Science
  • Structure and Interpretation of Computer Programs
  • Discrete Maths
  • How Computers Work
  • Object-oriented Program Design
  • Algorithms
  • Systems
  • Web Applications
  • Theory of Computation
  • Artificial Intelligence
  • Unix Workshop
  • Database Management Systems
  • Applied Probability

University of Washington : 비디오 강좌만 정리된 페이지가 있는지 모르겠지만, 좋은 강좌가 많네요.

University of Indiana CS 강좌: 안타깝게도 비디오는 인증 절차를 통해서 받을수 있도록 바뀌었네요. 예전에는 볼수 있었거든요…

SICP (Structure and Interpretation of Computer Programs) – 1986년 오래된 강의이지만 저자 두명이 직접 설명합니다. 설명이 필요없는 유명한 책이고 여러 대학에서 교재로 사용하고 있습니다. LISP(Scheme) 언어로 강의가 이루어집니다.

http://video.s-inf.de/ : 독일의 대학인데 이 중에서 FP가 Haskell 언어로 강의입니다. 현재 서버에 접속이 불가능한데 구글 캐시에서 페이지 보고 다운로드하면 비디오는 다운로드 되네요. 그리고 다른 강좌는 모르겠지만 FP는 영어로 강의를 합니다.

그리고 강좌를 잘 정리한 글들입니다. 🙂

그리고 성격이 조금 틀리지만 윈도우즈 프로그래밍에 관심이 있다면 MS에서 제공하는 강좌도 도움이 될거 같네요.