« Previous : 1 : 2 : 3 : 4 : 5 : 6 : Next »

Unix Signal 처리

2008/02/03 00:31 / Resource
SIGPIPE signal 처리

* Scenario: server가 다른 작업을 수행중인 동안 client가 종료한 경우 client는 server로 FIN packet을 전송한다. server가 client가 종료한 것을 모르는 상태에서 socket에 write를 하는경우 client쪽 TCP는 RST packet을 전송한다. server가 RST packet을 수신한 상태에서 다시 socket에 write를 하는 경우 SIGPIPE signal을 받게된다.

* FIN packet을 수신한 상태에서 socket을 read 하는 경우: EOF(0)
* RST packet을 수신한 상태에서 socket을 read 하는 경우: error (ECONNRESET)

* FIN packet을 수신한 상태에서 socket에 write를 하는 경우는 error가 아니지만 RST packet을 수신한 상태에서 socket에 write를 하는 경우는 error (EPIPE)

* SIGPIPE signal 처리
- SIGPIPE signal을 ignore (SIG_IGN)한다.
- write에서 return 되는 error (EPIPE)를 처리한다.



SIGCHLD signal 처리
* fork를 사용하여 child process를 생성하는 경우 parent process는 child process를 wait하여 child process가 zombie가 되는 것을 막아야 한다. 일반적으로 SIGCHLD 처리 함수안에서 waitpid 함수를 사용하여 child process가 zombie가 되는 것을 막는다.

* waitpid함수의 pid value를 -1을 사용하면 종료된 첫번째 child process를 wait하며 WNOHANG option을 사용하면 종료된 child process가 없더라도 waitpid 함수에서 block되는 것을 막을 수 있다.

* accept, read, write, select, open 과 같은 함수를 호출하는 중간에 SIGCHLD signal을 받아 interrupt된 경우 EINTR errno를 받게되며 이러한 경우 다시 위의 함수를 호출하면 된다. 하지만, connect 함수를 호출하는 중간에 interrupt된 경우 socket함수부터 다시 시작해야 한다.

* accept 함수를 사용하는 경우 block되는 것을 방지하기 위해서는 listening socket을 nonblocking으로 setting하며 EWOULDBLOCK, ECONNABORTED, EPROTO, EINTR error는 무시한다.

* 출처: UNIX Network Programming V.1 by W. Richard Stevens




◈ SIGHUP
- 터미널 인터페이스에 의해 연결의 단절이 감지되면 해당 제어 터미널과 연결된 제어 프로세스(세션 리더)에게 전달
- 세션 리더가 종료했을 때도 발생하는데 이때는 foreground 프로세스 그룹내의 모든 프로세스들에게 전달
- SIGHUP 시그널은 원래 모뎀 연결 끊김 등의 시리얼 라인이 끊어지면 발생하는 시그널이다.
- 이름 있는 시스템 데몬들은 SIGHUP 시그널을 configure file을 다시 읽어들이는 등의 초기화 신호로 해석한다.
  . bootp(8), gated(8), inetd(8), mountd(8), named(8), nfsd(8), ypbind(8)
  . pppd(8) 처럼 SIGHUP을 원래의 의도에 충실하게 세션 종료의 뜻으로 받아들이는 사례도 간혹 있는데, 요새는 보편적으로 이 역할을 SIGTERM이 맡는다.
  . daemon은 제어 단말기 없이 돌기 때문에 kernel로부터 SIGHUP 신호를 수신하지 못한다.
    그러므로 많은 daemon은 이 신호를 daemon의 구성 파일이 변경되어 daemon이 그 파일을 새로 읽어야 된다는 것을 알려주는 관리자로부터의 통지로 사용한다.
  . daemon이 수신할 수 없는 다른 두 개의 신호로 SIGINT와 SIGWINCH가 있고 이들도 역시 어떤 변화를 daemon에게 통지하기 위해 사용될 수 있다.

◈ SIGINT
- 인터럽트 키 (DELETE 또는 Control-C)가 눌렸을 때 발생

◈ SIGQUIT
- Control-backslash 에 의해 발생

◈ SIGCHLD
- 프로세스가 종료하거나 정지하면, 부모 프로세스에게 전달된다.
- 부모 프로세스는 wait() 시스템 콜을 사용하여 무슨 일이 일어났는지 알아본다.
- 이 시그널에 대한 default 처리는 무시하는 것이다. 즉 프로세스가 이 신호를 받으려고 할 때만 전달된다.

◈ SIGSEGV
- 유효하지 않은 가상 메모리 주소를 참조하거나 사용 권한이 없는 메모리에 접근할 때 프로세스로 전달된다.

◈ SIGTERM
- kill 명령에 의해 기본적으로 발생

◈ SIGKILL
- "극단의 조치(extreme prejudice)"로 프로그램을 종료하는 데 사용된다.
- 시그널 catch 하거나 무시할 수 없다.

◈ SIGALRM
- alarm()이나 setitimer() 시스템 콜로 설정한 알람 시간이 초과 했을 때 프로세스로 전달된다.

◈ SIGTSTP
-
Control-Z 키에 의해 발생
- 기본 처리 방법은 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.

