<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet href="http://rss.egloos.com/style/blog.xsl" type="text/xsl" media="screen"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
	<title>The truth will set you free</title>
	<link>http://sunyzero.egloos.com</link>
	<description>There are three kinds of lies: lies, damned lies, and statistics - Benjamin Disraeli (British Prime Minister)</description>
	<language>ko</language>
	<pubDate>Thu, 05 Nov 2009 15:33:44 GMT</pubDate>
	<generator>Egloos</generator>
	<image>
		<title>The truth will set you free</title>
		<url>http://pds8.egloos.com/logo/200806/09/55/b0026155.jpg</url>
		<link>http://sunyzero.egloos.com</link>
		<width>80</width>
		<height>53</height>
		<description>There are three kinds of lies: lies, damned lies, and statistics - Benjamin Disraeli (British Prime Minister)</description>
	</image>
  	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #4 ]]> </title>
		<link>http://sunyzero.egloos.com/4258873</link>
		<guid>http://sunyzero.egloos.com/4258873</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #4</span></strong><br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785">http://sunyzero.egloos.com/4227785</a><br />
- HOWTO #2 : <a href="http://sunyzero.egloos.com/4229235">http://sunyzero.egloos.com/4229235</a><br />
- HOWTO #3 : <a href="http://sunyzero.egloos.com/4234766">http://sunyzero.egloos.com/4234766</a><br />
<hr><br />
<br />
<strong>9. critical &amp; atomic construct</strong><br />
<br />
critical construct와 atomic construct는 공유(shared) 영역을 보호하는 lock의 일종이다. 따라서 critical과 atomic을 사용한 영역은 쓰레드 중에 단 하나의 쓰레드만 진입하고 나머지는 대기하게 된다. 즉 직렬실행구간이 된다. 따라서 역으로 말하면 critical과 atomic을 남발하는 프로그래밍을 하면 성능이 확 떨어진다는 소리이다. 그럼에도 불구하고 꼭 lock이 필요한 기능이므로 잘 익혀두어야만 한다.<br />
<br />
우선 둘의 차이부터 이해하고 넘어가자. 만일 두 단어만 보고도 감이 오는 사람은 학생 때 운영체제 수업에서 critical section이나 atomic operation을 배운 사람일 것이다.<br />
<br />
• critical : 범용적인 lock<br />
• atomic : critical보다 가볍고 빠른 lock (critical의 special한 케이스)<br />
<br />
critical은 노멀한 lock이지만 atomic은 매우 가벼운 용도에만 사용한다. 가볍다는 것은 보통 1개의 스칼라 변수를 업데이트 하는 정도를 의미한다. 만일 변수 업데이트를 넘어서 계산이나 캐스팅, 복잡한 데이터 핸들링을 하는 경우라면 critical을 사용하도록 한다.<br />
<span style="COLOR: #3366ff">[참고로 정수형 변수 1개의 값을 변경하는 정도라면 atomic 없이 프로그래밍을 하는 것도 추천한다. atomic을 쓰지 않으면 수치상의 오류가 생기는 경우도 있겠지만 일반적으로는 10ppm(part per million) 이하인 경우이므로 무시할 수 있는 오차 일 수 있다. 왜냐하면 atomic의 가벼운 lock도 멀티 쓰레드에서는 성능을 많이 떨어뜨리기 때문이다.]</span><br />
<span style="color:#009900;">[보충 설명: 일반적으로 공유된 데이터에 복수개의 쓰레드가 접근할 경우에는 무조건 lock으로 보호해야만 한다. 모든 교과서나 매뉴얼에는 이렇게 나와있다. 그러나 프로그래머가 어느 정도 멀티 쓰레드에 대한 이해가 깊어지면 교과서나 매뉴얼을 응용할 수 있어야만 한다. 필자가 앞에서 정수형 변수 1개의 값을 업데이트하는 경우에는 lock을 쓰지 않는 것도 좋다고 했는데 이는 감히 교과서에는 쓸 수 없는 내용이다. 하지만 공유된 변수가 약간의 오차가 생겨도 괜찮은 경우, 즉 근접해나 근사치를 계산하는 시뮬레이션이나 휴리스틱 코드라면 atomic을 쓰지 않을 경우 더 빠르게 작동할 수 있다. 그리고 빠른 실행이 가능해졌기 때문에 시행횟수를 더 늘림으로서 오차를 보정할 수 있게된다. 그러므로 오차 보정이 가능한 수치계산이나 오차보다 속도가 더 중요한 문제라면 atomic을 생략하는 것도 고려하라는 뜻이 담겨있는 것이다. 그러나 주의할 점은 꼭 프로그래밍을 할 때 atomic을 선택적으로 적용할 수 있도록 코딩해두고 atomic을 사용한 버전과 사용하지 않은 버전의 차이를 꼭 비교해봐야 한다. 만일 atomic을 사용하지 않은 버전이 메리트가 없다면 반드시 lock을 사용한 버전을 택하는 것이 좋다.</span><br />
<br />
<br />
<strong>9.1 critical construct</strong><br />
<pre>#pragma omp critical [(lock_name)]</pre><br />
critical에서 락 이름(lock_name)을 생략하는 경우는 동일한 lock을 사용하는 것으로 간주되어 critical 지시어가 여러 번 나오더라도 동일하게 보호되는 구간이 된다. 자세한 것은 예제를 보면서 익히도록 하자. 예제로 아래와 같은 pseudo code가 있다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
    chk_process();<br />
    process_b();<br />
    chk_process();<br />
    process_c();<br />
    chk_summary();<br />
}</pre>위의 예제에서 chk_process() 함수가 lock으로 보호되어야 한다면 critical 지시어를 사용하여 아래와 같이 할 수 있다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
#pragma omp critical<br />
    chk_process();<br />
    process_b();<br />
#pragma omp critical<br />
    chk_process();<br />
    process_c();<br />
    chk_summary();<br />
}</pre>critical construct을 2군데에서 사용했지만 lock_name을 지정하지 않았기 때문에 같은 lock으로 인식하여 보호된다. [위와 같이 프로그래밍하지 않고 chk_process()함수 내부에 critical을 선언하여도 결과는 같다.]<br />
<br />
그렇다면 이번에는 chk_summary()에 critical을 추가해보겠다. 하지만 chk_process()와는 상관없이 다른 락으로 보호하도록 하겠다. 이 경우에는 필수적으로 lock_name을 사용해야 한다.<br />
<pre>#pragma omp parallel for<br />
for (i=0; i&lt;MAX_ITERATION; i++) {<br />
    process_a();<br />
#pragma omp critical (lock1)<br />
    chk_process();<br />
    process_b();<br />
#pragma omp critical (lock1)<br />
    chk_process();<br />
    process_c();<br />
#pragma omp critical (lock2)<br />
    chk_summary();<br />
}</pre>이제 critical의 사용방법에 대해서는 어느정도 감이 왔을 것이다. 하지만 거듭 강조하지만 왠만하면 lock은 최소한으로 사용하는 것이 좋다. 멀티 쓰레드 프로그래밍에서 lock을 통한 직렬구간을 많이 만들면 프로그래밍은 쉽지만 성능은 바닥을 치기 때문에 멀티 쓰레드 프로그래밍을하는 의미가 사라지게 된다.<br />
<br />
<br />
<br />
<strong>9.1 atomic construct</strong><br />
<pre>#pragma omp atomic</pre>atomic은 스칼라 변수 한개를 업데이트 한다고 했다. 너무 간단하기 때문에 간단한 예제 하나로 끝내겠다.<br />
<pre>#pragma omp parallel<br />
...생략...;<br />
#pragma omp atomic<br />
    n_count++;<br />
}</pre>atomic에서 쓸 수 있는 표현식은 ++나 --같은 unary operation이나 "변수=값"정도의 간단한 대입식 정도이다.<br />
<br />
<br />
<br />
<hr><br />
<strong>10. OpenMP API</strong><br />
앞에서 omp_get_thread_num() 같은 openmp의 함수들을 보았다. 이번에는 주로 사용되는 함수들을 정리해서 보도록 하겠다.<br />
<br />
* 쓰레드 풀에 관련된 함수들<br />
<span style="COLOR: #993399">void omp_set_num_threads(int num_threads); 병렬 구간에서 생성할 쓰레드 개수를 설정<br />
int omp_get_num_threads(void); 병렬 구간에서 생성할 쓰레드 개수를 리턴<br />
int omp_get_max_threads(void); 쓰레드 최대값 리턴<br />
int omp_get_thread_num(void); 현재 쓰레드 번호 리턴<br />
int omp_get_num_procs(void); 물리적 프로세서의 개수 리턴<br />
<br />
int omp_in_parallel(void); 병렬 구간일 경우 true(0이 아닌값), 아니면 false(0)을 리턴<br />
<br />
void omp_set_dynamic(int dynamic_threads); 생성할 쓰레드 개수를 동적으로 조정할 지 여부 (dynamic_threads가 1이면 true, 0이면 false)<br />
int omp_get_dynamic(void); 위의 omp_set_dynamic의 설정을 리턴</span><br />
<br />
아래 예제처럼 omp_set_dynamic이 설정되면 사용량에 따라서 쓰레드의 개수가 10개를 넘지 않는 범위에서 적당하게 조정된다.<pre>#include &lt;omp.h&gt;<br />
int main()<br />
{<br />
    omp_set_dynamic(1);<br />
#pragma omp parallel num_threads(10)<br />
{<br />
    /* do work here */<br />
}<br />
    return 0;<br />
}</pre>예제 출처: OpenMP 2.5 Specification<br />
<br />
<hr><br />
<strong>* OpenMP에서 제공하는 lock 기능 API : 정교한 모델을 만들때 유용하므로 꼭 읽어두자.</strong><br />
<span style="COLOR: #3366ff"><br />
void omp_set_nested(int nested); nested가 1이면 병렬구간의 중첩을 허용, 0이면 허용하지 않음<br />
int omp_get_nested(void); 위의 nested 설정을 리턴<br />
<br />
void omp_init_lock(omp_lock_t *lock); OMP의 lock을 초기화<br />
void omp_init_nest_lock(omp_nest_lock_t *lock); 중첩 lock버전의 위와 같은 기능의 함수 (=재귀잠금이 가능한 lock)<br />
<br />
void omp_destroy_lock(omp_lock_t *lock); OMP의 lock을 제거<br />
void omp_destroy_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
void omp_set_lock(omp_lock_t *lock); OMP의 lock을 잠금<br />
void omp_set_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
void omp_unset_lock(omp_lock_t *lock); OMP의 lock을 잠금 해제<br />
void omp_unset_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수<br />
<br />
int omp_test_lock(omp_lock_t *lock); 잠금 여부를 테스트하여 가능한 상태면 잠그고 다른 쓰레드에 의해 잠긴 상태면 false를 리턴 (=nonblocking 버전의 함수임)<br />
int omp_test_nest_lock(omp_nest_lock_t *lock); nested lock버전의 위와 같은 기능의 함수</span><br />
<hr><br />
double omp_get_wtime(void); 병렬처리 구간에서의 수행 시간(단위: 초)<br />
double omp_get_wtick(void); omp_get_wtime()에서 제공하는 시간 정밀도 (최소 단위 시간)<br />
<pre>double start;<br />
double end;<br />
start = omp_get_wtime();<br />
    ... work to be timed ...<br />
