2012년 10월 26일 금요일

IP Fragmentation

http://ezbeat.tistory.com/321

Netlink

http://qos.ittc.ku.edu/netlink/html/netlink.html
Netlink Sockets - Overview
http://www.kernel.org/doc/man-pages/online/pages/man7/netlink.7.html
netlink man page
http://lhjgg.com.ne.kr/doc/Netlink_Sockets.doc
netlink 문서 - 한글
http://lethean.pe.kr/2012/03/19/detect-change-of-ip-address-and-link-status-in-linux/
netlink 사용 예제
http://sourceforge.net/projects/netlinksockets/files/release/1.0.0-pre-3/
netlink sockets C++ library
http://binwaheed.blogspot.kr/2010/08/after-reading-kernel-source-i-finally.html
how to use Netlink Sockets - 커널 모듈(예제)와 유저영역 프로그램(예제)과의 통신
http://www.linuxjournal.com/article/7356?page=0,0
Why and How to Use Netlink Socket - 설명이 아주 자세히 잘 되어있다

2012년 10월 10일 수요일

방화벽 성능 지표

네트워크 장비로서의 방화벽

방화벽은 외부와 내부를 연결하는 네트워크 선로중간에 네트워크 연결장비 형태로 설치되므로 네트워크 장비로서의 역할을 수행하며, 설정된 정책에 의해 허용된 트래픽만을 장애 없이 전달 해야 합니다. 따라서 방화벽 기능을 하는 네트워크 장비를 선정하는데 있어 여러 가지 성능 지표들을 정확히 이해하고 거기에 따른 항계 사항들에 대해 준비를 해야 합니다.

방화벽이 네트워크 장비로서의 성능 한계로 인하여 발생할 수 있는 장애의 유형은 다음과 같습니다.
  • 응답시간의 저하: 방화벽이 처리할 수 있는 성능 이상의 패킷이 쏟아져 들어온다면, 처리 못하는 패킷들은 계속 폐기될 것이고 이에 따른 TCP 재전달 현상은 더욱더 네트워크 성능을 악화할 것입니다. 따라서, 방화벽의 처리 성능은 예상되는 초당 패킷수보다 월등하게 높은 성능이어야 합니다.
  • 신규접속 불능의 동시 다발적 발생: 어떤 PC에서는 인터넷 사용이 가능하고, 또 다른 PC에서는 인터넷 신규 접속이 불가능한 경우가 발생하는 것으로, 이는 방화벽이 처리할 수 있는 총세션의 수가 가득 차 더 이상 새로운 세션을 맺을 수 없기 때문에 발생하는 것입니다. 이의 방지를 위해서는 역시 방화벽의 전체 처리 가능한 세션 수가 예상되는 전체 세션수보다 항상 월등히 높은 수치어야 할 것입니다.
  • 느린 초기 접속 또는 초기 접속 장애: 방화벽은 항상 접속의 첫번째 패킷을 토대로 정책을 적용합니다. 하지만 정책 판별을 받아야 할 초기 세션 패킷의 초당 처리수는 한계가 있습니다. 이 한계를 넘어서는 초기 세션 패킷들이 유입된다면 정책판별 처리를 할 수 없어 폐기하게 됩니다. 이 경우 초기 패킷의 재전송을 통해 접속이 느리게 이루어지거나 반복되는 재전송에도 불고하고 세션 처리 기회를 얻지 못하여 계속 폐기된다면 접속 장애로 이어 질 것입니다.


방화벽 네트워크 성능 지표의 의미와 종류

방화벽은 네트워크상에서 네트워크 장비의 일부로 활용되지만 일반적인 네트워크 장비와 다르게 정책 데이터베이스, 로깅, 리포트 프로그램, 모니터 프로그램 등 많은 기능들이 추가되 있습니다. 특히 네트워크 장비의 가장 중요한 기능인 패킷의 처리도 일반 네트워크 장비와 다르게 전송계층이나 응용계층에서 담당하며, 대부분이 소프트웨어적으로 구현되어 있습니다. 또한 일반 네트워크 장비는 인터페이스와 인터페이스 사이에 데이터 교환이 독립적으로 수행되는 반면, 방화벽에서는 모든 데이터가 중앙 프로세스를 거처 보안정책을 적용받고 처리됩니다.

따라서 방화벽은 일반 네트워크장비와 비교할 때 여러가지 면에서 성능상의 차이점을 갖고 있으며, 성능을 평가하는 지표 또한 다른 관점에서 관리되어야 합니다. 1Gbps를 처리 할 수 있다고 주아하는 어떤 방화벽을 고장 100Mbps 네트워크 접속 환경에 설치하더라도 성능한계에 따른 장애가 반복되는 경우를 흔히 볼 수 있습니다. 단순히 방화벽이 '100Mbps를 처리 할 수 있다', 또는 '1Gbps를 처리 할 수 있다'라는 문구들이 방화벽의 성능을 나타낸다고 볼 수는 없습니다. 어디까지나 가장 최적화된 조건하에서의 처리 능력일 뿐이기 때문입니다.

패킷 크기에 관계없이 초당 8만 3천개의 패킷을 처리하는 방화벽이 있다고 가정합니다. 이 방화벽은 크기가 1500Byte인 패킷들을 처리 할 때 1Gbps의 성능을 나타낼 것입니다. 하지만 TCP/IP 네트워크에서 64Byte에서 1518Byte까지 다양한 크기의 패킷들이 존재합니다. 64Byte크기의 패킷들만을 처리한다면 성능은 100Mbps에도 못 미치는 42Mbps에 불과하게 됩니다. 이와 같이 단순히 '초당 몇 비트를 처리하는가'를 성능 지표의 기준으로 삼는 것은 상당한 성능 판단상 오류가 있습니다.



1500byte 패킷처리 능력
83,000pps * 1500byte * 8 = 996,000,000bps = 1Gpbs 
% 1byte는 8bit이므로 8을 곱한다.

64byte 패킷처리 능력
83,000pps * 64byte * 8 = 42,496,000 = 42Mbps


실제 네트워크에서 방화벽의 능능 한계에 따른 장애발생 가능성이나 향후 네트워크 증가에 따른 성능상의 문제발생 여부 및 발생 시점 등을 판단하기 위해서는 몇 가지 중요한 방화벽 성능 지표를 설정하고, 이를 정확히 측정하는 것이 중요합니다. 주요한 방화벽 성능 지표들은 다음과 같이 정리할 수 있습니다.
  • 초당 패킷 처리 성능 및 패킷 사이즈별 throughput
  • 초당 세션 처리 성능
  • 전체 세션 처리 성능
  • 정책의 수 및 세션 수의 증가에 따른 성능 저하 여부
PPS 및 패킷사이즈별 throughput

"초당  처리 패킷 수(pps: Packet Per Seccond)"는 가장 중요한 방화벽 처리 성능 지표라고 할 수 있습니다. 방화벽은 패킷 단위로 정책을 판별, 통제규칙적용 등의 기능을 수행합니다. 따라서 하나의 패킷을 얼마나 빨리 처리할 수 있는가는 중요한 성능 지표가 됩니다.

성능 평가는 네트워크에 초당 출입되는 패킷 수들을 측정하여 평가하고자 하는 방화벽 패킷 처리 성능과 비교하여야 합니다.  하지만, 방화벽에 연결되는 회선의 성능 지표는 초당 처리 패킷 수(비트수로 환산가능)이고, 대부분의 네트워크 사용량 측정은 전체 대역폭 대비 사용된 대역폭으로 이루어지기 때문에 어떤 방화벽의 측정된 초당 처리 패킷 수를 통해 해당 방화벽이 해당 네트워크에 적합한지 여부가 즉시 판된되지는 않습니다.

또한 어떤 방화벽은 패킷 사이즈에 따라 처리 성능이 달라지는 방화벽이 있을 수 있습니다. 예를들어, 64byte 패킷 키기는 초당 36만개를 처리하면서, 128byte크기의 패킷은 초당 30만개를 처리하는 경우 입니다. 때문에 단순히 어떤 특정 사이즈의 패킷 처리 성능만으로는 정확한 방화멱 성능 평가가 불충분하다고 할 수 있습니다. 초당 처리 패킷 수의 측정과 함께, 패킷 크리별 초당처리 비트수인 throughput역시 함께 계산하여야 합니다.

다음 그리프는 특정 기가비트 방화벽의 패킷 사이즈별 처리 성능을 나타낸 것입니다. 참고로 이 방화벽의 패킷 처리 성능은 패킷 크기에 관계없이 초당 약 43만 패킷을 처리하고 있습니다. 이 기가비트 방화벽은 64byte크기의 패킷만 처리한다면 293mbps정도의 성능을 나타낸다고 할 수 있습니다.


패킷 사이즈별 throughput을 측정한 다음 이 방화벽이 네트워크에 설치되어 장애 없이 동작할 수 있는지를 판단하기 위해서는 설치 지점에 네트워크 스니핑 장비를 설치하여 현재 네트워크에 흐르고 있는 네트워크 패킷에 대한 사이즈별 초당 츨입 패킷 분포를 측정해야 합니다.

어떤 네트워크의 출입 패킷들의 사이즈별 분포가 다음과 같다고 가정해보겠습니다.


패킷 사이즈 분포
64Byte 35%
128Byte 10%
256Byte 10%
512Byte 15%
1024Byte 10%
1500Byte 20%

이 분포 퍼센트 값들을 위의 throughput측정치에 곱하여 값을 계산하면 이 방화벽이 이 네트워크에서 최대 처리 가능한 대역폭의 수치가 산출됩니다.

(293 X 35%) + (516 X 10%) + (850 X 10%) + (1200 X 15%) + (1600 X10%) +
(1800 X 20%) = 939Mbps

즉 933Mpbs정도의 대역폭을 이 방화벽은 현재 네트워크에 설치되었을 때 처리할 수 있습니다.



초당 세션 처리성능과 전체 세션 처리성능

방화벽 종류마다 차이는 있지만 최근 시장에 출시되는 대부분의 방화벽은 세션 중심으로 트래픽을 처리합니다. 출발지와 목적지의 IP 주소, 출발지와 목적지의 포트번화, 4개의 정보가 하나의 세션을 지칭합니다. 이 4가지 값 중에서 하나라도 틀리면 다른 세션으로 취급됩니다. 방화벽에서 언급되어지는 세션이라, TCP 트래픽인 경우와 UDP 트래픽인 경우를 구분하여 이해하여야 합니다.

TCP의 경우

연결을 맺고 데이터를 서로 주고 받다가 연결을 종료하는 단계까지를 하나의 TCP세션이라고 합니다. 예를 들어 telent이라는 응용프로그램의 경우 어떤 호스트에 telnet접속을 맺고 터미널을 통해 명령과 결과를 주고 받은 후 exit또는 logout을 입력하여 접속을 끊기 까지를 하나의 세션으로 취급합니다.

세션을 맺고부터 끊기까지의 모든 패킷들은 동일한 출발지와 목적지의 IP주소와 동일한 출발지 및 목적지의 포트를 가지게 됩니다. 물론 방향에 따라 목적지와 출발지 위치가 서로 바뀌긴 합니다.

UDP의 경우

UDP는 접속을 맺고 끊는 절차가 없기 때문에 세션을  TCP와 같이 규정하기는 힘들지만 병화벽내 세션을 관리하는 세션 정보 유지 cache 공간에 새로운 세션 정보(출발지와 목적지 IP 주소 및 출발지와 목적지 포트 번호 4개의 값)를 가진 UDP 패킷이 발생한다면 방화벽은 새로운 세션이 시작되었다고 판단하고 세션 정보 유지 cache에 등록하여 관리합니다. 통신이 지속된 이후 이 세션에 해당하는 패킷들이 일정 시간 이상 발생하지 않는다면 해당 세션은 종료되었다고 방화벽이 판단하여 세션 정보를 제가합니다.

예를 들어 어떤 호스트에서 도메인 이름 질의를 수행하였다면 목적지 주소를 domain name server로 하고 목적지 포트 번호가 53번인 UDP패킷이 발생할 것입니다. 이를 방화벽이 새로운 세션이 발생하였다고 판단하였다가 해당 질의가 성공적으로 수행되었다면 더 이상 이 세션에 관련된 패킷들은 발생하지 않을 것입니다. 방화벽은 이를 일정 시간 유지하고 있다가 시간이 다하면 세션이 종료되었다고 판단합니다.

UDP패킷의 이러한 특징 때문에 세션 정보를 관리하지 않고 출발지 주소, 목적지 주소, 출발지 포트, 목적지 포트만을 가지고 통과여부를 결정하는 packet filtering 방식의 방화벽에서는 정책을 설정할 때 출발지 쪽에서 들어오는 패킷과 목적지 쪽에서 응답되는 패킷 모두에 대해 정책을 설정해야 합니다. 하지만 일반적으로 출발지의 포트는 가변적으로 할당받는 포트이기 때문에 UDP패킷에  대한 정책적용이 packet filtering 방식의 방화벽에서는 거의 불가능합니다.

Classification과 성능

세션의 시작을 의미하는 첫번째 패킷(TCP의 경우 접속 절차를 수행하는 패킷, UDP의 경우 방화벽에 의해 어떤 세션에도 속하지 않은 처음 파악되는 패킷)이 유입되면 방화벽은 먼저 해당 세션의 시작을 허용하여도 될 것인가를 정책을 통해 판단합니다. 이를 방화벽에서는 clasification단계라고 합니다. 각 방화벽마다 독자적인 classification엔진을 가지고 있으나 그 성능은 제품에 따라 천차만별의 차이를 보입니다. 당연히 classification엔진이 비효율적이라면 신규로 접속을 요구하는 첫 패킷들에 대한 세션 처리가 원활하지 못하여 초기 접속에 많은 시간 지연이 발생하거나 초기 접속 자체가 불가능한 사태로까지 이어질 수 있습니다.