◈ SIGCONT
-
정지한 프로세스를 계속 실행시키려 할 때 발생
- 이 신호는 받을 수 있지만 블록하거나 무시할 수 없다.
- 기본 처리 방법은 중단된 프로세스를 재시작하는 것이다. 그러나 프로세스가 신호를 받지 않는다면 신호를 버린다.
- vi 에디터를 사용할 때
. Control-Z 를 눌러 수행을 잠시 정지시키면 쉘이 키 입력을 처리하게 되는데
. 이때 fg 명령을 실행시키면 쉘은 vi 에게 SIGCONT 시그널을 전달하며
. vi는 이 시그널에 대한 처리로 화면을 다시 그리고 사용자 키 입력을 받는 상태로 돌아간다.

◈ SIGSTOP
-
SIGTSTP과 동일하나 catch 하거나 무시할 수 없다.
- 이 신호를 받으면 무조건 SIGCONT 신호를 받을 때까지 프로세스를 중단한다.

◈ SIGABRT
-
abort() 함수의 호출로 발생

◈ SIGBUS
-
하드웨어 결함으로 발생

◈ SIGEMT
-
하드웨어 결함으로 발생

SIGFPE
-
divide-by-0나 부동 소숫점 오버플로우와 같은 산술 연산 오류에서 발생

◈ SIGILL

◈ SIGINFO

◈ SIGIO

◈ SIGIOT

◈ SIGPIPE
-
pipe 통신에서 수신 프로세스가 종료했을 때 송신 프로세스가 파이프에 write 하면 발생
- 프로세스가 RST를 받은 소켓에 데이터를 쓰면, 커널은 그 프로세스에 ISGPIPE 신호를 보낸다.
- 이 신호의 기본 동작은 프로세스를 종료시키는 것이므로, 프로세스가 원하지 않는 종료를 피하기 위해서는 이 신호를 포착해야 한다.

◈ SIGPOLL

◈ SIGROF

◈ SIGPWR

◈ SIGSYS

◈ SIGTTIN
-
background에 있는 프로세스가 제어 터미널로부터의 읽기를 시도한다.

◈ SIGTTOU
-
background에 있는 프로세스가 제어 터미널로부터의 쓰기를 시도한다.

◈ SIGURG
-
SIGIO와 SIGURG 라는 두 개의 신호는 소켓이 F_SETOWN 명령으로 소유주에게 할당되었을 때만 소켓에 대해 발생한다.

◈ SIGUSR1

◈ SIGUSR2

◈ SIGVTALRM

◈ SIGWINCH

◈ SIGXCPU

◈ SIGXFSZ
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.
TAGS ,

Leave your greetings here.

  
  
  
  
  
  
  
  
 
SQLCA (SQL Communication Area)

1. 정의
    ⊙ 프로그램내에서 각각의 SQL 실행문이 처리 될때마다 발생하는
         상황처리를
       RETURN해주는 ORACLE 제공의 통신 영역.

2. 사용법
    ⊙ "EXEC SQL INCLUDE sqlca.h"
    ⊙ Global SQLCA는 1개만 지정 가능하며, 트랜잭션에 의해 리턴된
        정보를 보존하기 위해, 각각의 루틴에서 "로컬변수"로 각각의
        SQLCA를 사용할 수도 있다.

3. 구조

      ⊙ struct sqlca {
              char sqlcaid[8];
              long sqlcabc;
              long sqlcode;
              struct {
                 unsigned short sqlerrm1;
                 char sqlerrmc[70]
              } sqlerrm;
              char sqlerrp[8];
              long sqlerrd[6];
              char sqlwarn[8];
              char sqlext[8];
       };
       struct sqlca sqlca;