end = omp_get_wtime();<br />
printf("Work took %f seconds\n", end - start);</pre>예제 출처: OpenMP 2.5 Specification<br />
<br />
OpenMP 2.5에서 제공하는 모든 API를 다루었다. 특히 lock관련 함수는 중요하기 때문에 잘 알아두면 도움이 된다.<br />
<br />
<br />
<br />
<hr><br />
<strong>11. OpenMP에서 제공하는 환경변수 및 전처리기</strong><br />
<br />
OpenMP에서는 스케줄링, 생성할 쓰레드 개수, 동적 조정 여부, 병렬구간에서 쓰레드의 중첩 허용을 runtime시에 결정할 수 있도록 환경변수를 설정할 수 있다.<br />
<br />
• OMP_SCHEDULE : 스케줄링 타입과 chunk 크기를 설정<br />
• OMP_NUM_THREADS : 병렬 구간에서 생성할 쓰레드 개수<br />
• OMP_DYNAMIC : 쓰레드 개수의 동적 조정 여부를 설정<br />
• OMP_NESTED : 쓰레드 중첩의 허용 여부를 설정<br />
<br />
사용예 : bash (or POSIX shell)의 명령<pre>export OMP_SCHEDULE="dynamic"</pre><pre>export OMP_SCHEDULE "guided,4"</pre><pre>export OMP_NUM_THREADS 16</pre><pre>export OMP_DYNAMIC true</pre><pre>export OMP_NESTED false</pre><br />
<br />
* OMP 조건부 컴파일을 위한 전처리기<br />
<pre><br />
#include &lt;stdio.h&gt;<br />
int main()<br />
{<br />
# ifdef _OPENMP<br />
    printf("Compiled by an OpenMP-compliant implementation.\n");<br />
# endif<br />
    return 0;<br />
}</pre><br />
<br />
<br />
<br />
<hr><br />
<strong>12. OpenMP vs pthread (POSIX thread)</strong><br />
<br />
OpenMP로 작성한 프로그램을 그대로 pthread 버전으로 바꾸면서 둘의 차이를 보도록 하겠다. <br />
예제로 볼 것은 앞에서 numerical integration으로 pi를 계산한 프로그램을 두가지 버전으로 작성해서 비교하도록 하겠다.<br />
<br />
우선 HOWTO #1에서 봤던 OpenMP 버전을 다시 보도록 하겠다.<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration.c">pi_numerical_integration.c</a><pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for private(x) reduction(+:sum)<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>OpenMP버전은 이미 설명했었기 때문에 이를 바로 Pthread 버전으로 바꾸도록 하겠다.<br />
<br />
pthread 버전 예제 파일 : <a href="http://pds16.egloos.com/pds/200910/19/55/pi_integration_pth.c">pi_integration_pth.c</a><br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;pthread.h&gt;<br />
<br />
static int num_steps=1000000000;<br />
double pi, step;<br />
struct start_arg {<br />
    int     i_start;<br />
    int     i_end;<br />
    double  sum;<br />
} start_arg[2];<br />
<br />
void *start_func(void *arg)<br />
{<br />
    int     i;<br />
    double  x, sum = 0.0;<br />
    struct start_arg *p_arg = (struct start_arg *) arg;<br />
<br />
    for (i=p_arg-&gt;i_start; i&lt;p_arg-&gt;i_end; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    p_arg-&gt;sum = sum;<br />
    return NULL;<br />
}<br />
<br />
int main()<br />
{<br />
    int     i;<br />
    double  sum;<br />
    pthread_t   pt_id[2];<br />
<br />
    step = 1.0/(double) num_steps;<br />
    start_arg[0].i_start = 0;<br />
    start_arg[0].i_end   = num_steps&gt;&gt;1;<br />
    start_arg[1].i_start = start_arg[0].i_end + 1;<br />
    start_arg[1].i_end   = num_steps;<br />
<br />
    printf("%d~%d, %d~%d\n",<br />
            start_arg[0].i_start, start_arg[0].i_end ,<br />
            start_arg[1].i_start, start_arg[1].i_end );<br />
<br />
    for (i=2; i--; ) { /* create pthread */<br />
        if (pthread_create(&amp;pt_id[i], NULL, start_func, &amp;start_arg[i])){<br />
            perror("pthread_create");<br />
            return 1;<br />
        }<br />
    }<br />
    for (i=2; i--; ) { /* join pthread : explicit barrier */<br />
        if (pthread_join(pt_id[i], NULL)){<br />
            perror("pthread_join");<br />
            return 1;<br />
        }<br />
    }<br />
    sum = start_arg[0].sum + start_arg[1].sum;<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>pthread 버전으로 바꾸면 openmp버전보다 코딩량이 훨씬 늘어난다.<br />
<br />
POSIX thread를 사용하면 OpenMP에서 없는 explicit barrier를 넣어주어야 하므로 pthread_join()함수를 사용했으며, OpenMP에서 간단하게 reduction을 했던 것이 POSIX thread에서는 개별적인 start_arg라는 구조체를 통해서 확인했다. 이로 인해서 코딩량과 자료구조까지 신경써야 하는 불편함이 생긴다.<br />
<br />
<strong>그러나 OpenMP가 POSIX thread보다 우월하다고 말하는 것은 아니다. 문제가 정교함을 요구하는 경우에는 오히려 OpenMP가 적합하지 않은 경우도 많기 때문이다. 실제로 조건변수를 이용한다든지 쓰레드 키를 이용한 라이브러리등을 사용하는 경우라면 POSIX thread외에는 대안이 없는 경우가 많기 때문이다. 이 글은 OpenMP를 소개하는 글이므로 OpenMP에 대해 좋은 말만 늘어놓고 있지만 필자의 말을 비판없이 받아들여서 OpenMP가 최고라는 편협한 사고에 휩싸이지는 않았으면 좋겠다.</strong><br />
<br />
잡설이 길었는데 마지막으로 두가지 버전의 실행 결과를 확인해보자. 첫번째 실행한 파일인 pi_integration이 OpenMP버전이고, 두번째의 pi_integration_pth가 Pthread버전이다.<br />
<pre>$ time ./pi_integration<br />
PI = 3.14159265 (sum = 3141592653.59036255)<br />
<br />
real    0m3.910s<br />
user    0m7.484s<br />
sys    0m0.018s</pre><br />
<pre>$ time ./pi_integration_pth<br />
0~500000000, 500000001~1000000000<br />
PI = 3.14159265 (sum = 3141592650.38979244)<br />
<br />
real    0m4.071s<br />
user    0m7.399s<br />
sys    0m0.022s</pre>결과를 보면 실행결과나 성능에 큰 차이가 없어 보인다. 하지만 몇몇 컴파일러에서는 특정 버전이 더 느린 경우도 있다.(이는 해당 플랫폼의 특징에서 기인한다. 표준안에는 성능에 대한 제약은 없기 때문에 몇몇 플랫폼은 OpenMP 구현시 덜 최적화 되어있는 경우도 있다.)<br />
<br />
<br />
<br />
<hr><br />
<strong>13. 마치면서.</strong><br />
<br />
처음 시작하면서 멀티 쓰레드 프로그래밍의 필요성에 대해서 언급했었다. CPU의 개별 코어의 주파수(frequency)는 거의 한계치에 와 있기 때문에 개별 코어의 속도는 파이프라인의 증설이나 몇가지 기술로 약간의 진보밖에 이룰 수 없게 되었다. 따라서 멀티코어를 적극적으로 사용하는 기술이 뒷받침되지 않고는 프로그램의 성능을 높일 수 있는 방법은 거의 없다.(일각에서 언급되는 그래픽카드의 코어를 병렬로 이용하는 GPGPU도 같은 맥락이다.) 하지만 멀티 쓰레드 프로그래밍은 다음과 같은 난제가 있다.<br />
<br />
<ol><li>멀티 쓰레드 프로그램의 문제점 : 디버깅이 힘들다. (재현성이 나쁘기 때문)</li><li>아키텍처와 메모리 모델에 대한 이해가 필요하다. (본인도 부족하다. 그래서 실수를 할 때가 있다.)</li></ol><br />
<br />
첫째로 문제인 디버깅에서 언급되는 재현성은 쓰레드 프로그래밍에서 큰 난제이다. 디버깅을 위해서는 같은 조건하에서 시행하였을때 동일한 문제가 재현되어야만 버그를 찾아낼 수 있다. 그러나 멀티 쓰레드 프로그램은 각각의 쓰레드들이 같은 순서로 실행된다는 보장이 없기 때문에 버그가 발생한 순서조합이 디버깅때도 항상 발생하지 않는다. 즉 문제를 찾아내려고 할때는 오히려 제대로 작동하는 경우도 많다는 것이다.<br />
<br />
따라서 멀티 쓰레드 프로그램을 대형 모델에 적용할 때는 투명성을 높여서 각각의 데이터나 태스크의 흐름을 모니터링할 수 있도록 만들어야 한다. 이를 위해서 여러 방법론이 나와있지만 스페셜한 케이스에는 딱 들어맞는 것은 없다. 그러므로 반복적인 프로그래밍 훈련만이 투명성을 높이고 디버깅이 쉬운 개발철학을 만들 수 있게 해준다.<br />
<br />
둘째로 우리가 종종 사용하는 x86 인텔 호환 플랫폼은 PC환경에서 가장 많이 쓰이지만 산업체에서는 IA64, Sparc, MIPS, PPC(PowerPC) 등등의 CPU에 여러가지 OS조합의 플랫폼이 사용된다. 그러나 같은 프로그래밍을 짜도 표준안에서 언급한 semantic을 만족하면서 미묘한 차이를 보이는 경우가 많다. 특히 기능은 제대로 작동하는데 성능은 이상하게 차이나는 경우가 발생할 수 있다. 이를 해결하기 위해서는 각각의 플랫폼에 대한 이해와 하드웨어 지식이 필요한데, 한사람의 프로그래머가 모든 플랫폼에 정통할 수는 없기 때문에 적극적으로 다른 플랫폼의 전문가와 소통할 수 있는 채널이 있어야만 한다. (<strong>더 중요한 점은 다른 플랫폼의 전문가와 제대로 소통할 수 있도록 질문을 잘 하는 방법을 익히는 것이다. 간혹 질문을 잘 못하고 횡설수설하면 답변을 해주고 싶어도 못한다. 필자도 좀 횡설수설하는 스타일이라 고치려고 하는데 참 어렵다.</strong>)<br />
<br />
<br />
하지만 이런 어려움은 점차 해소될 전망이다. 앞으로 나오는 많은 기술들과 소프트웨어 방법론들은 프로그래머가 복잡한 이론이나 방법론을 몰라도 멀티 쓰레드 프로그래밍을 쉽게 할 수 있도록 돕고 있다. 실제로 10년전과 지금은 하늘과 땅 차이로 멀티 쓰레드 프로그래밍이 쉬워졌다.(그럼에도 불구하고 아직도 멀티 쓰레드 프로그래밍은 어려운 분야이지만...)<br />
<br />
비슷한 예를 하나 들자면 15년전쯤에 필자가 프로그래밍을 처음 접하던 시절에는 그래픽 처리를 위해 어셈블리어를 배우는 것이 유행했던 시절이 있었다. 속도와 최적화 문제에서 다른 대안이 없었기 때문이었다. 그러나 지금은 어셈블리어를 몰라도 왠만한 그래픽 최적화를 하는데 무리가 없다.(아주 가끔 어셈블러에 의지해야 하는 스페셜 케이스가 있긴하다.) 이는 시간이 지나면 좀 더 편리한 프로그래밍 환경이 만들어진다는 것을 의미한다.<br />
<br />
그렇다면 이쯤에서 독자들은 필자에게 반문할 것이다. "<strong>멀티 쓰레드 프로그래밍이 쉬워지기를 기다리라는 말입니까?</strong>" 이에 대한 필자의 대답은 "절반은 그렇다"이다. 왜 절반만 Yes를 하는지는 그 쉬워지는 시점 때문이다. 많은 선도자들은 예측하기를 멀티 쓰레드 프로그래밍의 하드웨어적, 소프트웨어적 난제들이 해결되어 많은 부분에서 변화가 생기려면 적어도 5~10년후가 되어야 할 것이라고 한다. 그렇다면 본인이 앞으로 5~10년을 기다릴 수 있는 형편인지 아니면 당장 써야 하는지 결정해야한다. 한창 공부하는 학생이라면 더 기다리다가 공부해도 되겠지만 field에서 일하는 산업역군이라면 당장 공부하라고 말해주겠다.<br />
<br />
<br />
<span style="COLOR: #ff0000">그럼 여기까지 수고하셨습니다.~ 그리고 본문에서는 반말로 작성해서 죄송합니다. 글자수를 줄이기 위한 고육지책이었습니다.<br />
<br />
그리고 OpenMP 3.0의 새로운 기능들은 조만간 포스팅하도록 하겠습니다.</span><br />
<hr><br />
* History<br />
2009.10.20 초안 작성<br />
2009.10.21 atomic 보충 설명, 예제 파일 등록<br/><br/>tag : <a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/프로그래밍" rel="tag">프로그래밍</a>,&nbsp;<a href="/tag/C언어" rel="tag">C언어</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>openmp</category>
		<category>thread</category>
		<category>쓰레드</category>
		<category>프로그래밍</category>
		<category>C언어</category>

		<comments>http://sunyzero.egloos.com/4258873#comments</comments>
		<pubDate>Tue, 20 Oct 2009 03:56:01 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ RealForce 87U 기계식 키보드 ]]> </title>
		<link>http://sunyzero.egloos.com/4255230</link>
		<guid>http://sunyzero.egloos.com/4255230</guid>
		<description>
			<![CDATA[ 
  얼마전 조카가 키보드위에다 포카리 스웨트를 쏟는 바람에 키보드가 약간 뻑뻑해졌다.<br />
알콜로 세척하고 윤활제를 바르고 별짓을 다 했어도 FILCO 키보드의 쫀득한 키감이 무둑둑한 키감으로 바뀌어서<br />
세컨 컴퓨터에 달려있던 Sejin 103기계식(SKM-1080)을 가져와서 쓰고 있다.<br />
<br />
사실 Sejin SKM-1080도 알프스 스위치가 들어있어서 좋은 감압을 가지고 있는데 최고의 감압이라고 부르는<br />
리얼포스가 점점 가지고 싶어진다. 아 긴축재정인데 자꾸 이러면 안되는데...<br />
<br />
결국 RealForce를 찾아보니 tenkeyless의 신제품 RealForce 87U가 있기는 있다.<br />
<strong>하지만 가격이 36만원. -_-;</strong><br />
<br />
신중하게 생각하고 질러야겠다. 흠...<br />
<br />
http://www.leopold.co.kr/?doc=cart/item.php&amp;it_id=1237185827<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/14/55/b0026155_4ad5d4c10fb00.jpg" width="500" height="280.555555556" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/14/55/b0026155_4ad5d4c10fb00.jpg');" /></div><br/><br/>tag : <a href="/tag/리얼포스" rel="tag">리얼포스</a>,&nbsp;<a href="/tag/키보드" rel="tag">키보드</a>,&nbsp;<a href="/tag/realforce" rel="tag">realforce</a>			 ]]> 
		</description>
		<category>Hobby</category>
		<category>리얼포스</category>
		<category>키보드</category>
		<category>realforce</category>

		<comments>http://sunyzero.egloos.com/4255230#comments</comments>
		<pubDate>Wed, 14 Oct 2009 14:21:35 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Oracle 11g on Fedora11 (FAQ) ]]> </title>
		<link>http://sunyzero.egloos.com/4250369</link>
		<guid>http://sunyzero.egloos.com/4250369</guid>
		<description>
			<![CDATA[ 
  <strong>오라클 11g 설치후 문제가 발생했을때의 대처방법</strong><br />
<hr><br />
<strong>Q. Oracle EM manager의 관리자 웹 페이지에 접속할 수 없습니다.</strong><br />
A. Oracle EM Web based Manager의 기본 포트번호는 1158번이고 https로 접속하면 된다.<br />
하지만 간혹 설치 중에 다른 포트번호가 할당되는 경우가 있다. 이런 경우는 $ORACLE_HOME/install/portlist.ini 파일에 포트번호가 적혀있다. 아래는 기본값이 아닌 5500번으로 세팅된 것을 보여준다.<br />
<pre>$ cat $ORACLE_HOME/install/portlist.ini<br />
Enterprise Manager 콘솔 HTTP 포트(orcl) = 5500<br />
Enterprise Manager 에이전트 포트(orcl) = 3938</pre>위와 같은 경우라면 https://localhost:5500/으로 접속해야 한다.<br />
<hr><br />
<strong>Q. emctl 실행시 "EM Configuration issue. ...." 메시지가 나타납니다.</strong><br />
A. 설치 후에 재부팅을 했다면 Oracle EM manager를 재시동해야 한다.<br />
<pre>$ emctl start dbconsole</pre>허나 emctl 가동시 간혹 "EM Configuration issue. /u01/app/oracle/product/11.2.0/dbhome_1/localhost.localdomain_orcl" 등의 비슷한 메시지가 나오는 경우가 있다. 이는 EM Configuration에 잡아놓은 호스트명과 다르기 때문에 나타난다.<br />
<br />
해결방법은 일단 $ORACLE_HOME에 가서 ls 로 디렉토리를 확인하자.<br />
<br />
<pre>[oracle@localhost ~]$ cd $ORACLE_HOME<br />
[oracle@localhost dbhome_1]$ ls<br />
EMStagePatches_orcl  ctx               instantclient   mesg         owm           srvm<br />
OPatch               cv                inventory       network      perl          sysman<br />
apex                 dbs               j2ee            nls          plsql         timingframework<br />
assistants           dc_ocm            javavm          oc4j         precomp       ucp<br />
bin                  deinstall         jdbc            odbc         racg          uix<br />
ccr                  demo              jdev            olap         rdbms         utl<br />
cdata                diagnostics       jdk             ons          relnotes      wwg<br />
cfgtoollogs          emcli             jlib            opmn         root.sh       xdk<br />
clone                has               ldap            oraInst.loc  scheduler<br />
config               hs                lib             oracore      slax<br />
crs                  ide               localhost_orcl  ord          sqldeveloper<br />
csmig                install           log             oui          sqlj<br />
css                  install.platform  md              owb          sqlplus</pre>디렉토리에 보면 SID이름인 orcl이 붙어있는 디렉토리를 볼 수 있다. 위에서는 localhost_orcl 로 보인다. 즉 오라클이 인식하는 호스트명은 localhost라는 소리다.<br />
<br />
그렇다면 ORACLE_HOSTNAME에 localhost를 지정하고 다시 시도해보자.<br />
<pre>$ export ORACLE_HOSTNAME=localhost<br />
$ emctl start dbconsole<br />
Oracle Enterprise Manager 11g Database Control Release 11.2.0.1.0 <br />
Copyright (c) 1996, 2009 Oracle Corporation.  All rights reserved.<br />
https://localhost:1158/em/console/aboutApplication<br />
Starting Oracle Enterprise Manager 11g Database Control ........<br />
------------------------------------------------------------------<br />
Logs are generated in directory /u01/app/oracle/product/11.2.0/dbhome_1/localhost_orcl/sysman/log</pre>ORACLE_HOSTNAME이 잘 설정되었다면 oracle 유저의 환경 설정에 위의 내용을 넣어두자. 앞의 설치에서는 ~/.oraenv에 넣어두었으니 거기에 추가해두면 편리할 것이다.<br/><br/>tag : <a href="/tag/oracle" rel="tag">oracle</a>,&nbsp;<a href="/tag/오라클" rel="tag">오라클</a>,&nbsp;<a href="/tag/데이터베이스" rel="tag">데이터베이스</a>,&nbsp;<a href="/tag/oracle11g" rel="tag">oracle11g</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>oracle</category>
		<category>오라클</category>
		<category>데이터베이스</category>
		<category>oracle11g</category>

		<comments>http://sunyzero.egloos.com/4250369#comments</comments>
		<pubDate>Fri, 09 Oct 2009 06:34:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Oracle 11g on Fedora11 (or Fedora9) ]]> </title>
		<link>http://sunyzero.egloos.com/4241364</link>
		<guid>http://sunyzero.egloos.com/4241364</guid>
		<description>
			<![CDATA[ 
  Oracle 11g R2를 설치한 과정을 자세한 그림과 함께 살펴보도록 하겠다.<br />
<strong>* 목차<br />
<a href="#preinstall">0. 설치전 확인</a><br />
<a href="#runOUI">1. 오라클 설치 프로그램 실행</a><br />
<a href="#DBsecurityupdate">2. 데이터베이스 보안 갱신 구성</a><br />
<a href="#installoption">3. 데이터베이스 설치 옵션</a><br />
<a href="#systemclass">4. 시스템 클래스 설정</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#SystemclassDesktop">(4-A) 데스크탑 클래스를 지정한 경우</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#SystemclassServer">(4-B) 서버 클래스를 지정한 경우</a><br />
<a href="#prerequisites">5. 필요 조건 검사 수행</a><br />
<a href="#installoptionsummary">6. 설치 옵션 요약</a><br />
<a href="#startinstallation">7. 제품 설치 시작 (or 데이터베이스 생성)</a><br />
<a href="#emca">8. 오라클 엔터프라이즈 매니저</a><br />
<a href="#oraenv">9. 오라클 환경 변수의 수정</a></strong><br />
<hr>우선 오라클을 설치한 테스트 플랫폼은 다음과 같다.<br />
<br />
<ul><li>호스트 1: Fedora 9</li><li>호스트 2: Fedora 11</li></ul>오라클 설치 프로그램은 X윈도우 기반이므로 X윈도우가 일단 설치되어있어야 한다. 그리고 GNOME환경을 추천하는데 이는 간혹 KDE나 OpenGL의 compiz에서는 메시지 창이 먹통이 되는 경우가 있기 때문이다. <span style="COLOR: #ff0000">설치 과정은 시스템에 따라서 다르지만 테스트한 E8400 CPU에서는 프로그램 설치에 약 50여분, DB생성에 약 40여분이 걸렸었다. 따라서 넉넉하게 2시간 걸렸다.</span><br />
<hr><br />
<strong><a name="preinstall">0. 설치전 확인</a></strong><br />
<br />
<span style="COLOR: #3366ff">첫번째로 SELINUX 설정을 Permissive로 바꿔야 한다</span>.(enforcing레벨에서 디렉토리, 리소스 권한 조절이 가능하면 해도 된다. 하지만 그 정도 기술이 있으면 최소한 expert 레벨이므로 이런 글을 보지 않을 것이라고 확신하기에 permissive로 바꾸는 것부터 하겠다.) SELINUX 설정의 확인은 sestatus로 확인가능하고 현재 설정을 바꾸는 것은 setenforce를 사용한다. 아래 예를 보자.<br />
<br />
<pre>[root@localhost ~]# sestatus<br />
SELinux status:                 enabled<br />
SELinuxfs mount:                /selinux<br />
Current mode:                   enforcing<br />
Mode from config file:          enforcing<br />
Policy version:                 24<br />
Policy from config file:        targeted<br />
[root@localhost ~]# setenforce permissive<br />
[root@localhost ~]# sestatus<br />
SELinux status:                 enabled<br />
SELinuxfs mount:                /selinux<br />
Current mode:                   permissive<br />
Mode from config file:          enforcing<br />
Policy version:                 24<br />
Policy from config file:        targeted</pre>"setenforce permissive"대신에 "setenforce 0"으로 명령해도 결과는 같다. 길게 permissive로 치는게 더 직관적일 것 같아서 예제는 길게 타이핑했다. 그런데 Current mode가 permissive로 바뀌었지만 여전히 Mode from config file이 enforcing이므로 재부팅하면 다시 enforcing이 된다. 따라서 설정 파일을 바꿔야 한다. 설정 파일은 /etc/sysconfig/selinux이다. vim 에디터로 열어보자.<br />
<br />
<pre># This file controls the state of SELinux on the system.<br />
# SELINUX= can take one of these three values:<br />
#       enforcing - SELinux security policy is enforced.<br />
#       permissive - SELinux prints warnings instead of enforcing.<br />
#       disabled - No SELinux policy is loaded.<br />
SELINUX=enforcing<br />
# SELINUXTYPE= can take one of these two values:<br />
#       targeted - Targeted processes are protected,<br />
#       mls - Multi Level Security protection.<br />
SELINUXTYPE=targeted</pre>중간에 "SELINUX=enforcing"의 부분을 "SELINUX=permissive"로 고쳐놓으면 다음번에 재부팅할때도 permissive로 보이게 된다. 설정후 다시 sestatus를 실행하면 Current mode와 Mode from config file 부분이 둘다 permissive로 되어 있을 것이다.<br />
<br />
<span style="COLOR: #3366ff">두번째로 오라클 설치에 필요한 공간을 확보해야 한다. 우선 RAM 공간부터 확인하자.</span><br />
<ul><li>RAM 메모리 : 2G 이상 (최소 1G이지만 2G는 되어야 한다.)</li><li>SWAP 메모리 : RAM 크기 or 최대 2배 (RAM이 2G이면 SWAP도 2G정도면 충분하다.)</li></ul><p>이제는 디스크 공간을 확인해야 한다.</p><ul><li>Oracle 11g 압축을 풀어놓을 임시 공간 : 약 3G</li><li>Oracle 11g 프로그램을 설치할 공간 : 약 7G</li><li>Oracle 데이터베이스를 구축할 공간 : 약 4G</li><li>/tmp 디렉토리 : 최소 400M 정도의 빈 공간</li></ul>위의 공간은 조금 여유있게 잡은 크기이다.&nbsp;설치할 공간과 DB구축의 공간을 최소로 잡으면 약 8G정도에도 가능하다.(하지만 넉넉하게 잡기를 권장한다)&nbsp;오라클은 일반적으로 /u01, /u02 이런 식의 디렉토리에 설치한다. 꼭 그래야 되는 것은 아니지만 이왕이면 관습적으로 /u01 디렉토리에 10G이상의 공간을 확보해서 DB까지 같은 곳에 넣어두면 간편하긴 하다. (하지만 제대로 설치해서 사용할 요령이라면 DB공간은 프로그램 설치 공간과 다른 파티션이나 디스크로 분리하는 것이 좋다.)<br />
<br />
<span style="COLOR: #3366ff">세번째로&nbsp;오라클 설치에 필요한 각종 패키지의 버전을 확인해야 한다.</span> 그러나 여기서는 확인하지 않고 넘어가도록 하겠다. 왜냐하면 설치 프로그램이 자동으로 패키지의 버전을 확인해주기 때문에 그때&nbsp;가서 경고가 뜨는 패키지를 골라 설치해도 늦지 않기 때문이다.<br />
<br />
<span style="COLOR: #3366ff">네번째로 오라클 설치에 필요한 각종 시스템 자원의 커널 파라메터를 조정해야 한다.</span>&nbsp;오라클은 세마포어 개수, 공유메모리 제한 등등 여러가지를 조정해야 하는데 위와 마찬가지로 오라클 설치 프로그램이 자동으로 확인해준다. 더군다나 확인 후 부족한 자원의 파라메터를 조정할 수 있는&nbsp;쉘 스크립트 프로그램을 작성해 주므로 그때 가서 간단하게 실행해서 설정하도록 한다.(귀찮게 미리 타이핑 하는 것은 시간 낭비다.)<br />
<br />
<span style="COLOR: #3366ff">다섯번째로 오라클 설치 및 구동에 필요한 그룹명과 유저를 만들어야 한다.</span> 우선 root로 로그인 한다.<br />
<pre># groupadd oinstall<br />
# groupadd dba<br />
# useradd -g oinstall -G dba oracle<br />
# passwd oracle</pre><span style="COLOR: #ff0000">설치에 필요한 그룹은 oinstall, dba이며 유저명은 oracle 이다</span>. oracle유저의 primay group은 oinstall이고, suppliment group은 dba이다. oracle 유저의 암호는 알아서 정하자. 유저가 만들어 졌으면 이번에는 /etc/pam.d/login 파일을 손봐야 한다.<br />
참고로 /etc/pam.d/login 파일은 유저가 로그인 할 때 각종 테스트할 기능들을 정의하는 파일이다. <br />
<br />
이 파일에 추가할 PAM 기능은 pam_limits.so 로서 유저가 로그인할 때 /etc/security/limits.conf에 정의된 자원제한값을 적용하는 기능이다. 오라클은 유저가 사용가능한 최대프로세스 개수, 최대 파일 개수를 기본값보다 크게 잡아줘야 하기 때문에 이 기능을 설정해줘야 한다. <span style="COLOR: #ff0000">주의할 점은 /etc/pam.d/login파일을 잘못 건드리면 새롭게 로그인이 안되는 불상사가 발생하므로 root 유저로 로그인된 terminal을 1개 여분으로 열어두자.</span><br />
<br />
이제 /etc/pam.d/login을 열어서 "session&nbsp;&nbsp; required&nbsp; pam_limits.so" 라는 행이 있는지 확인하고, 없다면 맨 아래에 추가해두자. 설정이 완료된 /etc/pam.d/login 파일은 다음과 같다. (내용은 약간 다를수 있다. 맨 끝에 pam_limits.so 기능이 들어있는 행만 있으면 된다.)<br />
<br />
<pre># cat /etc/pam.d/login<br />
#%PAM-1.0<br />
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so<br />
auth       include      system-auth<br />
account    required     pam_nologin.so<br />
account    include      system-auth<br />
password   include      system-auth<br />
# pam_selinux.so close should be the first session rule<br />
session    required     pam_selinux.so close<br />
session    required     pam_loginuid.so<br />
session    optional     pam_console.so<br />
# pam_selinux.so open should only be followed by sessions to be executed in the user context<br />
session    required     pam_selinux.so open<br />
session    required     pam_namespace.so<br />
session    optional     pam_keyinit.so force revoke<br />
session    include      system-auth<br />
-session   optional     pam_ck_connector.so<br />
session    required     pam_limits.so</pre>설정이 다 되었으면 확인을 위해서 <span style="COLOR: #ff0000">CTRL+ALT+F2</span>를 눌러 콘솔로 나가든지, 혹은 ssh를 통해 자신의 서버에 oracle 유저로 로그인 해보자. 만일 로그인이 실패했다면 위의 설정중에 어딘가를 잘못 타이핑한 것이다. 미리 로그인해둔 터미널에서 재확인해보자.(만일 설정을 잘못했는데 실수로 로그아웃했다면 정상적인 방법으로는 로그인이 불가능해진다. 따라서 설치용 CD/DVD로 복구 모드로 들어가서 /etc/pam.d/login 파일을 다시 수정해야 한다.)<br />
<br />
<span style="COLOR: #3366ff">여섯번째로 오라클을 설치할 디렉토리와 권한을 조정해둬야 한다.</span>&nbsp;여기서는 /u01에 오라클을 설치하는 것으로 진행하겠다.(여기서는 미리 /u01에 충분한 파티션을 할당해 둔 것을 전제한다.)<br />
<pre># mkdir /u01/app<br />
# chown -R oracle:oinstall /u01 </pre>이제 본격적인 설치를 위해서 X 윈도우를 다시 oracle 유저로 로그인 하자. 이제부터 특별한 언급이 없으면 모든 작업은 oracle 유저로 작업하는 것이다.<br />
<br />
<span style="COLOR: #3366ff">일곱번째로 오라클 환경변수를 설정해야 한다. </span>환경변수는 관습적으로 ~/.oraenv 파일명으로 만든다. 파일 내용은 다음과 같다.<br />
<pre>ORACLE_BASE=/u01/app/oracle<br />
ORACLE_SID=orcl<br />
export ORACLE_BASE ORACLE_SID</pre><br />
ORACLE_SID는 오라클 데이터베이스 SID 값인데, 주고 싶은 값을 사용하면 된다. 최대 30자까지 가능하다. (설치시 기본 값은 orcl을 사용한다.) 파일을 만들었으면 로그인 할 때 반영되게 해야 하므로 bash runtime configuration인 .bashrc에 넣어줘야 한다. "echo ".  ~/.oraenv  >>  ~.bashrc"라고 명령하면 된다.<br />
<br />
<br />
<strong><a name="runOUI">1. 오라클 설치 프로그램 실행</a></strong><br />
오라클 설치 프로그램은 여러 개의 zip 파일로 압축되어있는데 다 합쳐서 약 2G가 좀 넘는다. 압축을 풀면 database 디렉토리가 보이는데 들어가보면 runInstaller 라는 실행파일이 있다. 실행해보자.<br />
<br />
<pre>[oracle@localhost database]$ ./runInstaller <br />
Oracle Universal Installer 시작 중...<br />
<br />
임시 공간 확인 중: 80MB 이상이어야 합니다..   실제 728MB    성공<br />
스왑 공간 확인 중: 150MB 이상이어야 합니다..   실제 511MB    성공<br />
모니터 확인 중: 최소 256 색상을 표시하도록 구성되어 있어야 합니다..    실제 16777216    성공<br />
다음에서 Oracle Universal Installer의 시작을 준비하는 중 /tmp/OraInstall2009-10-06_03-39-30PM. 기다리십시오.</pre>위와 같은 메시지가 나오면 좀 기다려주자. 설치 프로그램 화면이 나오기 까지는 시스템에 따라서 수 분이 걸리는 경우도 있으니 다운되었다고 착각하여 터미널을 닫아버리는 잘못을 범하지는 말자.<br />
<br />
<br />
<strong><a name="DBsecurityupdate">2. 데이터베이스 보안 갱신 구성</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9c206fe77.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9c206fe77.png');" /></div><br />
보안 설정에 대한 알림 메일을 받으려면 email 주소를 넣어주면 된다. Oracle Support에 유저를 등록한 경우라면 더 편리하지만 이도저도 아닌 그냥 테스트용으로 설치하는 경우는 "다음"으로 진행하면 된다.<br />
<br />
<br />
<strong><a name="installoption">3. 데이터베이스 설치 옵션</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9ceb0ff79.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9ceb0ff79.png');" /></div>설치옵션에서는 3가지 설치옵션을 지정할 수 있다. 여기서는 데이터베이스 생성 및 구성으로 진행하는 것을 보이도록 하겠다.<br />
<br />
- 데이터베이스 생성 및 구성 : 소프트웨어 설치 후 데이터베이스를 생성해준다.<br />
- 데이터베이스 소프트웨어만 설치 : 소프트웨어만 설치하고 데이터베이스는 생성하지 않는다.<br />
- 기존 데이터베이스 업그레이드 : 말그대로 업그레이드다. 구버전의 오라클이 설치되어있다면 업그레이드 시켜준다.<br />
<br />
<br />
<br />
<strong><a name="systemclass">4. 시스템 클래스 설정</a></strong><br />
시스템 클래스 설정은 데이터베이스를 어떤 목적으로 구성할 것인지를 결정한다. 데스크탑용인지 서버용인지를 선택할 수 있다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9d98188c1.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acb9d98188c1.png');" /></div>- 데스크톱 클래스 : 작은 규모용으로 설치한다.<br />
- 서버 클래스 : 자동 백업 및 분산 복제등 여러가지 기능이 추가된다.<br />
<br />
<strong><a name="SystemclassDesktop">(4-A) 데스크톱 클래스를 지정한 경우 : 총 9개의 필드를 가진 설치 구성화면으로 넘어간다.</a></strong><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc3137bada6.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc3137bada6.png');" /></div>- Oracle Base : Oracle software가 설치될 기준 디렉토리이다. OUI(Oracle Universal Installer)가 자동으로 인식하기 위해서는 /u0[1-9]/app로 시작해야 한다.<br />
- 소프트웨어 위치 : 오라클 DB 소프트웨어가 위치할 디렉토리로서 ORACLE_BASE/product/&lt;버전&gt;/dbhome_1로 자동 결정됩니다.<br />
- 데이터베이스 파일 위치 : DB파일이 저장되는 곳<br />
- 데이터베이스 버전 : Enterprise Edition, Standard Edition, Standard Edition One중에 하나로 설정한다.(각 버전의 특징은 Help참고)<br />
- 문자 집합 : DB내에서 사용할 문자세트(국제화 규격에 따라 UTF-8 유니코드를 추천)<br />
- OSDBA 그룹 : DBA 그룹<br />
- 전역 데이터베이스 이름 : DB이름+도메인으로 구성된 전역 데이터베이스 이름<br />
- 관리 비밀번호, 비밀번호 확인 : 비밀번호 8~30자가 되어야 하며, 대소문자와 숫자가 혼합되어야만 한다.(또한 사전상의 단어를 사용하면 안된다.)<br />
<br />
<br />
<strong><a name="SystemclassServer">(4-B) 서버 클래스를 지정한 경우 : 우선 "단일 인스턴스 데이터베이스"와 "Real Application Clusters" 중에 하나를 선택해야 한다.</a></strong><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc369e142cc.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc369e142cc.png');" /></div>서버 클래스 설치에서는 저장 영역 유형과 ASMSNMP 비밀번호를 지정할 수 있게 된다.<br />
- 저장 영역 유형 : 파일시스템, 원시 블록 장치 중에 선택할 수 있다.<br />
<br />
<br />
<br />
<strong><a name="prerequisites">5. 필요 조건 검사 수행</a></strong><br />
기본 설치를 지정한 뒤에는&nbsp;필요조건 검사를 행하게 된다. 여기서 커널 파라메터, 사용자 자원 제한, 패키지 버전들을 검사해준다.<br />
커널 파라메터와 사용자 자원 제한은 "수정 및 다시 확인"버튼을 누르면 자동으로 수정가능한 스크립트를 만들어 주기 때문에 패키지 버전이나 없는 패키지만 설치해주면 된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc379e134b1.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc379e134b1.png');" /></div><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc388e5c29b.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc388e5c29b.png');" /></div>"수정 및 다시 확인" 버튼을 누르면 "수정 스크립트 실행"화면이 나타난다. 위의 경로의 runfixup.sh 파일을 root 계정의 권한으로 실행하면 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc392bb8d99.png" width="500" height="196.96969697" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc392bb8d99.png');" /></div>위와 같이 runfixup.sh를 실행하면 각종 설정을 고쳐주게 된다. 수정되는 파일은 /etc/sysctl.conf와&nbsp;/etc/security/limits.conf이다. runfixup.sh가 실행한 뒤에 "다시 확인"을 누르면 재검사를 하게 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc39dbaa63d.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc39dbaa63d.png');" /></div>위에 그림을 보면 실패 상태가 3개가 나오는데 교체크기(스왑 공간), /tmp, pdksh 패키지가 실패라고 나온다. 현재 설치하려는 시스템은 RAM이 2GB이상이므로&nbsp;설치중에 굳이 스왑 공간이 사용하지 않으므로 그냥 무시해도 된다. /tmp 디렉토리도 300MB이상이면 그냥 넘어가도 된다.&nbsp;<span style="COLOR: #3366ff">페도라에서는 pdksh대신에 mksh를 사용하므로 대신 mksh를 설치하면 된다.</span><br />
<br />
<br />
<br />
<strong><a name="installoptionsummary">6. 설치 옵션 요약</a></strong><br />
여기까지 설정한 것을 요약화면으로 보여준다. 이상한 점이 있다면 뒤로 가서 수정하고 오면 된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc3b824d050.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc3b824d050.png');" /></div>이상이 없다면&nbsp;"완료"버튼을 눌러 설치를 시작한다.<br />
<br />
<br />
<strong><a name="startinstallation">7. 제품 설치 시작 (or 데이터베이스 생성)</a></strong><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc76d57e72c.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc76d57e72c.png');" /></div><br />
처음 설치할때 데이터베이스 구성을 선택했다면 파일 복사가 끝나고 dbca가 작동하여 데이터베이스를 구성하게 된다. 테스트한 호스트인 E8400에서는 거의 50여분을 걸렸다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc777690e11.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc777690e11.png');" /></div><br />
데이터베이스를 구성하다보면 DB 유저의 암호를 정하는 창이 뜬다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77ba844eb.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77ba844eb.png');" /></div>SYS와 SYSTEM 암호를 설정하고, 스크롤바를 더 내리면 SCOTT 유저가 있으므로 예제나 뭐 그런 것을 수행하려면 SCOTT유저의 암호도 넣어주자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77d50648c.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200910/07/55/b0026155_4acc77d50648c.png');" /></div>DB구성이 끝나면 root 유저로 해야할 작업을 root.sh란 쉘 스크립트로 만들어 준다. 이를 실행하고 원래 창으로 들어와서 확인을 하면 끝난다.<br />
<pre>[root@localhost ~]# /opt/app/oracle/product/11.2.0/dbhome_1/root.sh <br />
Running Oracle 11g root.sh script...<br />
<br />
The following environment variables are set as:<br />
    ORACLE_OWNER= oracle<br />
    ORACLE_HOME=  /opt/app/oracle/product/11.2.0/dbhome_1<br />
