2009. 4. 23. 21:22
MicroSoft 사에서 대학생들의 위해 몇개의 소프트웨어를 무료로 제공한다고 한다..
국제 학생증이 있으면 누구나 다운 받을수 있다..

아래의 사이트에서 다운 받을수 있다..

Visual Studio 2008, Visual Studio 2005
Window Server 2008, Window Server 2005
Virtual PC, SQL Server 등등...여러개가 있네..


https://www.dreamspark.com/Products/ProductList.aspx


Posted by 두장

리눅스에서 현재 시스템 시간 확인

# date


시스템의 하드웨어에 입력되어 있는 시간 출력

# clock


시스템 시간을 하드웨어 시간에 입력

# clock -w


실제 현재 시간과 시스템에 설정된 시간이 다를 경우가 있다.
이때 보라넷 서버 시간과 일치시키기 위해 아래와 같이 하면된다.
(보라넷 서버 시간은 실제 현재 시간과 일치하기때문에)

# rdate -s time.bora.net && clock -w


아래와 같이 하면 리눅스의 시간을 임의로 설정 할수도 있다.

다음은 2009년 06월 30일 15시 55분으로 시스템의 시간을 설정하는 명령어이다. 

# date 063015522009


Posted by 두장
정규표현식 (Regular Expression) 이란 무엇인가? 이 질문은 진짜 어렵다. 너도 나도 정규표현식을 얘기하지만 정작 정규표현식이 뭔지는 잘 안 나와 있다. (오토마타 수업을 들은 풍월로 굳이 얘기하자면 정규 문법에 의해 생성되는 정규 언어를 표현할 수 있는 표현식..이라고 하면 되려나) 어쨌거나, "하나 이상의 문자열을 한 번에 나타낼 수 있는 패턴"이 정규 표현식이다. 아래는 UNIX 서적이나 웬만한 웹사이트에 다 나오는 기본 정규 표현식이다.
a : 말 그대로 "a", b 는 당연히 "b", c 는...
. : 임의의 한 글자. 따라서 a.d 는 aad abd acd add aed afd...
[list] : list 중의 한 글자. 
  [adf] 는 a 또는 d 또는 f
  [a-f] 는 a, b, c, d, e, f
  [^adf] 는 a, d, f 를 제외한 나머지 중 한 글자. list 앞에 "^" 이 오면 뒤에 오는 것을 제외한 것을 의미한다.
  [^a-f] 는.. 말 안 해도 되겠지
 그러면 "^ 또는 a 또는 b"를 의미하고 싶을 때는? "^"를 list 의 제일 앞이 아닌 곳에 두면 된다.
  [ab^] - 마찬가지로 "-" 나 "]" 역시 [ab-] []df] 와 같이 쓴다.
* : 0번 이상의 임의 번 반복. a* 는 null string, a, aa, aaa, aaaa...

그런데 아무래도 저것만 가지고는 좀 불편하다. 그래서 "확장 정규 표현식"이 등장했다.

+ : 1번 이상의 임의 번 반복. a+ 는 a, aa, aaa, ... 즉 aa* 와 동일.
| : a|b 는 a 또는 b
() : group, (ab|cd)ef 는 abef 또는 cdef
? : 없거나 하나 있거나. ab? 는 a 또는 ab

위의 확장 정규 표현식은 Perl에서는 그냥 쓰면 되고, ViEditor 에서는 앞에 백슬래쉬를 붙여 a\+ 와 같이 사용한다.

임의 번 반복 대신이 구체적으로 숫자를 줄 수도 있다.

{n,m} : n번 이상 m번 이하 반복 (가능한 많이)
{n} : n번 반복
{n,} : n번 이상 반복 (가능한 많이)
{,m} : m번 이하 반복 (가능한 많이)
{} : 0 번 이상. * 와 동일

{-n,m} : n번 이상 m번 이하 (가능한 적게)
{-n} : n번
{-n,} : n번 이상 (가능한 적게)
{-,m} : m번 이하 (가능한 적게)
{-} : 0번 이상 (가능한 적게)

역시 vi 에서는 앞에 백슬래쉬를 붙여서 a\{2,5} 등과 같이 사용한다.

위에서 "가능한 많이"와 "가능한 적게"는 뭔가? abcdebcdebcde 라는 예로 들면, a.*d 는 abcdebcdebcd 에 매칭되고, a\{-}d 는 abcd 에 매칭된다.

참고:

Posted by 두장
linux vi 에디터에서 문자(열)을 찾아서 바꾸기
 :(시작줄),(끝줄)s/찾을패턴/바꿀스트링/옵션

- 시작줄, 끝줄 : 바꾸기를 할 범위를 행번호로 지정한다. "."는 현재 커서가 있는 행을 의미, "$"는 제일 마지막 행을 의미한다.
- 찾을 패턴, 바꿀 스트링 : 찾을 패턴은 정규 표현식으로 지정하고, 바꿀 스트링은 string지정한다.
- 옵션 : 
g (global) - 한줄에 일치하는 패턴이 여러개 나오면 모두 바꾼다. 지정하지 않으면 첫번째 패턴만 바꾼다.
i (ignore case) - 대소문자 구분을 하지 않는다
c (confirm) - 검색된 문자열에 대해서 바꿀지 말지를 물어본다.

Example
 :5,10s/a/b/      - 5번째 행부터 10번째 행까지 각 행의 첫번째 "a"를 "b"로 바꾼다.
 :.,.+10s/a/b/g  - 현재 행부터 (현재 행 번호 + 10)번째 행까지 모든 "a"를 "b"로 바꾼다.
 :1,$s/a/b/c     - 첫번째 행부터 마지막 행까지 (즉 문서 전체) 각 행의 "a"를 "b"로 바꾸되, 바꾸기 전 확인을 받는다.
 :%s/a/b/gi      - 문서 전체에서 "a"와 "A"를 "b"로 바꾼다.


Posted by 두장
보통의 경우그래픽 카드가 설치되어 있는 컴퓨터나 머신에는 직접 모니터를 연결하여 리눅스를 설치 할 것이다..

그럴 경우 시디롬으로 부팅한 후 부팅 옵션을 입력하는 화면이 나왔을 때 단지 Enter를 입력하거나 

"linux"라고 입력하고 GUI를 통해 리눅스를 설치한다.

하지만 그래픽 카드가 없는 컴퓨터나 머신에서 리눅스를 설치하기 위해서 모니를 직접 연결 할수 없으므로

콘솔을 터미널 서버에 연결해서 리눅스를 설치해야한다. 이때는 GUI를 통해 리눅스를 설치하는 것이 불가능하다..

이때도 똑같이 리눅스 시디를 시디롬에 넣고 시디롬 부팅을 한 후

부팅 옵션을 입력하라는 화면이 나오면 단순히 "linux"라고 해서는 안되고

이때 "linux text console=ttyS0,115200n8" 라고 부팅 옵션을 입력하면
("ttyS0, 115200n8" < -- 이부분은 각각의 머신마다 변경 될수 있다.)

텍스트 형식으로 화면을 보면서 리눅스를 설치할 수 있다.

이때는 설치 과정이 GUI로 마우스로 클릭할수 있는 화면이 아니라

설치과정이 text형식으로 나오기때문에 설치과정에 좀더 유의해야 한다...

Posted by 두장

* 프로그램을 검증하는 사용. 프로그램에 대한 함수들의 사용빈도, 사용시간등을 측정할 수 있는 툴

* 예제

- # vi gprof_test.c

- # gcc -o gprof_test gprof_test.c -pg

- # ./gporf_test
바이너리를 실행하면 gmon.out 파일이 생성됨.