4. 기능설명

    ⊙ sqlca.sqlcaid
       : 문자열. 이 필드는 글로벌 구조로서 사용하는 경우에
         한해서, 할당시에 "SQLCA"로 초기화 된다.

    ⊙ sqlca.sqlcabc
       : 4바이트 2진정수. 이필드에는 SQLCA 구조 자신의 길이가
         바이트로 설정된다.

    ⊙ sqlca.sqlcode
       : 4바이트 2진정수. 이필드에는 SQL문의 실행결과를 나타낸다.

       ▶ Zero(0) : 실행이 정상으로 종료한 것을 나타낸다.
       ▶ 양수 : 상태코드와 함께 실행이 성공한 것을 나타냄.
                 현재의 경우 양의 코드는 '1403'뿐이며, 이것은
                 "행이 발견되지 않는다" 또는 마지막 행이 리턴된
                 것을 나타냄.
       ▶ 음수 : 프로그램내의 에러 또는 시스템 장애를 나타냄.
                 : "오라클 에러 메세지 및 코드" 편 참조

    ⊙ sqlca.sqlerrm.sqlerrml
       :sqlca.sqlerrm.sqlerrmc의 텍스트의 길이를 나타낸다.

    ⊙ sqlca.sqlerrm.sqlerrmc
       : 가변길이의 문자열이고, sqlca.sqlcode내에 표시된
         에러번호에 대응하는 에러 메세지의 내용.

    ⊙ sqlca.sqlerrp
       : 현재의 경우는 사용하지 않는다. 이 필드는 문자열이다.

    ⊙ sqlca.sqlerrd
       : 4바이트 2진정수 6개로 된 배열이고, ORACLE RDBMS의 내부
         상황을 파악하기위해 사용한다.
         3번째의 요소[2]만이 사용되고 있고, INSERT나 UPDATE처럼
         DML 처리에 대해서 몇개의 행이 처리됐는지를 나타낸다.
       : 요소 0,1,3,4,5는 현재의 경우 사용되고 있지 않다.
       : 인포믹스에서 sqlca.sqlerrd[1]의 값은 insert 후의 serial
         값이 됩니다.

    ⊙ sqlca.sqlwarn
       : 1문자의 요소 8개로 구성된 구조이다.
       : 이들의 요소는 Pre-Compile중에 발생한 여러가지 상황에 대한
         경고를 나타낸다.
         예를 들면, ORACLE이 평균치의 계산에서 NULL을 무시한 경우
         등에 경고가 설정된다. 이 경우는 대응하는 요소의 값이 "W"로
         된다.
       : 첫번째 요소 [0]은 항상 "W"가 된다.

       ▶ sqlca.sqlwarn[0]
          : 그 문에 대해 경고가 1개도 설정되지 않았음을 나타냄.
          : sqlca.sqlwarn[0]에 "W"가 설정되어 있는 경우 1개 이상의
            경고가 설정되어 있는 것이므로 검사해야 한다. 치며적인
            에러는 아니지만 검사해야 할 문제나
            상황이 있음을 나타냄.

       ▶ sqlca.sqlwarn[1]
          : sqlca.sqlwarn[0]에 "W"가 설정되어 있는 경우, 호스트
            변수의 폭이 충분하지않기 때문에 리턴된 문자 필드가 1개
            이상 절사 되었음을 나타냄.
          : 이것은 문자 데이터에 대해서만 적용하며, 수치타입 데이터
            에 대해서는 경고를 설정하거나, 음의 sqlca.sqlcode를
            리턴하지 않고서 절사를 행함.

       ▶ sqlca.sqlwarn[2]
          : sqlca.sqlwarn[2]에 "W"가 설정되어 있는 경우, AVG, SUM,
            MIN, MAX등의 함수 계산에서 1개 이상의 NULL이 무시되었음
            을 보여줌.

       ▶ sqlca.sqlwarn[3]
          : sqlca.sqlwarn[3]에 "W"가 설정되어 있는 경우, SELECT 리스
            트내의 항목수가 INTO구의 호스트 변수 갯수와 같지 않음을
            보여줌.
          : 데이터는 리턴되지만, 리턴되는 항목의 수는 적은 쪽에
            일치함.

       ▶ sqlca.sqlwarn[4]
          : sqlca.sqlwarn[4]에 "W"가 설정되어 있는 경우, UPDATE또는
            DELETE문에 WHERE구가 지정되지 않았음을 보여줌. 즉
            어느행을 갱신.삭제할지 한정되지 않았기 때문에 테이블내
            의 모든 행이 갱신.삭제됨을 의미하며, ORACLE은
            확인 또는 롤백에 대한 경고를 설정하여 줌.
           
       ▶ sqlca.sqlwarn[5]
          : sqlca.sqlwarn[5]는 현재 사용하고 있지 않음.

       ▶ sqlca.sqlwarn[6]
          : sqlca.sqlwarn[6]에 "W"가 설정되어 있는 경우, 실행된
            SQL문에 의해 ORACLE 이 논리적 작업단위의 롤백을 행한
            경우에 해당된다.
          : Deadlock에 의한 트랜잭션의 Rollback.

       ▶ sqlca.sqlwarn[7]
          : sqlca.sqlwarn[7]에 "W"가 설정되어 있는 경우, 행의 현재
            데이터가 Query 와는 모순되는 데이터의 경우 혹은 행의
            현재 데이터가 이 Query의 시작
            후에 삭제된 경우등에 해당된다.

   ⊙ sqlca.sqlext
       : SQLCA의 마지막 필드인 sqlca.sqlext는 현재 사용되지 않고
         있음.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 

Sybase 날짜형(datetime)의 문자열 변환시 style number

select convert(varchar,날짜데이터, convertType) 형식으로 쓰고 convertType 위치에 숫자가  들어갈 때 옆에 적힌 형식대로 나온다고 보시면 됩니다.


Style number

  • 0 = Aug 27 2007  5:28PM
  • 1 = 08/27/07
  • 2 = 07.08.27
  • 3 = 27/08/07
  • 4 = 27.08.07
  • 5 = 27-08-07
  • 6 = 27 Aug 07
  • 7 = Aug 27, 07
  • 8 = 17:23:35
  • 9 = Aug 27 2007  5:28:08:563PM
  • 10 = 08-27-07
  • 11 = 07/08/27
  • 12 = 070827
  • 13 = 07/27/08
  • 14 = 08/07/27
  • 15 = 27/07/08
  • 16 = Aug 23 2007 17:28:08
  • 18 = 15:17:08
  • 19 = 5:11:39:086PM            
  • 20 = 17:12:30:633
  • 21 = 07/08/27
  • 22 = 07/08/27
  • 100 = Aug 27 2007  5:28PM
  • 101 = 08/27/2007
  • 102 = 2007.08.07
  • 103 = 27/08/2007
  • 104 = 27.08.2007
  • 105 = 27-08-2007
  • 106 = 27 Aug 2007
  • 107 = Aug 27, 2007
  • 108 = 17:28:08
  • 109 = Aug 27 2007 5:28:08:563PM
  • 110 = 08-27-2007
  • 111 = 2007/08/27
  • 112 = 20070827
  • 113 = 2007/27/08
  • 114 = 08/2007/27
  • 115 = 27/2007/08
  • 116 = Aug 23 2007 17:28:08