<br />
Enter the full pathname of the local bin directory: [/usr/local/bin]: <br />
   Copying dbhome to /usr/local/bin ...<br />
   Copying oraenv to /usr/local/bin ...<br />
   Copying coraenv to /usr/local/bin ...<br />
<br />
Creating /etc/oratab file...<br />
Entries will be added to the /etc/oratab file as needed by<br />
Database Configuration Assistant when a database is created<br />
Finished running generic part of root.sh script.<br />
Now product-specific root actions will be performed.<br />
Finished product-specific root actions.</pre><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc77f41e7e2.png" width="500" height="375" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc77f41e7e2.png');" /></div>이제 DB 설치와 구성이 모두 끝났다. 위의 Enterprise Manager Database Control URL이 보인다. 웹 브라우저를 열어서 접속해보자.(간혹 접속이 안되는 경우는 방화벽 문제일 수 있다.) <br />
<br />
<br />
<br />
<strong><a name="emca">8. 오라클 엔터프라이즈 매니저</a></strong><br />
EM에 관리목적으로&nbsp;접속할 때는 sys유저의 SYSDBA권한으로 접속해야 한다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc78dbaeed8.png" width="500" height="414.835164835" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200910/07/55/b0026155_4acc78dbaeed8.png');" /></div>emca화면은 웹 페이지이므로 직관적이다. 참고로 사용자 추가나 조정은 "서버"&gt;&gt;"사용자"에서 하면 된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc78f9b8b2d.png" width="500" height="378.125" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200910/07/55/b0026155_4acc78f9b8b2d.png');" /></div>그리고 항상 주의할 점은 서버 끌때는 위의 화면에서 DB를 "작동 중지" 시킨 후에 꺼야한다. 안그러면 다음번 DB 기동시에 문제가 발생할 수 있다.<br />
<br />
<br />
<strong><a name="oraenv">9. 오라클 환경 변수의 수정</a></strong><br />
앞에서 오라클 환경변수를 설정하기 위해서 ~/.oraenv 파일을 사용했는데, 설치후에는 이 파일을 수정해둬야 한다. 이유는 오라클의 바이너리 디렉토리를 PATH에 추가하고, Pro*C 컴파일등을 위한 라이브러리 경로를 추가해야 하기 때문이다. 따라서 앞의 3줄짜리 .oraenv를 다음과 같이 적절하게 수정한다.<br />
<br />
<pre>ORACLE_BASE=/u01/app/oracle<br />
ORACLE_SID=orcl<br />
ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1<br />
export ORACLE_BASE ORACLE_HOME ORACLE_SID<br />
<br />
if [ x`echo $PATH | grep $ORACLE_HOME/bin` = 'x' ]; then<br />
    echo "Setting oracle binary path: $ORACLE_HOME/bin"<br />
    PATH=$PATH:$ORACLE_HOME/bin<br />
    export PATH<br />
fi<br />
<br />
if [ x`echo $LD_LIBRARY_PATH | grep $ORACLE_HOME/lib` = 'x' ]; then<br />
    echo "Setting oracle LD_LIBRARY_PATH : $ORACLE_HOME/lib"<br />
    if [ x$LD_LIBRARY_PATH = 'x' ]; then<br />
        LD_LIBRARY_PATH=$ORACLE_HOME/lib<br />
    else<br />
        LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib<br />
    fi<br />
    export LD_LIBRARY_PATH<br />
fi<br />
</pre><br />
<br />
수정 후에 로그인을 다시 해보면 Setting oracle binary path: /u01/app/oracle/product/11.2.0/dbhome_1/bin 와 Setting oracle LD_LIBRARY_PATH : /u01/app/oracle/product/11.2.0/dbhome_1/lib 메시지가 보일 것이다. 이제 어느 경로든지 sqlplus를 실행할 수 있게 될 것이다.<br />
<br />
<br />
* 그 외 오라클 관련 문서는 아래의 웹 사이트에서 볼 수 있다.<br />
<a href="http://www.oracle.com/technology/documentation">http://www.oracle.com/technology/documentation</a><br />
<br />
* History<br />
2009.10.10 "9. 오라클 환경 변수의 수정" 추가<br />
2009.10.07 첫번째 초안 작성<br/><br/>tag : <a href="/tag/오라클" rel="tag">오라클</a>,&nbsp;<a href="/tag/oracle" rel="tag">oracle</a>,&nbsp;<a href="/tag/fedora" rel="tag">fedora</a>,&nbsp;<a href="/tag/데이터베이스" rel="tag">데이터베이스</a>,&nbsp;<a href="/tag/oracle11g" rel="tag">oracle11g</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>오라클</category>
		<category>oracle</category>
		<category>fedora</category>
		<category>데이터베이스</category>
		<category>oracle11g</category>

		<comments>http://sunyzero.egloos.com/4241364#comments</comments>
		<pubDate>Wed, 07 Oct 2009 11:08:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #3 ]]> </title>
		<link>http://sunyzero.egloos.com/4234766</link>
		<guid>http://sunyzero.egloos.com/4234766</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #3</span></strong><br />