- # gprof gprof_test gmon.out > gprof_profile.txt
gprof [실행 binary] [실행으로 생덩된 gmon.out]

- # cat gprof_profile.txt
Flat profile:
 
Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 71.43      0.05     0.05        1    50.00    50.00  func
 28.57      0.07     0.02        1    20.00    20.00  func2
 
 
                        Call graph
 
 
granularity: each sample hit covers 4 byte(s) for 14.29% of 0.07 seconds
 
index % time    self  children    called     name
                                                 <spontaneous>
[1]    100.0    0.00    0.07                 main [1]
                0.05    0.00       1/1           func [2]
                0.02    0.00       1/1           func2 [3]
-----------------------------------------------
                0.05    0.00       1/1           main [1]
[2]     71.4    0.05    0.00       1         func [2]
-----------------------------------------------
                0.02    0.00       1/1           main [1]
[3]     28.6    0.02    0.00       1         func2 [3]
-----------------------------------------------
 
 
Index by function name
 
   [2] func                    [3] func2

* 주의사항 

- 프로그램이 정상 종료해야 gmon.out파일이 생성됨.


--------------------------------------------------------------------------------------

프로그래머 도구상자:

gprof를 사용한 프로파일링

By Vinayak Hegde 
한글번역 전정호 
이 글은 한글번역판입니다. 원문은 여기에서 볼 수 있습니다.

연재 소개

리눅스(와 다른 유닉스)에는 같이 사용하여 놀라운 기능을 하는 작고 멋진 도구들이 많다. 이런 소프트웨어를 사용하거나 직접 만들면 재미있다. 나는 이번 연재에서 프로그래머에게 유용한 도구들을 살펴볼 것이다. 이 도구들을 사용하면 더 좋은 코드를 작성할 수 있고 작업이 더 편해질 것이다.

프로파일링(profiling)?? 왜 필요하지 ??

소프트웨어를 설계하고 코딩을 하다보면 프로그램을 최적화할 때가 있다. 일반적인 프로파일링과 최적화에 대해 다루기 전에 최적화에 대한 두 인용문을 소개하고 싶다.

  • 대부분의 계산 실수는 어리석음과 다른 어떤 이유보다도 효율성이란 이름으로 (효율성을 달성하건 안하건간에) 행해진다. 
    -- William A. Wulf
  • 우리는 사소한 효율성을 잊어야 한다: 어설픈 최적화는 모든 화의 근원이다. 
    -- Donald E. Knuth

대부분의 프로그램은 80:20 법칙을 따른다. 전체 코드의 20%가 전체 시간의 80%를 차지한다. 위의 인용문이 뜻하는 바는 컴퓨터의 시간보다 프로그래머의 시간이 더 귀중하다는 뜻이다. 그래서 프로그래머가 해당 컴퓨터 아키텍쳐의 세세한 부분보다는 논리에 집중하여 프로그래밍 시간을 줄여주는 자바나 C#같은 언어가 인기를 얻었다. 프로그램의 실행시간은 길어지지만, 프로그래머의 시간은 절약된다. 그러나 프로그램을 더 빨리 실행하기위한 최적화가 필요하지 않다는 말은 아니다. 많은 컴파일러는 알아서 최적화를 한다. 예를 들어, GCC 컴파일러는 (대문자 주의) -O 옵션으로 최적화 수준을 지정한다. 프로파일링(profiling)은 프로그램의 성능을 높이기위해 최적화할 코드및 함수의 위치를 발견하도록 도와준다. 프로그램에서 10번만 호출하는 함수보다 1000번 호출하는 함수를 최적화하는게 당연하다고 생각하지 않는가. 프로그램을 프로파일링하면 코드의 어떤 부분을 자주 사용하고 어떤 함수가 CPU 시간을 많이 잡아먹는지 알 수 있다. 이 두 정보는 최적화할 대상을 정하는데 유용하다. 실제 프로그램을 실행하면서 정보를 모으기때문에 감춰진 버그를 찾는데도 유용하다. 실행중에 예기치않게 어떤 함수를 1000번 호출한다면 설계상 문제이거나 버그일 수 있다. 또, 크고 복잡한 프로젝트에서 코드를 살펴볼때도 유용하다.

프로파일링 정보에는 두가지 종류가 있다 :-

  • Flat Profile 
    함수별로 사용하는 CPU 시간과 호출 횟수를 보여준다. 수집한 전체 프로파일링 정보의 간단한 요약이다. 성능을 높이기위해 어떤 함수를 다시 작성하거나 수정할지 알려준다.
  • Call Graph 
    모든 함수에 대해 자신을 포함하여 다른 함수가 호출한 횟수를 보여준다. 그래서 어떤 함수 호출을 없애거나 다른 효율적인 함수로 대체할지 제안한다. 이 정보는 함수들간의 관계를 드러내고, 감춰진 버그를 알려주기도 한다. 호출그래프를 본 후에 특정 코드 경로를 최적화하고 싶을 것이다.

어떻게 프로파일링 정보를 수집하나 ??

소스코드를 -pg 옵션을 사용하여 (줄단위 프로파일링을 한다면 -g 옵션도 같이) 컴파일해야 한다. Makefile의 내용이 적다면 컴파일 명령어마다 직접 이 옵션을 추가할 수 있다. 그러나 컴파일 명령어가 많다면 makefile에서 CFLAGS/CXXFLAGS 파라미터를 정의하거나 수정하여, 옵션을 makefile의 모든 컴파일 명령어에 추가한다. gnu make 도구를 사용하여 gprof 사용법을 설명하겟다.

gzip으로 압축한 tar 파일을 푼다
$ tar zxf make-3.80.tar.gz
$ cd make-3.80

configure 스크립트를 실행하여 makefile을 만든다
$ ./configure
[configure 출력 생략]

만들어진 makefile의 CFLAGS 파라미터에서 최적화 옵션을 빼고 -pg 옵션을 추가한다. 컴파일러가 최적화하면 프로파일링에 문제가 있는 경우가 있기때문에 GCC 최적화 옵션을 뺐다. 특히 줄단위(line-by-line) 프로파일링을 한다면, 소스코드를 최적할때 없어지는 줄이 있을 수 있다.

소스코드를 컴파일한다
$ make
[컴파일 출력 생략]

아파치, lynx, cvs 같은 소프트웨어를 컴파일할때 이 make를 사용할 수 있다. 그럼 이 make를 사용하여 아파치를 컴파일해본다. 아파치 소스 압축을 풀고, configure한 후, make를 실행하면 프로파일링 정보를 저장한 gmon.out 이라는 파일이 생긴다. 프로파일링 정보를 기록하기때문에 평소보다 make가 느리다. 중요한 점은 일상적인 입력을 가지고 평소와 같이 동작하면서 프로파일링 정보를 모은다는 점이다. 실세 상황에서 자료를 모은다.

프로파일링 결과 분석

이제 "gmon.out"이라는 바이너리파일을 얻었다. 불행히도 프로파일링 자료파일명을 지정하는 방법은 현재 없다. gprof가 "gmon.out" 파일을 해석하여 사람이 볼 수 있는 결과를 만든다. 문법은 다음과 같다:

gprof 옵션 [실행파일 [프로파일링 자료파일 ... ] ] [ > 사람이 볼 수 있는 출력파일]

$ gprof make gmon.out > profile-make-with-Apache.txt

전체 파일은 여기 있다