응용

select convert(char,GETDATE(),112)
:오늘날짜를 YYYYMMDD로

select convert(char(8), DATEADD(DD,-1,getdate()) ,112)  
:현재 날짜 하루전을 yymmdd형식으로 출력


select convert(char(8), DATEADD(DD,-1,'20070827') ,112)  
: 2007년 8월 27일 전날을 출력. string -> datetime은 convert라는 함수를 사용하지 않고 내부적(implicit)으로 자동으로 변경


select str_replace( convert(varchar,getdate(),20),':',null)

: 현재 분일초,밀리세컨드까지: 152515853


select convert(varchar,GETDATE(),112) || str_replace( convert(varchar,getdate(),20),':',null)

: 현재 연월일시분초밀리세컨드

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 

리눅스 한글처리

2008/01/30 16:20 / Resource

한글처리

Contents

1 한글처리
1.1 개요
1.2 조합형
1.2.1 한글코드 (조합형)
1.2.1.1 코드표 (조합형: 삼보, 대우, 현대, 쌍용, 큐닉스)
1.2.1.2 벌수 구성표
1.3 문서를 마치면서
1.4 저작권

  • 작성자 : 조재혁 (Mminzkn_at_infoeq.com)

    1.1 개요

    한국 사람이 한국어를 구현못한다면 낭패다.
    한글의 구조 및 공통점을 찾지 못하는 것도 낭패다.
    적어도 한국사람이라면 한글구현은 한번쯤 겪어보는게 좋다.
    물론 필자 역시 리눅스용으로 한글을 구현해서 공개하려고 했다. 하지만 그리 쉽지만은 않다.
    또한 한글출력은 그나마 쉽다. 하지만 중요한 문제는 한글오토마타의 구현이다.
    여러분들은 오토마타를 우습게 보지 말아야 한다. 구현해보면 알게 된다. 그 어려운 산을 넘는 법을 말이다.

    1.2 조합형

    조합형은 초성, 중성, 종성을 각각 폰트로 본다는 점에서 완성형과 다르다.
    또한 완성형과 비교하면 폰트의 크기가 보통 훨씬 작다.

    1.2.1 한글코드 (조합형)

    1.2.1.1 코드표 (조합형: 삼보, 대우, 현대, 쌍용, 큐닉스)

    • 밑의 테이블은 한글구현에 있어서 필수사항이다. 외우는것이 좋다. 외우자! 무조건.
    • 순번은 어디다가 쓰길래 적어놓았냐는 의문은 갖지 말자. 만들어보면 안다. 왜 있어야 했는지.
      비트조합 (bit) 10진 코드 16진 코드 초성(순번) 중성(순번) 종성(순번)
      0 0 0 0 0 0 00 미정의 미정의 미정의
      0 0 0 0 1 1 01 채움 미정의 채움
      0 0 0 1 0 2 02 ㄱ (0x00) 채움 ㄱ (0x00)
      0 0 0 1 1 3 03 ㄲ (0x01) ㅏ (0x00) ㄲ (0x01)
      0 0 1 0 0 4 04 ㄴ (0x02) ㅐ (0x01) ㄳ (0x02)
      0 0 1 0 1 5 05 ㄷ (0x03) ㅑ (0x02) ㄴ (0x03)
      0 0 1 1 0 6 06 ㄸ (0x04) ㅒ (0x03) ㄵ (0x04)
      0 0 1 1 1 7 07 ㄹ (0x05) ㅓ (0x04) ㄶ (0x05)
      0 1 0 0 0 8 08 ㅁ (0x06) 미정의 ㄷ (0x06)
      0 1 0 0 1 9 09 ㅂ (0x07) 미정의 ㄹ (0x07)
      0 1 0 1 0 10 0A ㅃ (0x08) ㅔ (0x05) ㄺ (0x08)
      0 1 0 1 1 11 0B ㅅ (0x09) ㅕ (0x06) ㄻ (0x09)
      0 1 1 0 0 12 0C ㅆ (0x0a) ㅖ (0x07) ㄼ (0x0a)
      0 1 1 0 1 13 0D ㅇ (0x0b) ㅗ (0x08) ㄽ (0x0b)
      0 1 1 1 0 14 0E ㅈ (0x0c) ㅘ (0x09) ㄾ (0x0c)
      0 1 1 1 1 15 0F ㅉ (0x0d) ㅙ (0x0a) ㄿ (0x0d)
      1 0 0 0 0 16 10 ㅊ (0x0e) 미정의 ㅀ (0x0e)
      1 0 0 0 1 17 11 ㅋ (0x0f) 미정의 ㅁ (0x0f)
      1 0 0 1 0 18 12 ㅌ (0x10) ㅚ (0x0b) 미정의
      1 0 0 1 1 19 13 ㅍ (0x11) ㅛ (0x0c) ㅂ (0x10)
      1 0 1 0 0 20 14 ㅎ (0x12) ㅜ (0x0d) ㅄ (0x11)
      1 0 1 0 1 21 15 미정의 ㅝ (0x0e) ㅅ (0x12)
      1 0 1 1 0 22 16 미정의 ㅞ (0x0f) ㅆ (0x13)
      1 0 1 1 1 23 17 미정의 ㅟ (0x10) ㅇ (0x14)
      1 1 0 0 0 24 18 미정의 미정의 ㅈ (0x15)
      1 1 0 0 1 25 19 미정의 미정의 ㅊ (0x16)
      1 1 0 1 0 26 1A 미정의 ㅠ (0x11) ㅋ (0x17)
      1 1 0 1 1 27 1B 미정의 ㅡ (0x12) ㅌ (0x18)
      1 1 1 0 0 28 1C 미정의 ㅢ (0x13) ㅍ (0x19)
      1 1 1 0 1 29 1D 미정의 ㅣ (0x14) ㅎ (0x1a)
      1 1 1 1 0 30 1E 미정의 미정의 미정의
      1 1 1 1 1 31 1F 미정의 미정의 미정의

    1.2.1.2 벌수 구성표

    • "왜 벌수가 필요한가?" 질문하신다면 저는 "예뻐보이라고요" 라고 답하겠습니다.
    • 초성은 6벌, 중성은 10벌, 종성은 4벌로 기본적인 벌수가 구성된다. 하지만 일부 폰트는 크기를 줄이고자 각 1벌씩만 가지는 경우가 있다.
    • 어떤 폰트는 좀더 모양새를 완전하게 하기 위해서 예외 벌이 들어 있다. 하지만 그것을 구현할 이유는 별로 없어보인다.
    • 초성
      벌수 초성의 종류 중성 사용의 예
      종성채움 1 초성이 'ㄱ', 'ㅋ'일 때 '가', '고', '크', '커', ...
      종성채움 2 초성이 'ㅎ'일 때 '하', '혀', '효', ...
      종성채움 3 1, 2벌이 아닌 경우의 나머지 '배', '마', '나', '다', ...
      종성코드 4 초성이 'ㄱ', 'ㅋ'일 때 '각', '곡', '클', '컽', '켞', ...
      종성코드 5 초성이 'ㅎ'일 때 '한', '형', '혼', ...
      종성코드 6 1, 2벌이 아닌 경우의 나머지 '백', '막', '낙', '독', ...
    • 중성
      벌수 중성의 종류 초성 사용의 예
      종성채움 1 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅣ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ' '가', '개', '갸', '걔', '기', ...
      종성채움 2 'ㅗ', 'ㅛ', 'ㅡ' '고', '교', '그', ...
      종성채움 3 'ㅜ', 'ㅠ' '구', '규', ...
      종성채움 4 'ㅘ', 'ㅙ', 'ㅚ', 'ㅢ' '과', '괘', '괴', '긔', ...
      종성채움 5 'ㅝ', 'ㅞ', 'ㅟ' '궈', '궤', '귀', ...
      종성코드 6 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅣ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ' '각', '객', '갹', '걕', '긱', ...
      종성코드 7 'ㅗ', 'ㅛ', 'ㅡ' '곡', '굑', '극', ...
      종성코드 8 'ㅜ', 'ㅠ' '국', '귝', ...
      종성코드 9 'ㅘ', 'ㅙ', 'ㅚ', 'ㅢ' '곽', '괙', '괵', '긕', ...
      종성코드 10 'ㅝ', 'ㅞ', 'ㅟ' '궉', '궥', '귁', ...
    • 종성
      벌수 중성의 종류 종성 사용의 예
      1 'ㅏ', 'ㅑ', 'ㅘ', 'ㅣ'의 중성이 있는 경우 '각', '얇', '인', ...
      2 'ㅓ', 'ㅕ', 'ㅚ', 'ㅝ', 'ㅟ', 'ㅢ'의 중성이 있는 경우 '건', '격', '원', '윈', '벽', ...
      3 'ㅐ', 'ㅒ', 'ㅔ', 'ㅖ', 'ㅙ', ㅞ'의 중성이 있는 경우 '객', '액', '왠', '췔', ...
      4 'ㅗ', 'ㅛ', 'ㅜ', 'ㅠ', 'ㅡ'의 중성이 있는 경우 '곡', '욕', '음', '흠', ...

    1.3 문서를 마치면서

    • 이 문서에서 틀린 부분이나 고쳐야 할부분이 있으면 꼭 알려주십시요.

    1.4 저작권

    • 본 문서는 [http]GFDL의 라이센스를 기반으로 작성되었고 유지됩니다.

    출처 : 조인 위키

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 