<br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785">http://sunyzero.egloos.com/4227785</a><br />
- HOWTO #2 : <a href="http://sunyzero.egloos.com/4229235">http://sunyzero.egloos.com/4229235</a><br />
<hr><br />
<br />
<strong><span style="FONT-SIZE: 130%">6. Single Construct</span></strong><br />
<br />
single construct로 지시된 구간은 단 한번만 실행된다. 실행되는 쓰레드는 여러 쓰레드중에 제일 먼저 진입하는 쓰레드이다.<br />
<br />
<ul><li>single construct는 처음으로 진입한 쓰레드가 실행한다.</li><li>나머지 쓰레드들은 single construct 끝에 존재하는 implicit barrier에서 대기한다.</li><li>single construct가 끝나고 모든 쓰레드들은 implicit barrier에서 동시에 시작한다.</li></ul><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafd7711fc57.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafd7711fc57.png');" /></div>그림에서 보이듯이 parallel 구간에서 쓰레드들 중에 한 개만 &nbsp;single construct를 실행하고 나머지는 뒤에 존재하는 implicit barrier에서 대기하는 것을 볼 수 있다. 그러면 위 소스코드를 컴파일하고 실행해보자. 실행결과는 예상대로 "1. Hello world"는 1번 출력되고, "2. Hello world"는 2번 실행된다.(테스트 호스트는 듀얼 코어이므로)<br />
<pre>$ gcc -o omp_single -fopenmp omp_single.c<br />
$ ./omp_single<br />
1. Hello world<br />
2. Hello world<br />
2. Hello world</pre><br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">7. Master Construct</span></strong><br />
<br />
master construct는 single construct와 매우 비슷하다.<br />
하지만 다른 점이 2가지 있다.<br />
<br />
<ul><li>master construct 구간은 무조건 master thread (main thread)가 1번 실행한다.</li><li>master construct 구간뒤에 implicit barrier가 없다. <br />
즉 모든 쓰레드는 master construct 실행되는 동안에도 계속 실행한다.</li></ul><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafda6d8829f.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/16/55/b0026155_4aafda6d8829f.png');" /></div>실행 결과는 위의 single construct와 같지만 위 그림에서 보듯이 약간의 차이는 있다. master construct는&nbsp;implicit barrier가 없다는 점이다. 중요한 차이므로 꼭 기억해야 한다.&nbsp;<br />
<br />
<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">8. Barrier</span></strong><br />
<br />
배리어란 동기화(synchronization)을 위해서 사용되는 기능이다.<br />
<br />
동기화는 시간적 개념이다.&nbsp;풀어서 설명하기 위해 예를 들자.&nbsp;스타크래프트 배틀넷은 왠만한 사람이면 다 해봤을 것이다. 최대 8명까지 게임에 참가할 수 있는데,&nbsp;어떤 유저가 매우 느린 모뎀을 쓰고 있으면 게임 중간에 타임을 세는 화면이 뜨고 기다려주는 것을 볼 수 있다. 이는 빠른 네트워크/컴퓨터를 가진 유저와 느린 네트워크/컴퓨터를 가진 유저의 게임 속도를 맞추기 위해서 배리어가 작동한 것이다. 따라서 결과적으로 배리어는 느린 사람에 맞춰서 앞서 가는 사람이 대기하도록 하는 기능이다.<br />
<br />
그러면&nbsp;프로그래밍에서는 배리어를 어떻게 사용해야 하는가? 작업이 병렬적으로 이뤄진다고 하더라도 전처리, 후처리 작업들이 나눠져 있을 경우에는 전처리 작업들을 병렬처리했을때 어떤 특정 쓰레드가 빨리 처리했다고 후처리 작업을 먼저 출발하면 데이터가 꼬이거나 로직이 망가질 수 있다. 이럴 경우 중간중간에 적절한 배리어를 넣어주면 깔끔하게 해결된다.(하지만 역으로 배리어가 많으면 그 만큼 대기도 많아질 수 있다.)<br />
<br />
<br />
<strong>8.a Implicit barrier</strong><br />
<br />
앞에서 implicit barrier(암묵적 배리어)에 대해서 이야기를 많이 했다. OpenMP는 각 작업의 동기화에 대한 편의성을 제공하기 위해서 implicit barrier를 잘 제공한다. 어떤 construct 에 대해서 implicit barrier가 제공되는지 정리하고 넘어가자.<br />
<br />
<ul><li>#pragma omp parallel</li><li>#pragma omp for</li><li>#pragma omp single</li></ul>위의 3가지의 경우는 블록 끝에 자동적으로 implicit barrier가 들어간다. 하지만 위의 3가지 construct 의 끝에 nowait clause를 지정하면 implicit barrier가 제거되고 대기하지 않고 이후 코드가 즉시 실행됩니다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/18/55/b0026155_4ab36d31cb9f2.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/18/55/b0026155_4ab36d31cb9f2.png');" /></div>위의 예제에서는 single construct에&nbsp;nowait를 적용하여 implicit barrier를 제거하는 것을 볼 수 있다. <br />
(그림 아래에 있는 implicit barrier는&nbsp;parallel construct에 있는 barrier입니다.)<br />
<br />
<br />
<strong>8.b Explicit barrier</strong><br />
<br />
이번에는 사용자가 직접 지정할 수 있는 explicit barrier 기능에 대해서 보겠습니다.<br />
<br />
<ul><li>#pragma omp barrier 구문을 지정하면 해당 부분에서 모든 쓰레드가 도착할 때까지 대기하게 된다.</li></ul><pre>char * get_time0(char *buf, size_t sz_buf);<br />
<br />
int main()<br />
{<br />
    int     t_sleep; char    buf[16];<br />
#pragma omp parallel private(t_sleep, buf)<br />
    {<br />
#pragma omp single nowait<br />
        sleep(1);<br />
        printf("[%s] phase1:sleep %ld sec.\n", get_time0(buf, sizeof(buf)), t_sleep = times(NULL)%8);<br />
        sleep(t_sleep);<br />
#pragma omp barrier<br />
        /* explicit barrier */<br />
        printf("[%s] phase2. Hello world\n", get_time0(buf, sizeof(buf)));<br />
    }<br />
    return 0;<br />
}<br />
<br />
char * get_time0(char *buf, size_t sz_buf)<br />
{<br />
    time_t  t0; struct tm   tm_now;<br />
    if (buf == NULL) return NULL;<br />
    if (time(&amp;t0) == ((time_t)-1)) return NULL;<br />
    localtime_r(&amp;t0, &amp;tm_now);<br />
    if (strftime(buf, sz_buf, "%H:%M:%S", &amp;tm_now) == 0) return NULL;<br />
    return buf;<br />
}</pre>이제 실행해보면 배리어 효과때문에 마지막 실행한 19:12:28에서 5초뒤에 phase2가 실행되는 것을 볼 수 있다.<br />
<pre>$ ./omp_barrier<br />
[19:12:27] phase1:sleep 1 sec.<br />
[19:12:28] phase1:sleep 5 sec.<br />
[19:12:33] phase2. Hello world<br />
[19:12:33] phase2. Hello world</pre><br />
이번에는 여기까지 마치고, 다음에 critical section이나 atomic, OpenMP API함수등에 대해서 다뤄보도록 하겠다.<br/><br/>tag : <a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>,&nbsp;<a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>openmp</category>
		<category>thread</category>
		<category>C언어</category>
		<category>쓰레드</category>

		<comments>http://sunyzero.egloos.com/4234766#comments</comments>
		<pubDate>Tue, 22 Sep 2009 12:31:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #2 ]]> </title>
		<link>http://sunyzero.egloos.com/4229235</link>
		<guid>http://sunyzero.egloos.com/4229235</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #2</span></strong><br />
<br />
<br />
<hr>- HOWTO #1 : <a href="http://sunyzero.egloos.com/4227785"><span style="COLOR: #5990bf">http://sunyzero.egloos.com/4227785</span></a><br />
<hr><br />
<br />
<span style="FONT-SIZE: 130%"><strong>* 연습문제 (멀티 쓰레딩과 reentrant 함수의 관계에 대한 문제)</strong></span><br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf31587fd1.png" width="500" height="251.953125" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf31587fd1.png');" /></div>이번에는 pi를 몬테 카를로 시뮬레이션을 이용해서 구해보도록 하겠다.<br />
각변의 길이가 1인 정사각형이 있다. 그리고 정사각형 안에 반지름(r)이 1인 원호를 그리도록 하자.<br />
그러면 중점으로부터 원호까지의 최단 길이는 무조건 1이 된다.<br />
<br />
그러면 이제 정사각형 안에 임의의 점(x,y)좌표를 찍어서 중점에서 선분을 연결하고 아랫변까지 선분을 내리면 직각삼각형이 된다.<br />
그리고&nbsp;이 직각삼각형의&nbsp;길이는 x, 높이는 y가 된다.<br />
피타고라스의 정리에 의해 (x의 제곱)+(y의 제곱)=(빗변의 제곱)이 되는데, 빗변이 길이가 1보다 작으면<br />
원호 안에 찍힌 점이 되고, 1보다 크면 원호 밖에 정사각형 내부에 찍힌 점이 된다.<br />
<br />
원호의 넓이는 반지름 r=1일 때 pi/4이 된다. 따라서 위의 수많은 랜덤 좌표를 찍은 뒤에 (원호 내부의 점의 개수)/(전체 랜덤 수)<br />
는 pi/4와 같아질 것이다. 그러면 이제 기본 코딩을 해보자.<br />
<br />
<pre>#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#define LOOP_ITERATION 200000000<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    int 	i;<br />
    double 	x, y, rns;<br />
    time_t	t_now;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
<br />
    t_now = time(0);<br />
    srand((unsigned int)t_now);<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand() * rns;<br />
        y = (double)rand() * rns;<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f\n", 4*(double)hits/LOOP_ITERATION);<br />
    return 0;<br />
}</pre>이제 컴파일 후 실행을 해 보겠다. 소스코드의 파일명은 pi_monte.c라고 저장했다.<br />
<br />
<pre>$ gcc -o pi_monte pi_monte.c<br />
$ time ./pi_monte<br />
Loop iteration = 200000000<br />
pi = 3.141588<br />
<br />
real    0m8.726s<br />
user    0m8.702s<br />
sys 0m0.023s<br />
</pre>CPU를 한 개만 사용했기 때문에 real과 user+sys 시간이 같게 나온다.<br />
이제 여기에 OpenMP를 적용해보도록 하자. 중복되는 코드는 모두 생략하고 중간에 loop부분만 적도록 하겠다.<br />
<pre>...생략...<br />
#pragma omp parallel for private(x,y) reduction(+:hits)<br />
for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
    x = (double)rand() * rns;<br />
    y = (double)rand() * rns;<br />
    if (x*x + y*y &lt; 1) {<br />
        hits++;<br />
    }<br />
}<br />
...생략...</pre>자 이제 수정된 소스코드를 컴파일하고 다시 실행해보겠다.<br />
<pre>$ gcc -o pi_monte_omp -fopenmp pi_monte.c<br />
$ time ./pi_monte_omp <br />
Loop iteration = 200000000<br />
pi = 3.141513<br />
<br />
real    0m54.909s<br />
user    0m54.173s<br />
sys 0m53.115s</pre>OpenMP버전의 실행 시간이 엄청나게 증가한 것을 볼 수 있다.<br />
이는 뭔가 문제가 발생한 것이다. profiler가 있으면 cache miss가 많아진 것을 추적할 수 있다. 그러면 왜 cache miss가 많아졌을까?<br />
<br />
<strong>이는 멀티 쓰레드 안전(thread-safe, MT-safe)한 함수를 사용하지 않았기 때문이다.</strong><br />
단도직입으로 원인은 rand()함수이다. rand()함수는 내부에 static 변수(BSS영역)를 사용하기 때문에 lock 없이 사용하면<br />
오염된 공간을 쓸 수 있다. 그렇다고 lock으로 보호하는 것은 성능상으로 좋지 못하다. 따라서 MT-safe한 랜덤생성 함수로<br />
대체해야 한다. 마침 rand()의 reentrant 버전인 rand_r()이 있으므로 이를 사용하도록 바꾸면 된다.<br />
<br />
<strong><u>* reentrant에 대한 이해가 필요하다면...<a href="http://sunyzero.egloos.com/4188321">http://sunyzero.egloos.com/4188321</a> 글을 읽도록 한다.</u></strong><br />
<br />
그러면 이제 수정된 버전을 보도록 하겠다.<br />
<br />
<pre>#define _REENTRANT<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#ifdef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    unsigned int state;<br />
    int     i;<br />
    double  x, y, rns;<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
    state = (unsigned int)time(0);<br />
#pragma omp parallel for firstprivate(state) private(x, y) reduction(+:hits)<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand_r(&amp;state) * rns;<br />
        y = (double)rand_r(&amp;state) * rns;<br />
#if LOOP_ITERATION &lt; 100<br />
        printf("THR[%d:%d] x,y/state = %f,%f/%u\n", omp_get_thread_num(), i, x, y, state);<br />
#endif<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f (hits = %d)\n", (double)hits/LOOP_ITERATION * 4, hits);<br />
    return 0;<br />
}</pre>중요한 변화를 확인하기 위해 LOOP_ITERATION이 100 이하인 경우에는 루프를 돌때마다 rand_r()로 얻어진 x, y, state의 값을 출력하도록 디버깅 코드를 심어놨다. 그러면 LOOP_ITERATION을 20으로 줄여서 컴파일&실행으로 디버깅 해보자.<br />
<br />
<pre>$ gcc -o pi_monte_omp_logging -DLOOP_ITERATION=20 -fopenmp pi_monte.c<br />
$ ./pi_monte_omp_logging<br />
Loop iteration = 20<br />
THR[0:0] x,y/state = 0.739258,0.373189/1534713604<br />
THR[0:1] x,y/state = 0.741886,0.152093/434332526<br />
THR[0:2] x,y/state = 0.399359,0.675170/1402811528<br />
THR[0:3] x,y/state = 0.140991,0.510551/3217124562<br />
THR[0:4] x,y/state = 0.117010,0.018486/2612351692<br />
THR[0:5] x,y/state = 0.151627,0.286311/535820534<br />
THR[0:6] x,y/state = 0.412198,0.450266/361487824<br />
THR[0:7] x,y/state = 0.126866,0.324974/1313573850<br />
THR[0:8] x,y/state = 0.923697,0.862684/1937324436<br />
THR[0:9] x,y/state = 0.828232,0.810037/3167316350<br />
THR[1:10] x,y/state = 0.739258,0.373189/1534713604<br />
THR[1:11] x,y/state = 0.741886,0.152093/434332526<br />
THR[1:12] x,y/state = 0.399359,0.675170/1402811528<br />
THR[1:13] x,y/state = 0.140991,0.510551/3217124562<br />
THR[1:14] x,y/state = 0.117010,0.018486/2612351692<br />
THR[1:15] x,y/state = 0.151627,0.286311/535820534<br />
THR[1:16] x,y/state = 0.412198,0.450266/361487824<br />
THR[1:17] x,y/state = 0.126866,0.324974/1313573850<br />
THR[1:18] x,y/state = 0.923697,0.862684/1937324436<br />
THR[1:19] x,y/state = 0.828232,0.810037/3167316350<br />
pi = 3.200000 (hits = 16)<br />
</pre>iteration이 20번이므로 pi의 정확도는 일단 포기하자. 지금은 pi의 결과를 보려는 것이 아니라, 각 쓰레드의 x, y값을 확인하기 위함이다. 0번째 쓰레드의 첫번째 데이터와 1번째 쓰레드의 첫번째 데이터를 비교하니 뭔가 이상하다. 이해를 돕기 위해 두개만 따로 떼어서 보도록 하자.<br />
<br />
THR[0:0] x,y/state = 0.739258,0.373189/1534713604<br />
THR[1:10] x,y/state = 0.739258,0.373189/1534713604<br />
<br />
둘이 놀랍도록 일치하지 않은가? 왜 이런 문제가 발생할까? 이는 rand_r()함수에 사용하는 seed값 변수인 state의 초기값이 모든 쓰레드들에 대해서 같기 때문에 발생하는 문제다. 그렇다면 각 쓰레드가 seed를 다르게 가져가도록 소스코드를 수정해야 한다.<br />
<br />
<pre>#define _REENTRANT<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#ifdef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    unsigned int state1, state2;<br />
    int     i;<br />
    double  x, y, rns;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
    state1 = (unsigned int)times(NULL);<br />
#pragma omp parallel private(x, y, state2) reduction(+:hits) shared(state1)<br />
    {<br />
#pragma omp critical<br />
        state2 = rand_r(&amp;state1);<br />
        printf("THR[%d] state1/state2 = %u/%u\n", omp_get_thread_num(), state1, state2);<br />
#pragma omp for<br />
        for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
            x = (double)rand_r(&amp;state2) * rns;<br />
            y = (double)rand_r(&amp;state2) * rns;<br />
#if LOOP_ITERATION &lt; 100<br />
            printf("THR[%d:%d] x,y/state,hits = %f,%f/%u,%d\n", omp_get_thread_num(), i, x, y, state2,hits);<br />
#endif<br />
            if (x*x + y*y &lt; 1) {<br />
                hits++;<br />
            }<br />
        }<br />
    }<br />
    printf("hits(%d), pi = %f\n", hits, (double)hits/LOOP_ITERATION * 4);<br />
    return 0;<br />
}</pre>#pragma omp critical 지시어는 아래의 블럭공간을 락으로 보호해준다. 즉 state1으로부터 state2라는 seed값을 생성하는 코드 부분은 각 쓰레드들이 직렬적으로 실행하게 되므로 각각 다른 seed (=state2)를 가질 수 있게 해준다.<br />
<br />
그러면 이제 수정된 버전의 확인을 위해 LOOP_ITERATION을 20번으로&nbsp;지정하고&nbsp;컴파일하여 다시 실행해보자.<br />
<pre>$ gcc -o pi_monte_omp_logging -DLOOP_ITERATION=20 -fopenmp pi_monte.c<br />
$ ./pi_monte_omp_logging<br />
Loop iteration = 20<br />
THR[0] state1/state2 = 2209777173/920556694<br />
THR[0:0] x,y/state,hits = 0.007502,0.800638/402955376,0<br />
THR[0:1] x,y/state,hits = 0.031584,0.366846/1879397754,1<br />
THR[0:2] x,y/state,hits = 0.084310,0.126795/2318644788,2<br />
THR[0:3] x,y/state,hits = 0.219188,0.054924/839319838,3<br />
THR[0:4] x,y/state,hits = 0.315477,0.640078/2081973432,4<br />
THR[0:5] x,y/state,hits = 0.423693,0.287051/4130577282,5<br />
THR[0:6] x,y/state,hits = 0.738140,0.376765/2380434428,6<br />
THR[0:7] x,y/state,hits = 0.127375,0.723816/3248220326,7<br />
THR[0:8] x,y/state,hits = 0.832496,0.256793/3481063424,8<br />
THR[0:9] x,y/state,hits = 0.939841,0.108509/3174395018,9<br />
THR[1] state1/state2 = 2209777173/754906038<br />
THR[1:10] x,y/state,hits = 0.446803,0.061819/2639594128,0<br />
THR[1:11] x,y/state,hits = 0.919237,0.585474/2439875226,1<br />
THR[1:12] x,y/state,hits = 0.122634,0.254468/2048737876,1<br />
THR[1:13] x,y/state,hits = 0.204702,0.526382/3058641982,2<br />
THR[1:14] x,y/state,hits = 0.642845,0.756832/477247192,3<br />
THR[1:15] x,y/state,hits = 0.550340,0.531024/1736039586,4<br />
THR[1:16] x,y/state,hits = 0.926036,0.217729/4074357788,5<br />
THR[1:17] x,y/state,hits = 0.266219,0.975137/2510577606,6<br />
THR[1:18] x,y/state,hits = 0.663719,0.557898/2880736800,6<br />
THR[1:19] x,y/state,hits = 0.491536,0.580296/2760522154,7<br />
hits(18), pi = 3.600000<br />
</pre>실행 결과를 보면 이제 x, y값이 각 쓰레드마다 달라지는 것을 볼 수 있다. <br />
<br />
그러면 이제 기능상의 문제는 해결되었으니 원래대로 200,000,000번의 시행 횟수로&nbsp;실행시간을 비교해보자.<br />
<pre>$ gcc -o pi_monte_omp -fopenmp pi_monte.c<br />
$ time ./pi_monte_omp<br />
Loop iteration = 200000000<br />
THR[0] state1/state2 = 319398670/265139008<br />
THR[1] state1/state2 = 319398670/1387545353<br />
hits(157082826), pi = 3.141657<br />
<br />
real    0m2.414s<br />
user    0m4.737s<br />
sys 0m0.013s</pre>싱글 쓰레드 버전이 8.6초 걸린 것에 비해 OpenMP로 돌린 버전은 2.4초로 확 줄어들었다. 원래대로라면 듀얼코어니 4.3초정도가 나와야 하겠지만, rand_r()자체가 rand()보다 가볍기 때문에 그로 인해서&nbsp;속도가 더&nbsp;빨라졌다. 즉 싱글 쓰레드 버전이라고 해도 rand()보다는 rand_r()을 사용하면 더 빠르다라는 교훈도 덤으로 알려드리는 문제였다.<br />
<br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>5. Section construct</strong></span><br />
section construct는 task level parallelism에 사용하며, 각각의 작업들이 서로 관련이 없는 경우에 사용한다.<br />
(task 레벨의 병렬화이므로 divide and conquer 형태의 문제해결에도 적용가능하다.)<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf1267a348.png" width="500" height="290.281329923" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/14/55/b0026155_4aadf1267a348.png');" /></div>그림에서 볼 수 있듯이 오렌지색의 섹션과 블루색의 섹션이 각각 독립적으로 작동하도록 구성할 수 있다.<br />
단 각각의 섹션은 누가 먼저 종료하든지 #pragram omp sections 블록의 끝에는 implicit barrier가 있으므로 대기하게 된다.<br />
<br />
참고로 병렬구간내에 섹션구간만 존재하는 경우라면 #pragma omp parallel sections로 구문을 합칠 수 있다.<br />
<br />
그러면 이번에는 앞에서 loop construct때 연습했던 2가지 pi 구하는 방법(numerical integration, monte carlo simulation)을<br />
sections를 이용해서 동시에 작동시키도록 바꿔보자.<br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>* 연습문제 (섹션별로 task를 할당하는 방법)</strong></span><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/15/55/b0026155_4aae889f94925.png" width="500" height="314.247669774" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/15/55/b0026155_4aae889f94925.png');" /></div><br />
<br />
앞에서 Single threaded 버전으로 만들었던 소스코드들을 연달아 붙여두면 된다.<br />
우선 Numerical Integration 방법의 소스코드를 다시 보자.<br />
<br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이번에는 Monte Carlo simulation방법을 다시 보겠다.<br />
<br />
<pre>#include &lt;stdlib.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;math.h&gt;<br />
#define LOOP_ITERATION 200000000<br />
int    hits;<br />
<br />
int main()<br />
{<br />
    int 	i;<br />
    double 	x, y, rns;<br />
    time_t	t_now;<br />
<br />
    printf("Loop iteration = %ld\n", (long)LOOP_ITERATION);<br />
    rns = 1.0/(double)RAND_MAX;<br />
<br />
    t_now = time(0);<br />
    srand((unsigned int)t_now);<br />
    for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
        x = (double)rand() * rns;<br />
        y = (double)rand() * rns;<br />
        if (x*x + y*y &lt; 1) {<br />
            hits++;<br />
        }<br />
    }<br />
    printf("pi = %f\n", 4*(double)hits/LOOP_ITERATION);<br />
    return 0;<br />
}</pre>이제 이 2개의 소스코드를 합쳐야 한다. 주의할 점은 두번째 Monte Carlo simulation에서는 rand()함수 대신에 rand_r()을 사용하는 것을 잊지 말아야 한다. 항상 Non-multi-threaded model을 Multi-threaded model로 바꿀때는 MT-safe function이나 reentrant function으로 골라 써야 한다는 점이다.<br />
<br />
그리고 false-sharing문제를 피하기 위해서 동일 캐시 라인에 올라갈 수 있는 전역변수나 힙 메모리를 사용하면 안된다는 점이다. 왠만하면 쓰레드 로컬 변수에서 대부분 해결하도록 하자.<br />
<br />
그러면 둘을 #pragma omp sections로 합친 코드를 보겠다. 보기 좋게 출력부분의 수정을 했으나 기본 코드는 같다.<br />
<br />
<pre>#define _XOPEN_SOURCE   600<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;unistd.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;time.h&gt;<br />
#include &lt;sys/times.h&gt;<br />
#include &lt;omp.h&gt;<br />
#ifndef LOOP_ITERATION<br />
#define LOOP_ITERATION 200000000<br />
#endif<br />
<br />
double  pi[2];<br />
int main()<br />
{<br />
    clock_t     start_m, end_m;<br />
    clock_t     sc_clk_tck = sysconf(_SC_CLK_TCK);<br />
    start_m = times(NULL);<br />
    printf("LOOP ITERATION = %ld\n",(long)LOOP_ITERATION);<br />
#ifdef _OPENMP<br />
    omp_set_num_threads(2);<br />
#endif<br />
#pragma omp parallel<br />
    {<br />
#pragma omp sections<br />
        {<br />
#pragma omp section <br />
            {<br />
                int     i;<br />
                double  x, step, hits;<br />
                float   t_elapsed;<br />
                clock_t start, end;<br />
                hits = 0.0;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] integration method by thread(%d)\n", omp_get_thread_num());<br />
#else<br />
                printf("[SECTION] start integration method\n");<br />
#endif<br />
                start = times(NULL); /* get clock tick */<br />
                step = 1.0/(double) LOOP_ITERATION;<br />
                for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
                    x = (i+0.5) * step;<br />
                    hits += 1.0/(1.0 + x*x);<br />
                }<br />
                pi[0] = 4 * hits * step;<br />
                end = times(NULL);<br />
                t_elapsed = (float) (end - start)/sc_clk_tck;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] end integration method by thread(%d):elapsed time(%.02f sec)\n", <br />
                        omp_get_thread_num(), t_elapsed);<br />