flat profile 부분은 다음과 같다 -

 Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 33.33      0.01     0.01      207     0.05     0.05  file_hash_2
 33.33      0.02     0.01       38     0.26     0.26  new_pattern_rule
 33.33      0.03     0.01        6     1.67     2.81  pattern_search
  0.00      0.03     0.00     2881     0.00     0.00  hash_find_slot
  0.00      0.03     0.00     2529     0.00     0.00  xmalloc
  0.00      0.03     0.00     1327     0.00     0.00  hash_find_item
  0.00      0.03     0.00     1015     0.00     0.00  directory_hash_cmp
  0.00      0.03     0.00      963     0.00     0.00  find_char_unquote
  0.00      0.03     0.00      881     0.00     0.00  file_hash_1
  0.00      0.03     0.00      870     0.00     0.00  variable_buffer_output

위에서 알아낼 수 있는 결론은 :

  1. 3 함수가 (file_hash_2, new_pattern_rule, pattern_search) 거의 대부분의 시간을 잡아먹는다.
  2. pattern_search 함수는 6번만 호출하지만, 호출당 평균 2.81 밀리초가 걸린다.

그러나 정보가 충분하지 않다. 그래서 이 make를 사용하여 lynx, cvs, make, patch를 컴파일해 보았다. 만들어진 gmon.out 파일의 이름을 변경하고, 마지막에 다음과 같이 프로파일링 정보를 가공한다.

$ gprof make gmon-*.out > overall-profile.txt

파일은 여기 있다 
flat profile 부분은 다음과 같다.

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 18.18      0.06     0.06    23480     0.00     0.00  find_char_unquote
 12.12      0.10     0.04      120     0.33     0.73  pattern_search
  9.09      0.13     0.03     5120     0.01     0.01  collapse_continuations
  9.09      0.16     0.03      148     0.20     0.88  update_file_1
  9.09      0.19     0.03       37     0.81     4.76  eval
  6.06      0.21     0.02    12484     0.00     0.00  file_hash_1
  6.06      0.23     0.02     6596     0.00     0.00  get_next_mword
  3.03      0.24     0.01    29981     0.00     0.00  hash_find_slot
  3.03      0.25     0.01    14769     0.00     0.00  next_token
  3.03      0.26     0.01     5800     0.00     0.00  variable_expand_string

아파치를 컴파일할 때와 조금 차이가 있음을 알 수 있다.

  1. find_char_unquote 함수를 23480번 호출하고, 모두 합쳐서 프로그램 실행시간의 1/6 이상을 차지한다.
  2. eval 함수는 37번만 호출하지만, 프로그램 실행시간의 1/11 정보를 차지한다. 아마도 이 함수가 많은 작업을 하며, 여러 함수로 나누어보는 것이 좋을 수 있다. 또, eval 호출에 평균 4.76 밀리초가 걸리는데 다른 함수와 비교하여 매우 크다
  3. pattern_search와 update_file_1 함수를 합치면 거의 실행시간의 1/4을 차지하지만, 전부 268번만 호출한다. 아마도 이 함수들을 작은 함수로 나눌 수 있을 것이다.

이제 아파치 컴파일 프로파일링 자료에서 call graph 부분을 보자.

index % time    self  children    called     	name
-----------------------------------------------
                                   6             eval_makefile  [49]
[25]     3.7    0.00    0.00       6         eval  [25]
                0.00    0.00     219/219         try_variable_definition [28]
                0.00    0.00      48/48          record_files [40]
                0.00    0.00     122/314         variable_expand_string  [59]
                0.00    0.00       5/314         allocated_variable_expand_for_file  [85]
                0.00    0.00     490/490         readline [76]
                0.00    0.00     403/403         collapse_continuations [79]
                0.00    0.00     355/355         remove_comments [80]
                0.00    0.00     321/963         find_char_unquote [66]
                0.00    0.00     170/170         get_next_mword [88]
                0.00    0.00     101/111         parse_file_seq [93]
                0.00    0.00     101/111         multi_glob [92]
                0.00    0.00      48/767         next_token [70]
                0.00    0.00      19/870         variable_buffer_output [68]
                0.00    0.00      13/2529        xmalloc [64]
                0.00    0.00       2/25          xrealloc [99]
                                   5             eval_makefile  [49]
-----------------------------------------------

위의 정보는 다음과 같이 읽는다 : 

  1. 첫번째 열은 gprof 출력의 마지막에 있는 함수 목록 번호다.
  2. 두번째 열은 eval 함수와 eval 함수가 호출한 함수들의 전체 실행시간이다.
  3. 세번째 열과 네번째 열은 각각 순수하게 해당 함수에서 보낸 시간과 호출한 다른 함수들에서 보낸 시간이다
  4. 다섯번째 열의 첫번째 숫자는 eval에서 해당 함수를 호출한 횟수이고, 두번째 숫자는 프로그램에서 해당 함수를 호출한 (자신이 자신을 호출한 경우를 제외한) 전체 횟수이다.
  5. 함수가 자신을 호출하는 재귀호출이 있거나 상호 재귀호출이 있다면 (위의 eval_makefile과 eval과 같이) 함수명 뒤에 cycle 이 붙는다.
  6. 어떤 함수는 항상 eval에서만 호출한다. 함수를 합쳐서 함수를 호출하는 부담을 줄이면 성능이 좋아지는 경우가 있다.

다른 gprof 기능

gprof를 사용하면 상세한 소스 목록과 줄단위 프로파일링 정보도 얻을 수 있다. 일단 어느 부분을 최적화할지 결정하였다면 이 정보가 유용하다. 이 기능을 사용하여 소스코드를 한줄씩 살펴보며 비효율적인 부분을 찾을 수 있다. 줄단위 프로파일링과 flat profile을 사용하면 코드가 어떤 경로로 가장 자주 실행되는지 알 수 있다. 상세한 소스 목록을 사용하여 함수 호출에서 (반복문과 분기문의) 기본 블럭에까지 살펴보면 어떤 반복문이 가장 많이 실행되는지 어떤 분기를 가장 많이 하는지 알 수 있다. 최적의 성능을 얻기위해 코드를 세밀히 조절할 때 유용하다. 여기서 다루지 않은 기능도 있다. 자세한 내용은 gprof info 문서를 참고하라. kprof라는 gprof의 KDE 그래픽 인터페이스도 있다. 참조자료에 URL을 적어두었다.

결론

gprof와 같은 프로파일링 도구는 프로그램을 최적화할때 매우 도움이 된다. 프로파일링은 프로그램의 병목지점을 발견하고 수작업을 최적화하는 첫번째 단계다.

참고자료

  • gprof의 GNU Info 문서
  • gprof의 KDE 그래픽 인터페이스 KProf
  • Function Check - gprof의 몇가지 결점을 해결한 다른 프로파일링 도구 


출처 :
KLDP http://kldp.org/node/32606
한글 번역 http://blog.empas.com/stoneshim/1393358

Posted by 두장

Libpcap 사용하기

노광민

           dalgu2 (at) kldp.org
        

libpcap의 정의와 사용법, 응용 등을 제시한다.

고친 과정
고침 0.4 2002-01-01 고친이 Kwang-Min Noh
Text 문서를 DocBook으로 수정 및 응용 부분 추가