적용된 알고리즘에 따라 차이는 있지만 classification성능은 정책의 수에 영향을 받습니다.  정책의 수가 많으면 많을수록 classification을 수행하는데 걸리는 시간은 늘어나게 됩니다. 어떤 알고리즘은 정책의 수와 무관하게 classification성능을 나타내는 것도 있습니다. 방화벽 개발자들은 지속적으로 정책의 수와 무관하게 신속한 classification이 수행되도록 하는 알고리즘을 연구하고 적용하고 있습니다. 따라서 방화벽 평가는 평가 대상 방화벽에 필요로 하는 정책들을 충분히 입력한 후 해당 방화벽을 경유는 세션들을 무작위로 생셩시켜 초당 몇 개의 세션을 정상적으로 처리하는지를 측정하여야 합니다. 또한 설치하고자 하는 네트워크 상에 방화벽이 없는 상태에서 초당 몇개의 신규 세션들이 발생하는지도 파악하여야 합니다.

Classifcation 알고리즘에 따라 다음과 같은 방화벽 성능 및 기능들이 영향을 받습니다.
  • 초당 처리 세션 성능
  • 적용된 정책 수에 따른 초당 처리 세션 성능
  • 적용되는 정책이 그룹 객체를 허용하는지 여부
  • 마스크로 구분된 네크워크만 적용 가능한지 임의의 범위를 지정할 수 있는지여부
모든 패킷들에 대해 classification을 적용하지는 않습니다. 세션정보를 만드는 첫 패킷에 대해 정책상 허용한다고 방화벽이 classification을 통해 판단하였다면 해당 세션 정보는 방화벽에 따라 차이가 있겠지만 세션 정보 유지 cache 공간에 일시적으로 저장됩니다. 따라서 뒤이어 오는 동일 세션에 소속되는 패킷들은 cache공간을 먼저 검색하여 해당 세션 정보에 저장되어 있는 calssification 수행 결과를 토대로 방화벽이 처리하게 됩니다. 일일이 모든 패킷들에 대해 classification을 수행하는 것보다 효율적이고 빠릅니다. 그리고, 세션이 종료되거나 일정 시간 해당 세션에 소속되는 패킷이 발생하지 않으면 해당 정보를 삭제합니다.

이들 cache의 크기나 구조에 따라 제품의 전체 세션 처리 성능이 결정됩니다. 메모리는 유한한 물리적 공간이기 때문에 무한대의 세션 정보를 저장할 수는 없기 때문입니다. 따라서 제품에 따라 최대 처리 세션 수에 차이가 나게 됩니다. 최대 처리 세션의 수를 초과하여 세션이 발생한다면, 기존에 맺어져 있는 세션들을 정상 통신을 훌 수 있지만 새로이 맺어지는 세션들은 정책에 관계 없이 모두 폐기될 것입니다. 따라서 어떤 호수트는 인터넷이 연결되어 있으나 어떤 호스트들은 접속이 불가능한 상태가 동시 다발적으로 발생할 것입니다.


세션수 및 정책수 증가에 따른 성능저하

세션 정보를 유지하는 cache 구조와 검색 방법 특성에 의해 세션수가 증가하면 전체 cache크기가 증가하게 되고 검색 소도는 떨어지면서 전체적인 패킷 처리 성능이 떨어지게 됩니다. 다음 그래프는 두개의 서로 다른 제품의 가가비트 방화벽을 세션수를 증가시켜 가면서 성능 저하 정도가 어떻게 되는지 비교한것입니다.
방화벽 A는 세션수가 5,000에서 25,000으로 증가하여도 성능변화가 없지만 방화벽 B는 세션수의 증가에 따라 급격하게 성능이 떨어지고 있음을 알 수 있습니다. 이는 cache구조가 제품마다 틀리고 cache를 검색하는 방식 또한 많은 차이가 나기 때문입니다.

또한 정책수에 따라 성능이 떨어지는 경우도 있습니다. 이러한 제품들은 주로 1.세션 cache를 사용하지 않는 제품, 2. 부분적으로 cache를 사용하고 있는 제품 또는 3. cache를 적절하게 이용하고 있으나 거부되는 패킷들이 압도적으로 많은 경등에 해당합니다.

앞에서 언급하였듯이 classification 알고리즘에 따라, 정책의 수에 따라  classification  처리 시간이 결정되고 직접적으로 초당 세션 처리 성능에 영향을 미치게 됩니다. 모든 패킷들에 대해 classification을 수행하는 구조로 되어 있는 제품들은 cache를 사용하지 않기 때문에 일일이 classification을 수행하는데 정책의 수에 따라 classification 성능이 영향을 받아 전체 성능이 떨어지게 됩니다.

정상적으로 cache를 활용한다고 하더라도 거부되는 패킷들이 많으면, 이 도한 정책의 수에 따른 성능 저하 현상이 발생하게 됩니다. cache에는 거부되는 패킷들에 대한 정보가 없습니다. 따라서 모든 거부되는 패킷들은 calssification에 의해서만 "거부' 판단이 내려지게 됩니다. 많은 수의 거부 정책에 의ㅣ해 거부되는 패킷들로 인해 classification 처리가 지연되고 있는 동안, 새롭게 유입되는 정상 패킷들은 cache검색 기회마저 주어지지 않을 수 있기 때문입니다. 따라서 방화벽 관리자들은 가능하면 허용정책 위주로 정책을 설정하고 거부정책을 적게 적용하는 것이 성능향상에 도움을 줍니다.

출처:http://archiblue.tistory.com/78

2012년 9월 11일 화요일

make: phony target

Phony Targets


"make의 타겟과 동일한 이름을 가진 파일과 충돌을 피하고, 퍼포먼스 개선을 하기 위해서 사용한다. " 라고 하는데 말이 어렵다. 하나 하나 뜯어 보자

make에서 타겟이란?
make를 구성하는 기본적인 형태는 다음과 같다.

target: prerequisite1 prerequisite2 ...
        command


make는 target을 생성하기 위해 prerequisite들이 update되었는지를 확인한 후 command를 사용하여 target파일을 update한다. 실제 예를 들어 설명해보겠다.

test: test.c
      cc -o test test.c

위의 예제에서 target은 test가 되고 prerequisite는 test.c가 된다. 그리고 command는 cc -o test test.c가 된다.  make는 test.c가 변했는지를 확인한 후 test.c에 변경된 내용이 있다면 cc -o test test.c를 사용하여 test라는 파일을 새롭게 작성할 것이다.

타겟과 동일한 이름을 가진 파일과 충돌을 피하고?

clean:
      rm *.o

위의 예제에서는 target과 command는 있지만 prerequisite가 존제하지 않는다. 이럴 경우 만약 clean이라는 파일이 있다면 prerequisite가 존제하지 않기 때문에(prerequisite가 변경되었는지 판단을 할 수 없기 때문에) make는 update된 내용이 없다고 판단하고 command를 수행하지 않는다. 어려운 말로 묵시적 규칙(Using Implicit Rules)이라고 한다.

그래서 다음과 같이 clean이 가짜 목적물(target 파일이 생성되지 않는 목적물)이라는  규칙을 사용하여야 된다 .

.PHONY: clean
clean:
      rm *.o

퍼포먼스 개선은?

실제 파일의 존재 여부를 신경쓰지 않고 command를 수행하기 때문에 실제 파일이 존재 하는지 않하는지 검사하는 루틴이 make에서 제거된다. 따라서  성능적 효과를 볼 수 있다는 이야기...

2012년 9월 9일 일요일

yum: repositore 추가 하기


Webtatic Yum Repository

The Webtatic Yum repository is a CentOS/RHEL repository containing updated web-related packages. Its main goals are:
  • to provide CentOS/RHEL administrators with the latest stable minor releases of web development/hosting software, which are not provided in CentOS/RHEL distribution minor releases.
  • to serve as an additional installation option for some of Webtatic’s projects.
All packages are signed using GnuPG, and are verified using the Webtatic GPG key:
Installation

To set up the repository, install the webtatic-release RPM:

CentOS/RHEL 6:




rpm -Uvh http://repo.webtatic.com/yum/el6/latest.rpm

CentOS/RHEL 5:
rpm -Uvh http://repo.webtatic.com/yum/centos/5/latest.rpm

Additional information
The RPMs are built from the also provided SRPMs. The Mock build system is used to build both i386 and x86_64 RPMs which satisfy the base, updates and extra repositories of CentOS, which are set up in the base installation of CentOS.

CentOS/RHEL 6 usage
As of el6, Webtatic avoids using the same package names as the base repositories that CentOS/RHEL 6 uses, so that packages are not implicitly automatically upgraded without consent. This also means that the repository can be run enabled=1.

To install packages you just need to use the correct package name as described in the packages wiki:




yum install package-name

CentOS/RHEL 5 usage
It is recommended to use the “enabled=0″ parameter in the webtatic.repo file (which is provided in this state wherever it can be downloaded/installed on this site). This can be used to limit which packages you would like to update, as you may prefer to keep some packages at their CentOS-provided versions.

Packages can then be installed using the flag –enablerepo=webtatic to explicitly enable the repository for only that command, e.g.:









yum install --enablerepo=webtatic package-name








  
http://www.webtatic.com/projects/yum-repository/

2012년 8월 26일 일요일

사서삼경(四書三經)을 공부하는 이유

우리가 흔히 얘기하는 사서삼경이란 사서와 삼경을 하나로 통칭하는 말로, 옛날에 글공부를 하는 사람이라면 반드시 익혀야 할 유학(儒學)의 기본 경전으로 7개의 책명(七書)을 지칭한다. 오늘날에도 공부를 하는 데 있어서 순차적 과정이 있듯이 옛날에도 순차적 과정에 따라 소학을 마친 뒤에는 칠서, 곧 사서삼경을 차례대로 공부해 나갔다. 그 이유와 궁극적인 목표는 무엇인지 알아보자.

사서(四書)에 해당하는 대학(大學) ․ 중용(中庸) ․ 맹자(孟子) ․ 논어(論語)는 공자의 문인 또는 후대의 현인이 지은 글이다. 이에 반해 삼경(三經)은 시경(詩經) ․ 서경(書經) ․ 역경(易經, 흔히 周易이라 함)으로 공자가 직접 손을 대었다. 성경현전(聖經賢傳)이란 말로 나눠 볼 수 있는데 사서는 ‘현인의 전(傳)’에 해당하고, 삼경은 공자가 직접 편찬하였으므로 ‘성인의 경(經)’에 해당한다. 다만 주역의 경우 예외적으로 성인 공자가 지은 십익(十翼)을 대전(大傳)이라 하여 전(傳)으로 본다. 이것은 역(易)의 괘를 긋고 글을 붙인 복희씨와 문왕 ․ 주공이 공자보다 앞선 시대의 성인들이므로 이들을 먼저 받들고 높이는 것이 예(禮)에 합당하기 때문이다.

칠서 가운데 맨처음 배우는 글은 대학으로 중용과 서로 표리(표리)를 이룬다. 대학이 겉이라면 중용은 속이고, 중용이 겉이라면 대학은 속이 되는 것이다. 이 사서삼경을 놓고서 단적으로 말한다면, 대학은 ‘착할 선(善)’, 중용은 ‘정성 성(誠)’, 맹자는 ‘옳을 의(義)’, 논어는 ‘어질 인(仁)’으로 말할 수 있고, 시경은 사무사(思無邪 : 생각에 간사함이 없음)로서 ‘바를 정(正)’, 서경은 백성을 다스리는 데 가장 중요한 ‘공경 경(敬)’, 주역은 음양불측(陰陽不測 : 음과 양으로 헤아리지 못함)의 ‘귀신 신(神)’으로 요약할 수 있다.

대학에는 삼강령(三綱領)과 팔조목(八條目), 즉 세 가지의 강령과 여덟 가지의 조목이 있는데, 강령이 뿌리하면 조목은 그 가지가 된다. 삼강령은 명명덕(明明德)과 신민(新民 : 경문에는 親民, 경문을 해설한 전문에는 新民으로 풀이), 지어지선(止於至善) 세 가지로서 밝은 덕을 밝히는 것(明明德), 백성을 새롭게 해주는, 즉 모두 새로운 백성을 만들어주는 것(新民), 그리고 지극히 선한 데 그치는 것(止於至善)을 가리킨다. 이 세 가지 강령은 ‘벼리 강(綱)’, ‘옷깃 령(領)’, 즉 그물로 말하면 벼리에 해당하고 옷으로 말하면 옷깃에 해당한다.

팔조목은 격물(格物) ․ 치지(致知) ․ 성의(誠意) ․ 정심(正心) ․ 수신(修身) ․ 제가(齊家) ․ 치국(治國) ․ 평천하(平天下)이다. 격물(格物)은 모든 사물에 부딪쳐보는 것으로 ‘이를 격(格)’자 그대로 이른다는 뜻이다. 내가 이 세상에 살면서 온갖 사물과 마주하고 살아야 하는데, 모든 사물을 ‘사물은 사물이고 나는 나’ 하고 본체만체한다면 사물의 이치를 알지 못하여 답답한 사람이 된다. 그래서 그러한 이치를 알려고 접촉을 하는 것이다.

치지(致知)는 격물한 다음 그 속에 있는 이치를 알아내는 것으로 ‘이룰 치(致)’와 ‘알 지(知)’ 곧 앎을 이루는 것입니다. 이렇게 앎을 이루었으면 성의(誠意), 곧 뜻을 성실히 가지게 된다. 뜻을 성실히 가짐에 따라서 정심(正心), 곧 마음을 바로하게 된다. 수신(修身)은 마음을 바로해서 몸을 닦는 것이다. 자기 몸을 닦은 다음에는 제가(齊家), 곧 집을 가지런히 하는 것이다. 수신도 못한 사람이 제가할 수 있고, 제가도 못하는 사람이 치국할 수 있느냐는 말들을 한다. 제가 이후에는 치국(治國), 곧 나라를 다스리고, 나라를 다스린 뒤에는 평천하(平天下), 즉 천하를 평치하는 데에 이르게 된다.

이러한 여덟 가지 격물 ․ 치지 ․ 성의 ․ 정심 ․ 수신 ․ 제가 ․ 치국 ․ 평천하를 하나하나 체계적으로 설명한 책이 대학이다. 대학에서 이러한 삼강령이나 팔조목에 대한 공부를 하다보면 밖으로 정신이 산만해지므로 대학 다음에는 안으로 정신을 집중하기 위해 중용을 공부한다.