Contents

1 소개
2 시나리오
3 구현방안
3.1 메시지큐 구현
3.1.1 worker Thread 관리
3.1.2 쓰레드 동기화
3.1.3 프로시져
3.1.4 코드 구현
4 공유메모리 구현

1 소개

쓰레드 프로그래밍을 할 때 가장 신경쓰이는건 역시 쓰레드동기화와 쓰레드간 메시지 전달과 관련된 문제일 것이다. 또한 쓰레드간 메시지 전달에는 쓰레드 동기화 문제까지 함께 고민해야 한다. 이 문서는 다중쓰레드에서 쓰레드간 메시지를 효과적으로 전달하기 위한 다양한 방법들을 기술한다.

여기에서 소개하는 방법들은 수많은 방법들 중 몇가지 방법들일 뿐이다. 실제 프로젝트에서는 다양한 응용을 생각해야 할 것이다.

2 시나리오

영어문서를 파싱해서 Term을 얻어오고, 출현한 Term의 빈도수를 계수하는 프로그램을 만들도록 하겠다. 빠른 파싱을 위해서, 문서가 주어지면 문서를 라인수를 기준으로 4등분 한다음, 4개의 쓰레드를 돌려서 병렬로 처리하도록 할 것이다. 이 프로그램은 다음의 사항을 만족시켜야 한다.
  1. Main 쓰레드는 문서를 4등분한다음 생성된 work thread갯수를 파악한다음
  2. 파일을 open()한 후, 4개의 쓰레드에게 읽어야될 파일지정자와 파싱할 줄의 범위를 알려준다.
  3. 파일지정자와 범위를 전달받은 work thread는 해당범위의 문장을 분석해서 <Term, count> 자료구조를 만든다.
  4. 모든 작업이 끝났다면, Main Thread에게 작업이 끝났음을 알려준다. 이때, 자신이 작업한 결과에 대한 정보를 Main Thread에게 알려줘야 한다.
  5. 모든 work Thread에서 작업종료 메시지를 받았다면, Main Thread는 각 work Thread의 <Term, count>를 취합해서, 하나의 파일로 만든다.