Posted by 두장
Top Applications Listed in the WinTasks Process Library
000stthk.exe acrord32.exe acrotray.exe
acs.exe acsd.exe adeck.exe
adhelper.exe admserv.exe adservice.exe
adskscsrv.exe adsser~1.exe adusermon.exe
agentdp2.dll agrsmmsg.exe ahui.exe
aim.exe alcwzrd.exe aluschedulersvc.exe
andreavc.exe aniserv.exe aniwzcsds.exe
aolacsd.exe aolsoftware.exe aolsp scheduler.exe
aolssc.exe aoltpspd.exe apache.exe
apdproxy.exe apoint.exe appservices.exe
apvxdwin.exe arp.exe as_agent.exe
ashserv.exe aso.exe asr_fmt.exe
asr_ldm.exe asr_pfu.exe at.exe
ati2evxx.exe atiptaxx.exe atirw.exe
atkosd.exe atl70.dll atl71.dll
atmadm.exe atmclk.exe attrib.exe
auditusr.exe aupdate.exe autolfn.exe
avgfwsrv.exe avgnt.exe avguard.exe
avkernel.exe avscan.exe avtask.exe
azureus.exe backweb-8876480.exe bcmsmmsg.exe
bcmwltry.exe bigfix.exe bittorrent.exe
boinc.exe bootok.exe btntservice.exe
bttnserv.exe bttray.exe btwdins.exe
c4ebreg.exe caissdt.exe calmain.exe
cap3swk.exe cappswk.exe capturedaemon.exe
casvc.exe cavaud.exe ccproxy.exe
ccsetmgr.exe cdac11ba.exe cdantsrv.exe
ceekey.exe cfd.exe cfgbkend.dll
cfsserv.exe cftmon.exe chkdsk.exe
chkrzm.exe cimwin32.dll ckcnv.exe
clipbrd.exe clmlserver.exe clmlservice.exe
clptray.exe clsched.exe cmmon32.exe
cmstp.exe cnbjmon.dll communicator.exe
compstui.dll comrepl.exe comrereg.exe
connect.exe connectscheduler.exe connmngmntbox.exe
core.exe cpldbl10.exe cpqset.exe
cpuidle.exe crypserv.exe cryptdlg.dll
cscui.dll ctdvddet.exe ctsvccda.exe
ctsysvol.exe cvpnd.exe cwupdate.exe
czfmdser.exe daemon.exe dao360.dll
datalayer.exe dataserver.exe davclnt.dll
dbnetlib.dll dcomcnfg.exe dcsuserprot.exe
ddeshare.exe defwatch.exe deletesatellite.exe
desktopweather.exe devdet~1.exe devdtct2.exe
devenv.exe devldr32.exe dialup.exe
diantz.exe digstream.exe ding.exe
directcd.exe directdb.dll discover.exe
discupdatemgr.exe dit.exe dkservice.exe
dlactrlw.exe dlbfbmon.exe dlbxcoms.exe
dlbxmon.exe dlccmon.exe dlg.exe
dllhst3g.exe dllml.exe dlpwdnt.exe
dmaster.exe dmxlauncher.exe docmgr.exe
dotnetinstaller.exe dplaysvr.exe dpnsvr.exe
dpvacm.dll dpvsetup.exe drgtodsc.exe
drives~1.exe drmlfc.exe drprov.dll
drwatson.exe dthtml.exe dumpbin.exe
duser.dll dvdidlepro.exe dvdplay.exe
dvdramsv.exe dvpapi.exe dvzincmsgr.exe
e_fati9aa.exe e_fati9le.exe e_fatiada.exe
e_fatiaie.exe e_s4i2r1.exe e_s4i2s1.exe
easyclip6.exe easyshare.exe eausbkbd.exe
ectaskscheduler.exe edonkey2000.exe ehrecvr.exe
ehsched.exe ehtray.exe elkctrl.exe
elnk_pcc.exe emproxy.exe eouwiz.exe
esentutl.exe eudcedit.exe eventcreate.exe
evrep.exe excnvt.exe expand.exe
exshow.exe ezdmontr.exe ezi_hnm2.exe
ezsp_px.exe fasttvsync.exe faxpatch.exe
fbguard.exe fbserver.exe fdm.exe
firefox.exe fltmc.exe fnkeyhook.exe
foldersizesvc.exe forcedos.exe fotomatdeviceconnect.exe
fsguidll.exe fsrw.exe fwupdate.exe
fxssvc.exe fxsvr2.exe g-vga.exe
gacutil.exe gbtray.exe gearsec.exe
ggviewer.exe gigaget.exe googledesktop.exe
googledesktopcrawl.exe googledesktopindex.exe googletoolbarnotifier.exe
googlewebaccwarden.exe gpdownloadmanager.exe gstart.exe
gwmdmmsg.exe halsv.exe harmonyclient.exe
hcontrol.exe help.exe helpsvc.exe
hh.exe hmmapi.dll hnetcfg.dll
hostname.exe hotfixq0306270.exe hotsync.exe
hpcmpmgr.exe hpconfig.exe hpdarc.exe
hpgs2wnd.exe hpgs2wnf.exe hphipm09.exe
hphipm11.exe hphmon05.exe hphmon06.exe
hpoevm08.exe hposts08.exe hpotdd01.exe
hpovdx05.exe hppapml0.exe hpqcmon.exe
hpqgalry.exe hpqste08.exe hpqtoa~1.exe
hpqtra08.exe hpqwmiex.exe hpwirelessmgr.exe
hpwuschd.exe hpwuschd2.exe hpzeng10.exe
hpzinw12.exe hpzipm12.exe hpztsb12.exe
hrtzzm.exe httpd.exe iaantmon.exe
iap.exe ibmpmsvc.exe ico.exe
icwrmind.exe iedw.exe iemaximizer.exe
iexpress.exe ifrmewrk.exe igdctrl.exe
igfxpers.exe igfxsrvc.exe igfxtray.exe
imapp.exe imjpmig.exe imscinst.exe
incd.exe inodist.exe instaverse.exe
integrator.exe intuit.spc.map.entitlementclient.server.service.exe ioctlsvc.exe
iolosgctrl.exe ipclient.exe ipconfig.exe
ipodservice.exe ipsmsnap.dll ir.exe
ispnews.exe isuspm.exe itouch.exe
ituneshelper.exe iyuv_32.dll java.exe
javadoc.exe javaw.exe js3250.dll
jsdaemon.exe jucheck.exe k2index.exe
kbd.exe keyhook.exe keymgr.dll
khalmnpr.exe krnl386.exe launch application 2.exe
launch~1.exe lbtserv.exe lcdclock.exe
lclock.exe lexbces.exe lexpps.exe
lgdcore.exe lib.exe link.exe
lkads.exe lkcitdl.exe lktsrv.exe
lmanager.exe lnkstub.exe lnssatt.exe
loadqm.exe localsch.exe lodctr.exe
logi_mwx.exe logitray.exe logman.exe
logoff.exe logwatnt.exe lpq.exe
lpr.exe lsburnwatcher.exe lssrvc.exe
ltmoh.exe ltmsg.exe lucallbackproxy.exe
lvcoms.exe lvcomsx.exe lvprcsrv.exe
lxbbbmgr.exe lxbbbmon.exe lxbrbmgr.exe
lxbrbmon.exe lxbrksk.exe lxbtbmon.exe
lxbxcoms.exe lxcccoms.exe lxcecoms.exe
lxcicoms.exe magnify.exe mccitrayapp.exe
mclogsrv.exe mcshield.exe mcsysmon.exe
mcupdui.exe mcusrmgr.exe mcvsshld.exe
mdigraph.dll mdimon.dll mdiui.dll
mdnsresponder.exe medialifeservice.exe mediamonkey.exe
metamail trust manager.exe migload.exe migpwd.exe
migwiz_a.exe mim.exe mixer.exe
mm_tray.exe mmcomp~1.exe mmdiag.exe
mmreminderservice.exe mmtask.exe mobsync.exe
mofcomp.exe monw32.exe motivesb.exe
motive~1.exe moviel~1.exe mp10setup.exe
mp3dancer.exe mpfsrv.exe mpftray.exe
mps.exe mpsevh.exe mpstd.exe
mqsnap.dll mqsvc.exe mrinfo.exe
mroute~2.exe msdart.dll msdev.exe
msdmo.dll msftesql.exe msfwsvc.exe
msgslang.dll msgsys.exe mshearts.exe
msimn.exe mskalert.exe msmsgs.exe
msn6.exe msncall.exe msoeres.dll
mspmspsv.exe mssysmgr.exe msvcp80.dll
msvcr80.dll msxml2.dll muisetup.exe
mysqld-nt.exe navapw32.exe nbtstat.exe
nclbthandler.exe ncprov.dll ndassvc.exe
ndstray.exe nerocheck.exe netint.exe
netsetup.exe netsh.exe newstray.exe
nhksrv.exe nicconfigsvc.exe nidmsrv.exe
nipalsm.exe nmbgmonitor.exe nopdb.exe
nppagent.exe nppdf32.dll npwmsdrm.dll
nsrtray.exe nsvcip.exe nsvclog.exe
ntsystem.exe nvcpl.dll nvmctray
nvsvc32.exe nwiz.exe objectdock.exe
odbcad32.exe odbcconf.exe odhost.exe
offcln.exe offprv10.exe oledb32.dll
omtsreco.exe onenotem.exe onetouchmon.exe
onetou~2.exe onspeedgui.exe oobebaln.exe
opagent.exe ophaldcs.exe oprotsvc.exe
opware14.exe opware15.exe opware32.exe
opwarese2.exe osa.exe oscmutilityservice.exe
osd.exe ose.exe outlook.exe
p2pnetworks.exe p2pnet~1.exe paint shop pro x.exe
pastatus.exe pbeser~1.exe pchsvc.dll
pcletray.exe pcmservice.exe pctspk.exe
pctwpasv.exe pds.exe pduip6220dmon.exe
pdvdserv.exe pelmiced.exe perfmon.exe
pgaccount.exe pgpserv.exe photoshopelementsfileagent.exe
picappsrv.exe ping6.exe pinger.exe
plaxohelper.exe playlist.exe pnpdetect.exe
pnroutsv.exe point32.exe ppactivedetection.exe
pplinks.exe ppshared.exe pptd40nt.exe
prismcfg.exe prismxl.sys processtamertray.exe
procguard.exe promon.exe pronomgr.exe
proxy.exe proxycfg.exe ps2.exe
puxpman.exe puxpman2.exe qappsrv.exe
qcwlicon.exe qmgr.dll qprocess.exe
qttask.exe quickdcf.exe quickset.exe
quickstart.exe qw.exe ramasst.exe
rapimgr.exe rcbdyctl.dll rcimlby.exe
rcman.exe rcp.exe rdsaddin.exe
reader_sl.exe realmon.exe realplay.exe
realsched.exe recguard.exe regedt32.exe
registryrepairpro.exe regsrvc.exe regwiz.exe
relog.exe reportingservicesservice.exe reschanger2005.exe
reservemodule.exe reset.exe rexec.exe
richvideo.exe roamsvc.exe rotatelogs.exe
roxmediadb.exe rrservice.exe rsh.exe
rsnotify.exe rsopprov.exe rtcshare.exe
rtlcpl.exe rtlwake.exe rtvscan.exe
rtwlan.exe rupsmon.exe rush 24-7 media center.exe
rvsezm.exe rwinsta.exe rxmon.exe
s24evmon.exe s3tray2.exe s7oiehsx.exe
sagent2.exe sagent4.exe saimfd.exe
save.exe scannerfinder.exe scanwrapper.exe
scheduler daemon.exe scncfg32.exe scriptle2.dll
scsiaccess.exe sd monitor.exe sdc.exe
sdclientmonitor.exe sdhelp.exe sdlss.exe
sdmcp.exe searchfilterhost.exe searchprotocolhost.exe
secedit.exe servicelayer.exe servic~1.exe
sethc.exe setiathome_5.15_windows_intelx86.exe setpoint.exe
setup_wm.exe sfagent.exe sgtray.exe
shellmon.exe shgina.dll shrpubw.exe
shstat.exe shsvcs.dll shwicon2k.exe
sispamfilterengine.exe sistray.exe skype.exe
slase.exe slee503.exe slserv.exe
sm1bg.exe smbinst.exe smc.exe
smoothview.exe smsystemanalyzer.exe smtray.exe
snagit32.exe sniffpol.dll snoopfreeui.exe
soundman.exe sparebackup.exe spbbcsvc.exe
speedfan.exe spgrmr.dll spiisupd.exe
sprintdslalert.exe spuvolumewatcher.exe spy-quake2.exe
sqldb20.dll sqlservr.exe sqlwriter.exe
srchctls.dll srclient.dll srdiag.exe
ssaad.exe sscevthdlr.exe starteak.exe
status_glance.exe statusclient.exe status~1.exe
stisvc.exe stsystra.exe stylexpservice.exe
suservice.exe svcguihlpr.exe svchqs.exe
symlcsvc.exe syncapp.exe syncor.exe
syntplpr.exe sysedit.exe syskey.exe
system.exe systemguardalerter.exe szserver.exe
tabuserw.exe tb2launch.exe tcmsetup.exe
teatimer.exe teks_service.exe telnet.exe
tfncky.exe tfnf5.exe tfswctrl.exe
tftp.exe themeui.dll tivobeacon.exe
tivoserver.exe tivotransfer.exe tkbellexe
tlntadmn.exe tlntsess.exe tmas_oemon.exe
tmasrv.exe tmntsrv.exe topsearch.exe
tosbthid.exe tosbtproc.exe toscdspd.exe
tosobex.exe totalcmd.exe tour.exe
tourstart.exe tp4ex.exe tphkmgr.exe
tpsbattm.exe tpshocks.exe tpsmain.exe
tpsrv.exe tptray.exe tracerpt.exe
tracert.exe trapmnnt.exe trayapplication.exe
trayap~1.exe trueassistant.exe tscupgrd.exe
tsdiscon.exe tskill.exe tsoc.dll
tvstray.exe tvtsched.exe twunk_16.exe
twunk_32.exe umxlu.exe unlodctr.exe
update.exe updaterui.exe updreg.exe
upgrader.exe uploadm.exe urlmap.exe
usbpnp.exe usbtip.exe usisrv.exe
usrprbda.exe utorrent.exe vcuserve.exe
ventc.exe ventcfg.exe verizonservicepoint.exe
verizo~1.exe versioncuecs2tray.exe viritsvc.exe
vm303_sti.exe vmnat.exe vmware-vmx.exe
volpanel.exe vpnd.exe vprosvc.exe
vptray.exe vrmonsvc.exe vsmon.exe
vssapi.dll w32tm.exe w3ssl.dll
w3wp.exe w9xpopen.exe wanmpsvc.exe
waol.exe wb32.exe wbcdws.exe
wbemcomn.dll wbemsvc.dll wbemtest.exe
wcescomm.exe wcesmgr.exe wdfmgr.exe
weatherdataclient.exe webcacheservice.exe webshotstray.exe
wextract.exe wfpscheduler.exe wiaacmgr.exe
winampa.exe winchat.exe wincolorreminder.exe
windowssearchfilter.exe winipsec.dll winmine.exe
winmsd.exe winshfhc.dll winterwalltoy.exe
winver.exe winword.exe winxpdisablezeroconfigation.exe
wkcalrem.exe wkdstore.exe wkswp.exe
wkufind.exe wlancfgg.exe wlkeeper.exe
wlservice.exe wltrysvc.exe wlusbcfg.exe
wmccds.exe wmccfg.exe wmic.exe
wmp54gsv1_1.exe wmp54gv4.exe wpnpinst.exe
write.exe wscsvc.dll wuauserv.dll
wusb54gsv2.exe wuser32.exe wwsecure.exe
wzcsapi.dll wzcsldr2.exe wzqkpick.exe
xcommsvr.exe xcopy.exe xfire.exe
xfr.exe yahoom~1.exe ybrwicon.exe
ycommon.exe ymsgr_tray.exe yop.exe
yztoolbar.exe zcfgsvc.exe zeteraservice.exe
zhotkey.exe zoominghook.exe {a93c9e60-29b6-49da-ba21-f70ac6aade20}.exe