그 머릿장에는 “천명지위성(天命之謂性)이요 솔성지위도(率性之謂道)요 수도지위교(修道之謂敎)니라”고 되어 있는데, 하늘이 우리에게 명해준 것이 성품이므로 곧 천명지위성이고, 하느님에게서 타고난 성품을 그대로 따라가는 것이 길(道)이므로 곧 솔성지위도이고, 그 성품을 따르는 도를 잘 닦아 나가 마름질해놓는 것이 가르침이므로 곧 수도지위교이다.



도를 잘 닦아 나가는 것, 즉 마름질하는 것이 하나의 교육적인 가르침(敎)이 되는 것이다. 성(性) ․ 도(道) ․ 교(敎), 여기에서 시작하는 중용을 공부하다보면 안으로 정신집중이 잘된다. 그래서 중용의 핵심을 ‘정성 성(誠)’ 한 글자로 표현하는데, 이렇게 해서 대학의 선(善)과 중용의 성(誠)은 겉과 속이 된다(內誠外善).

그런데 중용만을 공부하면 속으로 육조배포(六曹配布)만 했지 밖으로 발표를 못할 우려가 있으므로 표현력을 기르기 위해서 다음에 맹자를 공부한다. “맹자(孟子)ㅣ 견양혜왕(見梁惠王)하신대, 왕왈(王曰) 수(叟)ㅣ 불원리이래(不遠千里而來)하시니 역장유이리오국호(亦將有以利吾國乎)잇가?” (맹자가 양혜왕을 찾아가시니까 양혜왕 하는 말이, 노인네가 천릿길을 멀다 않고 이렇게 날 찾아오셨으니, 앞으로 우리 나라를 이롭게 해주시렵니까?) 이렇게 ‘이로울 리(利)로 말했다. 이에 대해 “맹자(孟子)ㅣ 대왈(對曰) 왕(王)은 하필왈리(何必曰利)잇고? 역유인의이이의(亦有仁義而已矣)니이다” (맹자가 답하시기를 왕은 하필 이를 말하시오? 또한 인의가 있을 뿐입니다) 하였다. 인의(仁義)를 설명하는 것으로 시작하여 맹자를 공부하면 표현력이 좋아지고 발표를 잘하게 된다. 참으로 호변(好辯)이다. 그래서 예로부터 맹자 7편을 읽은 사람하고는 말하지 말라는 이야기가 있다.

그러나 거기에서 그치고 말면 말로만 한몫 보기 쉽고 말만 앞세울 수가 있으므로 이를 행동에 옮겨 실천하라고 논어를 가르친다. 논어는 위기지학(爲己之學)으로서, 남을 위해서나 남한테 잘 보이려고, 또는 배운 것이 많음을 남한테 보여주려고 공부하는 것이 아니고 남이야 뭐라 하든 나 스스로 사람이 되기 위한 공부를 하는 것이다. 논어 첫 대목에 보면 “자왈(子曰) 학이시습지(學而時習之)면 불역열호(不亦說乎)아! 유붕(有朋)이 자원방래(自遠方來)면 불역낙호(不亦樂乎)아! 인부지이불온(人不知而不慍)이면 불역군자호(不亦君子乎)아!” (공자 말씀에 배우고 때때로 다시 익히면 또한 기쁘지 않으랴! 나를 알아주는 벗이 먼 곳에서 오게 되면 또한 즐겁지 않으랴! 남들이 나를 알아주지 않아도 성내지 아니하면 또한 군자가 아니랴!)고 하였다.

또한 논어를 보면 안자(顔子)가 공자에게 인(仁)을 여쭈니까 극기복례(克己復禮), 즉 자기 몸의 편벽된 기질과 사사로운 욕심 때문에 인을 행할 수 없으니 이를 다 이겨내고서 하늘로부터 본래 타고난 예를 회복해야 한다고 말씀하시며 “일일극기복례(一日克己復禮)면 천하귀인언(天下歸仁焉)이라” (하루라도 극기복례를 잘하면 온 천하가 인으로 돌아간다)고 하였다. 공자는 이런 사람이 있다면 인을 아주 잘 하는 사람으로 인정해주겠다고까지 말씀하셨다.

논어를 공부하면 참 점잖아지고 행동을 잘합니다만 좀 고리타분한 데가 있게 된다. 본래 사람은 이성(理性)만 있는 것이 아니라 감성(感性)을 아울러 갖추고 있으므로 흥도 제때에 풀어야 하는데 그걸 못하는 것이다. 그리고 “남녀칠세부동석(男女七歲不同席)이라”(남녀가 일곱 살이 되면 한자리에 앉으면 안 된다) 해서 어려서부터 이성교제를 엄격히 규제하고, “신체발부(身體髮膚)는 수지부모(受之父母)라”(몸과 터럭은 모두 부모에게 받은 것이다)고 해서 어떻게 손톱을 깍아내고 머리털을 베어낼 수 있느냐고 한다. 그래서 이렇게 너무 고지식한 데에서 탈피하고 흥을 좀 풀라고 시경을 가르친다.

시경의 첫 구절에는 쌍쌍이 끼룩끼룩 울며 물가에 노니는 물수리들을 보고서 하는 말이, “관관저구(關關雎鳩)ㅣ 재하지주(在河之洲)로다. 요조숙녀(窈窕淑女)ㅣ 군자호구(君子好逑)로다”(한번 만나 제 짝을 정하면 다시 다른 짝을 구하지 않고 늘 쌍쌍이 노니는 저 물수리처럼 요조숙녀는 군자의 좋은 배필이니 서로 만나야 한다)고 하였다. 이 시구는 주나라 문왕(文王)과 그 후비(后妃)인 사씨(太姒)를 두고 한 말인데, 시경의 이 글귀에 따라 덕성과 행실이 훌륭한 여자를 요조숙녀라 하고 그러한 남자를 구자라고 일컫는 것이다. 또 “참치행채(參差荇菜)를 좌우류지(左右流之)로다”, 즉 마름나물을 이리저리 구하듯이 요조숙녀를 자나깨나 찾고 구해서 결국은 만나 “요조숙녀(窈窕淑女)를 금슬우지(琴瑟友之)로다”, 즉 거문고 뜯고 비파 뜯어가며 서로 즐긴다고 하였다.

이렇게 시경을 배우며 흥을 푸는 데만 빠지면 나라가 어떻게 되는지 또 정치가 부패하는지 나는 모른다 하고서 ‘흥야(興也)라, 부야(賦也)’라‘하며 낙관적이고 낭만적으로만 놀게 된다. 그래서 백성과 나라를 생각하고 정치를 할 줄 알라고 서경을 가르친다.

“왈약계고제요(曰若稽古帝堯)한대 왈방훈(曰放勳)이시니 흠명문사(欽明文思)ㅣ 안안(安安)하시며 윤공극야(允恭克讓)하사 광피사표(光被四表)하시며 격우상하(格于上下)하시니라”, 저 요임금의 세상을 다스린 덕을 생각해보니 햇빛과 같은 광명함이 온 누리에 가득찼다는 내용이다.

이 서경에는 이제삼왕(二帝三王)의 치천하지대경대법(治天下之大經大法), 즉 천하를 잘 다스린 요순과 삼대(하 ․ 은 ․ 주)의 시조인 우임금 ․ 탕임금 ․ 무왕이 세상을 다스린 큰 벼리와 큰 법이 있으므로 이를 공부하면 정치도 할 줄 알게 되지만 앞일은 몰라 막상 눈 뜨고 앞 못 보는 봉사나 다름없다. 그래서 앞을 멀리 내다보아 정치를 잘 하고 사람이 생을 영위하는 데도 미래를 예측해가면서 슬기롭게 살아보라고 주역을 가르친다.

‘건(乾)’ 하면 하늘이 열리고 ‘곤(坤)’하면 땅이 열리며, 또 ‘원(元)’코 하면 화창한 봄에 만물이 파릇파릇 싹터 나오고, ‘형(亨)’코 하면 여름 더위에 무럭무럭 자라고, ‘이(利)’코 하면 서늘한 가을 기운에 결실을 맺고, ‘정(貞)’하니라 하면 모두 땅 속에 수장(收藏)된다. 그 원형이정 춘하추동 생장수장으로부터 배우는 주역, 이 주역을 배우면 드디어 완전한 인격이 이루어진다는 것이다. 모든 학문이 여기에서 다 끝나기에 주역이 칠서의 우두머리이며 끝을 이루는 책이 된다.

사서삼경이 아주 오래된 옛 글이기는 하지만 오늘날에도 여전히 유효한 내용이 담긴 보배로운 글이다. 켸켸묵은 먼지를 털어내고 차근차근 배워 익혀 나간다면 현대문명이 가진 많은 문제점들을 해결할 수 있는 실마리를 찾는데 큰 도움을 주리라고 본다. 누가 알랴, 알라딘의 마술램프가 될런지...



[산사에 향기 혜룡스님]

2012년 6월 21일 목요일

개발자를 위한 Oracle Berkeley DB 가이드

SQL 개발자를 위한 Oracle Berkeley DB 가이드


Oracle Berkeley DB 팀원들은 "Berkeley DB에서 (이런 저런) SQL 쿼리를 실행하려면 어떻게 해야 하나요?"라는 질문을 매우 자주 접하곤 합니다. 본 문서를 통해 Oracle Berkeley DB에서 자주 사용되는 SQL 기능을 소개 드리고자 합니다. Oracle Berkeley DB는 신속하고, 안정적인 로컬 퍼시스턴스 환경을 제공하는 오픈 소스 임베디드 데이터베이스 엔진으로 별도의 관리를 전혀 필요로 하지 않습니다. Oracle Berkeley DB는 상대적으로 적은 수의 쿼리를 고정적으로 실행하는 환경에서 성능을 극대화하고자 하는 경우 최선의 선택이 될 수 있습니다.


저자 : Margo Seltzer


그럼 가장 기본적인 내용부터 시작해 봅시다. 먼저 Berkeley DB에서 사용되는 용어를 알아 둘 필요가 있습니다.

SQL 프로그래머들을 위한 "번역 가이드"가 아래와 같습니다.



이제 고전적이면서도 단순화된 형태의 직원 데이터베이스를 애플리케이션 도메인으로 선정해 봅시다. 또 Berkeley DB의 가장 기본적인 기능, 즉 동시성, 트랜잭션, 복구성 등에 관련된 기능을 모두 활용하는 것으로 가정하겠습니다.

데이터베이스의 생성

SQL에서는 아래와 같은 방법으로 데이터베이스를 생성합니다.

CREATE DATABASE personnel

Berkeley DB에서는 애플리케이션 데이터가 위치하는 환경(environment)을 생성하게 됩니다. 앞으로 코드 전반에 걸쳐 DB_ENV 타입의 환경 핸들(environment handle)을 통해 환경을 참조하게 됩니다. 또 환경에 대한 작업을 수행할 때에도 핸들을 활용하게 됩니다. 예제에서는 에러 핸들링 코드를 생략하고 API에만 집중하여 코드를 구현하기로 합니다.



이것으로 새로운 환경이 생성, 오픈되었습니다. 여기서 몇 가지 참고할 사항이 다음과 같습니다:

• 이 작업을 수행하기 전에 my_databases/personnel 디렉토리가 미리 생성되어 있어야 합니다.
• 오픈 콜에 대한 최종 매개변수는 환경의 일부로서 생성하는 환경의 일부로서 생성되는 파일의 모드입니다.
• 이곳에 명시된 플래그를 이용하여 환경을 생성
• (DB_CREATE)하고, 락을 적용(DB_INIT_LOCK)하고, 공유 메모리 버퍼 풀을 설정(DB_INIT_MPOOL)하고, 트랜잭션을 사용(DB_INIT_TXN)하고 서로 다른 컨트롤을 갖는 쓰레드를 환경 내에서 동시 사용(DB_THREAD)할 수 있습니다.

SQL 구문은 일반적으로 별도의 서버에 의해 처리되며, 이 서버는 DBA에 의해 별도의 튜닝 과정을 거칩니다. Berkeley DB은 애플리케이션에 임베드된 형태로 구현되므로 애플리케이션에서 이러한 튜닝 작업의 대부분을 수행할 수 있을 것입니다. 하지만 이러한 내용들은 데이터베이스 튜닝에 관련된 것이므로 별도의 문서를 통해 설명하는 것이 적절할 것입니다.

데이터베이스를 생성한 다음으로는 몇 가지 테이블을 생성해 주어야 합니다. Berkeley DB에서 테이블은 DB * 타입의 핸들에 의해 참조됩니다. 일반적으로 애플리케이션의 각 테이블에 대해 핸들을 하나씩 오픈하고, 하나 또는 그 이상의 쓰레드에서 핸들을 이용하는 방법이 주로 사용됩니다.

SQL에서 테이블을 생성하는 방법이 아래와 같습니다:

CREATE TABLE employee
(primary key empid int(8), last_name varchar(20), first_name varchar(15),
salary numeric(10, 2) salary, street varchar (20), city varchar (15),
state char(2), zip int(5))

위 SQL 구문을 Berkeley DB에서 구현하는 방법을 설명하기 전에 한 가지 꼭 알아 두어야 할 사항이 있습니다. SQL에서는 데이터베이스가 데이터 스키마의 구현 및 해석을 담당합니다. Berkeley DB에서는 이러한 해석 작업이 애플리케이션에 의해 수행됩니다. DML 구문을 통해 보다 분명하게 확인하게 되겠지만, Berkeley DB는 employee 테이블의 프라이머리 키에 대한 정보만을 가지고 있으며 데이터베이스의 각 필드에 대한 정보는 가지고 있지 않습니다.

먼저, 생성되는 테이블을 참조하기 위한 데이터베이스 핸들을 생성합니다. (여기에서도 에러 핸들링 코드는 생략되어 있습니다.)



위 코드를 실행하면 B-트리를 1차 인덱스 구조로 사용하는 테이블이 생성됩니다. 이 테이블은 employee.db라는 이름으로 my_databases/personnel 디렉토리에 저장됩니다. 이 파일은 하나의 테이블만을 포함하며 최종 매개변수(0644)에 의해 명시된 파일 시스템 권한을 갖습니다. 테이블 생성 과정에서 설정된 플래그는 트랜잭션 기반 작업을 허용 (DB_AUTO_COMMIT)하고, 테이블이 존재하지 않는 경우 테이블을 생성하도록 허용(DB_CREATE)하고 있으며, 생성된 핸들이 여러 쓰레드에 의해 동시에 사용될 수 있도록 허용(DB_THREAD)하고 있습니다.