#else<br />
                printf("[SECTION] end integration method: elapsed time(%.02f sec)\n",<br />
                        t_elapsed);<br />
#endif<br />
            }<br />
#pragma omp section<br />
            {<br />
                int     i, state, hits;<br />
                double  x, y, rns;<br />
                float   t_elapsed;<br />
                clock_t start, end;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] monte carlo method by thread(%d)\n", omp_get_thread_num());<br />
#else<br />
                printf("[SECTION] start monte carlo method\n");<br />
#endif<br />
                start = times(NULL); /* get clock tick */<br />
                state = time(0);<br />
                rns = 1.0/(double)RAND_MAX;<br />
                hits = 0;<br />
                for (i=0; i&lt;LOOP_ITERATION; i++) {<br />
                    x = (double)rand_r((unsigned int *)&amp;state) * rns;<br />
                    y = (double)rand_r((unsigned int *)&amp;state) * rns;<br />
                    if (x*x + y*y &lt; 1) {<br />
                        hits++;<br />
                    }<br />
                }<br />
                pi[1] = (hits / LOOP_ITERATION) * 4;<br />
                end = times(NULL);<br />
                t_elapsed = (float) (end - start)/sc_clk_tck;<br />
#ifdef _OPENMP<br />
                printf("[SECTION] end monte carlo method by thread(%d): elapsed time(%.02f sec)\n",<br />
                        omp_get_thread_num(), t_elapsed);<br />
#else<br />
                printf("[SECTION] end monte carlo method: elapsed time(%.02f sec)\n",<br />
                        t_elapsed);<br />
#endif<br />
            }<br />
        }<br />
    }<br />
    end_m = times(NULL);<br />
    printf("integration PI = %.8f\n", pi[0]);<br />
    printf("monte carlo PI = %.8f\n", pi[1]);<br />
    printf("* Total elapsed time(%.02f sec)\n", (double)(end_m - start_m)/sc_clk_tck);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 실행을 해보자.<br />