Posted by 두장
2008. 12. 23. 14:01

JOINC.co.kr

쓰레드 풀 작성

Date: 2002/12/23

Topic: 시스템 프로그램

윤상배: dreamyun@yahoo.co.kr

 

 

쓰레드 풀은 연결/종료가 자주 일어나는 웹 서버와 같은 바쁜 서버에게 있어서 효율적인 클라이언트 연결 처리를 위해서 사용하는 프로그래밍 기법이다. 이번에는 쓰레드 풀을 이용한 애플리케이션 제작 방법에 대해서 알아보도록 하겠다.

 

1절. Thread Pooling

 

1.1절. Thread Pooling 이란

 

pool의 사전적인 뜻을 찾아보면 연못, 저수지, 수영장 풀 등 "무엇을 담아놓는"의 뜻을 가진다. 이대로 해석하자면 Thread Pooling이란 쓰레드를 담아 놓는 용기(메모리가 될 것이다)를 뜻하며, 프로그래밍 측면에서 해석하자면, "미리 쓰레드를 할당시켜 놓는 기법"을 뜻한다.

 

그렇다면 쓰레드를 미리 할당시켜 놓는 이유에 대해서 생각해보자, 지금까지 이 사이트에서 다루었던 쓰레드 프로그래밍 기법은 기본적으로 fork 방식과 매우 비슷하며, 쓰레드를 생성시켜야 될 필요가 있을 때 pthread_create(3) 등의 함수를 이용하여 새로운 작업 쓰레드를 생성시키는 방식을 사용했다. 보통 쓰레드 프로그래밍은 네트웍 프로그래밍시 주로 사용됨으로 accept(2)로 연결을 기다리다가 연결이 만들어지면 accept에서 넘어온 소켓 지시자를 인자로 하는 쓰레드를 생성했다.

 