위의 코드에서 프라이머리 키(인덱스)를 구성하는 요소가 무엇인지, 또 테이블에 저장되는 데이터 필드가 무엇인지 전혀 정의되지 않고 있음을 참고하시기 바랍니다. 이러한 모든 작업은 애플리케이션에서 담당합니다. 자세한 사항은 INSERT, SELECT, UPDATE 작업을 설명하면서 좀 더 분명하게 확인하실 수 있을 것입니다.

이제, employee id를 기준으로 1차 인덱스를, 그리고 last name을 기준으로 2차 인덱스를 생성하는 작업을 수행해 봅시다.

이를 위한 SQL 구문이 아래와 같습니다:

CREATE INDEX lname ON employee (last_name)

Berkeley DB에서는 2차 인덱스가 테이블과 동일한 형태를 가집니다. 따라서 2차 인덱스를 생성한 뒤 테이블과 연계하는 작업이 필요합니다. 이 작업을 위해, 애플리케이션이 사용하는 데이터 구조에 대해 좀 더 이해할 필요가 있습니다.

애플리케이션에서 C 언어의 데이터 구조를 사용하여 employee 테이블의 튜플(tuple)을 저장한다고 가정해 봅시다. 데이터 구조는 아래와 같이 정의될 수 있습니다:



이제 employee ID가 정수 타입을 갖는다고 가정해 보겠습니다:

typedef int emp_key;

Berkeley DB에서 키 또는 데이터 아이템을 처리하기 위해서는 DBT라는 이름의 구조를 사용해야 합니다. BBT는 바이트 문자열을 인캡슐레이션하고 포인터(pointer)와 길이(length)로 표현합니다. 포인터는 DBT의 데이터 필드에 의해 참조되며, 길이는 DBT의 size 필드에 저장됩니다. 직원 레코드를 가리키는 키/데이터 페어(key/data pair)를 처리하기 위해서는 emp_key를 위한 DBT와 emp_data를 위한 DBT를 함께 활용해야 합니다.



위에서 SQL의 튜플은 키/데이터 페어로 표현되고 있지만, 이 키/데이터 페어를 어떻게 해석할 것인가는 애플리케이션에 의해 달라집니다.

이제 2차 인덱스의 문제로 다시 돌아가 보겠습니다. Berkeley DB는 키/데이터 페이어의 데이터 엘리먼트의 구조 또는 스키마를 이해하지 못하므로, 2차 인덱스로 사용할 필드가 무엇인지 확인하기 위해서는 애플리케이션의 도움이 필요합니다. 이를 위해 애플리케이션은 콜백 함수(callback function)를 사용합니다. 콜백 함수는 키/데이터 페어를 취하고 2차 키로 사용되는 값을 참조하는 DBT를 반환합니다.

따라서 last_name을 기준으로 2차 인덱스를 생성하려면, 키/데이터 페어를 입력으로 받아들인 후 해당 데이터 아이템의 last_name 필드를 참조하는 DBT를 반환하는 콜백 함수를 작성해야 합니다.



콜백 함수를 작성했다면, 이제 2차 인덱스를 설정할 수 있습니다. 앞에서 설명한 것처럼 2차 인덱스는 테이블의 형태를 가집니다. 아래와 같이 테이블을 하나 생성합니다:



last_name의 인덱싱을 위해 B-트리 구조가 사용되므로, 앞에서 사용한 것과 동일한 플래그와 모드를 그대로 적용합니다.

마지막으로 2차 인덱스 테이블과 메인 테이블(employee 테이블)을 연계합니다. 앞에서 설명한 것처럼 employee 테이블을 처리하기 위한 핸들로는 dbp가, 2차 인덱스 테이블을 처리하기 위한 핸들로는 sdbp가 사용됩니다.

ASSERT(dbp->associate(dbp, NULL, sdbp, lname_callback, flags) == 0);

몇 가지 참고할 사항이 다음과 같습니다:

• 생성 가능한 2차 인덱스의 수에는 제한이 없습니다. 여기서 2차 인덱스가 INSERT 작업의 성능을 저하시키는(각 2차 인덱스별로 인덱스 엔트리가 생성되어야 하므로) 반면, 튜플에 대한 SELECT 쿼리의 성능은 극적으로 개선될 수 있다는 측면을 감안해야 합니다.
• 2차 인덱스가 오픈, 연계된 상태에서 메인 테이블을 업데이트하면, 2차 인덱스는 동시에 업데이트 처리됩니다. 하지만 2차 인덱스를 오픈, 연계하지 않은 상태에서 베이스 테이블을 수정하면, 2차 인덱스의 최신 상태를 유지할 수 없게 됩니다.
따라서 이러한 문제가 발생하지 않도록 주의해야 합니다.

함께 고려해야 할 DDL 커맨드로 drop index, drop table, drop database가 있습니다.

SQL에서와 마찬가지로, Berkeley DB에서도 인덱스를 drop 처리하고 테이블을 delete 처리할 수 있습니다. SQL에서는 아래와 같은 명령이 사용됩니다.

DROP TABLE employee
or
DROP INDEX lname

SQL에서 테이블을 drop 처리하면 연계된 인덱스들도 함께 삭제됩니다. 하지만 Berkeley DB에서는 인덱스의 삭제를 명시적으로 수행해 주어야 합니다. Berkeley DB에서 테이블 또는 인덱스를 drop 처리하는 방법은 동일합니다.

테이블을 제거하기 전에 테이블에 대한 모든 데이터베이스 핸들을 close 처리해야 합니다. 테이블을 close 처리하는 방법은 간단합니다. employee 데이터베이스에서 2차 인덱스를 drop 처리하는 경우를 가정해 봅시다. 먼저 2차 인덱스를 close 처리합니다:

sdbp->close(sdbp, 0)

데이터베이스 핸들에 대해 close 메소드를 실행하면, 이 핸들은 다시 사용될 수 없습니다.

2차 인덱스 테이블을 close 처리한 뒤에는 dbenv 핸들에 대해 dbremove 메소드를 사용하여 삭제할 수 있습니다:



동일한 호출 과정(close 및 dbremoving)을 이용하여 테이블을 drop 처리하는 것도 가능합니다.

이번에는 테이블을 drop하는 것이 아니라 테이블 이름만을 변경해야 하는 경우를 가정해 봅시다. 이 작업도 비슷한 방법으로 진행됩니다.

remove 작업과 마찬가지로, 먼저 테이블 핸들을 close 처리해야 합니다:

dbp->close(dbp, 0);

이제 테이블의 이름을 변경합니다:



마지막으로 데이터베이스를 완전하게 삭제해야 하는 경우입니다. SQL에서는 아래와 같은 명령이 사용됩니다.

DROP DATABASE personnel

Berkeley DB에서도 같은 작업을 수행할 수 있습니다.

먼저 환경을 close 처리합니다:

ASSERT(dbenv->close(dbenv, 0) == 0);

테이블 핸들을 close하는 경우와 마찬가지로, 환경 핸들(environment handle)을 close 처리하면 이 핸들을 더 이상 사용할 수 없게 됩니다. 따라서 데이터베이스를 drop 처리하려면, 새로운 핸들을 생성하고 이 핸들을 이용하여 데이터베이스(환경)를 제거해야 합니다.

ASSERT(db_env_create(&dbenv, 0) == 0);
ASSERT(dbenv->remove(dbenv, "my_databases/personnel", 0) == 0);

지금까지 SQL의 DDL 구문을 Berkley DB에서 사용되는 명령으로 변환하는 방법에 대해 설명했습니다. 다음으로는 SQL DML 구문을 Berkeley DB에 적용하는 방법에 대해 설명해 보겠습니다.

Berkeley DB에서 SQL DML 작업 수행하기

지금부터는 Berkeley DB에서 INSERT, UPDATE, DELETE 작업을 실행하는 방법에 대해 논의하겠습니다.

SQL에서 INSERT 구문을 이용하여 테이블에 데이터를 추가하는 방법이 아래와 같습니다:

INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00,
"Main Street", "Disney Land", "CA", 98765);

Berkeley DB에서는 INSERT 작업을 위해 put 메소드를 사용합니다.

앞에서 예로 든 employee 테이블을 참조하는 dbp 데이터베이스 핸들이 열려 있다고 가정해 봅시다. 이제, Mickey Mouse가 새로운 직원으로 채용되었습니다.



employee 테이블과 2차 인덱스가 연계된 상태라면, INSERT 작업을 수행하면서 2차 인덱스도 자동으로 업데이트될 것입니다.

이제 테이블에 저장된 데이터의 일부를 변경하는 경우를 생각해 봅시다. Mickey의 연봉을 인상해 주려 합니다. 몇 가지 방법이 가능합니다.

첫 번째 방법은 위에서 설명한 INSERT 코드와 동일한 형태로 구현됩니다. 테이블에 put 메소드를 실행했을 때 키가 이미 존재한다면(또 테이블이 하나의 키에 대해 중복 데이터 값을 허용하지 않는다면), put 메소드는 기존 버전의 데이터를 새로운 데이터로 대체합니다. 아래의 코드를 실행하면 Mickey의 salary가 $1,000,000에서 $2,000,000으로 변경됩니다.



하지만 이 방법은 실행하기 번거롭다는 문제가 있습니다. 데이터베이스의 다른 필드의 값을 미리 알고 있어야 하기 때문입니다. 아래 구문의 경우,

UPDATE employees SET salary = 2000000 WHERE empid = 000100002

employee ID만을 미리 알고 있으면 충분하지만, put 메소드를 이용한 방법에서는 모든 정보를 다 알고 있어야 합니다. Berkeley DB에서도 이런 방법이 가능하지 않을까요? 물론 가능합니다. 업데이트할 데이터 아이템의 바이트 정보를 정확하게 파악하고 있다면, UPDATE 커맨드와 유사한 방법으로 명령을 실행할 수 있습니다.

이를 위해 사용되는 것이 바로 커서(cursor)입니다. 커서는 테이블 상에서의 위치를 표현하기 위해 사용되며, 테이블 상의 현재 위치를 기준으로 반복적인 데이터 처리를 가능하게 합니다.

Berkeley DB에서 커서를 생성하는 것은 쉽습니다. 커서는 데이베이스 핸들을 이용한 메소드로서 지원됩니다:



이제 생성된 커서를 Mickey의 레코드에 위치시키고 업데이트 작업을 실행해 봅시다. 이를 위해 SQL 구문의 WHERE 절과 유사한 문법이 사용됩니다.



다음으로 salary를 변경할 차례입니다.

/* Change the salary. */
edata = data_dbt->data;
edata.salary = 2000000;

마지막으로 UPDATE 명령을 실행합니다:

dbc->c_put(dbc, &key_dbt, &data_dbt, DB_CURRENT);

지금까지 Mickey 레코드에 저장된 salary 값을 미리 알고 있지 않은 상태에서, 데이터를 가져와 업데이트하는 방법을 설명했습니다.

또는, 레코드를 아예 가져오지 않고도 업데이트를 수행할 수도 있습니다. DBT의 DB_DBT_PARTIAL 플래그는 레코드의 일부만을 가져오거나 업데이트할 때 사용되며, 이 플래그를 사용하면 해당 부분을 제외한 다른 모든 컨텐트를 무시할 수 있습니다.

다시 아래와 같이 실행합니다:



전체 레코드를 가져오는 대신 "PARTIAL get"을 통해 데이터 아이템의 0 바이트를 가져오도록 설정함으로써 데이터의 포지셔닝만을 수행합니다.



데이터 인출

지금까지는 테이블에 데이터를 입력하는 방법을 설명했습니다. 이제는 데이터를 가져오는 방법을 설명할 차례입니다. 먼저 가장 간단한 방법부터 알아봅시다. 프라이머리 키를 기준으로 값을 조회하는 방법입니다.

SELECT * FROM employees WHERE id=0010002

커서를 이용하여 데이터를 조회하는 방법은 앞에서도 설명한 바 있습니다.



앞에서는 레코드 업데이트를 위해 커서를 사용한 바 있습니다. 단순히 레코드를 조회하는 목적이라면 커서를 사용할 필요가 없습니다. dbp 핸들에 대해 get 메소드를 사용하기만 하면 됩니다.



이 코드는 위에서 사용한 SELECT 구문과 동일합니다.

지금까지 프라이머리 키를 기준으로 레코드를 조회하는 방법을 설명했습니다. 하지만 키를 미리 알고 있지 못하다면 어떻게 해야 할까요? 몇 가지 방법이 아래와 같습니다:

• 2차 키 값을 기준으로 레코드를 조회
• 키를 공유하는 일련의 아이템에 대해 반복(iteration) 작업을 실행
• 데이터베이스에 대해 반복 실행

각각에 대해 좀 더 자세히 설명하겠습니다.

2차 키의 사용
SQL에서와 마찬가지로, 2차 키(secondary key)를 기준으로 데이터를 조회하는 방법은 프라이머리 키를 이용하는 방법과 매우 유사합니다.

SQL 쿼리에서 달라지는 부분은 WHERE 절 뿐입니다.

SELECT * FROM employees WHERE last_name = "Mouse"

Berkeley DB 호출은 방법은 프라이머리 키를 사용한 경우와 유사합니다.

프라이머리 키를 사용한 예제에서 dbp를 사용한 것과 달리, 2차 키를 이용하는 경우에는 sdbp를 사용합니다:



여기서 중요한 것은 data_dbt에서 반환되는 레코드가 운영 데이터베이스의 데이터라는 사실입니다. 다시 말해, 프라이머리 키를 사용하거나 2차 키를 사용하거나 DBT에 의해 반환되는 데이터는 똑같습니다.

하지만 2차 키를 이용해서 데이터를 조회할 때에는 프라이머리 키 또는 SQL 구문을 이용할 때와 그 결과가 똑같이 표시되지 않음을 확인할 수 있습니다. 프라이머리 키가 제공되지 않았기 때문에 프라이머리 키를 반환할 수가 없는 것이 당연합니다. 위의 코드는 실제로는 다음과 같은 SQL 구문으로 구현됩니다.

SELECT last_name, first_name, salary, street, city, state, zip FROM
employees WHERE last_name="Mouse"