3 구현방안

다음은 문서를 파싱해서 <Term, count>를 얻어오는 프로그램이다. 단일 쓰레드로 작동하는 프로토타입의 프로그램으로 아래의 코드를 멀티쓰레드방식으로 수정할 것이다.

#include <sys/types.h> 
#include <regex.h> 
#include <string.h> 
#include <stdio.h> 
#include <errno.h> 
#include <vector> 
#include <string> 
#include <iostream> 
 
#include <fcntl.h> 
 
using namespace std; 
 
/* 
 * 주어진 문서에서 단어를 얻어온다. 
 * 입력된 라인은 strtok를 통해서 토큰으로 먼저 분리되고 
 * 분리된 문자열은 정규표현을 만족하면 Term으로 판단하고 출력한다. 
 * 실행인자 : 정규표현식 
 * 사 용 예 : 
 */ 
 
int main(int argc, char **argv) 
{ 
  FILE *fp; 
  int rtv; 
  regex_t preg; 
  char linebuf[1024]; 
  char *tr; 
  char seps[] = " -.,()\";:{}'+@/<>[]|!?#"; 
  char msg[64]; 
  int i; 
  vector<string> FileList; 
 
  // 파싱할 파일을 등록한다. 
  FileList.push_back("rfc.dat"); 
 
  if (argc != 2) 
  { 
    printf("Usage : %s [pattern]\n", argv[0]); 
    return 1; 
  } 
 
  // 정규표현을 위한 컴파일러 생성 
  rtv = regcomp(&preg, argv[1], REG_EXTENDED|REG_NOSUB); 
  if(rtv != 0) 
  { 
    regerror(rtv, &preg, NULL, 0); 
    return 1; 
  } 
 
  for(int i = 0; i < FileList.size(); i++) 
  { 
    fp = fopen(FileList.c_str(), "r"); 
    if (fp == NULL) 
    { 
      perror("Error "); 
      return 1; 
    } 
 
    while(fgets(linebuf, 1024, fp) != NULL) 
    { 
      linebuf[strlen(linebuf)-1] = '\0'; 
 
      // 토큰으로 구분한다음 
      tr = strtok(linebuf, seps); 
      while(tr != NULL) 
      { 
        tr = strtok(NULL, seps); 
        if (tr != NULL) 
        { 
          // 정규표현을 만족하는지 확인한다. 
          if (regexec(&preg, tr, 0, NULL, 0) == 0) 
          { 
            cout << "Find Term : " << tr << endl; 
          } 
        } 
      } 
    } 
    fclose(fp); 
  } 
} 
 
다음과 같이 실행하면 된다.
# ./getterm "[a-zA-Z0-9]" 
 

3.1 메시지큐 구현

메시지큐 구현에 대한 아이디어는 다음과 같다.
  • worker Thread가 하나의 Term을 얻어오면, 해당 Term을 메시지 형태로 Main Thread에게 바로 전달한다.
  • Main Thread는 <Term, count>자료구조를 유지하면서, 계수를 한다.
  • 메시지큐의 ID는 하나로 통일한다.
  • 메시지큐를 이용한 메시지의 전달 가능성 확인을 목적으로 한다.(병렬성 효율등은 부차적 문제로 언급한다)

사용자 삽입 이미지