이러한 방식 - 요청이 있을 때 쓰레드를 생성시키는 - 의 쓰레드 프로그래밍 기법은 대부분의 작업을 처리하기에 충분히 효율적이며, 빠르긴하지만 클라이언트로부터의 연결과 종료가 매우 바쁘게 일어나는 서버의 경우, 계속적으로 쓰레드를 생성하고 종료해야 하는 비용을 무시할 수 없게 된다. 쓰레드가 비록 fork()에 비해서 생성과 소멸시에 훨씬 적은 비용을 소모한다고는 하지만, 이건 어디까지나 상대적인 것으로 실상은 꽤 많은 시간과 비용을 소비하는 작업이다. 특히 Linux에서의 Pthread의 경우 clone(2)을 이용한 구현임으로 더욱더 많은 비용을 소비하게 된다.

 

Thread Pooling은 이러한 반복적인 쓰레드의 생성/소멸에 의한 비효율적인 측면을 없애고자 하는 목적으로 만들어진 프로그래밍 기법이다.

 

1.1.1절. Thread Pool의 구현방식

 

개념적으로 보자면 Thread Pool을 구성하는 건 매우 간단하다. 생성하고자 하는 크기만큼 ptread_create 함수를 돌리면 되기 때문이다.

 

하지만 이건 어디까지나 개념적인 것으로 대부분의 경우 각각의 쓰레드를 스케쥴링 해주어야 함으로, 때에 따라서는 구현을 위해서 매우 복잡한 프로그래밍 기법을 동원해야 할 때도 있다. 간단히 웹 서버를 Thread Pool로 구현한다고 가정을 해보자. - 보통 웹 서버는 HTTP의 특성상 연결/종료가 빈번하게 일어 남으로 쓰레드 풀을 사용할 경우 많은 이익을 얻을 수 있다 - 만약 100개의 Thread를 미리 생성시켰고, 각각의 Thread는 하나의 클라이언트 연결을 처리한다고 가정했을 때, main 쓰레드는 accept(2)를 통해서 클라이언트를 받아들였을 때, accept()로 만들어진 소켓 지정 번호를 미리 만들어진 100개의 쓰레드 중 "놀고" 있는 쓰레드에게 넘겨주어야 할 것이다. 그러기 위해서는 main 쓰레드에서 각각의 쓰레드 상태를 유지해서 적당한 쓰레드에게 파일 지정자를 넘겨줘야 할 것이다.

 

그나마 위의 경우는 하나의 쓰레드가 하나의 연결을 처리함으로 어렵지 않게 구현하겠지만, 만약 100개의 쓰레드가 있고, 거기에 각각의 쓰레드가 10개씩의 클라이언트 연결을 처리하도록 구성한다면, 거기에다가 적당한 로드 밸런싱 기능까지 포함시키고자 한다면, 구현이 꽤 복잡해 질 수도 있다.

 

위는 Thread Pool의 대략적인 구현 상태를 그림으로 나타낸 것이다. Thread Pool에 들어있는 각각의 쓰레드를 관리하기 위해서는 필수적으로 각각의 쓰레드의 상태를 가지고 있는 Schedul 자료 구조를 가지고 있어야 한다. 그래야만 MAIN THREAD에서 쓰레드 상태를 확인해서 적당한 쓰레드로 작업 분배가 가능할 것이기 때문이다. - 실제 Linux 커널도 각각의 task의 스케쥴링을 위해서 task 구조체를 유지한다. -

 

1.1.2절. 구현 프로세스

 

이제 구현 방식에 대한 밑그림이 나왔으니, 실제로 구현을 위한 프로세스를 만들어 보도록 하자.
프로세스는 슈도 코드로 구성을 하도록 하겠다. 네트웍 서버 작성을 기준으로 하겠다.

구현은 구현하는 프로그래머가 상황에 따라서 선택하기 나름이긴 하지만 보통은 위의 방법을 기본으로 해서, 약간의 변경을 가하는 정도가 될 것이다. 위의 슈도 코드를 보면 main 쓰레드에서 accept를 받으면 휴식 상태에 있는 쓰레드를 깨운다고 되어 있는데, 이때 깨우기 위해서는 쓰레드 조건 변수를 사용하면 될 것이다.

 

그렇다면 스케쥴 관련 자료 구조는 어떻게 구현하는게 쉬운 방법인지 생각해보도록 하자. 구현하는 방법은 프로그래머 맘이겠지만, 필자가 구현하고자 한다면 multimap을 이용해서 구현할 것이다. 이 자료 구조는 아마 다음과 같을 것이다.

멀티맵의 key는 쓰레드의 활성화 여부로 1 혹은 0이 된다. 그리고 value는 해당 쓰레드 정보가 될 것이다. 이렇게 멀티맵으로 만든 이유는 간단하다. 멀티맵은 정렬 연관 컨테이너임으로 key를 기준으로 자동적으로 정렬이 될 것이다. 만약 첫 번째 쓰레드가 처리 중(1)으로 변경되었다면 이 원소는 multimap의 가장 뒤로 정렬이 될 것이다. 그럼으로 우리는 클라이언트의 수가 총 연결 가능한 클라이언트 수(Thread Pool에 생성된 쓰레드 수)를 초과하지 않는 한 phinfo.begin()로 가져온 쓰레드는 휴식 상태(0)이라는 걸 믿을 수 있게 된다. 다시 말해서 복잡해서 쓰레드 상태가 0인지 1인지 처음부터 검사할 필요가 없다는 뜻이다.

 