프라이머리 키가 필요한 경우는 어떻게 해야 할까요? 그럴 때는 dbp->pget 또는 dbc->pget 메소드를 이용하면 됩니다. 이 두 가지는 get 메소드와 동일한 역할을 수행하지만 2차 인덱스 쿼리에서 프라이머리 키를 반환할 수 있도록 설계되었다는 차이점을 갖습니다. 이 메소드를 실행하면 그 결과로 프라이머리 키, 2차 키, 그리고 데이터 엘리먼트가 함께 반환됩니다.



여러 레코드에 대한 반복 작업
지금까지는 하나의 레코드에 대해 작업하는 경우만을 설명했습니다. SQL을 이용하면 여러 개의 레코드를 동시에 반환할 수 있습니다. (예를 들어 'Mouse'라는 last_name을 갖는 모든 직원들을 조회할 수 있습니다.) Berkeley DB에서는 어떻게 할 수 있을까요?

두 가지 경우를 생각해 봅시다. 첫 번째 경우에서는 키를 기준으로 여러 개의 아이템을 조회합니다. 그리고 두 번째 경우에서는 "non-keyed" 필드를 기준으로 데이터베이스 내에서 아이템을 검색합니다.

'Mouse'라는 last_name을 갖는 모든 직원들을 조회해야 한다고 가정해 봅시다. Mouse라는 성을 가진 직원들은 여러 명이 있을 수 있고, 따라서 last_name에 대한 2차 인덱스는 중복 데이터를 허용해야 합니다. 따라서 데이터베이스를 열기 전에 중복 데이터를 허용하도록 설정해 주어야 합니다.

sdbp->set_flags(sdbp, DB_DUP);

ASSERT(sdbp->open(sdbp, NULL, "emp_lname.db", NULL, DB_BTREE,
DB_AUTO_COMMIT | DB_CREATE | DB_THREAD, 0644) == 0);

2차 인덱스를 기준으로 데이터를 조회하면서 커서를 사용할 필요가 있어 보입니다. 앞에서 사용한 것과 같은 코드를 사용하되, 루프를 추가하여 동일한 2차 키를 공유하는 모든 아이템에 대해 반복 작업이 수행될 수 있도록 합니다.



커서는 명시된 키를 기준으로 첫 번째 아이템을 조회하는 시점에 초기화됩니다. 그런 다음, 같은 키를 가진 모든 아이템에 대해 같은 작업을 반복합니다.

키를 기준으로 반복 작업을 수행하는 또 방법으로 아래와 같은 쿼리 형식을 생각해 볼 수 있습니다.

SELECT * FROM employees WHERE id >= 1000000 AND id < 2000000

여기에서도 반복 작업을 위해 커서가 사용되어야 합니다. 하지만 이번에는 시작 지점과 종료 지점을 설정해 줄 필요가 있습니다. 시작 지점은 Berkeley DB에서 쉽게 설정할 수 있습니다. 종료 지점은 애플리케이션에 의해 설정됩니다.



여기서 두 가지 사실을 참고할 필요가 있습니다. 1) 루프는 DB_SET_RANGE 플래그와 함께 시작됩니다. 이 플래그는 설정된 키보다 크거나 같은 첫 번째 아이템에 커서를 위치시킵니다. 2) 애플리케이션에서 루프의 종료 지점을 확인해 주어야 합니다.

또 key_dbt에 DB_DBT_USERMEM 플래그를 설정함으로써, 조회되는 키가 사용자가 설정한 메모리에 저장될 수 있도록 하고 있습니다. 따라서 ekey 변수를 이용한 키의 확인이 가능합니다.

이제 SELECT 작업에 대한 설명을 마무리하면서, 키를 기준으로 하지 않은 필드를 조건으로 하나 또는 그 이상의 아이템을 조회하는 쿼리를 작성해 봅시다. 아래와 같은 쿼리가 있습니다.

SELECT * FROM employees WHERE state=ca

state 필드에는 키가 존재하지 않으므로, 전체 데이터베이스에 대해 조회 작업을 반복하는 것 말고는 다른 방법이 없습니다. 따라서 아래와 같은 커서 반복 실행 루프가 필요합니다.



이 작업은 효율적이지 않은 것으로 보이지만, 필드에 인덱스가 존재하기 때문에 다른 대안이 없습니다. 또, SQL 데이터베이스에서 인덱스가 적용되지 않은 필드에 대해 쿼리를 실행할 때에도 이와 동일한 작업이 수행됩니다.

데이터의 삭제

지금까지 데이터를 입력, 변경, 조회하는 방법에 대해 설명했습니다. 마지막으로 데이터를 삭제하는 방법을 알아 봅시다. 데이터베이스의 튜플을 삭제하는 방법은 기본적으로 두 가지가 있습니다. 삭제하고자 하는 아이템의 키 값을 알고 있다면 "keyed delete"를 수행할 수 있습니다. 키 값을 알지 못한다면 반복 실행을 이용한 "cursor delete" 작업이 필요합니다. 아주 간단한 예로 시작해 봅시다. Mickey Mouse를 해고하려 합니다.

DELETE FROM employees WHERE id= 0010002

SELECT 작업과 매우 유사한 코드를 이용하되, 이번에는 del 메소드를 사용합니다.

DBT key_dbt;
emp_key ekey;

ekey = 0010002;
memset(&key_dbt, 0, sizeof(key_dbt));
key_dbt.data = &ekey;
key_dbt.size = sizeof(ekey);

dbp->del(dbp, NULL, &key_dbt, 0);

이번에는 Mouse라는 last_name을 갖는 모든 직원들을 해고하는 경우를 생각해 봅시다. last_name에 2차 키가 설정되어 있으므로 기본적인 테크닉은 동일합니다.

DELETE FROM employees WHERE last_name = "Mouse"

DBT key_dbt;

memset(&key_dbt, 0, sizeof(key_dbt));
key_dbt.data = "Mouse";
key_dbt.size = strlen(key_dbt.data);

dbp->del(dbp, NULL, &key_dbt, 0);

어쩌면 우리가 너무 심했는지도 모릅니다. 모든 Mickey들을 해고하는 대신 Minnie Mouse만 해고하는 것으로 충분할 것 같습니다. Minnie를 쉽게 해고할 수 있는 방법이 있을까요? 다시 말해, 아래의 SQL 작업을 어떻게 구현해야 할까요?

DELETE FROM employees where last_name = "Mouse" AND first_name = "Minnie"

당연한 얘기지만, 데이터 아이템에 대해 커서를 반복 실행하면서 삭제할 아이템을 찾아야 합니다.



트랜잭션의 관리

트랜잭션이 SQL 환경에서 동작하는 원리에 대해 잠시 생각해 봅시다. 사용자가 SQL에서 DML 구문을 실행했을 때, 구문은 현재 트랜잭션의 일부로서 수행됩니다. 이후 실행되는 모든 구문들 역시 같은 트랜잭션에 포함됩니다. 트랜잭션은 SQL 세션이 종료되거나 애플리케이션이 COMMIT 구문을 실행했을 때 커밋 처리됩니다. ROLLBACK 구문을 실행하면 어느 시점에서든 트랜잭션을 취소할 수 있습니다.

SQL 환경에 AUTOCOMMIT 기능이 활성화되어 있는 경우에는 각각의 DML 구문이 별도의 트랜잭션으로 간주됩니다. AUTOCOMMIT 모드가 활성화된 상태에서 아래의 구문은,

statement 1
COMMIT
statement 2
COMMIT
statement 3
COMMIT

아래 구문과 동일합니다.

statement 1
statement 2
statement 3

Berkeley DB는 트랜잭션에 데이터베이스 작업을 인캡슐레이트 하는 기능을 지원합니다. SQL과 달리 트랜잭션을 사용하지 않고 Berkeley DB를 실행할 수도 있습니다. 실제로, Berkeley DB에서는 명시적으로 트랜잭션을 요청하지 않는 이상 트랜잭션이 사용되지 않습니다. 그렇다면 Berkeley DB에서 트랜잭션을 요청하려면 어떻게 해야 할까요?

환경을 오픈하면서 플래그를 설정하면 됩니다.