3.1.1 worker Thread 관리

주고 받는 데이터에 타입을 두어서 관리하도록 할 것이다.
struct Data 
{ 
    int type; 
    char *Data; 
    int size; 
}; 
 
Type는 다음과 같이 정의 할 것이다.
1 << 1 일반 데이터
1 << 2 제어 데이터
  • 일반데이터라면 Data를 파싱된 Term으로 인식해서 처리한다.
  • 제어데이터라면 Data를 제어 명령이라고 인식해서 처리한다. Data가 1 이라면, 모든 일을 끝냈다고 판단한다.

이 방식은 구현이 단순하긴 하지만 worker thread -> Main thread로의 단방향 데이터 전송만 가능하다는 단점이 있다. 메시지큐를 하나더 만드는등 다양한 방법이 있을 수 있는데, 이는 뒤에서(몇가지만) 다루도록 하겠다.

3.1.2 쓰레드 동기화

쓰레드간 동기화는 mutex 잠금과 조건변수를 이용할 것이다.

3.1.3 프로시져

대충 그림을 그려야 코드가 만들어 지는 스타일이라서...
main 
{ 
    생성 쓰레드 갯수는 4개로 한다. 
    문서를 읽어들여서 문서의 Line수를 계수한다. 
    메시지큐를 생성한다. 
    for(i = 0; i < 4; i++) 
    { 
        // 쓰레드 동기화 Start 
        Worker 쓰레드를 생성한다. 인자로 문서의 Offset정보를 넘긴다. 
        쓰레드 함수명은 WThread로 한다.  
        // 쓰레드 동기화 End  
    } 
    메인 쓰레드를 수행한다. 
    while(1) 
    { 
        메시지 큐로부터 데이터를 읽는다. 
        switch(데이터 타입) 
        case 일반데이터 
        { 
            Term을 계수한다. 
        } 
        case 제어데이터 
        { 
            종료루틴을 수행한다. 
            모든 쓰레드가 작업을 종료했다면, Break;  
        } 
    } 
    <Term,Count>결과를 출력한다. 
} 
 

3.1.4 코드 구현

<!> 미완성 코드다. g++ 로 컴파일 하면, 메시지큐를 통해서 worker thread에서 main thread로 분석된 Term이 전달되는걸 확인할 수 있다. 메시지큐를 통한 데이터 통신의 기본적인 구현은 끝났다고 볼 수 있다. 코드를 좀더 깔끔하게 하고, 몇 군데 예외처리를 해주어야 한다.
#include <pthread.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
#include <sys/stat.h> 
#include <regex.h> 
 
#include <fcntl.h> 
#include <string.h> 
 
#define ThreadNum 4 
 
pthread_mutex_t mutex_lock; 
pthread_cond_t  sync_cond; 
 
pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t  gcond  = PTHREAD_COND_INITIALIZER; 
 
const char seps[] = " -.,()\";:{}'+@/<>[]|!?#\n"; // Token 
 
// 주고 받을 데이터 
struct Data 
{ 
    int queuenum; 
    int msgtype;  
    int tid; 
    char msg[256]; 
    int size; 
    key_t key_id; 
}; 
 
// worker 쓰레드에 넘겨줄 정보 
struct DocInfo 
{ 
    int tnum;       // thread id 
    int start;      // 문서시작 위치 
    int end;        // 문서 끝 위치 
    char fname[80]; // 작업 파일명 
    char regex[24]; // 정규표현 문자열  
}; 
 
/* 
 * worker 쓰레드 함수 
 */ 