사실 multimap을 쓴다면 굳이 "현재 연결된 클라이언트 수"를 유지하기 위해서 별도의 변수를 둘 필요가 없을 것이다. multimap에서 제공하는 count()를 이용해서 key가 "1" 인 요소의 수를 구하면 되기 때문이다. 만약 multimp의 begin() 값이 1이라면 MAX 클라이언트가 가득 찼다는 걸 의미할 것이다.

 

물론 multimap의 경우 기본적으로 key 값의 수정은 허용하지 않기 때문에 0을 1로 변경할 경우 실제로는 0을 가지는 요소를 삭제하고, 1을 가지는 새로운 요소를 삽입하는 방식을 취해야 할 것이다. 마찬가지로 클라이언트가 종료해서 1을 0으로 변경할 때에도 삭제/인서트를 해야할 것이다. Value(값)는 그대로 복사해서 삭제/인서트를 해야 한다.

 

이 방법이 번거롭다면, 그냥 배열을 쓰거나 혹은 다른 어떤 자료 구조를 쓰더라도 전혀 관계없기는 하다. 그건 자기의 기호에 맞게 선택해서 사용하면 될 문제이다.

 

1.2절. 예제

 

지금까지 Thread POOL의 구현 방법에 대해서 알아봤으니, 간단하게 구현해 보도록 하겠다. 이 코드는 지극히 기능 구현에만 신경쓴 코드이다. 에러 처리와 몇 군데 뮤텍스 잠금 처리는 각자의 재량에 맡기겠다.

 

예제 : pool_echo.cc

이 프로그램은 두 개의 인자를 받아들이며, 클라이언트의 입력을 되돌려 주는 일을 한다(echo 서버). 첫 번째 인자는 서비스 할 PORT 번호이고, 두 번째 인자는 쓰레드 생성 개수이다. 프로그램은 인자의 정보를 이용해서 PORT를 열고 클라이언트를 받아들인다. 클라이언트가 연결하면, Thread Pool에 남는 공간이 있는 지를 확인하고, 남는 공간이 있다면 클라이언트와 통신하게 된다.

 

단지 쓰레드를 미리 생성시키고 나서, 이것을 스케쥴링하기 위한 코드가 몇 줄 추가되었을 뿐 특별히 복잡한 코드는 아닐거라고 생각된다.

 

2절. 결론

 

이상 간단한 쓰레드 풀의 작성 요령에 대해서 알아보았다. 위에서 설명했듯이 쓰레드 풀이란 개념적인 요소에 가까움으로 어떻게 구현할 지는 상황에 따라서 매우 달라지게 되며, 위의 예제는 그러한 여러 가지 상황 중 가장 기본적인 상황을 예로 해서 만들어진 것이다. 어쨋든 위의 예제를 충분히 이해한다면 다른 상황으로의 응용 역시 별 어려움 없을 것이라고 생각된다.

 

쓰레드 풀은 보통 매우 효율적인 성능을 보장해주는 애플리케이션의 작성을 위해서 사용되어짐으로, 가능한 한 빠른 쓰레드 간 전환이 가능하도록 고민해서 코딩을 해야 한다. 위의 경우 쓰레드 간 전환을 위해서 multimap을 사용하고 있는데, accept가 들어왔을 경우 해당 클라이언트에 대한 쓰레드 할당은 매우 빠르다고 볼 수 있을 것이다. 그러나 종료할 경우에는 multimap의 첫 번째 원소부터 마지막 번 원소까지 search 해야 한다. 이것은 매우 비효율적임으로 개선할 여지가 있다. 가장 간단하게 생각할 수 있는 것은 multimap의 key 값이 1인 원소 내에서만 검색하는 것이다. 우리는 쓰레드 풀의 크기와 현재 연결된 클라이언트의 수를 알고 있음으로, multimap의 몇 번째 요소부터 key 값이 1인지를 계산해 낼수 있기 때문이다. 이렇게 할 경우 약간의 시간 단축 효과를 기대할 수 있을 것이다.

이 시간 단축 효과는 연결된 클라이언트의 수가 전체 POOL 사이즈에 비례해서 작을 수록 커질 것이다.

 

나머지 방법은 각자 고민을 해보기 바란다. 아마 전혀 다른 자료 구조를 사용할 수도 있을 것이다.

 

This article comes from Joinc

http://www.joinc.co.kr

Posted by 두장
2008. 12. 23. 14:00

JOINC.co.kr

쓰레드와 시그널

Date: 2003/10/27

Topic: 시스템 프로그램

윤상배: dreamyun@yahoo.co.kr

 

 

그렇잖아도 애매 모호한 쓰레드에 헷갈리는 시그널을 사용하고자 하면 여러 가지 애로 사항이 꽃피게 된다. 각 쓰레드별로 시그널이 전달되거나 전달되지 않도록 설정할 수 있어야 하기 때문인데, 개념적으로는 간단하지만 막상 적용하려면 그 과정이 머리에 그려지지 않기 때문이다.

 

1절. 쓰레드에서의 시그널 사용

 

쓰레드에서의 시그널 사용은 시그널에 대한 기본적인 이해만 가지고 있다면 약간의 응용으로 충분히 해결할 수 있는 문제이긴 하지만 범 유닉스적으로 응용하고자 한다면(특히 리눅스가 포함된다면) 운영체제 간 신경써줘야 할 문제가 있다. 이번 장에서는 쓰레드에서의 시그널을 이용하는 방법과 운영체제가 다름으로 인해 발생할 수 있는 문제들에 대해서 알아보도록 하겠다.

 

1.1절. 시그널을 특정 쓰레드로 보내기

 

쓰레드에서 시그널은 서로 공유된다는 걸 알고 있을 것이다. 문제는 공유된다는 점인데 만약 프로세스에 시그널을 보낼 경우 해당 프로세스에서 생성된 모든 쓰레드에 시그널이 전달이 되게 된다. 이것은 우리가 원하는게 아니다.

 

우리가 원하는 것은 특정 쓰레드에서만 시그널을 받도록 하는 것이다. 이러한 작업을 위해서 우리는 시그널 마스크를 사용한다. 시그널 마스크는 말 그대로 특정 시그널에 대해서 마스크를 씌우는 것으로 해당 쓰레드에서 특정 시그널에 대해서 마스크를 씌우면 마스킹된 시그널은 해당 쓰레드로 전달되지 않는다. 이 시그널을 받기를 원하는 쓰레드에서는 이 시그널에 대한 마스크를 제거시킨다. 그러면 블록되어 있는 시그널은 마스크가 제거된 쓰레드로 전달 될 것이다. 일종의 필터기다.

 

그림 1. 시그널 마스크의 작동원리

 

위의 그림은 시그널 마스크의 작동 원리를 보여준다. 메인 쓰레드에서는 SIGINT와 SIGUSR2에 대해서 시그널 마스크를 설치한다. 그리고 쓰레드 1에서는 SIGINT에 대한 마스크를 제거하고, 쓰레드 2에서는 SIGUSR2에 대한 마스크를 제거한다. 이렇게 되면 SIGINT가 메인 쓰레드에 도착했을 때 마스크 때문에 메인 쓰레드에는 도착하지 못하고 쓰레드 1로 전달될 것이다. SIGUSR2가 도착했을 경우 메인 쓰레드와 쓰레드 1에서는 마스크 때문에 전달되지 못하고 쓰레드 2로 시그널이 전달된다. 1.1.1절에서는 위의 작동 원리대로 구현된 예제 코드를 다루고 있다.

 