DB_ASSERT(dbenv->open(dbenv, "my_databases/personnel",
DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD,
0644);

이 플래그들은 애플리케이션을 위해 Berkeley DB를 설정해 줍니다. 위에서는 DB_INIT_TXN 플래그가 명시되어 있으므로 트랜잭션이 활성화됩니다. 이 플래그가 설정되지 않는다면 애플리케이션은 트랜잭션을 사용하지 않은 상태에서 실행될 것입니다.

Berkeley DB는 SQL의 AUTOCOMMIT과 유사한 기능을 제고합니다. 환경 핸들(environment handle)에서 set_flags 메소드를 사용하면 전체 데이터베이스(환경)에서 항상 autocommit를 사용하도록 설정할 수 있습니다.

dbenv->set_flags(dbenv, DB_AUTOCOMMIT, 1);

또는 데이터베이스 오픈 시점에 DB_AUTOCOMMIT을 설정하여, 이후 실행되는 모든 작업에서 트랜잭션을 실행하도록 할 수도 있습니다.

이번에는 AUTOCOMMIT 옵션을 사용하지 않고 애플리케이션에서 임의의 작업들을 논리적인 트랜잭션으로 묶는 방법에 대해 알아 보겠습니다. Mickey Mouse를 employees 테이블에 추가하고 Mickey의 관리자를 지정해 봅시다.

INSERT INTO employees VALUES (00010002, "mouse", "mickey", 1000000.00,
"Main Street", "Disney Land", "CA", 98765);

INSERT INTO manages(00000001, 000100002)
COMMIT

(위의 구문은 id=00000001 조건을 만족하는 직원이 Mickey의 관리자임을 의미합니다.)

여러분들이 이미 데이터 작업을 실행하는 방법을 잘 알고 있다고 가정하고, 트랜잭션을 지정하는 방법에만 초점을 맞추어 설명하겠습니다.

먼저, 트랜잭션을 명시적으로 시작합니다. 트랜잭션을 생성하는 작업은 환경(environment) 레벨에서 수행되며, 따라서 환경 핸들의 메소드를 통해 처리되어야 합니다. 이 메소드는 트랜잭션 핸들(DB_TXN)을 생성합니다.

DB_TXN *txn;

dbenv->txn_begin(dbenv, NULL, &txn, 0);

이제 생성된 트랜잭션 핸들을 트랜잭션에 포함시킬 임의의 데이터베이스 작업에 전달할 수 있습니다.

emp_dbp->put(emp_dbp, txn, &key, &data, 0);
man_dbp->put(man_dbp, txn, &key, &data, 0);

그런 다음, 트랜잭션 핸들의 메소드를 호출하는 방법으로 트랜잭션을 커밋하거나 취소할 수 있습니다.

트랜잭션을 커밋하는 방법이 아래와 같습니다.

txn->commit(txn, 0);

트랜잭션을 취소하는 방법이 아래와 같습니다.

txn->abort(txn);

두 가지 메소드 모두 "destructor"로, 트랜잭션 핸들을 더 이상 사용할 수 없게 만듭니다.

SQL과 달리 Berkeley DB에서는 트랜잭션을 이용해서 DDL 작업을 보호하는 것도 가능합니다. 따라서 DB_TXN 핸들을 dbenv->dbremove, dbenv->dbrename, dbp->open(… DB_CREATE …)과 같은 작업에도 전달할 수 있습니다. DDL 작업은 설정된 트랜잭션의 컨텍스트 내에서 실행되며, 다른 트랜잭션과 마찬가지로 커밋 또는 취소가 가능합니다.

결론

Oracle Berkeley DB는 SQL 데이터베이스와 동일한 유형의 기능을 제공하고 있지만, 사용되는 패키지는 전혀 다릅니다. 개발자는 프로그램 코드를 이용하여 API를 호출해야 하며, 전체 데이터베이스는 애플리케이션 내부에 직접 "임베드" 됩니다. (다시 말해, 애플리케이션과 데이터베이스가 동일한 주소 공간에서 실행됩니다.) 그 결과로 수십 배에 이르는 성능 개선 효과를 거둘 수 있습니다. 하지만 이러한 성능 효과의 대가로 애플리케이션이 감당해야 할 부담이 커지는 문제가 있습니다. Oracle Berkeley DB는 애플리케이션이 매우 예외적인 수준의 고성능을 요구하는 경우, 또는 애플리케이션이 기본적으로 관계형 속성을 갖지 않는 데이터를 처리해야 하는 경우 유용하게 활용될 수 있습니다.

2012년 4월 17일 화요일

리눅스 커널의 이해: 인터럽트와 예외

인터럽트(Interrupt): 프로세서가 실행하는 명령어의 순서를 바꾸는 사건
  • Synchronous interrupt:
  • Asynchronous interrupt:

2012년 4월 8일 일요일

solaris: 오브젝트 덤프를 이용하여 커널 패닉 분석하기

가정

1.  커널 패닉이 발생하여 덤프 파일이 생성되어 있다
2.  커널 패닉이 발생한 위치가 자신이 빌드한 소스에서 발생하였다

위 두가지 가정하에서 커널 패닉이 유발 시킨 자신의 '코드의 위치'를 찾는 방법에 대하여 소개한다.

커널 덤프 확인

디폴트로 설치된 솔라리스 시스템에서 커널 패닉이 발생하면 덤프 파일이 생성되게 된다.
보통 /var/adm/messege 로그를 보면 최근 발생한 패닉의 정보를 확인 할 수 있는데, messege 파일이
생성되지 않는 경우에서 덤프 파일을 이용하여 패닉 정보를 확인하여야 한다.

덤프 파일이 생성되는 위치는 /var/crash/노드이름/ 하위에 생성되는데, 덤프 파일 이름은 unix.#, vmcore.# 이다.
덤프 파일을 확인 하기 위해서는 adb를 사용하면된다.


shell> adb -k unix.0 vmcore.0

physemem 
5b01

0x40027 .....

$<msgbuf     <== 타이핑 후 
엔터

......

0x300014f5b23:  000002a1007a3b50 unix:die+a4 (31, 2a1007a3e20, 
7374616c6c2f636f, 0, ffffffffffffffff, 3720e)

0x300014f5623:    %l0-3: 
0000000000000000 7374616c6c2f636f 000002a1007a3e20 000002a1007a3d18

%l4-7: 
0000000000000031 0000000000002000 0000000000000000 
0000000000000000

0x300014f5123:  000002a1007a3c30 unix:trap+874 (2a1007a3e20, 
0, 10000, 10200, 7374616c, 2a1007a4024)

0x300014f4c23:    %l0-3: 
0000000000000001 0000000000000000 00000300066a4a88 0000000000000031

%l4-7: 
0000000000000005 0000000000000001 0000000000000000 
0000000000000000

0x300014f4723:  000002a1007a3d70 unix:ktl0+48 (0, 2, 
ffffffffffffffff, 0, 81010100, ff00)

0x3000154dde3:    %l0-3: 
0000000000000007 0000000000001400 0000000080001606 000000000102ce88

%l4-7: 
000003000000b5b0 000000000144e280 0000000000000000 
000002a1007a3e20

0x3000168ce63:  000002a1007a3ec0 test_ke:putMemUse+120 (6d, 
2, 3000905bb10, 780ea000, 10, 7374616c6c2f636f)   <=== 여기에서 뭔가 잘못된거 같은 
느낌...

0x3000168d363:    %l0-3: 0000000000078000 000000000000000f 
00000300062f9380 000003000000b408

%l4-7: 0000030009323e48 0000000000000000 
0000030009323e57 0000000078217d98

0x3000154c123:  000002a1007a3f70 
test_ke:retryMalloc+140 (300062f9380, 2, 13aa2d0, 1000, e, 
10)

0x3000168dd63:    %l0-3: 0000000000000000 0000000000000000 
000000000000000f 0000030009323e58

%l4-7: 00000000013ad758 0000000000000020 
00000000013ad000 00000000000013ad

0x3000168d5e3:  000002a1007a4040 
test_ke:getPathForSymlink+818 (2a1007a52d0, 1, 2a1007a56e0, 2, 2a1007a5ae0, 
1)

0x30000438ae3:    %l0-3: 0000000000000026 0000000000001395 
000003000905bae8 00000000013aa300

%l4-7: 0000000000000026 0000000000000000 
0000000000000025 000002a1007a49c1

0x30000438ea3:  000002a1007a4dc0 
test_ke:hooked_link_sys32+2d0 (13a05c0, ffbfe78c, 13a0, 13a0000, 0, 
300092b61c8)

0x30000439263:    %l0-3: 00000000013a01c0 00000000780ea388 
0000000000000000 0000000000000000

%l4-7: fffffffffffffffc fffffffffffffffc 
000002a1007a4e90 000002a1007a4ed4

0x30000439620:

0x300004399e3:  syncing 
file systems...

0x30000439da3:   2

0x3000043e1e3:   1

0x3000043e5a3:   
done

0x3000043e963:  dumping to /dev/dsk/c1t0d0s1, offset 1718288384, 
content: kernel

위와 같이 패닉 위치를 확인 할 수 있다.



오브젝트 덤프 생성 하기

커널 패닉 위치 감은 잡았지만 적확히 소스의 위치를 확인 하기위해서는 오브젝트 덤프를 생성하여야 한다.
위 패닉 정보에서
'0x3000168ce63: 000002a1007a3ec0 mdp_ke:putMemUse+120 (6d, 2, 3000905bb10, 780ea000, 10, 7374616c6c2f636f)'
패닉의 원인으로 가장 유력해 보이지만 소스의 어느 위치인지 모른다.
솔라리스에서 오브젝트 덤프를 생성하기 위해서는 -fast -xcg92 -S 옵션을 주고 오브젝트 파일을 생성하지 않으면 .s파일이
생성 된다.


shell> cc  -D_SOL9_ -D_KERNEL -DSVR4 -DSOL2 -DDEBUG 
-D_SYSCALL32 -D_SYSCALL32_IMPL -DSOLARIS_SPARC -m64  -xO5 -xregs=no%appl -v 
-fast -xcg92 -S  -c module_memchk.c



shell> vi 
module_memchk.s



2513 !  338            !int putMemUse(const char 
*pr_fname, long pr_size, char pr_type)

2514 
!  339            !{

2515

2516 !

2517 ! SUBROUTINE putMemUse

2518 
!

2519 ! OFFSET    SOURCE LINE LABEL   
INSTRUCTION

2520

2521                         .global 
putMemUse

2522

2523

2524             putMemUse:

2525 /* 
000000    339 */     save    %sp,-96,%sp

2526

2527 
!  340            !  MEMCHKLIST  *find_member = NULL;

2528 
!  341            !  int         rtv = 0;

2529 
!  342            !  char        *func  = NULL;

2530 
!  343            !  int         len    = strlen(pr_fname);

2531

2532 /* 
0x0004    343 */     or  %g0,%i0,%o0

2533 /* 0x0008    339 */     sll 
%i2,24,%l6

2534 /* 0x000c    343 */     call    strlen  ! params =  %o0 ! 
Result =  %o0

2535 /* 0x0010    339 */     sra %l6,24,%l7

2536

2537 
!  345            !  if( pr_fname == NULL )

2538

2539 /* 0x0120    345 
*/     cmp %i0,0                                      <== 여기에서 커널 패닉 
발생

2540 /* 0x0018        */     bne .L77001322

2541 /* 
0x001c        */     nop


위와 같이 오브젝트 덤프를 확인 할 수 있다. 덤프 내용을 살펴보면 어셈블과 0x00으로된 주소, 그리고 자신의 코드를 확인
할 수 있다. 앞에서 확인한 커널패닉 로그를 다시 보면
'0x3000168ce63: 000002a1007a3ec0 mdp_ke:putMemUse+120 (6d, 2, 3000905bb10, 780ea000, 10, 7374616c6c2f636f)'
putMemUser함수에서 +120 번째에서 났다는것을 의미 한다.
그러므로 위 코어 더프내용에서 putMemUse 함수를 찾고 그함수 내부에서 0x0120을 찾으면 된다.

AIX: how to create the user core file

멀티스레드 프로그램의 코어 파일 요구사항

기본적으로 프로세스는 전체 코어 파일을 생성하지 않습니다. 응용프로그램이 공유 메모리 영역 특히, 스레드 스택의 데이터를 디버그해야 할 경우 전체 코어 덤프를 생성해야 합니다. 전체 코어 파일 정보를 생성하려면 다음 명령을 루트 사용자로 실행하십시오.


# chdev -l sys0 -a fullcore=true

각 pthread는 생성된 코어 파일의 크기에 추가합니다. pthread가 필요로 하는 코어 파일 공간의 양에는 사용자가 pthread_attr_setstacksize 서브루틴으로 제어할 수 있는 스택 크기가 포함됩니다. 널(NULL) pthread 속성으로 작성된 pthread의 경우, 32비트 프로세스의 각 pthread는 128KB를 코어 파일의 크기에 추가하고 64비트 프로세스의 각 pthread는 256KB를 코어 파일의 크기에 추가합니다.

2012년 4월 5일 목요일

잡담: 지식의 여신은 쉽게 옷을 벗지 않는다

지식의 여신은 쉽게 옷을 벗지 않는다 [펌]

나자신도 크게 다를바 없었겠지만 사회생활을 시작하는 입장에 서있는 사람들은 일자리를 고르면서 보수나 복지, 근무시간을 먼저 따지게 되는 경우가 많을 것이다. 어느 인사 담당자가 사회에 첫발을 내딛는 패기만만해야 할 젊은이들이 맨 처음 챙기는 것이 고작 퇴직규정이라고 개탄했다는 얘기도 있다. 하지만 당장 눈앞의 현실에 대한 걱정이 앞서는 보통 사람들의 입장을 비난할 수만은 없을 것이다. 하옇든 젊은이들의 부푼 기대와는 달리 이 땅에서 나처럼 평범한 엔지니어가 되면 일단은 그저그런 봉급에 대부분의 시간을 회사에 반납해야할 상황에 처하게 될 확률이 많을 것이다. 금전적인 문제야 요즘 시대상황이 급변하는 만큼, 변수가 많아 논외로 치더라도 엔지니어로서 새로운 것을 생각해내야 하거나, 도무지 종잡을 수 없는 혼돈 속에서 질서를 잡아내며 코 앞에 닥친 문제를 해결해 나가기 위해서는 일에 많은 시간의 투자가 필요하기 마련이다. 모든 직업이 마찬가지 겠지만, 이런저런 애환을 들먹거리지 않더라도 일을 통해 나름대로의 가치를 찾지 못한다면 그 생활은 그저 고달픈 것일 수 밖에 없을 것이다.

그렇다면 엔지니어 생활의 가치란 무엇일까? 사람마다 다를 수 있겠지만 나의 경우를 들자면 세간의 입에 오르내릴 대단한 일은 못될 지라도, 일단 주어진 문제를 해결하고 새로운 지식을 얻는 과정에서 발생하는 나름대로의 창조적 과정을 즐기는 것이다. 새로 맡은 개발 업무를 부담스러워하는 후배들에게 본인이 잘 인용하는 지식에 대한 몇가지 법칙이 있다. 그 첫번째는 바로 '지식의 여신(또는 남신)은 쉽게 옷을 벗지 않는다'라는 것이다. 지식의 여신은 새침떼기에 콧대가 높아서, 절대 함부로 그 신비한 내면을 드러내지 않는 법이다.

새로운 프로젝트가 떨어지면 각자의 위치와 내공에 따라 업무를 할당받게 될 것이다. 익숙한 업무를 반복하는 것이라면 이미 통성명에 술자리까지 해서 친숙해진 몇몇 여신들과 반갑게 하이 파이브나 하면서 일사천리로 일을 진행시켜 나갈 수 있겠지만, 대신 재미가 좀 덜하고 자칫 지루해지기 쉽상이다. 좀더 재미있게 세상을 살기 위해서는 흔히 말하는 대로 보다 도전적이고 진취적인 선택을 할 필요가 있다. 그 동기는 단순히 재미를 위한 것일 수도 있고 아니면 내공 증진을 통한 몸 값 키우기 같은 것처럼 좀더 세속적인 것일 수도 있다. 어쨌든 나름대로 대단한 결심을 한 직 후, 우리는 자신의 앞에 낯설은 문이 하나 가로놓여 있음을 깨닳게 된다. 또한 그 문을 여는 순간 신비스러운 자태의 지식의 여신이 범접하기 어려운 모습으로 우리의 길을 가로막고 있음을 보게 된다. 여신의 허락이 없이는 더이상 진행할 수가 없다. 적절한 내공을 갖추지 못한 채 아부나 인맥으로 정치공작을 해댄다고 해도, 돌아오는 것은 야속하리 만치 냉정한 새침떼기 여신의 괄시와 배척뿐이다.

길은 오로지 정성을 다해 공을 들이는 것 뿐이다. 그동안 한 두 번 재수로 일이 쉽게 풀렸다고 할 지라도, 아니면 타고난 천재라서 여지껏 문제없이 일을 해결해 왔을지라도 언젠가는 각자 레벨에 맞는 호적수를 만나게 된다고 보면 틀림없을 것이다. 여신의 비밀을 풀기 위해서 우리들은 오늘도 디버거에 올린 코드를 수십, 수백번 재 실행시키거나 이미 누더기가 된 칩 사양서를 읽고 또 읽으며, 다운된 보드를 하루에도 수백번 재부팅시켜야만 한다. 포기하고픈 마음이 생기는 경우도 있을 것이다. 하지만 잘나가는 엔지니어가 되기위해서, 또는 상급자가 무서워서, 아니면 회사의 중요한 전략적 과제에 대한 책임때문에 포기란 있을 수 없는 법이다. 하지만 단순히 난관을 극복하고 어쩌구 저쩌구하는 통속적인 설명을 떠나서, 쉽게 포기해서는 안될 한가지 더 큰 이유가 있다. 잘 알려지지는 않았지만 지식의 여신은 처음 생각했던것 보다 더 큰 보상을 돌려주는 경향이 있다. 이는 지식에 관한 두번째 법칙으로, 지식의 여신은 쉽게 지식을 허락하지는 않지만 일단 한번 자신을 극복한 사람에 대해서는 보너스까지 덧붙여서 마구 퍼준다는 것이다. 한번 여신이 따뜻한 미소를 지으며 친밀감을 표시하는 순간 뜻밖의 선물을 받게 될 지도 모른다. 복잡하게 얽히고 섥혀 자신을 괴롭히던 많은 문제들이 한순간 제자리를 잡으며 머릿속에 정돈될 때 느끼는 환희는 어떤 것과도 바꿀수 없지만, 더불어 이제 지식의 여신은 자기가 가지고 있던 모든 것을 차례 차례 보여주기 시작할 것이다. 우리는 다시 아름다운 여신의 안내에 따라 미쳐 생각지도 못했던 것까지 친절한 설명을 듣게 되는 것이다. 하긴 요즘 쇼핑은 제품보다 경품이라는데 아까워서라도 쉽게 포기할 수는 없는 것이다.

게다가 한번 지식의 여신의 선물을 받아보게 되면 그것은 하나의 성공 체험이되어 자신에게 깊은 영향을 주게된다. 그것은 나이 어린 사람에게 일생을 좌우할 만한 중요한 경험이 되기도 한다. 후배를 가르쳐야 하는 입장이라면 어떤 식으로건 이러한 성공 체험을 할 수있도록 유도하는 것이 중요하다고 생각한다. 쓸 만한 후배라면 그 다음부터는 자기가 알아서 할 것이다.

이제 마지막으로 한가지 법칙만 남았다. 우리는 얼마간 그 여신의 방에서 즐거운 시간을 보내게 된다. 쾌적한 상태가 지속될수록 이제 떠나야 할 시간이 얼마남지 않았다는 것을 직감적으로 느끼게 된다. 프로젝트는 계속되어야 하는 것이다. 여태까지의 문제는 어느 정도 정리가 되었고 여신과의 작별인사도 이미 나누었으니 아쉬움을 뒤로하고 다시 걸음을 재촉할 수 밖에 없다. 잠시 여신과의 추억을 되새겨 보기도 한다. 하지만 무심코 코너를 돌아선 순간 우린 '악' 하는 비명과 함께 그 자리에 멈춰서고 만다. 지식에 관한 세번째 법칙, '지식의 여신은 코너를 돌면 또 나온다'. 새로운 얼굴의 여신이 또 길을 막아선 것이다. 아마도 뉴톤은 이 법칙을 너무나 잘 알고 있었기 때문에 자신은 진리의 바다에 이제 막 발목을 담구었을 뿐이라고 겸손을 보였던 모양이다.


[끝]

2001/06/15 나성언 (http://user.chollian.net/~lase) :현재는 폐쇄중입니다.
출처: http://ehclub.tistory.com/entry/%EC%A7%80%EC%8B%9D%EC%9D%98-%EC%97%AC%EC%8B%A0%EC%9D%80-%EC%89%BD%EA%B2%8C-%EC%98%B7%EC%9D%84-%EB%B2%97%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4

잡담: 망하는(or 흥하는) 제품의 흔한 개발 과정

망하는 제품의 흔한 개발 과정

  • 리더 : 요즘 유행하는 대세를 들고 온다. 이것이 대세다!
  • 리더 : 속으로는 이런 것들을 쓰는 사람들은 사회부적응자라 생각하고 본인은 정작 써 본 적이 없다.
  • 기획 : 써 본적은 없지만 들어는 봤다. 이런 것을 쓰는 사람은 격이 떨어지는 사람이라 생각하고, 내가 우아하고도 유럽 명품에 견줄 수 있는 것을 보여주어야 겠다 생각한다.
  • 기획 : 해당 제품군을 모조리 조사한다. 그래서 해당 제품군의 모든 특징을 합한 고질라 같은 것을 그려 낸다.
  • 리더 : 그것만으로는 뛰어 넘을 수 없다고 한다.
  • 기획 : 아이디어를 동원한다. 이제 그 고질라에 스타워즈, 반지의 제왕, 해리포터를 더하기 시작한다. 자신의 상상력의 끝은 어디인가 하면서 스스로 놀라워 한다.
  • 리더 : 고질라에서 빠진 게 없나 살핀다. 다소 억지 스럽지만, 비슷한류의 제품을 가져와 하나 더 붙인다. 이런게 바로 리더의 통찰력이라 으쓱거린다. 기획자의 아이디어를 보고는 기획자가 미쳐 생각하지 못한 경우의 수를 생각해서, 더 복잡하게 만든다. 아직 가르칠게 많다고 생각한다.
  • 개발 : 그런건 못만들어요 불평을 늘어놓는다.
  • 리더 : 내앞에서 안된다는 말은 하지 말라고 하며, 할 수 없다는 것부터 이야기하는 태도가 문제라고 한다. 그리고는, 자신의 인생 역정기를 늘어 놓는다.
  • 개발 : 기획에 대한 조언을 해 줘야 겠다고 생각한다. (사실 해당 제품군을 사용해 본 유일한 사람이다.)
  • 리더 : 넌 아직 인지과학, 심리학을 모른다고 일축한다.
  • 기획 : 파워포인트로 찍어 내는 노가다를 시작한다.
  • 리더 : 문서에서 오타를 찾아 낸다.
  • 개발 : 이 프로젝트는 어짜피 산으로 갈 것이라고 떠들어 대기 시작한다.
  • 리더 : 최근 세미나에서 본 솔루션들을 쓰면 금방 할 것이라고 말한다. 그리고 비싼 돈을 들여 도입을 추진한다.
  • 개발 : 그게 뭔지 모른다. 다만, 대충 들어보니, 그것 보다는 자기간 만들어 놓은 자작 솔루션이 훨씬 더 좋은거라고 속으로 생각한다.(사실 지금 이 상황에 그걸 배워서 만드는 것은 엄두가 나지 않는다) 그리고, 쓰는 척 시늉만 하기로 결심한다. 타인이 만든 것을 사용하는 것은 하수들이나 하는 짓이라 생각한다.
  • 리더 : 개발기간은 3개월이라 한다.
  • 개발 : 불가능한 일정이라 하고, 기획안을 조정하라고 주장한다.
  • 리더 : 나는 어찌 저런 무능하고 게으른 개발자만 옆에 있는지 탄식한다. 나에게 해외 유수기업의 개발자를 붙여주면 단박에 성공 할 수 있으리라 생각한다.
  • 개발 : 투덜거리며 밤 샌다. 불행하게도 고질라를 만들어 내는 과정과 SF 가 붙여 지는 과정은 개발 과정 진행중에 병행해서 발행하는 일이다. 스타워즈를 다 붙여놓으면, 어느덧 스토리는 해리포터로 바뀌어 있다. 다시 밤을 샌다.
  • 리더 : 3개월 후면 다 되어 있겠지 생각을 한다. 개발 과정에는 관심이 없다. 개발이 진행되는 중간 중간, 어제밤 자다가 생각난 환타스틱한 장면을 기획자에게 넣으라고 말한다. 이 장면을 놓쳤으면 이번 제품에 핵심이 빠졌을 거라고 생각하고, 이제라도 넣게 되어 다행이다 생각한다. 그리고, 자신이 얼마나 디테일에 강한가 다시 한번 생각해 본다.
  • 개발 : 코드는 개떡이 되어 간다. 어짜피 이건 내탓이 아니다. 정말 제대로 된 환경에서 했다면, 난 정말 멋지게 해 낼 수 있었을 텐데, 운없이 이런 놈들이랑 팀을 해서 이렇게 된거라 생각한다. 이 제품은 내 손에서 나왔지만, 내가 만든건 아니라 생각한다.
  • 리더 : 3개월후, 생각했던게 안나오자 개발자에게 책임 추궁을 해야겠다 생각한다. 처음부터 태도도 안좋았고, 자기가 말한 것을 구현해 낼 실력도 없었던 사람이었다 생각한다. 후회한다. 이 모든 것은 개발의 문제다. 하지만, 일단 출하한다.
  • 기획 : 자신의 유럽 명품적 감각의 파워포인트를 어떻게 이런 제3세계 제품으로 만들어 냈는지 의아해 한다.
  • 리더 : 다시 시작하자 으쌰 으쌰 해 본다. 그리고, 그 사이 대세가 바뀌지 않았다 살펴 본다.
  • 개발 : 어짜피 이렇게 된거, 처음부터 다시 시작하자고 한다. 나는 다시 내가 만든 것을 들여다 보고 싶지 않다.
  • 리더 : 역시 중요한 것은 사람이다 라고 생각한다.

흥하는 제품의 흔한 개발 과정

  • 리더 : 자신에게 꼭 필요했던 핵심가치(기능)을 발견한다. 현존하는 타 제품에서는 발견할 수 없기에, 만들어야 겠다고 결심한다.
  • 기획,개발 : 자신도 꼭 필요했던 것이라 생각하고, 만들면 정작 자신이 가장 큰 혜택을 받을 것이라 생각한다.
  • 리더,기획,개발 : 다 같이 모여서 기존 제품들을 맹렬히 비판해 낸다. 왜 다 이렇게 될 수 밖에 없었는지 생각해 본다.
  • 개발 : 관련된 기술을 조사한다. 그리고, 조사한 결과를 공유한다.
  • 기획 : 수없이 많은 기술을 가지고, 두개의 연결(조합)을 시도한다. 전혀 상관없을 것이라 생각했던 두가지 기술을 합하니, 매우 멋진 모습이 되었다.
  • 리더 : 이 멋진 조합이 핵심가치를 구현하는 결정적 요소가 아니면, 버리자고 한다. 핵심가치에만 촛점을 맞춘다.
  • 개발 : 핵심가치를 구현할 가장 단순한 방법을 찾는다. 구현이 단순할 수록 생각치 못한 일이 발생할 가능성이 줄어 든다.
  • 리더 : 개발된 시제품을 써 본다. 하루고 이틀이고 계속 써 본다. 불편한 점을 찾거나, 그 보다 더 단순하게 할 방법을 생각해 낸다.
  • 개발 : 반복적으로 만들어 낸다. 구현 방법이 단순하였기에, 이 반복과정이 고통스럽지 않다. 이 반복과정을 더 쉽게 할 수 있는 방법을 계속 추가해 낸다.
  • 기획 : 이 단순한 핵심가치를 제공하는 이 제품이 생각보다 많은 곳에서 응용될 수 있다는 것을 찾아 낸다.
  • 리더 : 기쁘지만, 처음 생각한 것에 집중하기로 한다.
  • 리더 : 충분히 만족스러운 상태가 되면 제품으로 출하한다. 충분히 고민한 것이기 때문에, 아주 오랫동안 다시 이 문제를 생각할 필요가 없을 거라 생각한다. 누군가 흉내내면서 새로운 것을 덧붙여 내거나 변형을 시켜내도 크게 신경 쓰지 않는다.
  • 기획 : 현재까지 이룩한 것에서 최소한의 노력으로 추가할 수 있는 핵심가치를 다시 찾기 시작한다.
  • 리더 : 역시 중요한 것은 사람이다 라고 생각한다.

출처: http://wangsy.com/blog/2012/01/how-to-make-totally-shitty-things/

2012년 4월 4일 수요일

html: example

BOX
source:
<TABLE CLASS="SCREEN" BGCOLOR="#DCDCDC" WIDTH="100%" ><TR><TD>
<PRE CLASS="SCREEN"><TT CLASS="COMPUTEROUTPUT" >
Disk geometry for /dev/hda: 0.000-9765.492 megabytes
Disk label type: msdos
Minor    Start       End     Type      Filesystem  Flags
1          0.031    101.975  primary   ext3        boot
2        101.975    611.850  primary   linux-swap 
3        611.851    760.891  primary   ext3       
4        760.891   9758.232  extended              lba
5        760.922   9758.232  logical   ext3       

</TT></PRE></TD></TR></TABLE>

result:
Disk geometry for /dev/hda: 0.000-9765.492 megabytes
Disk label type: msdos
Minor    Start       End     Type      Filesystem  Flags
1          0.031    101.975  primary   ext3        boot
2        101.975    611.850  primary   linux-swap  
3        611.851    760.891  primary   ext3        
4        760.891   9758.232  extended              lba
5        760.922   9758.232  logical   ext3        

AIX: kernel extention debug(how to make the dump file)

dump 파일 생성

#snap -ac/tmp/ibmsupt/dump 밑에 unix.Z와 dump.BZ (또는 dump.Z) 파일이 생성됨을 확인할 수 있다.

용량이 크므로 적절한 위치로 이동시킨후 압축을 푼다.
#dmpuncompress dump.BZ
#uncompress unix.Z



unix 와 dump 파일이 생성됨을 확인 할 수 있다.


kdb 로 디버깅 한다.
#kdb dump unix

solaris: get the system call error in the kernel

#define GET_ERRNO() ttolwp(curthread)->lwp_errno

#define SET_ERRNO(no) set_errno(no)

solaris: 시스템 마운트 정보 가져오기

1 #include <stdio.h>
2 #include <sys/mnttab.h>
3 #include <sys/types.h>
4 #include <sys/statvfs.h>
5 int main()
6 {
7       FILE *fp = NULL;
8       struct mnttab mountEntry;
9       struct statvfs m;
10     int ret = 0;
11


12     fp = fopen("/etc/mnttab", "r");
13     if( fp )
14     {
15         while(getmntent(fp, &mountEntry) == 0 )
16        {
17             if( statvfs(mountEntry.mnt_mountp, &m))
18                  continue;
19             if( m.f_blocks > 0 )
20            {
21                 printf("name: %s ", mountEntry.mnt_mountp);
22                 printf(" (free size: %d KB)\n", (int)(m.f_bavail * (m.f_bsize / 1024.0)));
23
24            }
25         }
26      }
27      fclose(fp);
28 }

solaris: install gcc (solaris 9)

1. Freeware For Solaris http://www.sunfreeware.com
2. 오른쪽 위에 Sparc/Solaris 9 또는 Intel/Solaris 9이라되어있습니다.
자신의 기종에 맞는것을 클릭합니다. (Solaris 9)
3. 다시 오른쪽 아래를 보시면 패키지의 리스트가 보입니다. 여기서 gcc-최신버전을 클릭합니다.
4. 가운데 화면에서 파란색으로 gcc-최신버전.gz 을 클릭해 다운로드 받습니다.
5. 다운로드 받은 파일을 솔라리스 머신에 전송합니다. (ftp로 SUN에 upload한다.)
6. telnet으로 SUN에 접속하여 root권한으로 설치시작. 디렉토리로 가서 압축을 풉니다.
# gunzip gcc-파일명.gz 또는 # gzip -d gcc-파일명.gz
7. 패키지를 설치합니다. # pkgadd -d gcc-최신버전-sol8-intel-local
(처음에 물어보는 것 all 두 번째 물어보는 것 y라 치고 엔터를 치면됩니다)

이와 같이 설치시 특별히 명령어를 지정해주지 않으면 /usr/local/ 이하 디렉토리에 파일이

설치됩니다. 각각의 면모를 살펴봅시다.

/usr/local/bin - 컴파일러에 필요한 실행 프로그램들이 설치되는 디렉토리,

이후 몇몇 프로그램들은 이곳에 실행 파일을 설치합니다.
/usr/local/lib - 컴파일러의 라이브러리. 이후 각각의 라이브러리들의 기본 설치

디렉토리로 사용됨
/usr/local/include - /usr/local/lib에 저장된 라이브러리들의 헤더(header 확장자 .h)

들이 설치되는 장소
/usr/local/man - /usr/local/bin에 설치된 명령어의 메뉴얼이 있을 경우 매뉴얼 설치

저장소
/usr/local/doc - /usr/local/bin에 설치된 명령어의 도큐먼트가 있을 경우 설치되는

저장소

8-1. path 설정 및 link
방법1. # ln -s /usr/local/bin/gcc /usr/bin/gcc
이렇게 하면 시스템의 모든 사용자가 gcc 를 사용할 수 있습니다.

방법2. # vi /.profile
PATH=$PATH:/usr/local/bin
/.profile에 위 항목을 집어 넣으시면 root 는 gcc 를 사용할수 있지만 다른 사용자는
일일이 .profile 에 위 PATH 설정을 해주어야 합니다.

8-2. vi편집기를 이용해 PATH와 LD_LIBRARY_PATH를 수정합니다. 아래의 내용을 가장 아래에

추가합니다.
# vi /etc/profile
....
PATH=/usr/local/bin:$PATH
export PATH
LD_LIBRARY_PATH=/usr/lib:/usr/local/lib:/usr/openwin/lib:/usr/openwin/bin:/usr/dt/lib:

/usr/ucblib
export LD_LIBRARY_PATH

* 주의사항: LD_LIBRARY_PATH에서 디렉토리 경로의 순서를 바꾸시면 안됩니다!

역시 PATH에서 /usr/local/bin이 앞에 나와야 합니다.

9. 확인
# gcc
No Input file
이렇게 나오면 정상



# gcc

ld.so.1 파일 없음이나 libiconv 파일이 없다고 나오는 경우에는 sunfreeware 에서

libiconv 파일을 다운 받은후 패키지 설치를 해준후 다시 테스트 해본다.






Q : ld.so.1: 열기 실패: 해당 파일이나 디렉토리가 없음
A : 동적 라이브러리(Dynamic Library; Shared Object라고 불리는 것입니다.)를 로드하지 못해서

발생한 문제입니다. 말 그대로 libASH.so 파일을 실행 시간에 찾지 못한 문제네요.

해당 파일을 찾아서 LD_LIBRARY_PATH에 추가해 주세요.

Q1: ld.so.1: ./gen_lex_hash: fatal: libstdc++.so.5: open faild: No such file of directory
A : ld.so.1 파일은 동적 라이브러리를 처리하는데 필요한 라이브러리입니다. 이 파일을 통해

라이브러리가 가지는 함수 또는 사용 방법을 알아냅니다.
그런데 영어를 해석해봤듯이 ld.so.1 파일이 libstdc++.so.5 파일 열기에 실패했다고 나옵니다.

실패 원인 파일이 없다고 나오는 군요. 이 파일은 라이브러리입니다. 파일이 있다면 라이브러리

패스를 추가해주면 될 것이고 없다면 저 라이브러리를 설치해줘야 합니다.



Q2: configure:6089:error:readline libarary not found
A : configure 시 에러가 뜬 경우 이군요 에러 코드 6089는 찾아보면 알겠지만 찾기 귀찮습니다.

그러나 에러에 대한 메시지가 나온는 군요. readline 라이브러리가 없다고 말입입니다.

설치해주면 되겠지요?



Q3: configure:error: no acceptable C cmpiler found in $PATH
A : configure failed for srclib/apr 에러 메시지를 봅니다. configure 실행시 C 컴파일러가

패스에서 찾을 수 없다는 군요. C 컴파일러가 설치 않된걸까요??

설치 않되어 있으면 컴파일러를 설치합니다.
만약 설치되어 있다면 env, printenv를 쳐서 환경 파일을 살펴봅니다.
컴파일러가 설치된 디렉토리를 패스에 추가해줬는지 찾아봐야겠군요... ^^;



Q4: #/usr/ccs/bin/make
make: Fatal error: No arguments to bulid
A : 보통 configure 를 실행 후 만들어 지는 파일은 여러가지가 있지만 그 중 가장 중요한 파일은

Makein 파일입니다. 이 파일이 만들어져 있지 않으면 make명령이 실행되지 않습니다.



Q5: 컴파일 중 al-local 어쩌고 저쩌고 나오면서 컴파일이 중지되는 경우가 있습니다.
A : 이런 경우 make 버전이 낮기 때문입니다. 최신의 make 패키지를 설치하시면 됩니다.
설치했는데도 이런 에러가 뜬다면 패스 경로상 /usr/ccs/bin/make 보다 /usr/local/bin/make

의 경로가 뒤에 있기 때문입니다.
직접 패스까지 쳐서 실행하시거나 패스의 경로를 /usr/local/bin 을 앞으로 옮기시면 됩니다.

[출처] 솔라리스 9 GCC 설치 하기|작성자 큐피트

solaris: make the time-string in the kernel

커널 내부에서 time string을 만드는 방법


#define KOR_PARALLAX_SEC (32400) // 9 hour
static char *month_string[12] = {  "Jan", "Feb", "Mar", "Apr",
                                                     "May", "Jun", "Jul", "Aug",
                                                     "Sep", "Oct", "Nov", "Dec"};

static int getoftime_kor(todinfo_t *tod)
{
        time_t utc = 0;

        if( tod == NULL )
                return -1;

        //get the current unix time
        utc = ddi_get_time();

        // 한국 시차를 생각하여 9시간을 플러스 한다.
        utc = utc + KOR_PARALLAX_SEC;   

        // 년, 월, 일, 분, 초를 계산하여 tod 구조체를 설정 한다.
        *tod = utc_to_tod(utc);

        return 0;
}

AIX: How to make packege on AIX

1. 가상의 mount point(디렉토리)에 설치할 파일들을 인스톨 (/usr/local/project_name의 bin, sbin에 설치한다고 가정)

2. 가상 mount point를 베이스 디렉토리로 하여 bff 메타 파일 생성 (최초에 한번만 생성)
- template 파일을 interative 한 방법으로 생성해두면 계속해서 사용할 수 있습니다.

$ mkinstallp -d tmp
Using tmp as the base package directory.
Cannot find tmp/.info. Attempting to create.
Using tmp/.info to store package control files.
Cleaning intermediate files from tmp/.info.

************************************************************
| Beginning interactive package input |
| * - required; () - example value; [] - default value |
************************************************************

* Package Name (xyz.net) []: Project.Name
* Package VRMF (1.0.0.0) []: 1.0.0.0
Update (Y/N) [N]: (return)
Number of Filesets in SVault.miAR (1) [1]: (return)

Gathering info for new fileset (1 remaining)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Fileset Name (SVault.miAR.rte) []: Project.Name.rte
* Fileset VRMF (1.0.0.0) []: 1.0.0.0
* Fileset Description (some text) []: How To make packege on AIX
Do you want to include an installp pre_i script for this fileset? (Y/N) [N]: (return)
Do you want to include an installp post_i script for this fileset? (Y/N) [N]: (return)
Do you want to include an installp pre_rm script for this fileset? (Y/N) [N]: (return)
Do you want to include an installp config script for this fileset? (Y/N) [N]: (return)
Bosboot required (Y/N) [N]: (return)
License agreement acceptance required (Y/N) [N]: (return)
Include license files for SVault.miAR.rte in this package (Y/N) [N]: (return)
Do you want to specify Requisites using a file for this fileset? (Y/N) [N]: (return)
Number of Requisites for SVault.miAR.rte (1) [0]: (return)
Number of filesystems requiring additional space for SVault.miAR.rte [0]: (return)

You should include any directories that you are creating in the file count.
(ie: For /usr/proj/myFile, enter 2; 1 for /usr/proj and 1 for /usr/proj/myFile)
Number of USR part Files in SVault.miAR.rte (1) [0]: (return)

You should include any directories that you are creating in the file count.
(ie: For /etc/proj/myFile, enter 2; 1 for /etc/proj and 1 for /etc/proj/myFile)
Number of ROOT part files in SVault.miAR.rte (1) [0]: (return)
Would you like to create an empty ROOT part? (Y/N) [N]: (return)


Using tmp/.info/project.name.template as the template file.
Project.name 1.0.0.0
processing projec.name.rte
creating ./.info/liblpp.a
creating ./tmp/Project.name.1.0.0.0.bff

3. "tmp/.info/Project.name.template" 파일을 수정
(Linux-rpm의 .spec, HPUX의 .psf, solaris의 pkginfo/prototype와 같은 패키지를 위한 파일)
- 버전 넘버 수정 (버전 넘버는 x.x.x.x 의 형태만 가능하며 알파벳 허용 안 됨)
- USRFiles 부분에 설치될 파일을 추가

Package Name: Project.name.miAR
Package VRMF: 1.0.0.0
Update: N
Fileset
Fileset Name: Project.name.rte
Fileset VRMF: 1.0.0.0
Fileset Description: How to make pakege on AIX
Bosboot required: N
License agreement acceptance required: N
Include license files in this package: N
Requisites:
USRFiles
/usr/local/Project/bin/바이너리 파일들 추가

....
EOUSRFiles
ROOT Part: N
ROOTFiles
EOROOTFiles
EOFileset

4. template 파일을 이용해서 패키지를 생성한다.
$ mkinstallp -d tmp -T tmp/.info/Project.name.template

Using tmp as the base package directory.
Using tmp/.info to store package control files.
Cleaning intermediate files from tmp/.info.

Using tmp/.info/Project.name.template as the template file.
Project.name.
processing Project.name.rte
creating ./.info/liblpp.a
creating ./tmp/Project.name.bff

5. 설치 테스트
$ installp -a -d tmp/tmp/Project.name.1.0.0.0.bff all

6. 설치 확인
$ lslpp -L | grep Project.name
Project.name.rte 1.0.0.0 C F How to make pakege on AIX

7. 제거
$ installp -u Project.name

2012년 4월 3일 화요일

AIX: 32bit -> 64bit

change of kernel mode on AIX

32 bit -> 64 bit

# ln -sf /usr/lib/boot/unix_64/unix
# ln -sf /usr/lib/boot/unix_64/usr/lib/boot/unix
# bosboot -ad /dev/ipldevice
# shutdown -r

64 bit -> 32 bit

# ln -sf /usr/lib/boot/unix_mp/unix
# ln -sf /usr/lib/boot/unix_mp/usr/lib/boot/unix
# bosboot -ad /dev/ipldevice
# shutdown -r

출처: http://msnote.tistory.com/category/How%20to./IBM%20AIX

hpux: aio_write()

man: aio_write
The aiocbp is already in use for another asynchronous I/O operation.
Once the write operation request has been enqueued by aio_write(), all of the errors normally reported by the write() function and the following errors may be reported asynchronously and returned in a subsequent call to aio_error() or aio_return() referencing the aiocb supplied in the successful aio_write() call.

error code:

[EBADF]
The aiocbp->aio_fildes was not a valid file descriptor open for writing.
[EINVAL]
The aiocb->aio_sigevent is not a valid address in the process virtual address space.
[EINVAL]
The parameters of the indicated sigevent in aiocb->aio_sigevent are invalid.
[EINVAL]
The value of aiocbp->aio_reqprio is not valid.
[EINVAL]
The value of aiocbp->aio_nbytes is invalid.
[EINVAL]
The file offset implied by aiocbp->aio_offset or aiocbp->aio_offset+ aiocbp->aio_nbytes are not valid for the file at the time the request is processed.
[ECANCELED]
The write operation was canceled due to a subsequent call to aio_cancel() referencing the same aiocb that was used to start the operation.

EXAMPLE
The following code sequence and call to aio_write() starts an asynchronous write operation.

#include
#include
#include

char buf[4096];
int retval;
ssize_t nbytes;
struct aiocb myaiocb;

bzero( &myaiocb, sizeof (struct aiocb));
bzero( &buf, sizeof (buf));
myaiocb.aio_fildes = open( "/dev/null", O_RDWR); myaiocb.aio_offset = 0;
myaiocb.aio_buf = (void *) buf;

myaiocb.aio_nbytes = sizeof (buf);
myaiocb.aio_sigevent.sigev_notify = SIGEV_NONE; retval = aio_write( &myaiocb );

if (retval) perror("aio_write:");

/* continue processing */
...
/* wait for completion */

while ( (retval = aio_error( &myaiocb) ) == EINPROGRESS) ;

/* free the aiocb */
nbytes = aio_return( &myaiocb);

solaris: understand the init level

Solaris - understand the init level

init s or S = single user mode/ console
init 0 = firmware level
init 1 = all the local FS will be mounted. Only root can login . Similar to init 1 in LINUX
init 2 = multi user mode only
init 3 = multi user mode with network
init 4 = not in use
init 5 = shutdown
init 6 = reboot

check current init level
bash-3.00# who -r
run-level 3 Jun 30 00:28 3 0 S

OS별 시스템 정보 확인

the installed operating system name
AIX, Solaris, HP-UX
# uname -s

the installed operating system version
AIX
# oslevel

Solaris, HP-UX
# uname -r

model name
AIX
# lsattr -E -l sys0 | awk '/modelname/ {print $2}'

Solaris
# uname -i

HP-UX
# uname -m

CPU information

number of CPU
AIX
# lsdev -Cc processor | wc -l

Solaris
# psrinfo | wc -l

HP-UX
# ioscan -fknC processor | grep processor | wc -l

clock
AIX
# value=`lscfg -vp | grep "PS=" | head -1 | awk -F"PS=" '{print $2}' | awk -F"," '{print $1}'`
# value="0x$value"
# value=`printf "%dn" $value`
# expr $value / 1000000


Solaris
# psrinfo -v `psrinfo | awk '{print $1}' | head -1` | awk '/operates/ {print $6}'

HP-UX
# echo itick_per_user/D | adb -k /stand/vmunix /dev/mem | tail -1 | awk '{print $2}'


memory

number of memory
AIX
# lsdev -C | grep -i memory | wc -l


size of memory
AIX
# lsattr -E -l mem0 -a size | awk '{print $2}'

Solaris
# prtconf | awk '/Memory size/ {print $3}'

HP-UX
# value=`dmesg | grep Physical | grep Kbytes | awk '{print $2}'`
# expr $value / 1024


disk

number of disk
AIX
# lsdev -Cc disk | wc -l

Solaris
# format <<-! > /tmp/format.dat
> 0
> q
> !
# cat /tmp/format.dat | grep cyl | wc -l


HP-UX
# ioscan -fknC disk | grep disk | grep -v "CD-ROM" | grep -v "DVD-ROM" | wc -l

size of disk
AIX
# lspv hdisk0 | grep "TOTAL PPs" | awk -F"(" '{print $2}' | awk '{print $1}'

Solaris
# bps=`prtvtoc /dev/dsk/c0t0d0s0 | grep "bytes/sector" | awk '{print $2}'`
# sectors=`prtvtoc /dev/dsk/c0t0d0s0 | grep -v "*" | grep "^ *2" | awk '{print $5}'`
# bc -l << !
> scale=0
> $sectors * $bps / 1024 / 1024
> !


HP-UX
# hwpath=`ioscan -fknC disk | grep disk | grep -v "CD-ROM" | grep -v "DVD-ROM" | head -1`
# name=`ioscan -fkn -H $hwpath | grep "/rdsk/" | awk '{print $2}'`
# value=`diskinfo $name | awk '/size:/ {print $2}'`
# expr $value / 1024



check the AIX hadware bit
#bootinfo -y

check the AIX kernel bit
#bootinfo -K

information of system on AIX
#prtconf | head