<pre>$ gcc -o pi_sections_omp -fopenmp pi_sections.c<br />
$ time ./pi_sections_omp <br />
LOOP ITERATION = 200000000                                    <br />
[SECTION] integration method by thread(0)                     <br />
[SECTION] monte carlo method by thread(1)                     <br />
[SECTION] end integration method by thread(0):elapsed time(1.79 sec)<br />
[SECTION] end monte carlo method by thread(1): elapsed time(5.05 sec)<br />
integration PI = 3.14159265                                          <br />
monte carlo PI = 3.14161276                                          <br />
* Total elapsed time(5.05 sec)                                       <br />
<br />
real    0m5.052s<br />
user    0m6.858s<br />
sys     0m0.016s<br />
</pre>당연히 Numerical Integration이 더 빠르기 때문에 먼저 끝난다. 하지만 implicit barrier가 있기 때문에 대기하게 된다. Monte Carlo simulation은 더 오래 걸리기 때문에 전체 수행 시간은 Monte Carlo simulation이 끝나는 시간에 종료한다.<br />
<br />
* 오늘은 여기까지... 나머지는 다음 장에. (이것도 정말 힘들군요... 본문의 반말은 이해해주세요.)<br/><br/>tag : <a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/MT-safe" rel="tag">MT-safe</a>,&nbsp;<a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>C언어</category>
		<category>MT-safe</category>
		<category>openmp</category>
		<category>쓰레드</category>
		<category>thread</category>

		<comments>http://sunyzero.egloos.com/4229235#comments</comments>
		<pubDate>Tue, 15 Sep 2009 10:32:00 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ Fedora 11 업그레이드 후 한글입력기 작동 불가시 ]]> </title>
		<link>http://sunyzero.egloos.com/4229870</link>
		<guid>http://sunyzero.egloos.com/4229870</guid>
		<description>
			<![CDATA[ 
  Fedora11을 바로 설치하지 않고 업그레이드로 설치한 경우에<br>한글 입력기가 scim에서 ibus로 대체되지 않는 경우가 있다.<br><br>물론 scim을 그대로 사용해도 되지만, 이왕이면 brand new를 써보고 싶은 프로그래머들이 많을것이다.<br>그러나&nbsp;scim을 제거하고 ibus를 설치하고 나면 어찌된 노릇인지 KDE에서는 잘 작동하지만 <br>GNOME에서는 작동하지 않는 경우가 많다. 예를 들어 gnome-terminal, firefox ...같은 경우는 당연히 한글 입력이 안된다.<br><br>원인은&nbsp;ibus와&nbsp;연관된 패키지의 버전이 제대로 업데이트 되지 않아서이다.<br><br><strong>따라서 ibus (혹 ibus-gtk, ibus-hangul도 같이)가 설치되어있다면 일단 제거한뒤에<br>먼저 glib, glib2, glibmm24를 먼저 업데이트하고, 그 후에 ibus-gtk, ibus-hangul을 설치하라.</strong><br/><br/>tag : <a href="/tag/linux" rel="tag">linux</a>,&nbsp;<a href="/tag/fedora" rel="tag">fedora</a>,&nbsp;<a href="/tag/한글입력기" rel="tag">한글입력기</a>,&nbsp;<a href="/tag/ibus" rel="tag">ibus</a>,&nbsp;<a href="/tag/scim" rel="tag">scim</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>linux</category>
		<category>fedora</category>
		<category>한글입력기</category>
		<category>ibus</category>
		<category>scim</category>

		<comments>http://sunyzero.egloos.com/4229870#comments</comments>
		<pubDate>Tue, 08 Sep 2009 09:55:13 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #1 ]]> </title>
		<link>http://sunyzero.egloos.com/4227785</link>
		<guid>http://sunyzero.egloos.com/4227785</guid>
		<description>
			<![CDATA[ 
  <strong><span style="COLOR: #ff6600; FONT-SIZE: 130%">C언어: OpenMP를 이용한 멀티 쓰레드 프로그래밍 HOWTO #1</span></strong><br />
<br />
<strong><span style="FONT-SIZE: 130%">0. 시작하기에 앞서</span></strong><br />
병렬처리 프로그래밍을 위한 기법중 OpenMP를 이용하는 방법에 대해서 보도록 할 것이다.<br />
<br />
불과 10여년 전만 하더라도 병렬 처리를 위한 프로그래밍은 사치에 가까웠고 CPU를 여러 개 장착한 PC는 보기 힘들었다. 하지만 최근 CPU의 발달은 Hz 속도를 거의 한계에 다다르게 하였다:현재 한계는 약 3GHz 전후이다.&nbsp;물론 이론상으로는 5GHz도 가능하지만 엄청난 발열과 전력 소비로 인해서 효과적이지 못하다. 그리하여 CPU 벤더들은 Hz속도의 경쟁보다는 복수개의 코어로 진화할 수 밖에 없게 되었다.<br />
<br />
그러나 기존 프로그램은&nbsp;멀티 코어를 지원하도록 작성된 경우가 거의 없기에 심각한 문제로 대두되기 시작했다. 앞으로의 추세는 병렬 처리를 위한 멀티 쓰레드 프로그래밍이지만 이 분야가 결코 쉽지 않다는 점이 발목을 잡는다. 일반적인 native multi-thread programming은 고급 레벨이 아니고서는 작성하기 힘들어서 해당 분야 전공자가 아닌 응용 프로그래머는 손대기가 힘들었다. 예를 들어 물리나 수학, 화학에서 C, C++, fotran을 이용한 수치계산을 한다고 가정해보자. 연구중인 앨거리즘이나 휴리스틱 기법을 테스트하려고 하는데 더 빠른 계산을 위해서 고급 멀티 쓰레드 프로그래밍을 배우려고 한다면 얼마나 걸릴 것인가? 물론 천재적인 사람이라면 금방 가능하겠지만 일반적으로는 프로그래밍 배우는데 몇개월에서 반 년이상을 투자해야 제대로 할 수 있을 것이다 그러나 이는 배보다 배꼽이 더 큰 문제가 되어버린다.<br />
<br />
따라서 간단한 멀티 쓰레드 프로그래밍을 구현하기 위한&nbsp;좀 더 쉬운 adaptive multi-thread programming 기법으로 OpenMP가 나오게 되었다. OpenMP는 뛰어난 프로그래밍 실력을 아니더라도 단 몇일만 투자해도 쉽게 멀티 쓰레드 프로그래밍을 할 수 있게 도와 준다.<br />
<hr><br />
<br />
<br />
<span style="FONT-SIZE: 130%"><strong>1. OpenMP란?</strong></span><br />
멀티 쓰레드 프로그래밍을 간단하게 하기 위해서 개발된 기법.<br />
OpenMP는 컴파일러 지시자만으로서 블록을 멀티 쓰레드로 작동하게 할 수 있다.<br />
현재 대부분의 컴파일러(e.g. gcc, Intel Compiler, Microsoft visual studio 등등)는 OpenMP지시자를 지원한다.<br />
<br />
<ul><li>지원언어 : C, C++, Fortran</li><li>표준 : <a href="http://www.openmp.org/">http://www.openmp.org</a>&nbsp; (version 3.0 - May, 2008)</li></ul><br />
<br />
<strong><span style="FONT-SIZE: 130%">2. OpenMP의 시작</span></strong><br />
<br />
코드에 #pragma omp 로 시작하는 지시어를 삽입함으로서 가능해짐.<br />
<br />
<pre>#pragma omp parallel [clause[ [, ]clause] ...] new-line<br />
structured-block<br />
</pre><br />
그러면 간단한 Hello world 예제를 Openmp를 이용해서 멀티 쓰레드 프로그램으로 만들어 보자. <br />
우선 아래의 프로그램은 그냥 일반 single thread 프로그램이다.<br />
<br />
<pre># include &lt;stdio.h&gt;<br />
int main() <br />
{<br />
    printf("Hello world\n");<br />
    return 0;<br />
}</pre>여기에 OpenMP 지시어를 추가하여 멀티 쓰레드 프로그램으로 바꾸면 아래처럼 작성된다.<br />
<pre># include &lt;stdio.h&gt;<br />
int main() <br />
{<br />
#pragma omp parallel<br />
    {<br />
        printf("Hello world\n");<br />
    }<br />
    return 0;<br />
}</pre>실제로 엄청 간단하지 않은가? 그러면 컴파일 해보자.&nbsp;<br />
단 주의할 점은 OpenMP를 인식할 수 있도록&nbsp;컴파일러에 옵션을 넣어줘야 한다.<br />
<br />
<ul><li>OpenMP 컴파일 옵션 : <strong>-fopenmp</strong> (GCC인 경우), <strong>-openmp</strong> (Intel C Compiler인 경우)</li><li>윈도우즈용 비주얼 스튜디오는 알아서 컴파일 해준다.</li><br />
</ul><br />
<pre>$ gcc -fopenmp -o helloworld_omp helloworld.c<br />
$ ./helloworld_omp<br />
Hello world<br />
Hello world</pre>파일명이 helloworld.c라고 가정할 때 위와 같이 컴파일하면 된다.<br />
make를 쓸줄 안다면 "make CFLAGS=-fopenmp helloworld"라고 해도 된다.<br />
<br />
그리고 실행해보면 필자의 플랫폼에서는 2번 출력되었다. 하지만 3번이나 4번 출력되는 사람도 있을것이다.<br />
이는 기본적으로 OpenMP가 물리적 CPU개수만큼 쓰레드를 만들기 때문이다. 즉 필자는 듀얼코어를 쓰고 있다.<br />
<br />
그러면 쓰레드의 개수를 임의로 늘릴 수는 없는가? 가능하다. 환경변수나 omp_set_num_threads(int)함수를 이용하면 된다.<br />
그러면 임의로 쓰레드의 개수를 4개로 만들어 보겠다.<br />
<pre># include &lt;stdio.h&gt;<br />
# include &lt;omp.h&gt;<br />
int main() <br />
{<br />
    omp_set_num_threads(4);<br />
#pragma omp parallel<br />
    {<br />
        printf("Hello world\n");<br />
    }<br />
    return 0;<br />
}</pre>앞의 예제에서 달라진 것은 omp.h 헤더를 포함한 것과 병렬구간을 시작하기 전에 omp_set_num_threads(4)를 실행한 것이다.<br />
혹은 OMP_NUM_THREADS라는 환경변수를 세팅해도 된다. 본쉘이라면 "export OMP_NUM_THREADS=4"으로 명령하면 되겠다.<br />
<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">3. OpenMP를 이용한&nbsp;모델 구조와 decomposition<br />
</span></strong>이번에는 OpenMP를 이용한 모델구조 분류를 배우도록 하겠다. 분류는 3가지 정도 된다.(1가지가 더 있기는 한데 fortran전용이라 여기서는 설명하지 않는다.)<br />
<br />
<ul><li>loop construct</li><li>section construct</li><li>single/master construct</li></ul>loop construct는 for 루프문을 병렬처리하는 기법이고, section construct 는 블록단위로 병렬처리를 하도록 하는 방법이다. single과 master는 병렬구간에서도 한번만 실행되어야 하는 구간을 지정할 때 사용한다.<br />
<br />
<br />
<strong><span style="FONT-SIZE: 130%">4. Loop construct<br />
</span></strong><br />
<span style="COLOR: #3333ff"><strong>4.a Loop consturct 기본 형태</strong></span><br />
loop construct는 for 루프문을 병렬처리 한다고 했다. 기초적인 예제를 보자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4ca4591280.png" width="500" height="290.816326531" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4ca4591280.png');" /></div>예제에서 보면 총 8번의 루프동안 2개의 쓰레드로 병렬처리하는 것을 보여주고 있다. 물론 쓰레드가 4개라면 2개씩 처리할 것이다.<br />
그런데 위에는 병렬처리 구간이 for 루프 구간과 겹치므로 #pragma omp parallel 과 #pragma omp for를 합칠 수 있다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4cb7eee5a0.png" width="500" height="285.445420326" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200909/07/55/b0026155_4aa4cb7eee5a0.png');" /></div>합치니까 매우 깔끔해 보인다.<br />
그런데 루프문 안에서 printf()의 출력결과는 단지&nbsp;index만 출력하고 있으므로&nbsp;어떤 쓰레드가 어떤 결과를 출력하는지<br />
알 수 있는 방법이 없다. 그래서 쓰레드 번호를 찍는 것을 추가해 보도록 하자.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200909/07/55/b0026155_4aa4cdaef2fd4.png" width="500" height="286.072772898" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200909/07/55/b0026155_4aa4cdaef2fd4.png');" /></div>omp_get_thread_num()은 쓰레드 번호를 출력해 주므로 각각의 쓰레드가 어떻게 출력하는지 나타난다.<br />
그러면 수정된 예제를 컴파일 후 실행해보면 결과는 어떻게 나올까? (위의 파일명은 loop_exam1.c라고 하자)<br />
<pre>$ gcc -fopenmp -o loop_exam1 loop_exam1.c<br />
$ ./loop_exam1<br />
[1-4] Hello world<br />
[1-5] Hello world<br />
[1-6] Hello world<br />
[1-7] Hello world<br />
[0-0] Hello world<br />
[0-1] Hello world<br />
[0-2] Hello world<br />
[0-3] Hello world<br />
</pre><br />
결과를 보면 0번 쓰레드가 0~3까지 처리하고, 1번 쓰레드가 4~7까지 처리하는 것으로 나온다. 위에는 이쁘지 않게 순서가 거꾸로 출력되었지만, 이 순서는 항상 같으리라고 보장할수는 없다. 따라서 출력 순서는 그냥 신경쓰지 말자.<br />
<br />
<br />
<strong><span style="COLOR: #3333ff">4.b 병렬 처리시 주의점</span></strong><br />
이번에는 예제를 하나 풀어보면서 Loop construct를 적용할 때 주의점도 보겠다. 간혹 잘못된 멀티 쓰레드 적용은 false-sharing문제나 멀티 쓰레드가 계산한 결과의 reduction처리를 빼먹어서 잘못된 값이 나올 수 있기 때문이다. <br />
<br />
풀어볼 예제는 3.141592의 숫자로 알고 있는 파이(pi) 계산을 멀티 쓰레드로 만드는 것이다. pi를 계산을 선택한 이유는 쉽게 CPU를 혹사시킬 수 있는&nbsp;방법이기 때문이다. 우선 Single threaded 버전을 한번 보자.<br />
<br />
<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>위의 pi 구하는 소스코드가 어떻게 나왔는지는 그냥 양념으로 알아두자.<br />
(생각없이 그냥 그런가보다 할 수도 있지만 이런 계산법을 알아두는 것도 고등학교때 추억을 떠올리는 즐거움이 있다.)<br />
<br />
우선 위의 pi를 구하는 공식은 그레고리 급수를 전개하기 전까지의 방법이다.<br />
그레고리는 계산의 편리함을 위해서 급수로 전개했지만, 컴퓨터가 있으니 그냥 적분값을 무수히 계산시키면 쉽다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds16.egloos.com/pds/200909/10/55/b0026155_4aa8964dc351c.png" width="500" height="204.365079365" onclick="Control.Modal.openDialog(this, event, 'http://pds16.egloos.com/pds/200909/10/55/b0026155_4aa8964dc351c.png');" /></div><br />
이제 y=1/(1+x^2)의 0~1까지의 정적분에 4를 곱하면 pi의 값이 나온다는 것을 알았다. 이를 컴퓨터를 이용해서 계산할 때는 해당 구간을 무수히 작은 사각형으로 쪼개서 더하는 numerical integration으로 구하면 쉽다. 즉&nbsp;0~1까지의 x의 값을&nbsp;n개로 쪼갠 뒤에 y와 곱한 직사각형을 더할 것이다.(이때 x의 좌표값은 중간값을 취하는 Midpoint rule을 적용하므로 0.5씩 더했다.)<br />
<br />
하지만 정의역(x)의 범위인 0~1에 대해 y의 범위는 4~2가 나오지만, numerical integration을 위해서 <br />
정의역을 n개로 나누면 n이&nbsp;작을수록 한 개의 직사각형의 크기는 엄청 작아진다.(물론 정밀도가 올라간다.)<br />
<br />
하지만&nbsp;소수점으로 너무 작아지는 x값을 사용하면 계산에도 무리가 있으므로 직사각형의 높이(y)는 그대로 두고<br />
x만 n번을 곱한 수로 치환하면 x는 1이 된다. 그리고&nbsp;n개의 직사각형을 더한 뒤 n으로 다시 나누면&nbsp;pi가 나오게 된다.<br />
<br />
이제 수학적인 부분을 다 설명했으므로 위의 pi계산 프로그램을 single thread버전으로 돌려보자.(파일명을 pi_numerical_integration.c라고 지정했다.)<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration.c">pi_numerical_integration.c</a><pre>$ make pi_numerical_integration<br />
$ time ./pi_numerical_integration<br />
PI = 3.14159265 (sum = 2513274122.87223005)<br />
<br />
real    0m8.994s<br />
user    0m8.993s<br />
sys     0m0.001s<br />
</pre>time명령으로 수행 시간을 보니 약 8.994초이며 user/sys영역에서 소비한 CPU타임도 이와 같은 것을 볼때 1개의 CPU만 사용했음을 알 수 있다. 이번에는 OpenMP를 적용하도록 소스코드를 수정하겠다. 중간에 #pragma omp parallel for 구문을 넣어주었다.<pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 컴파일 후 실행해야한다. 컴파일은 make에&nbsp;CFLAGS (Compiler flags) 변수에 openmp기능을 넣고 컴파일한다. (make를 이용하는 것이 쉬워서인데 gcc 명령어를 직접 타이핑하는게 좋다면 gcc -o pi_numerical_integration -fopenmp pi_numerical_integration.c 를 모두 타이핑하면 된다.)<br />
<pre>$ make CFLAGS="-fopenmp" pi_numerical_integration<br />
$ time ./pi_numerical_integration<br />
PI = 2.26234349 (sum = 1809874795.18165779)<br />
<br />
real    0m50.394s<br />
user    1m38.686s<br />
sys     0m0.013s<br />
</pre><br />
멀티 쓰레드를 사용했는데 실제 수행 시간은 50여초나 걸렸고, CPU는 무려 1분 38초 넘게 사용되었다. 어째서 수행 시간이 더 늘어난 것일까? 더군다나 pi값도 틀리게 출력된다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/05/55/b0026155_4af2e312d52c8.png" width="500" height="91.2305516266" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/05/55/b0026155_4af2e312d52c8.png');" /></div><br />
수행시간의 오버헤드는 True-sharing, False-sharing 때문에 발생한다. True-sharing은 복수개의 CPU가 공유 변수인 x, sum을 접근하려고 할 때 발생시키는 cache miss 오버헤드이며, 이는 정확한 값을 유지하려고 하는 동기화 오버헤드를 가져온다. False-sharing은 예제의에서 x와 sum은 연속된 공간에 있을 확률이 높기에(<span style="font-size:85%;">64 byte cache line을 사용하는 최근 Intel CPU에서는 x와 sum이 같은 cache line에 있을 가능성이 높다.</span>) x나 sum 둘 중에 하나만 update되어도 cache line단위로 업데이트 되어 무효(invalid) 시켜버리는 문제를 가져온다. 특히 위의 예제처럼 x, sum이 종종 업데이트 되는 계산구조라면 cache miss가 비약적으로 증가하여 엄청난 페널티가 있으므로 이를 회피하도록 설계를 바꿔야만 한다. <span style="color:#0000ff;">- object(minjang.egloos.com)님의 지적</span><br />
<br />
따라서 프로그래머는 True-sharing, False sharing을 피하기 위해 멀티 쓰레드가 사용하는 메모리 공간은 서로 공유하지 않도록 설계하거나, 혹은 lock을 사용하거나, 동일 cache line에 걸칠 가능성이 있는 데이터 구조에는 padding을 넣어 설계하는 방법등이 있다. (자세한 False-sharing 문제는 OS관련 책에 나오니 그 부분을 참고)<br />
<br />
예제의 경우에는 해결책으로 x와 sum을 공유하지 않도록 로컬 변수로 만들어 버리면 된다. 마침 OpenMP에서는 private(변수, ...)이란 지시어를 제공하고 있는데, private에 선언된 변수는 병렬처리 구간에서 같은 이름을 가지는 새로운 스택 변수로 선언되어 공유를 피할 수 있게 된다.<br />
<br />
그러면 private(x, sum)으로 선언해야 할까? 아니다. x는 private으로 선언하는 것이 맞지만 sum값은 작동 방법이 조금 다르다. sum은 각각의 쓰레드가 작동하여 따로 계산할지라도 끝에서는 각 쓰레드의 값을 합산하여 보정해야 되기 때문이다. 이를 위해서 보정하는 reduction(...)이 제공된다. reduction(op:변수...)로 선언하며 op에 해당하는 operand로 reduction시켜주며, reduction에 선언된 변수는 자동으로 private변수가 된다. 위의 예제는 덧셈이므로 op부분에 +를 넣었다. 플러스 이외에 사용가능한 reduction operand는 다음과 같다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200911/06/55/b0026155_4af2e8f7424ff.png" width="500" height="176.090468498" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200911/06/55/b0026155_4af2e8f7424ff.png');" /></div>그러면 private()과 reduction()을 적용한 예제를 보도록 하자.<br />
예제 파일 : <a href="http://pds17.egloos.com/pds/200910/21/55/pi_numerical_integration_omp.c">pi_numerical_integration_omp.c</a><pre>#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
int num_steps=1000000000; /* 10억번 : 너무 많으면 조금 줄이시길... */<br />
<br />
int main()<br />
{<br />
    int i;<br />
    double x, step, sum = 0.0;<br />
    step = 1.0/(double) num_steps;<br />
#pragma omp parallel for private(x) reduction(+:sum)<br />
    for (i=0; i&lt;num_steps; i++) {<br />
        x = (i+0.5) * step;<br />
        sum += 4.0/(1.0 + x*x);<br />
    }<br />
    printf("PI = %.8f (sum = %.8f)\n", step*sum, sum);<br />
    return EXIT_SUCCESS;<br />
}</pre>이제 컴파일후 실행해보면 real time이 약 CPU개수만큼 줄어드는 것을 볼 수 있다. <br />
듀얼코어인 필자 시스템에서는 절반으로 줄어들었다.<br />
<br />
<br />
<br />
<strong><span style="COLOR: #3333ff">4.c 스케줄링</span></strong><br />
앞의 for문을 여러 쓰레드가 나눌때는 정확하게 1/n (n=# of threads)로 나누는 것을 볼 수 있다.<br />
이렇게 스케줄링하는 방법은 편리하긴 하지만 때에 따라서는 언밸런스를 가져올 수 있으므로 OpenMP는 각 반복작업(iteration)을 스케줄링 하는 3가지 방법을 제공한다. 이는 #pragram omp for의 지시어의 뒤에 붙여서 사용한다.<br />
<ul><li>schedule(static [, x])</li><li>schedule(dynamic [, x])</li><li>schedule(guided [, x])</li></ul><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2e9fb389a9.png" width="500" height="136.482939633" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2e9fb389a9.png');" /></div><br />
static 스케줄링은 round-robin 방식으로 각 쓰레드에게 작업을 할당한다.<br />
뒤의 x은 한번에 할당할 chunk의 개수로 생략하면 1로 지정된다.<br />
static 스케줄은 OpenMP의 기본 스케줄 방식이며 기본값일 경우는 x는 전체 iteration을 쓰레드의 개수로 나눈 값이 지정된다.<br />
이 방식은 각 iteration의 완료 시간이 규칙적일때 모든 쓰레드들은 비슷한 시간에 종료하게 된다.<br />
만일 각 iteration의 완료 시간이 불규칙적이라면 static 스케줄링은 좋지 못한 결과를 가져올 수도 있다.<br />
<br />
dynamic 스케줄링은 작업을 빨리 마치고 idle상태인 쓰레드에게 chunk개수만큼씩을 할당한다.<br />
마찬가지로 x가 생략되면 chunk의 개수는 1로 지정된다.<br />
dynamic은 각 chunk들의 작업 완료 시간이 불규칙적일 때 매우 유용하다.<br />
왜냐하면 쓰레드 중에 작업을 빨리 끝내는 경우가 있다면 다음 작업을 빨리 할당받아 실행할 가능성이 높기 때문이다.<br />
(단 주의할 점은 너무 적은 chunk의 개수를 할당하면 스케줄링하는 오버헤드가 더 커질 수 있으니 적당한 값을 써야 한다.)<br />
<br />
guided는 dynamic과 비슷하여 idle 상태인 쓰레드에게 먼저 chunk를 할당한다.<br />
다만 다른 점은 dynamic은 chunk의 개수가 고정인데 반해, guided는 chunk의 개수를 큰 수에서 점점 작은 수로<br />
줄여나간다는 점이다. guided는 chunk를 지수적으로 감소시키되 지정된 x의 크기 이하로는 감소시키지 않는다.(x는 생략시 1이다)<br />
chunk의 크기는 다음 공식을 따른다.<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2ee6f243c4.png" width="500" height="74.6173469388" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200911/06/55/b0026155_4af2ee6f243c4.png');" /></div><br />
<br />
<strong><span style="FONT-SIZE: 130%">-- 오늘은 여기까지... 다음 게시물로 --</span></strong><br />
<hr><br />
History<br />
* 2009.09.10 처음판<br />
* 2009.09.22 0번 소개글 등록, loop construct에 약간 살을 더 붙임<br />
* 2009.10.21 예제 파일을 따로 등록<br />
* 2009.11.05 reduction, schedule 일부 추가 (그림)<br />
* 2009.11.06 False-sharing부분 수정, True-sharing 추가 - object(minjang.egloos.com)님의 지적 <br/><br/>tag : <a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/MT-safe" rel="tag">MT-safe</a>,&nbsp;<a href="/tag/omp" rel="tag">omp</a>,&nbsp;<a href="/tag/openmp" rel="tag">openmp</a>,&nbsp;<a href="/tag/쓰레드" rel="tag">쓰레드</a>,&nbsp;<a href="/tag/thread" rel="tag">thread</a>			 ]]> 
		</description>
		<category>책 강의 PDF,PPT</category>
		<category>C언어</category>
		<category>MT-safe</category>
		<category>omp</category>
		<category>openmp</category>
		<category>쓰레드</category>
		<category>thread</category>

		<comments>http://sunyzero.egloos.com/4227785#comments</comments>
		<pubDate>Sat, 05 Sep 2009 14:42:50 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ C언어:FTP 사이트 검색 프로그램 (curl 라이브러리 사용) ]]> </title>
		<link>http://sunyzero.egloos.com/4208261</link>
		<guid>http://sunyzero.egloos.com/4208261</guid>
		<description>
			<![CDATA[ 
  출처: 본인 (비상업적 용도 복제 허용, 소스코드는 당연히&nbsp;apache 라이선스)<br><br>일을 하다보니 FTP 사이트에서 특정 파일명을 검색할 일이 있었는데 비슷한 기능을 찾다보니 도저히 찾을 수 없었다. 몇몇 프로그램이 기능은 있는데 원하는 형태로 출력을 바꿀 수 없어서 그냥 만들기로 결심했다.<br><br>원래 쓰던 curl 라이브러리를 이용해서 간단하게 제작했고, 비슷한 기능이 필요한 사람들을 위해서 글을 쓴다. (원래 만든 프로그램은 모듈형식으로 만들었기 때문에 이를 사용할 수 있는 개별 유틸리티로 만들기 위해 코드를 슬림하게 줄였다(기능도 줄였다). 따라서 필요한 기능이 있다면&nbsp;각자 본인들이&nbsp;수정해서 만드시길...)<br><br>소스코드 : <a href="http://pds16.egloos.com/pds/200908/12/55/curl_ftpdir.c">curl_ftpdir.c</a>&nbsp;(curl 라이브러리를 사용했으므로 -lcurl 로 링킹해주는 것을 잊지 마시길...)<br><br><br><strong>0. 기능설명</strong><br>FTP&nbsp;사이트에서 특정 문자열(or 정규표현식)에 맞는 파일명을 찾아내는 기능.<br><br><strong>1. 사용법</strong><br><img class="image_left" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200908/12/55/b0026155_4a82d011201b8.jpg" width="400" height="122.170542636" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200908/12/55/b0026155_4a82d011201b8.jpg');" align="left" /> <div style="TEXT-ALIGN: left"></div><br><br><br><br><br><br><br><br><br><br><br><strong>curl_ftpdir [-s "string or expression"] [-u userid:password] [-a] [-l] [-g] &lt;FTP URL&gt;</strong><br><br>유닉스 표준 명령행 문법에 따라서 [ ] 괄호는 optional field이고, &lt; &gt;나 무괄호는 required field입니다. 즉 최소한으로 FTP URL만 입력되어도 작동은 합니다.<br><br><strong>-s "string or expression"&nbsp;</strong><br>해당 FTP URL주소 아래에서 검색할 정확한 파일명이나 정규표현식을 사용합니다. 정규표현식은 POSIX 표준 형식을 지원합니다.<br><br><strong>-u userid:password&nbsp;</strong><br>FTP 사이트에 접속할 때 사용할 유저명과 패스워드로서&nbsp;콜론을 구분자로 사용하여&nbsp;"userid:password"의 형식으로 입력합니다.<br>이 옵션을 사용하지 않으면 anonymous (익명 FTP)로 접속합니다.<br><br><strong>-a</strong><br>매칭된 파일만을 출력합니다. 기본적으로 이 유틸리티는&nbsp;FTP URL의&nbsp;모든 파일명을 보여주고, 구분하기 위해 매치된 파일명만 밝게 Yellow색상으로 표시합니다. 그러나 이 옵션이 사용되면 검색옵션(-s ...)에 매치된 파일명만 보여주고, 매치되지 않은 나머지 파일은 표시하지 않습니다.<br><br><strong>-l<br></strong>하이라이트 기능을 끕니다. 이 기능은 ANSI 컬러를 이용하여 출력하는 것을 사용하지 않습니다. (옵션은 small L입니다.)<br><br><strong>-g</strong><br>디버깅 기능을 사용합니다. 작동하는 동안의 FTP 명령어들이 어떻게 작동되는지 보여주고, 마지막에 검색한 URL의 하위 디렉토리 리스트도 보여줍니다.<br><br><strong>&lt;FTP URL&gt;</strong><br>URL은 꼭 ftp:// 으로 시작하여야 합니다. 예를 들어 <a href="ftp://ftp.sunyzero.com/">ftp://ftp.sunyzero.com/</a>&nbsp;이라든지 <a href="ftp://192.168.0.10/work/">ftp://192.168.0.10/work/</a> 라든지의 형식으로 입력합니다.<br><br><br><strong>2. 사용예</strong><br><br><img class="image_left" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds17.egloos.com/pds/200908/12/55/b0026155_4a82d03aba810.jpg" width="400" height="311.598746082" onclick="Control.Modal.openDialog(this, event, 'http://pds17.egloos.com/pds/200908/12/55/b0026155_4a82d03aba810.jpg');" align="left" /><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>위 그림은 <a href="ftp://192.168.0.10/work/">ftp://192.168.0.10/work/</a>&nbsp;의 밑에서 c언어 소스 파일만 찾아내는 것입니다. 유저명과 암호는 sunyzero에 tjsdud을 사용했습니다. Yellow로 표기되는 것은 매칭되어 찾은 파일이고, Gray색으로 나오는 것은 매칭되지 않은 파일입니다. 여기에 -a 옵션을 주면&nbsp;매칭된 Yellow색 파일만 출력합니다.<br><br><img class="image_left" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200908/12/55/b0026155_4a82d07e7660d.jpg" width="400" height="140.199335548" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200908/12/55/b0026155_4a82d07e7660d.jpg');" align="left" /><br><br><br><br><br><br><br><br><br><br><br><br><br>매칭된 파일명만 나오기 훨씬 보기가 편합니다. 만일 색상이 눈에 거슬리면 -l 옵션으로 high-light기능을 끄면 됩니다.<br>이번에는 -g 옵션으로 디버깅 모드로 작동하도록 하겠습니다. 이 경우에는 FTP 명령어나 모든 과정이 출력됩니다.<br><br><img class="image_left" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds15.egloos.com/pds/200908/12/55/b0026155_4a82d1b62bdc4.png" width="400" height="761.57480315" onclick="Control.Modal.openDialog(this, event, 'http://pds15.egloos.com/pds/200908/12/55/b0026155_4a82d1b62bdc4.png');" align="left" /><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>사용법은 전부 보았습니다. 필요하신 분은 유용하게 사용하기 바랍니다. <br>소스코드를 다운로드하지않고 그냥 보시려면 아래 "이어지는 내용"부분을 클릭하시기 바랍니다.<br>아래 "이어지는 내용"은&nbsp;문법표기 된&nbsp;보기편한 소스코드입니다.(vim TOhtml 기능으로 출력한 내용입니다.)<br><br><br /><br />curl_ftpdir.c 소스코드<br><br><table width="95%" bgcolor="#666666"><tbody><tr><td><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;stdio.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;stdlib.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;unistd.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;string.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;curl/curl.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;curl/types.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;curl/easy.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;regex.h&gt;</span><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;errno.h&gt;</span><br><br><span style="COLOR: #ff6060"><b>#include </b></span><span style="COLOR: #00ffff">&lt;curses.h&gt;</span><br><span style="COLOR: #ff6060"><b>#define COLOR_BOLD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b></span><span style="COLOR: #ffff00">1</span><br><span style="COLOR: #ff6060"><b>#define COLOR_FINDFILE&nbsp;&nbsp;</b></span><span style="COLOR: #ffff00">30</span><span style="COLOR: #ff6060"><b>+COLOR_YELLOW</b></span><br><span style="COLOR: #ff6060"><b>#define COLOR_DIRECTORY </b></span><span style="COLOR: #ffff00">30</span><span style="COLOR: #ff6060"><b>+COLOR_BLUE</b></span><br><span style="COLOR: #ff6060"><b>#define COLOR_DEBUGMSG1 </b></span><span style="COLOR: #ffff00">30</span><span style="COLOR: #ff6060"><b>+COLOR_RED</b></span><br><span style="COLOR: #ff6060"><b>#define COLOR_DEBUGMSG2 </b></span><span style="COLOR: #ffff00">30</span><span style="COLOR: #ff6060"><b>+COLOR_CYAN</b></span><br>/*<br>&nbsp;* add 30 to COLOR_* definition (ANSI terminal color code)<br>#define COLOR_BLACK 0<br>#define COLOR_RED&nbsp;&nbsp; 1<br>#define COLOR_GREEN 2<br>#define COLOR_YELLOW&nbsp;&nbsp;&nbsp;&nbsp;3<br>#define COLOR_BLUE&nbsp;&nbsp;4<br>#define COLOR_MAGENTA&nbsp;&nbsp; 5<br>#define COLOR_CYAN&nbsp;&nbsp;6<br>#define COLOR_WHITE 7<br>*/<br><br><br>/*&nbsp;----------------[&nbsp;&nbsp; Structures&nbsp;&nbsp; ]--------------- */<br>/*&nbsp;mode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;links userid&nbsp;&nbsp; groupid&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;size time&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filename&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>/*&nbsp;drwxr-xr-x&nbsp;&nbsp;&nbsp;&nbsp;6 500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;500&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4096 Jul 24&nbsp;&nbsp;2008 intel_training */<br><span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir {<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;mode[<span style="COLOR: #ffff00">11</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;links[<span style="COLOR: #ffff00">4</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;userid[<span style="COLOR: #ffff00">6</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;groupid[<span style="COLOR: #ffff00">6</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;size[<span style="COLOR: #ffff00">13</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;time[<span style="COLOR: #ffff00">16</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;filename[<span style="COLOR: #ffff00">128</span>];<br>};<br><span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar {<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">short</span>&nbsp;&nbsp;len;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;&nbsp; s[<span style="COLOR: #ffff00">256</span>];<br>};<br><span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list {<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;&nbsp; hostaddr[<span style="COLOR: #ffff00">256</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;regex_t&nbsp;&nbsp;&nbsp;&nbsp; regbuf_ftpfilename; /*&nbsp;for parsing ftp file list */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;&nbsp; *ftpdir_findstr;<br>&nbsp;&nbsp;&nbsp;&nbsp;regex_t&nbsp;&nbsp;&nbsp;&nbsp; regbuf_findstr;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;for searching specified filename */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;&nbsp; *ftpdir_userid;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nmem;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;number of mem */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nmem_used;&nbsp;&nbsp;/*&nbsp;number of used mem */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;idx;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;current index */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar *dir;/*&nbsp;list */<br>};<br><br><span style="COLOR: #ff6060"><b>#define FLAG_ON_DEBUG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </b></span><span style="COLOR: #ffff00">0x00000001</span><br><span style="COLOR: #ff6060"><b>#define FLAG_OFF_ALLFILES&nbsp;&nbsp; </b></span><span style="COLOR: #ffff00">0x00000002</span><br><span style="COLOR: #ff6060"><b>#define FLAG_OFF_HIGHLIGHT&nbsp;&nbsp;</b></span><span style="COLOR: #ffff00">0x00000004</span><br>/*&nbsp;----------------[&nbsp;&nbsp;Gloabal var.&nbsp;&nbsp;]--------------- */<br><span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list ftpdir_list;<br><span style="COLOR: #00ff00">char</span>&nbsp;&nbsp;&nbsp;&nbsp;curl_errbuf[<span style="COLOR: #ffff00">256</span>];<br><br><br>/*&nbsp;----------------[&nbsp;&nbsp;Func. Definition ]------------ */<br><span style="COLOR: #00ff00">size_t</span>&nbsp;curl_ftpcallback(<span style="COLOR: #00ff00">void</span>&nbsp;*buf, <span style="COLOR: #00ff00">size_t</span>&nbsp;sz_elem, <span style="COLOR: #00ff00">size_t</span>&nbsp;n_elem, <span style="COLOR: #00ff00">void</span>&nbsp;*arg);<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_init(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list);<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_push(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list, <span style="COLOR: #00ff00">char</span>&nbsp;*dirname, <span style="COLOR: #00ff00">size_t</span>&nbsp;len);<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_walk(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list);<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_parselist(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir *fdir, <span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list, <span style="COLOR: #00ff00">const</span>&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;*buf);<br><br><span style="COLOR: #00ff00">int</span>&nbsp;main(<span style="COLOR: #00ff00">int</span>&nbsp;argc, <span style="COLOR: #00ff00">char</span>&nbsp;*argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;CURL *curl;<br>&nbsp;&nbsp;&nbsp;&nbsp;CURLcode curl_ret;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i, opt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(argc == <span style="COLOR: #ffff00">1</span>) { /*&nbsp;check argc. this program require two or more argument at least. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #00ffff">&nbsp;-h</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">&nbsp;Print help message</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, argv[<span style="COLOR: #ffff00">0</span>]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">EXIT_FAILURE</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;parse command line arguments. */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>while</b></span>&nbsp;((opt = getopt(argc, argv, <span style="COLOR: #00ffff">"aghls:u:"</span>)) != -<span style="COLOR: #ffff00">1</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>switch</b></span>(opt) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'a'</span>: /*&nbsp;only print searched result files on target site */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.flag |= FLAG_OFF_ALLFILES;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>break</b></span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'g'</span>: /*&nbsp;turn on debugging message */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.flag |= FLAG_ON_DEBUG;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>break</b></span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'l'</span>: /*&nbsp;turn off highlight search */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.flag |= FLAG_OFF_HIGHLIGHT;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>break</b></span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'s'</span>: /*&nbsp;argument for searching string */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.ftpdir_findstr = strdup(optarg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>break</b></span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'u'</span>: /*&nbsp;userid:passwd */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.ftpdir_userid = strdup(optarg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>break</b></span>;<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>default</b></span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>case</b></span>&nbsp;<span style="COLOR: #ffff00">'h'</span>:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;help */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"* FTP Directory search</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"Usage : </span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #00ffff">&nbsp;[-s exp] [-u userid:password] [-a] [-l] [-g] &lt;ftp address&gt;</span><span style="COLOR: #ffff00">\n\n</span><span style="COLOR: #00ffff">"</span>, argv[<span style="COLOR: #ffff00">0</span>]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"Option: -s </span><span style="COLOR: #ffff00">\"</span><span style="COLOR: #00ffff">search string or regular expression</span><span style="COLOR: #ffff00">\"\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-u </span><span style="COLOR: #ffff00">\"</span><span style="COLOR: #00ffff">userid:password</span><span style="COLOR: #ffff00">\"\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Use anonymous ftp if not use this option.</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-a&nbsp;&nbsp;only print matched files.</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-l&nbsp;&nbsp;turn off high-light mode (ANSI color).</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-g&nbsp;&nbsp;debugging option (print ftp command message)</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"e.g. </span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #00ffff">&nbsp;-s '.*</span><span style="COLOR: #ffff00">\\</span><span style="COLOR: #00ffff">.c$' -u 'sunyzero:steven1234' ftp://192.168.0.10/work</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, argv[<span style="COLOR: #ffff00">0</span>]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"e.g. </span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #00ffff">&nbsp;-g ftp://192.168.0.10</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, argv[<span style="COLOR: #ffff00">0</span>]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">EXIT_SUCCESS</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;} /*&nbsp;end of while */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(argc != (optind+<span style="COLOR: #ffff00">1</span>)) { /*&nbsp;check target address. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"&nbsp;&nbsp;Require FTP URL(e.g. ftp://blah.glah.com</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">EXIT_FAILURE</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;} <span style="COLOR: #8080ff"><b>else</b></span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy(ftpdir_list.hostaddr, argv[optind]);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;init curl */<br>&nbsp;&nbsp;&nbsp;&nbsp;curl_global_init(CURL_GLOBAL_DEFAULT);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;((curl = curl_easy_init()) == <span style="COLOR: #ffff00">NULL</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(<span style="COLOR: #00ffff">"curl_easy_init()"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">EXIT_FAILURE</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;init dir_list */<br>&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_init(&amp;ftpdir_list);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;debugging mode? */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.flag &amp; FLAG_ON_DEBUG) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_setopt(curl, CURLOPT_VERBOSE, <span style="COLOR: #ffff00">1</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;register callback function. */<br>&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_ftpcallback);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;set initial address. */<br>&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_push(&amp;ftpdir_list, argv[optind], strlen(argv[optind]));<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.ftpdir_userid != <span style="COLOR: #ffff00">NULL</span>) {/*&nbsp;set "userid:password" if exist. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_setopt(curl, CURLOPT_USERPWD, ftpdir_list.ftpdir_userid);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;walk target URL recursivly. */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>for</b></span>&nbsp;(i=<span style="COLOR: #ffff00">0</span>; i&lt;ftpdir_list.nmem_used &amp;&amp; i &lt; <span style="COLOR: #ffff00">32</span>; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;set target URL from ftpdir_list structure. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_setopt(curl, CURLOPT_URL, ftpdir_list.dir[ftpdir_list.idx].s);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;connect the URL &amp; perform the callback function. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;((curl_ret = curl_easy_perform(curl))) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stderr</span>, <span style="COLOR: #00ffff">"[ERR] curl_easy_perform:</span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, curl_errbuf);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.idx++; /*&nbsp;next URL */<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;fflush(<span style="COLOR: #ffff00">stdout</span>);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;debugging code */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.flag &amp; FLAG_ON_DEBUG) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_walk(&amp;ftpdir_list);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;free up */<br>&nbsp;&nbsp;&nbsp;&nbsp;curl_easy_cleanup(curl);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">0</span>;<br>}<br><br>/*<br>&nbsp;* Name : curl_ftpcallback<br>&nbsp;* Argv :<br>&nbsp;* Ret&nbsp;&nbsp;: 0 (if success, -1 (if fail)<br>&nbsp;* Date : 2009-08-07 19:32:58 (금)<br>&nbsp;* User : sunyzero<br>&nbsp;* Note : curl ftp callback function<br>&nbsp;*/<br><span style="COLOR: #00ff00">size_t</span>&nbsp;curl_ftpcallback(<span style="COLOR: #00ff00">void</span>&nbsp;*buf, <span style="COLOR: #00ff00">size_t</span>&nbsp;sz_elem, <span style="COLOR: #00ff00">size_t</span>&nbsp;n_elem, <span style="COLOR: #00ff00">void</span>&nbsp;*arg)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;*p_buf = (<span style="COLOR: #00ff00">char</span>&nbsp;*)buf, *p_newline;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;buf_hostaddr[<span style="COLOR: #ffff00">256</span>];<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;len_buf_hostaddr;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;&nbsp;&nbsp;&nbsp;sz_read, sz_totread = <span style="COLOR: #ffff00">0</span>; /*&nbsp;sz_read(one line len), sz_totread(cumulative len) */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;sz_buf = (sz_elem * n_elem); /*&nbsp;sz_buf will be 1 if character */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir fdir;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;flag_matched;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;regmatch_t&nbsp;&nbsp;rm_matchtab[<span style="COLOR: #ffff00">5</span>];<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.flag &amp; FLAG_OFF_HIGHLIGHT) { /*&nbsp;highlight off */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stdout</span>, <span style="COLOR: #00ffff">"Address&nbsp;&nbsp; </span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, ftpdir_list.dir[ftpdir_list.idx].s);<br>&nbsp;&nbsp;&nbsp;&nbsp;} <span style="COLOR: #8080ff"><b>else</b></span>&nbsp;{ /*&nbsp;highlight on */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stdout</span>, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\e</span><span style="COLOR: #00ffff">[1;</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">mAddress&nbsp;&nbsp; </span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #ffff00">\e</span><span style="COLOR: #00ffff">[0;00m</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;COLOR_DIRECTORY, ftpdir_list.dir[ftpdir_list.idx].s);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>while</b></span>&nbsp;(sz_totread &lt; sz_buf) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag_matched = <span style="COLOR: #ffff00">0</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p_newline = strchr(p_buf, <span style="COLOR: #ffff00">'\n'</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sz_totread += sz_read = (p_newline - p_buf)+<span style="COLOR: #ffff00">1</span>; /*&nbsp;"+1" is newline */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(p_buf[<span style="COLOR: #ffff00">0</span>] == <span style="COLOR: #ffff00">'d'</span>) { /*&nbsp;if dir */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;memset(&amp;fdir, <span style="COLOR: #ffff00">0</span>, <span style="COLOR: #ff6060"><b>sizeof</b></span>(fdir));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_parselist(&amp;fdir, &amp;ftpdir_list, p_buf);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len_buf_hostaddr = sprintf(buf_hostaddr, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%s%s</span><span style="COLOR: #00ffff">"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_list.dir[ftpdir_list.idx].s, fdir.filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_push(&amp;ftpdir_list, buf_hostaddr, len_buf_hostaddr);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <span style="COLOR: #8080ff"><b>else</b></span>&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(p_buf[<span style="COLOR: #ffff00">0</span>] == <span style="COLOR: #ffff00">'-'</span>) { /*&nbsp;if regular file */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.ftpdir_findstr) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ftpdir_parselist(&amp;fdir, &amp;ftpdir_list, p_buf); /*&nbsp;extract file information from a ftp LIST string. */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(regexec(&amp;(ftpdir_list.regbuf_findstr), fdir.filename, <span style="COLOR: #ffff00">5</span>, rm_matchtab, <span style="COLOR: #ffff00">0</span>) != REG_NOMATCH) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;matched filename? */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;flag_matched = <span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(ftpdir_list.flag &amp; FLAG_OFF_HIGHLIGHT) { /*&nbsp;high-light off */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stdout</span>, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #00ffff">"</span>, sz_read, p_buf);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <span style="COLOR: #8080ff"><b>else</b></span>&nbsp;{ /*&nbsp;high-light on (default) */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stdout</span>, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\033</span><span style="COLOR: #00ffff">[</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">;</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">m</span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #ffff00">\033</span><span style="COLOR: #00ffff">[0;00m"</span>,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;COLOR_BOLD, COLOR_FINDFILE, sz_read, p_buf); /*&nbsp;high-light */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} <span style="COLOR: #8080ff"><b>else</b></span>&nbsp;{ /*&nbsp;other types (device or ...) */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;nothing to do. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(!(ftpdir_list.flag &amp; FLAG_OFF_ALLFILES) &amp;&amp; (flag_matched == <span style="COLOR: #ffff00">0</span>)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;print all files (default option). */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stdout</span>, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #00ffff">"</span>, sz_read, p_buf);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p_buf += sz_read;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;fflush(<span style="COLOR: #ffff00">stdout</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;sz_buf;<br>}<br><br>/*<br>&nbsp;* Name : ftpdir_init<br>&nbsp;* Argv : list (ftpdir_list structure)<br>&nbsp;* Ret&nbsp;&nbsp;: 0 (if success, -1 (if fail)<br>&nbsp;* Date : 2009-08-05 04:08:01 (수)<br>&nbsp;* User : sunyzero<br>&nbsp;* Note : init ftpdir_list<br>&nbsp;*/<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_init(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;&nbsp; buf_err[<span style="COLOR: #ffff00">256</span>]; /*&nbsp;for regerror() */<br><span style="COLOR: #ff6060"><b>#define NUM_FTPDIR_LIST_DEFAULT </b></span><span style="COLOR: #ffff00">64</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;((list-&gt;dir = (<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar *)calloc(NUM_FTPDIR_LIST_DEFAULT, <span style="COLOR: #ff6060"><b>sizeof</b></span>(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar))) == <span style="COLOR: #ffff00">NULL</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;-<span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;nmem = NUM_FTPDIR_LIST_DEFAULT;<br>&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;nmem_used = <span style="COLOR: #ffff00">0</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;idx = <span style="COLOR: #ffff00">0</span>;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;REGEX_FTPDIR_LIST : pattern for 2nd~7th column */<br><span style="COLOR: #ff6060"><b>#define REGEX_FTPDIR_LIST&nbsp;&nbsp; </b></span><span style="COLOR: #00ffff">"[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+)[ ]+([0-9]+) ([^ ]+[ ]+[^ ]+[ ]+[^ ]+) (.+)"</span><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;compile pattern to extract file information from FTP URL file list. */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(regcomp(&amp;(list-&gt;regbuf_ftpfilename), REGEX_FTPDIR_LIST, REG_EXTENDED|REG_NEWLINE)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regerror(errno, &amp;(list-&gt;regbuf_ftpfilename), buf_err, <span style="COLOR: #ff6060"><b>sizeof</b></span>(buf_err));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stderr</span>, <span style="COLOR: #00ffff">"FAIL:regcomp():</span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, buf_err);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;-<span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;compile pattern for searching if exist */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(list-&gt;ftpdir_findstr != <span style="COLOR: #ffff00">NULL</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(regcomp(&amp;(list-&gt;regbuf_findstr), list-&gt;ftpdir_findstr, REG_EXTENDED|REG_NEWLINE)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;regerror(errno, &amp;(list-&gt;regbuf_findstr), buf_err, <span style="COLOR: #ff6060"><b>sizeof</b></span>(buf_err));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fprintf(<span style="COLOR: #ffff00">stderr</span>, <span style="COLOR: #00ffff">"FAIL:regcomp():</span><span style="COLOR: #ffff00">%s</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, buf_err);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;-<span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">0</span>;<br>}<br><br>/*<br>&nbsp;* Name : ftpdir_push<br>&nbsp;* Argv :<br>&nbsp;* Ret&nbsp;&nbsp;: 0 (if success, -1 (if fail)<br>&nbsp;* Date : 2009-08-07 19:33:18 (금)<br>&nbsp;* User : sunyzero<br>&nbsp;* Note : push directory name to search list<br>&nbsp;*/<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_push(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list, <span style="COLOR: #00ff00">char</span>&nbsp;*dirname, <span style="COLOR: #00ff00">size_t</span>&nbsp;len)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar *p_varchar;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(list-&gt;nmem == list-&gt;nmem_used) { /*&nbsp;re-allocate memory if the memory list is full.&nbsp;&nbsp;*/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;((p_varchar = (<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar *)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;realloc(list-&gt;dir, (list-&gt;nmem + NUM_FTPDIR_LIST_DEFAULT)*<span style="COLOR: #ff6060"><b>sizeof</b></span>(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar))) == (<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;varchar *)<span style="COLOR: #ffff00">NULL</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;-<span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;dir = p_varchar;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;nmem += NUM_FTPDIR_LIST_DEFAULT;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;dir[list-&gt;nmem_used].len =<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(list-&gt;dir[list-&gt;nmem_used].s, <span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #00ffff">"</span>, dirname[len-<span style="COLOR: #ffff00">1</span>] == <span style="COLOR: #ffff00">'\n'</span>&nbsp;? len-<span style="COLOR: #ffff00">1</span>:len, dirname);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;append a slash(represent directory symbol) if not exist */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(list-&gt;dir[list-&gt;nmem_used].s[list-&gt;dir[list-&gt;nmem_used].len-<span style="COLOR: #ffff00">1</span>] != <span style="COLOR: #ffff00">'/'</span>) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Every URL must have last slash if directory. */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;dir[list-&gt;nmem_used].s[list-&gt;dir[list-&gt;nmem_used].len] = <span style="COLOR: #ffff00">'/'</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;dir[list-&gt;nmem_used].len++;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;list-&gt;nmem_used++;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">0</span>;<br>}<br><br><br>/*<br>&nbsp;* Name : ftpdir_parselist<br>&nbsp;* Argv :<br>&nbsp;* Ret&nbsp;&nbsp;: 0 (if success, -1 (if fail)<br>&nbsp;* Date : 2009-08-05 19:41:45 (수)<br>&nbsp;* User : sunyzero<br>&nbsp;* Note : extract file info. from the ftp file list string<br>&nbsp;*/<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_parselist(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir *fdir, <span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list, <span style="COLOR: #00ff00">const</span>&nbsp;<span style="COLOR: #00ff00">char</span>&nbsp;*buf)<br>{<br><span style="COLOR: #ff6060"><b>#define NUM_FTPDIR_FIELDS&nbsp;&nbsp; </b></span><span style="COLOR: #ffff00">8</span><br><span style="COLOR: #ff6060"><b>#define sprintf_rm(b, rm, src)&nbsp;&nbsp;sprintf(b, </b></span><span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #00ffff">"</span><span style="COLOR: #ff6060"><b>, rm.rm_eo - rm.rm_so, &amp;src[rm.rm_so])</b></span><br>&nbsp;&nbsp;&nbsp;&nbsp;regmatch_t&nbsp;&nbsp;rm_matchtab[NUM_FTPDIR_LIST_DEFAULT];<br><br>&nbsp;&nbsp;&nbsp;&nbsp;memset(fdir, <span style="COLOR: #ffff00">0</span>, <span style="COLOR: #ff6060"><b>sizeof</b></span>(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir));<br>&nbsp;&nbsp;&nbsp;&nbsp;strncpy(fdir-&gt;mode, buf, <span style="COLOR: #ffff00">10</span>); /*&nbsp;1st column : file mode (filetype + unix mode) */<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(regexec(&amp;(list-&gt;regbuf_ftpfilename), &amp;buf[<span style="COLOR: #ffff00">10</span>], NUM_FTPDIR_LIST_DEFAULT, rm_matchtab, <span style="COLOR: #ffff00">0</span>) == REG_NOMATCH) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;-<span style="COLOR: #ffff00">1</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*sprintf(fdir-&gt;links, "%.*s", rm_matchtab[1].eo - rm_matchtab[1].so, buf[rm_matchtab[1].so]);*/<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;links, rm_matchtab[<span style="COLOR: #ffff00">1</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>]));&nbsp;&nbsp;/*&nbsp;2nd col. : hard links */<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;userid, rm_matchtab[<span style="COLOR: #ffff00">2</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>])); /*&nbsp;3rd col. : user id */<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;groupid, rm_matchtab[<span style="COLOR: #ffff00">3</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>]));/*&nbsp;4th col. : group id */<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;size, rm_matchtab[<span style="COLOR: #ffff00">4</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>]));&nbsp;&nbsp; /*&nbsp;5th col. : file size */<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;time, rm_matchtab[<span style="COLOR: #ffff00">5</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>]));&nbsp;&nbsp; /*&nbsp;6th col. : time */<br>&nbsp;&nbsp;&nbsp;&nbsp;sprintf_rm(fdir-&gt;filename, rm_matchtab[<span style="COLOR: #ffff00">6</span>], (&amp;buf[<span style="COLOR: #ffff00">10</span>]));/*&nbsp;7th col.: filename */<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">0</span>;<br>}<br><br><br>/*<br>&nbsp;* Name : ftpdir_walk<br>&nbsp;* Argv :<br>&nbsp;* Ret&nbsp;&nbsp;: 0 (if success, -1 (if fail)<br>&nbsp;* Date : 2009-08-05 19:41:10 (수)<br>&nbsp;* User : sunyzero<br>&nbsp;* Note : walk list to debug ftpdir_list.<br>&nbsp;*/<br><span style="COLOR: #00ff00">int</span>&nbsp;ftpdir_walk(<span style="COLOR: #00ff00"><b>struct</b></span>&nbsp;ftpdir_list *list)<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #00ff00">int</span>&nbsp;i;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>if</b></span>&nbsp;(list-&gt;flag &amp; FLAG_ON_DEBUG) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//printf("\e[1;%dm==========&nbsp;&nbsp;Found Directory List&nbsp;&nbsp;==========\e[0;00m\n", COLOR_DEBUGMSG);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\e</span><span style="COLOR: #00ffff">[1;</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">m==========&nbsp;&nbsp;Found Directory List&nbsp;&nbsp;==========</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, COLOR_DEBUGMSG1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\e</span><span style="COLOR: #00ffff">[1;</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">m"</span>, COLOR_DEBUGMSG2);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"* Number of URL : </span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">/</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, list-&gt;nmem_used, list-&gt;nmem);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>for</b></span>&nbsp;(i=<span style="COLOR: #ffff00">0</span>; i&lt;list-&gt;nmem_used; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"[</span><span style="COLOR: #ffff00">%d</span><span style="COLOR: #00ffff">] </span><span style="COLOR: #ffff00">%.*s</span><span style="COLOR: #ffff00">\n</span><span style="COLOR: #00ffff">"</span>, i, list-&gt;dir[i].len, list-&gt;dir[i].s);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(<span style="COLOR: #00ffff">"</span><span style="COLOR: #ffff00">\e</span><span style="COLOR: #00ffff">[0;00m"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;fflush(<span style="COLOR: #ffff00">stdout</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="COLOR: #8080ff"><b>return</b></span>&nbsp;<span style="COLOR: #ffff00">0</span>;<br>}<br></td></tr></tbody></table><br/><br/>tag : <a href="/tag/C언어" rel="tag">C언어</a>,&nbsp;<a href="/tag/curl" rel="tag">curl</a>,&nbsp;<a href="/tag/FTP" rel="tag">FTP</a>,&nbsp;<a href="/tag/정규표현식" rel="tag">정규표현식</a>,&nbsp;<a href="/tag/REGEX" rel="tag">REGEX</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>C언어</category>
		<category>curl</category>
		<category>FTP</category>
		<category>정규표현식</category>
		<category>REGEX</category>

		<comments>http://sunyzero.egloos.com/4208261#comments</comments>
		<pubDate>Mon, 10 Aug 2009 11:31:32 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
	<item>
		<title><![CDATA[ 솔라리스 10 에서 svc 제어하기: inetadm, svcadm, svccfg ]]> </title>
		<link>http://sunyzero.egloos.com/4197997</link>
		<guid>http://sunyzero.egloos.com/4197997</guid>
		<description>
			<![CDATA[ 
  원출처 : 본인 홈페이지(http://www.sunyzero.com/zboard/view.php?id=sunycomputer&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=163 )<br />
홈페이지 폐쇄로 인해서 옮겨옴. (비상업적 용도일시 복제 허용)<br />
<br />
솔라리스(Solaris) 10 에서는 기존과 다르게 서비스를 /etc/rc.* 으로 제어하지 않고, 대부분의 서비스를 svc 라는 것으로 제어한다.<br />
<br />
아마도 Windows 의 영향을 받은거 같은데, 사용하기가 귀찮아서 별로 좋지는 않다. <br />
그래도 기존의 /etc/init.d 의 실행스크립트보다 이녀석을 사용해야 하기때문에 사용하는 법을 알아두도록 하죠.<br />
<br />
우선 사용하는 프로그램의 종류를 살펴보자.<br />
<br />
<strong>0. Utilties for facility control</strong><br />
svcs : (svc status)현재 서비스들(데몬서비스)의 상태를 보게 해준다.<br />
svcadm : svc 들을 제어한다. 재시작, 설정변경들이 가능하다.<br />
svccfg : svcadm 의 interactive 버전이라고 생각하면 된다.<br />
inetadm : inetd.conf 를 대신하여 inet 관련 서비스들을 제어한다. 당연히 데몬서비스중에 network 관련들만 제어가능하다.<br />
<br />
<br />
<strong>1. svcs</strong><br />
우선 svcs 는 실행해보면 다음과 같은 메시지가 나온다.<br />
<br />
<pre># svcs<br />
STATE          STIME       FMRI<br />
lagacy_run     12:51:42    lrc:/etc/rcS_d/S50sk98sol<br />
... (생략)<br />
online         12:51:49    svc:/network/inetd:default<br />
</pre><br />
<br />
여기서 FMRI (Fault Managed Resource Identifier) 라는 것이 등장한다. 이것은 서비스이름이 주소형식으로 되어있어서 이를 제어하는데 사용되는 URI 이름을 의미한다. 아마도 앞으로 웹주소와 통합하여 브라우저에서 제어하게 해줄것도 같다.<br />
<br />
즉 위에서 FMRI 중 svc:/network/inetd:default 를 예를 들면 svc 는 서비스데몬이라는 것을 의미하고 /network/inetd 는 카테고리가 network 분류에 있는 inetd 라는 데몬이라는 것이다. 그리고 끝의 default 는 inetd 의 기본설정에 의한 서비스라는 것이다. 이것은 설정을 여러개 만들어놓고 선택적으로 사용할 수 있게 해주는것 같은데, 귀찮아서 해보지는 않았다.<br />
<br />
자 그러면 이제 svc 를 이용해서 한번 ftp 를 보도록 하겠다.<br />
<br />
<pre># svcs | grep ftp<br />
online                  12:51:49       svc:/network/ftp:default<br />
# svcs -l ftp<br />
fmri     svc:/network/ftp:default<br />
이름    FTP server<br />
사용    참<br />
상태    online<br />
...<br />
# svcs -x<br />
svc:/application/print/server:default (LP print server)<br />
상태: disabled(2005년 10월 17일 월요일 오후 12시 51분 36초 이후)<br />
이유: 관리자가 비활성화했습니다.<br />
   참조: http://sun.com/msg/SMF-8000-05<br />
   참조: lpsched(1M)<br />
영향: 2개의 종속 서비스가실행되지 않습니다.  (목록을 보려면 -v 사용)<br />
</pre><br />
<br />
몇몇 예인데, svcs 에 대해서는 맨페이지를 참고하고, svcs -x 를 해보면 뭔가 이상한 점이 있다면 보고를 자세하게 설명해준다. <br />
<br />
이제 svcadm 을 보자.<br />
<br />
<br />
<strong>2. svcadm</strong><br />
<br />
<pre># svcadm<br />
사용법: svcadm [-v] [cmd [args ... ]}<br />
<br />
        svcadm enable [-rst] <서비스> ...       - 서비스를 활성화하고 온라인으로 설정함<br />
        svcadm disable [-st] <서비스> ...       - 서비스를 비활성화하고 오프라인으로 설정함<br />
        svcadm restart <서비스> ...             - 지정한 서비스를 다시 시작함<br />
        svcadm refresh <서비스> ...             - 서비스 구성을 다시 읽음<br />
        svcadm mark [-It] <상태> <서비스> ...   - 유지 보수 상태로 설정함<br />
        svcadm clear <서비스> ...               - 유지 보수 상태를 해제함<br />
        svcadm milestone [-d] <이정표>  - 서비스 이정표로 진행함<br />
<br />
        서비스는 FMRI, 약어 또는 fnmatch(5) 패턴을 사용하여 지정할 수 있습니다.<br />
        svc:/network/smtp:sendmail에 대한 다음 예를 참조하십시오.<br />
<br />
        svcadm <cmd> svc:/network/smtp:sendmail<br />
        svcadm <cmd> network/smtp:sendmail<br />
        svcadm <cmd> network/*mail<br />
        svcadm <cmd> network/smtp<br />
        svcadm <cmd> smtp:sendmail<br />
        svcadm <cmd> smtp<br />
        svcadm <cmd> sendmail<br />
</pre><br />
<br />
간단하게 사용법이 나온다.<br />
<br />
종종 mark, clear 를 사용할 이유가 생기는데 서비스를 구성하고, 작동시키기 전까지는 mark 로 해두고 작업하는게 좋다.<br />
그런뒤에 작업이 끝나면 clear 시켜주면 된다. enable/disable 이야 당연히 알테고...<br />
<br />
그러면 간단한 예를 보자.<br />
<br />
<pre># svcadm restart svc:/network/ftp:default<br />
</pre><br />
<br />
이건 default 설정으로 되어있는 ftp 를 재시작한다. 시작이 안되어있으면 시작할테고.<br />
<br />
<br />
<br />
<strong>3. svccfg</strong><br />
그러면 svccfg 도 보자.<br />
<br />
svccfg 는 실행하면 바로 interactive 모드로 작동한다.<br />
<br />
<pre># svccfg<br />
svc:> list<br />
system/console-login<br />
...<br />
network/rpc-100068_2-5/rpc_udp<br />
network/ftp/tcp<br />
svc:> select ftp<br />
:properties<br />
default<br />
svc:/network/ftp> listprop<br />
general                        framework<br />
general/entity_stability       astring  Unstable<br />
general/restarter              fmri     svc:/network/inetd:default<br />
inetd                          framework<br />
svc:/network/ftp> editprop<br />
...<br />
svc:/network/ftp> select default<br />
svc:/network/ftp:default> listprop <br />
general                    framework<br />
general/enabled            boolean  true<br />
inetd_start                framework<br />
inetd_start/exec           astring  "/usr/sbin/in.ftpd -l"<br />
svc:/network/ftp:default> end<br />
</pre><br />
<br />
간단하게 list 는 현재 위치에서 하위 URI를 보여주고, select 는 검색해서 같은 이름을 가지는 URI로 이동한다.<br />
URI 의 이동은 현재 위치가 우선순위가 있고 현재위치에 없으면 하위로 가다가 그래도 없으면 다시 위로 간다.<br />
listprop 는 property를 보여주고, editprop 는 에디터를 띄워서 설정을 파일로 에디팅할 수 있게 한다.<br />
crontab -e 와 비슷하다고 생각하면 된다. editprop 시에는 모두 # 로 comment 처리 되어있는데<br />
바꾸고자 하는 부분은 uncomment 한뒤에 저장하고 나오면 업데이트 된다.<br />
<br />
<br />
<strong>4. inetadm</strong><br />
inetadm 은 svcadm 에서 inet 부분만 축소해놓은 것 같은 인상을 주는데 기능은 비슷하거나 조금 많다.<br />
inetadm 도 실행하면 svcs 처럼 서비스 리스트가 주룩 나온다.<br />
<br />
<pre># inetadm<br />
ENABLED   STATE          FMRI<br />
enabled   online         svc:/network/rpc/mdcomm:default<br />
...(생략)...<br />
enabled   online         svc:/network/rpc-100068_2-5/rpc_udp:default<br />
disabled  disabled       svc:/network/ftp/tcp:default<br />
# inetadm -?<br />
사용법:<br />
  inetadm<br />
  inetadm -?<br />
  inetadm -p<br />
  inetadm -l {FMRI | 패턴}...<br />
  inetadm -e {FMRI | 패턴}...<br />
  inetadm -d {FMRI | 패턴}...<br />
  inetadm -m {FMRI | 패턴}... {name=value}...<br />
  inetadm -M {name=value}...<br />
</pre><br />
<br />
옵션을 지정하지 않으면 inetadm은 inetd의 관리 대상 서비스를 모두 나열합니다.<br />
<br />
옵션:<br />
  -?    도움말을 인쇄합니다.<br />
  -p    기본 inetd 등록 정보 값을 모두 나열합니다.<br />
  -l    inet 서비스에 대한 inetd 등록 정보 값을 모두나열합니다.<br />
  -e    inet 서비스를 활성화합니다.<br />
  -d    inet 서비스를 비활성화합니다.<br />
  -m    inet 서비스 inetd 등록 정보 값을 수정합니다.<br />
  -M    기본 inetd 등록 정보 값을 수정합니다.<br />
<br />
<br />
사용하기 엄청 쉽지 않은가? <br />
list 를 보고자 하면 inetadm -l svc:/network/ftp:default 로 속성을 보고<br />
서비스를 활성화할려면 svc:/network/ftp:default 를 enable 시키기 위해서<br />
inetadm -e svc:/network/ftp:default 라고 해주면 된다. <br />
앞의 -l 로 본 속성값중에 max_copies 를 바꾸고 싶다면<br />
inetadm -m svc:/network/ftp:default max_copies=20 라고 해주면 된다.<br />
<br />
<pre># inetadm -l svc:/network/ftp:default<br />
SCOPE    NAME=VALUE<br />
         name="ftp"<br />
         endpoint_type="stream" <br />
         proto="tcp"<br />
         isrpc=FALSE<br />
         wait=FALSE<br />
         exec="/usr/sbin/in.ftpd -l"<br />
         user="root"<br />
default  bind_addr=""<br />
default  bind_fail_max=-1<br />
default  bind_fail_interval=-1<br />
default  max_con_rate=-1<br />
default  max_copies=-1<br />
default  con_rate_offline=-1<br />
default  failrate_cnt=40<br />
default  failrate_interval=60<br />
default  inherit_env=TRUE<br />
default  tcp_trace=FALSE<br />
default  tcp_wrappers=FALSE<br />
# inetadm -m svc:/network/ftp:default max_copies=20<br />
# inetadm -l svc:/network/ftp:default<br />
SCOPE    NAME=VALUE<br />
         name="ftp"<br />
         endpoint_type="stream"<br />
         proto="tcp"<br />
         isrpc=FALSE<br />
         wait=FALSE<br />
         exec="/usr/sbin/in.ftpd -l"<br />
         user="root"<br />
default  bind_addr=""<br />
default  bind_fail_max=-1<br />
default  bind_fail_interval=-1<br />
default  max_con_rate=-1<br />
         max_copies=20<br />
default  con_rate_offline=-1<br />
default  failrate_cnt=40<br />
default  failrate_interval=60<br />
default  inherit_env=TRUE<br />
default  tcp_trace=FALSE<br />
default  tcp_wrappers=FALSE<br />
</pre><br />
<br />
참고) http://www.sun.com/bigadmin/content/selfheal/smf-quickstart.html<br />
<br />
<br/><br/>tag : <a href="/tag/UNIX" rel="tag">UNIX</a>,&nbsp;<a href="/tag/유닉스" rel="tag">유닉스</a>,&nbsp;<a href="/tag/Solaris" rel="tag">Solaris</a>,&nbsp;<a href="/tag/솔라리스" rel="tag">솔라리스</a>,&nbsp;<a href="/tag/svc" rel="tag">svc</a>			 ]]> 
		</description>
		<category>Com. Science</category>
		<category>UNIX</category>
		<category>유닉스</category>
		<category>Solaris</category>
		<category>솔라리스</category>
		<category>svc</category>

		<comments>http://sunyzero.egloos.com/4197997#comments</comments>
		<pubDate>Mon, 27 Jul 2009 08:53:02 GMT</pubDate>
		<dc:creator>SY Kim</dc:creator>
	</item>
</channel>
</rss>