이러한 쓰레드별 시그널 마스킹을 위해서 pthread는 pthread_sigmask(3)라는 함수를 제공한다.

#include <pthread.h>
#include <signal.h>

 

int pthread_sigmask(int how, const sigset_t *newmask, sigset_t *oldmask);

 

이 함수는 현재 쓰레드에 시그널 newmask와 how를 이용해서 시그널 마스크를 만든다. how는 SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK 중 하나를 선택할 수 있다. SIG_BLOCK은 현재 설정된 시그널 마스크에 newmask를 추가하며 SIG_UNBLOCK은 현재 설정된 시그널 마스크에서 newmask를 제거하고 SIG_SETMASK는 newmask로 현재 시그널 마스크를 설정한다.

 

1.1.1절. 간단 예제

 

그럼 pthread_mask(3)를 이용한 간단한 예제를 만들어 보도록 하겠다. 코드는 여러분이 시그널과 쓰레드에 관한 최소한의 지식을 가지고 있다는 가정하에 작성될 것이며, 설명은 주석으로 대신하도록 하겠다.

 

예제 : th_signal.c

위 프로그램을 실행시킨 뒤 kill 명령으로 SIGINT와 SIGUSR2 시그널을 PID로 보내보면 해당 쓰레드로 시그널이 전달되고 시그널 핸들러가 실행되는 걸 확인할 수 있을 것이다.

 

1.2절. 쓰레드 간 시그널 전송

 

외부의 다른 프로세스에서 시그널을 발생시키는 것 외에도 같은 프로세스에서 작동하는 쓰레드 간에 시그널을 전송해야 하는 경우도 생길 것이다.

 

이러한 쓰레드 간 시그널 전송은 여러 가지 목적으로 사용할 수 있다. 일정 시간마다 특정 쓰레드에 시그널을 전송하므로써 쓰레드를 깨워서 코드를 실행시키게 한다거나 네트워크 애플리케이션에서 write, read에 타임아웃을 검사하는 용도로도 사용 가능하다.

 

네트워크 애플리케이션에서 스레드 간 시그널 전달을 통해 타임아웃을 검사한다는 생각은 좀 생소할 수도 있을 것 같다. 보통은 select나 alarm을 사용할 건데, 멀티 쓰레드 프로그램의 경우 alarm(2)의 사용은 사실상 어렵다고 볼 수 있다. 여러 개의 쓰레드에서 alarm(2)를 사용할 경우 단지 하나의 alarm(마지막 alarm 값)만이 등록되어서 사용할 수 있기 때문이다. 그렇다면 select를 사용해야 할 건데, select 대신에 전용의 시그널을 발생하는 쓰레드를 이용해서 사용할 수 있다.

 

read(2)를 예로 들어서 설명해 보자 read(2)를 하기 전에 특정 (전역) 값을 0으로 세팅하고 read를 수행한 후 1로 값을 변경하도록 한다. 그리고 타임아웃 체크를 위한 쓰레드에서는 타임아웃 시간 간격으로(sleep(2)를 이용하면 된다) 이 값을 검사한다. 만약 값이 0으로 세팅되어 있는 걸 확인 했는데, 다음 시간이 돌아온 뒤에도 이 값이 0이라면 read 영역에서 타임아웃이 발생했다고 판단 할 수 있을 것이다. 그러면 타임아웃이 발생한 쓰레드에 시그널을 전송하도록 한다. 쓰레드에 시그널을 전송하면 인터럽트가 발생하고 read에서 빠져나오게 된다.

if (read(..) < 0)
{
        // 만약 인터럽트로 인하여 빠져나온 거라면..
        if (errno == EINTR)
        {
                ...
        }
}

시그널 발생시 인터럽트가 전달되게 하려면 약간의 부가적인 작업이 필요한데, 이것은 소켓 타임아웃을 참고하기 바란다.

 

1.2.1절. 다른 쓰레드로 시그널 전송

 

이러한 쓰레드 간 시그널 전송을 위해서 pthread_kill(3)가 제공된다.

#include <pthread.h>
#include <signal.h>


int pthread_kill(pthread_t thread, int signo);

 

첫 번째 인자 thread는 시그널을 전달받을 쓰레드의 식별자이고 signo은 전달하고자 하는 시그널 번호이다. 보내는 쪽은 pthread_kill(3)을 이용해서 비교적 간단하게 구현이 가능하다.

 

1.2.2절. 시그널받기

 

시그널을 받는 쓰레드의 경우 동기와 비동기 두 가지 방식을 통해서 받을 수 있다. 동기 방식으로 받을 경우는 sigwait(3) 함수를 이용해서 시그널이 전달될 때까지 블록되면서 기다린다.

#include <pthread.h>
#include <signal.h>

 

int sigwait(const sigset_t *set, int *sig);

 

이 함수는 시그널 set에 설정된 시그널 중 하나가 전달될 때까지 호출된 영역에서 대기한다. 시그널을 받았다면 리턴되고 전달 받은 시그널 번호는 sig를 통해서 넘어온다. 시그널을 기다린다는 특징을 이용해서 쓰레드 간 동기화를 위한 목적으로도 유용하게 사용할 수 있을 것이다.

 

두 번째는 비동기적인 방식으로 코드 실행 중에 시그널이 전달되면 인터럽트가 걸리고 시그널 핸들러가 수행되는 방식이다. 일반적인 시그널 사용 방식과 동일하다.

 

1.2.3절. 예제

 

sigwait(3)를 통해서 동기적으로 기다리는 것은 구현이 간단하므로 따로 다루지 않고 시그널 핸들러를 등록해서 비동기적으로 시그널을 기다리는 코드를 구현해 보도록 하겠다. 1.1.1절의 코드를 약간 수정했다.

 

예제 : thtoth_sig.c

위의 코드의 경우 시그널을 받을 쓰레드를 명시해줄 수 있으므로 시그널 마스크 등을 설치할 필요가 없다. SIGINT가 원하는 쓰레드로 정확하게 전달되는 걸 확인할 수 있을 것이다.

 

1.3절. 운영체제별 차이점

 

쓰레드의 작동 방식은 운영체제별로 많은 차이를 보여줄 수 있으며, 차이점에 유의해서 프로그램을 작성해야 한다. 여기에서는 솔라리스와 리눅스를 비교해서 설명하도록 하겠다.

 

지금까지의 쓰레드와 시그널에 대해서 다루었던 내용은 솔라리스와 같이 하나의 프로세스에서 다중의 쓰레드를 관리하는 경우를 기준으로 했다. 그러나 리눅스의 경우 clone(2)를 통한 다중 프로세스 형태로 쓰레드가 생성된다. 때문에 ps를 이용해서 확인할 경우 다중 쓰레드 프로세스임에도 불구하고 각각의 PID를 가지는 프로세스로 쓰레드가 생성되는걸 확인 할 수 있다.

 

이런 특징 때문에 리눅스 시스템에서 외부 프로세스에서 시그널을 특정 쓰레드로 보낼 경우에는 메인 쓰레드가 아닌 해당 쓰레드의 PID를 명시해 주어야 한다.

 

This article comes from Joinc

http://www.joinc.co.kr


Posted by 두장
이전버튼 1 2 3 4 5 이전버튼