void *Tfunction(void *data) 
{ 
    struct Data lData; 
    struct DocInfo *lDocInfo;  
    FILE *fp; 
    char line[256]; 
    int rtv; 
    int bsize; 
    int readn = 0; 
  regex_t preg; 
  char *tr; 
 
    lData = *(struct Data *)(data); 
    lDocInfo = (struct DocInfo *)lData.msg;  
 
    bsize = lDocInfo->end - lDocInfo->start; 
    printf("DEBUG Thread %d %d\n", lDocInfo->tnum, bsize); 
    if((fp = fopen(lDocInfo->fname, "r")) == NULL) 
    { 
        perror("Fopen Error"); 
        exit(0); 
    } 
 
    lseek(fileno(fp), lDocInfo->start, SEEK_SET);     
  rtv = regcomp(&preg, lDocInfo->regex, REG_EXTENDED|REG_NOSUB); 
 
    pthread_mutex_lock(&mutex_lock); 
    pthread_cond_signal(&sync_cond); 
    pthread_mutex_unlock(&mutex_lock); 
 
    if (rtv != 0) 
    { 
        regerror(rtv, &preg, lDocInfo->regex, REG_EXTENDED|REG_NOSUB); 
        exit(0); 
    } 
    for (readn = 0; readn < bsize; ) 
    { 
        if(fgets(line, 256, fp) == NULL) 
            break; 
        tr = strtok(line, seps); 
        if (tr != NULL) 
        { 
            if (regexec(&preg, tr, 0, NULL, 0) == 0) 
            { 
                lData.queuenum = 1; 
                lData.msgtype = 1 << 1; 
                lData.size = strlen(tr); 
                sprintf(lData.msg, "%s", tr); 
 
                if(msgsnd(lData.key_id, (void *)&lData, sizeof(lData), IPC_NOWAIT) <0) 
                { 
                    perror("msg snd error"); 
                    exit(0); 
                } 
            } 
        } 
        readn += strlen(line); 
        sleep(1); 
    } 
    // 종료 메시지를 보낸다. 
} 
 
 
int main(int argc, char **argv) 
{ 
    struct Data lData; 
    struct DocInfo lDocInfo;  
 
    pthread_t p_thread[ThreadNum]; 
 
    int fd; 
    char *fname; 
    int fsize = 0; 
    int blocksize = 0; 
    struct stat fileinfo; 
    char *regex; 
 
    // Message queue 
    key_t key_id; 
    int msgtype;     
 
    fname = argv[1]; 
    regex =  argv[2]; 
 
    // 메시지큐 생성 
    key_id = msgget((key_t)8888, IPC_CREAT|0666); 
    if (key_id == -1) 
    { 
        perror("msgget error : "); 
        exit(0); 
    } 
 
    // Open File 
    fd = open(fname, O_RDONLY);     
    if (fd < 0) 
    { 
        perror("Open file error"); 
        return 1; 
    } 
    if(fstat(fd, &fileinfo)< 0)  
    { 
        return 1; 
    } 
    fsize = fileinfo.st_size; 
    printf("T Size is %d\n", fsize); 
 
    blocksize =  fsize / ThreadNum;  
 
    for (int i = 0; i < ThreadNum; i++) 
    { 
        lDocInfo.start = (i*blocksize); 
        lDocInfo.end = lDocInfo.start+blocksize; 
        sprintf(lDocInfo.fname, "%s", fname); 
        sprintf(lDocInfo.regex, "%s", regex); 
        lDocInfo.tnum = i; 
 
        if (i == (ThreadNum -1)) 
            lDocInfo.end += fsize%ThreadNum; 
        lData.queuenum = 1;  
        lData.msgtype = 1 << 2;  
        lData.size = sizeof(lDocInfo);  
        lData.key_id = key_id; 
        lData.tid = lDocInfo.tnum;  
        memcpy((void *)lData.msg, (void *)&lDocInfo, sizeof(lDocInfo)); 
 
        pthread_mutex_lock(&mutex_lock); 
        pthread_create(&p_thread, NULL, Tfunction, (void *)&lData); 
        pthread_cond_wait(&sync_cond, &mutex_lock); 
        pthread_mutex_unlock(&mutex_lock); 
    } 
 
    while(1) 
    { 
        if(msgrcv(key_id, (void *)&lData, sizeof(lData), (1 >> 1), 0)    == -1) 
        { 
            perror("msgrcv error: "); 
        } 
        printf("Get Term %d %s\n", lData.tid, lData.msg); 
    } 
} 
 

4 공유메모리 구현

공유메모리는 커널에 메모리 공간을 할당함으로써, 시스템 전역적으로 자원을 사용할 수 있도록 지원하는 IPC 설비중 하나다. 이러한 특성을 이용 쓰레드간 메시지 교환은 공유메모리상에 환형큐를 만드는 것으로 구현가능할 수 있을 것이다.

이 환형큐에는 여러개의 쓰레드가 접근을 하게 될 것이므로, 쓰레드간 동기화가 필수 적일건데, mutex가 아닌 레코드 잠금으로도 쓰레드간 접근제어를 할 수 있다. 레코드 잠금을 통해서 쓰레드동기화를 제어하는 방법은 공유메모리와 파일잠금을 이용한 프로세스간 데이터 공유에서 이미 다룬바가 있다.

쓸만한 예제라고 생각은 하지만 이번 구현에 그대로 적용하기에는 성격이 다르다. 이번 구현은 여러개의 생산자와 하나의 소비자가 존재하는 형식이기 때문이다. 뭐 그렇다고 크게 문제될건 없다. 아래와 같이 생산/소비자 방식을 약간 수정하는 것으로 해결책을 삼았다.
사용자 삽입 이미지


쓰레드간 동기화는 그대로 파일의 레코드 잠금을 이용할 것이다. 이 경우 다수의 생산자를 제어해야 하는데, 처음 8 byte를 생산자간 접근제어를 위한 잠금으로 사용할 것이다. 처음 레코드에 대한 생산자만이 레코드에 큐(빨간색)에 쓸권한을 가지게 된다.

데이터 쓰기 권한을 얻은 쓰레드는 공유메모리에 데이터를 쓰고, 해당 공유메모리에 대응되는 레코드에 잠금을 풀게된다. 그러면 소비자는 잠금을 얻고, 잠금에 대응되는 공유메모리를 찾아가서 정보를 읽어오면 된다. 예제로 제시한 문서를 이해하고 있다면, 위의 방식으로 수정하는건 그리 어렵지 않을 것이니, 굳이 코드를 만들진 않도록 하겠다.

출처 : 조인시 위키
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
Posted by 소리나는연탄.

Leave your greetings here.

  
  
  
  
  
  
  
  
 
« Previous : 1 : 2 : 3 : 4 : 5 : 6 : Next »