|
System Program/pthread 2014. 3. 10. 11:01
1. 쓰레드와 시그널
시그널은 정말 복잡하다. 여러가지 경우의 수를 모두 제쳐두고 간단히 말해서 아래와 같이 나는 정리하고 싶다.
시그널은 프로세스 기반으로 동작한다. 즉 시그널의 처리 기본 단위는 프로세스이다.
단 시그널 기반으로 제어가 가능한데 이 경우는 쓰레드 기반 시그널 마스크를 통해서 가능하다.
2. 시그널 마스크 제어
프로세스 기반이 아닌 쓰레드 기반으로 제어를 하고 싶으면 아래 함수를 통해서 쓰레드 전용 시그널 마스크
를 설정하여야 한다.
#include <signal.h>
int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
위 함수의 사용법은 프로세스 기반 sigprocmask() 함수와 같다.
3. 쓰레드 기반 시그널 전송
#include <signal.h>
int pthread_kill(pthread_t thread, int sig);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
위의 함수는 프로세스 내의 다른 쓰레드에 시그널 sig를 보낸다. 대상 쓰레드는 thread로 지정한다.
#include <signal.h>
int pthread_sigqueue(pthread_t thread, int sig, const union sigval value);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_kill() 과 siggueue() 의 기능을 합친 것이다.
같은 프로세스 내에 시그널에게 데이터까지 보낼 경우 위의 함수를 사용한다.
4. 마무리
프로세스 기반의 시그널을 이해하고 적용하기에도 매우 복잡하다. 이를 쓰레드 기반까지 확장하여 처리하는
응용프로그램이 얼마나 될까 쉽다. 여기서는 단지 시그널을 쓰레드에서도 다룰 수 있다는 정도만 이해하고 넘어간다.
System Program/pthread 2014. 3. 7. 14:01
1. 쓰레드 조인
pthread_create()함수를 통해 생성된 쓰레드는 다른 쓰레드에 의해 아니면 자체적으로 종료될 수 있다.
쓰레드 조인은 주 쓰레드가 추가적으로 생성된 쓰레드의 종료 상태를 확인하고
자신이 최종적으로 종료하겠다는 내포적인 의미를 갖는다.
즉 다른 장에서 기술한 것처럼 주 쓰레드가 종료하면 다른 추가적으로 생성된 쓰레드 또한 모두
강제적으로 즉시 종료한다. 쓰레드 조인은 주 쓰레드가 추가적인 쓰레드가 모든 작업을 완료할 수 있게
기다려 주며 마지막까지 살아서 모든 쓰레드의 인무가 완료될 때까지 책임지는 구조이다.
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_join() 함수의 retval 값은 pthread_exit() 함수를 통해서 설정한 값을 확인할 수 있다.
1.1. 명심해야 할 사항.
pthread_create() 함수에서 atrribute NULL로 주고 기본적으로 생성된 쓰레드는 쓰레드 조인으로
종료를 시켜줘야 한다. 그렇지 않으면 좀비 쓰레드가 계속적으로 생성되어 차후에는
더 이상 쓰레드를 생성할 수 없는 상태로 빠지게 된다.
1.2. 예제
예제는 쓰레드 생성 부분을 참조하길 바람.
2. 쓰레드 분리
기본적으로 쓰레드는 조인할 수 있다. 만약 쓰레드의 종료 상태를 주 쓰레드에서 확인할 필요가 없거나
추가적인 쓰레드의 뒷정리를 시스템에서 자동적으로 하기를 원할 때는 쓰레드를 아래 함수를 통해서 분리한다.
#include <pthread.h>
int pthread_detach(pthread_t thread);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
2.1. 주의 사항
위 쓰레드 조인에서도 기술한 것처럼 주 쓰레드가 종료되면 추가적인 쓰레드도 모두 즉시 강제적으로 종료된다.
따라서 쓰레드 분리 통해 추가적인 쓰레드를 분리하기는 했지만 분리된 쓰레드가 정상적으로 모두
일처러를 완료하기 위해서는 주 쓰레드는 종료해서는 안된다. 보통 주 쓰레드는 무한 루프를 도는 것이 일반적이다.
2.2. 예제
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// 쓰레드 함수
// 1초를 기다린후 아규먼트^2 을 리턴한다.
void *t_function(void *data)
{
char a[100000];
int num = *((int *)data);
printf("Thread Start\n");
sleep(5);
printf("Thread end\n");
}
int main()
{
pthread_t p_thread;
int thr_id;
int status;
int a = 100;
printf("Before Thread\n");
thr_id = pthread_create(&p_thread, NULL, t_function, (void *)&a);
if (thr_id < 0)
{
perror("thread create error : ");
exit(0);
}
// 식별번호 p_thread 를 가지는 쓰레드를 detach
// 시켜준다.
pthread_detach(p_thread);
pause();
return 0;
}
위의 예제는 pthread_detach()함수의 사용 예를 보기위해서 인용하였으나 일관된 쓰레드 생성 관리를
위해서 attrubute를 사용하는 것이 더 좋은 방법이라 생각된다.
System Program/pthread 2014. 3. 7. 11:59
1. 쓰레드 종료
쓰레드 종료에 대해서 크게 두가지 경우로 나누고 싶다.
1.1. 강제 종료
- main() 함수의 주 쓰레드가 종료 시에 모든 추가 쓰레드는 종료된다.
- 주 쓰레드나 추가 쓰레드 중에 exit() 함수 호출에 의한 종료 시 모든 쓰레드는 종료된다.
- pthread_cancle() 함수 호출에 의해서 종료
- pthread_kill() 함수에 의한 종료
pthread_canel() 및 pthread_kill() 함수에 대해서는 다른 장에서 자세히 살펴본다.
1.2. 쓰레드 자체 종료
- 쓰레드 콜백함수 return에 의한 종료
- 쓰레드가 pthread_exit() 호출에 의한 종료 (아래 함수 정의 참조)
#include <pthread.h>
void pthread_exit(void *retval);
pthread_exit()함수의 호출은 return을 수행하는 것과 유사하나 다음과 같은 차이가 있다.
콜백 함수에서 다른 함수를 호출하는 경우 그 호출 함수에서 pthread_exit()함수를 호출할 수 있다.
이 때 retval에 종료 상태를 리턴할 수 있다.
만약 주 쓰레드에서 pthread_exit() 함수를 호출할 경우에는 주 쓰레드는 종료하지만 다른 쓰레드는 생존한다.
2. 쓰레드 ID 비교 함수
쓰레드 종료나 다른 작업을 수행 시 유용한 pthread 함수를 아래에 소개한다.
2.1. 자기 자신의 쓰레드 ID 얻기
현재 자신의 쓰레드 ID는 아래와 함수를 통해서 알아볼 수 있다.
pthread_jon(), pthread_detach(),pthread_cancle(), pthread_kill() 함수 등에서 유용하게 사용할 수 있다.
#include <pthread.h>
pthread_t pthread_self(void);
호출 쓰레드의 쓰레드 ID를 리턴
2.2. 쓰레드 ID 비교 함수
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
두 쓰레드가 동일하면 '0'이 아닌 값을 그렇지 않으면 '0'을 리턴한다.
3. 예제
예제는 쓰레드 생성 부분의 예제 코드를 보면 pthread_exit()를 사용한 내용이 있다.
System Program/pthread 2014. 3. 7. 11:52
1. 새로운 쓰레드 생성 의미
쓰레드의 관점에서만 보면 main() 함수로 시작하는 프로세스는 주 쓰레드이다.
쓰레드를 새로 생성한다는 것은 주 쓰레드 뿐만 아니라 추가적인 쓰레드를 만드는 것이다.
2. pthread_create() 함수
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*entry_point)(void *), void *args);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
3. 예제
아래 예제는 모양만 낸 예제이다. 의도하는 바는 TS File로부터 TS Section을 뜯어온다는 개념에서 출발한 것이다.
#include <stdio.h>
#include <pthread.h>
void *SI_ProcessTSFilter(void *pData)
{
UINT16 PID = 0x00;
FILE *fp = NULL;
UINT32 nRead = 0;
UINT8 buffer[188] = {0, };
UINT32 filesize = 0;
fp = fopen("hurrycain.ts", "rb");
if(!fp)
{
printf("fopen error \n");
}
/*
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("filezie = %d \n", filesize);
*/
while(1)
{
nRead = fread(buffer, 1, 188, fp);
if(nRead < 188)
break;
if(buffer[2] == 0)
printf("TS Header = %#x %#x %#x\n", buffer[0], buffer[1], buffer[2]);
//sleep(1);
}
fclose(fp);
pthread_exit((void *) 0);
//return 0;
}
int main(int argc, char *argv[])
{
pthread_t tid;
void *res;
int s;
s = pthread_create(&tid, NULL, SI_ProcessTSFilter, NULL);
if(s != 0)
{
printf("pthread_create error \n");
}
s = pthread_join(tid, &res);
if(s != 0)
{
printf("pthread_join error \n");
}
printf("thread joined .....%ld\n", (long)res);
return 0;
}
System Program/pthread 2013. 7. 12. 18:58
우선 순위 조정을 통하여 pthread 의 동작성을 확인해 본다
#include <stdio.h>
#include <string.h>
#include <pthread.h>
static int count = 0;
pthread_attr_t attr[2];
void *threadFunc1(void *arg)
{
char *s = (char *)arg;
struct sched_param sched;
while(1)
{
count++;
printf("%s [%d]\n", s, count);
pthread_attr_getschedparam(&attr[0], &sched);
printf("threadFunc1 priority = %d\n", sched.sched_priority);
//printf("pthread_self value = %d\n", pthread_self());
//usleep(100);
}
return (void *)strlen(s);
}
void *threadFunc2(void *arg)
{
char *s = (char *)arg;
struct sched_param sched;
while(1)
{
count--;
if(count < 0)
count = 0;
printf("%s [%d]\n", s, count);
pthread_attr_getschedparam(&attr[1], &sched);
printf("threadFunc2 priority = %d\n", sched.sched_priority);
//printf("pthread_self value = %d\n", pthread_self());
//usleep(100);
}
return (void *)strlen(s);
}
int main(int argc, char *argv[])
{
printf("pthread test start !!\n");
struct sched_param sched;
pthread_t tid[2];
void *res;
int retval;
pthread_attr_init(&attr[0]);
pthread_attr_init(&attr[1]);
pthread_attr_setinheritsched(&attr[0], PTHREAD_EXPLICIT_SCHED);
pthread_attr_setinheritsched(&attr[1], PTHREAD_EXPLICIT_SCHED);
printf("Message from main() \n");
pthread_attr_getschedparam(&attr[0], &sched);
printf("threadFunc1 priority = %d\n", sched.sched_priority);
sched.sched_priority = 1;
pthread_attr_setschedparam(&attr[0], &sched);
printf("threadFunc1 priority = %d\n", sched.sched_priority);
pthread_attr_getschedparam(&attr[1], &sched);
printf("threadFunc2 priority = %d\n", sched.sched_priority);
sched.sched_priority = 90;
pthread_attr_setschedparam(&attr[1], &sched);
printf("threadFunc2 priority = %d\n", sched.sched_priority);
retval = pthread_create(&tid[0], &attr[0], threadFunc1, "threadFunc1");
if(retval != 0)
{
printf("pthread_create error\n");
}
retval = pthread_create(&tid[1], &attr[1], threadFunc2, "threadFunc2");
if(retval != 0)
{
printf("pthread_create error\n");
}
retval = pthread_join(tid[0], &res);
if(retval != 0)
{
printf("pthread_join error\n");
}
retval = pthread_join(tid[1], &res);
if(retval != 0)
{
printf("pthread_join error\n");
}
printf("tid[0] = %d Thread returned %ld\n", tid[0], (long) res);
printf("tid[1] = %d Thread returned %ld\n", tid[1], (long) res);
return 0;
}
System Program/pthread 2013. 7. 3. 15:15
클린업이란
쓰레드 취소 요청에 의해 쓰레드가 취소될 때, 경우에 따라서 정리가 필요한 부분을 정리하지 않고
쓰레드가 취소되어 종료되면 문제가 발생할 여지가 생긴다.
한 예로 뮤텍스로 묶인 임계영역을 두고 여러 쓰레드가 경쟁상태에 있을 경우, 어떤 쓰레드가
뮤텍스 락 상태로 취소되면 데드락에 빠질 수 있는 상황이 발생할 수 있다.
쓰레드 취소 요청에 대해서 정리가 필요한 부분이 있으면 아래 함수를 통해서 핸들러를 등록하여
핸들러를 통해서 정리하도록하여 문제가 발생하지 않도록 조치할 수 있다.
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void *), void *arg);
void pthread_cleanup_pop(int execute);
pthread_cleanup_push() 함수는 routine으로 지정한 함수를 클린업 핸들러로 등록하고 등록된 핸들러는
함수의 push/pop의 의미에서 유추할 수 있듯이 구현이 스택으로 구현되어 있음을 유추할 수 있다. 따라서
등록하면 스택상의 최상위에 차곡차곡 쌓이게된다.
pthread_cleanup_push() 함수의 routine 함수의 형태는 아래와 같다.
void routine(void *arg)
{
/* cleanup codes */
}
pthread_cleanup_pop() 함수의 execute 인자가 '0'이 아닌 값이 설정되면 클린업 핸들러가 pthread_cleanup_pop()
함수에서도 호출된다.
System Program/pthread 2013. 7. 2. 17:15
1. pthread attributes
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_create() 함수의 두 번째 인자인 const pthread_attr_t *attr에 NULL을 넘기면 default값이 설정된다.
Default값 이외의 값을 설정하고자 할 때, 위의 attribute설정 함수들을 사용한다.
Attribute의 설정은 이 기본값을 쓰레드 생성시 변경할 수 있지만 생성 후에는 변경 할 수 없다.
2. 예제
pthread_detach()함수 대신에 attribute 설정을 통하여 thread 생성 시, detachable하게 생성할 수 있다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "common.h"
void *SI_ProcessTSFilter(void *pData)
{
UINT16 PID = 0x00;
FILE *fp = NULL;
UINT32 nRead = 0;
UINT8 buffer[188] = {0, };
UINT32 filesize = 0;
fp = fopen("hurrycain.ts", "rb");
if(!fp)
{
printf("fopen error \n");
}
while(1)
{
nRead = fread(buffer, 1, 188, fp);
if(nRead < 188)
break;
if(buffer[2] == 0)
printf("TS Header = %#x %#x %#x\n", buffer[0], buffer[1], buffer[2]);
}
fclose(fp);
pthread_exit(0);
}
int main(int argc, char *argv[])
{
int opt;
pthread_t tid;
pthread_attr_t attr;
int s;
// Initialize attribute structure
s = pthread_attr_init(&attr);
if(s != 0)
{
printf("pthread_attr_init error \n");
exit(1);
}
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(s != 0)
{
printf("pthread_attr_setdetachstate error \n");
exit(1);
}
s = pthread_create(&tid, &attr, SI_ProcessTSFilter, NULL);
if(s != 0)
{
printf("pthread_create error \n");
exit(1);
}
s = pthread_attr_destroy(&attr);
if(s != 0)
{
printf("pthread_attr_destory error \n");
exit(1);
}
while((opt = getchar()) != 'q');
return 0;
}
그 밖의 스택사이즈/우선순위/CPU 친화도 등 attribute설정하는 함수는 여러가지 존재한다.
create생성함수와 attribute 설정 함수 등을 하나로 뭉쳐서 설계하면 더 좋은 구조가 될 것이라 본다.
System Program/pthread 2013. 5. 27. 08:15
1. 역사
1996년에 처음 리눅스에서 리눅스쓰레드(LinuxThread)를 지원했으나 현재의 모습과는 달리 Thread를 라이블러리 형태로 지원하였다. 이 형태는 POSIX표준과 약간의 불일치점이 있었는데 큰 차이는 신호처리 부분이다.
리눅스는 다양한 프로젝트를 거치면서 NPTL(Native POSIX Thread Library)이라는 새로운 표준으로 자리잡았으며 이는 레드헷 9부터 지원하기 시작했다. NPTL은 POSIX표준을 따르도록 수정하기보다는 사용단의 Thread를 커널 수준의 쓰레드로 대응하는 방안으로 초점을 맞쳐서 개선활동을 하였다.
우리가 Thread를 사용하여 User단에 기능 구현을 하지만 실제 커널은 프로세스와 쓰레드는 크게 차이는 없다. 차이는 프로세스가 Fork를 통해서 새로 생성되면 자식프로세스는 부모프로세스와 독립된 객체 반면 쓰레드는 자신이 생성한 지역변수를 제외한 프로세스가 생성한 전역변수, 파일설명자, 신호처리기, 현재 디렉토리 상태에 대한 정보를 공유한다.
2. 쓰레드마다 고유한 속성
생성된 쓰레드들은 프로세스내에 존재하고 같은 가상메모리상에 있으므로 대부분의 프로세스의 자원을 공유한다. 다만 아래와 같은 속성은 쓰레드마다 독립적으로 관리한다.
- 쓰레드 ID
- 시그널 마스크
- 쓰레드 고유 데이터
- 대체 시그널 스택
- errno 값
- 부동 소수점 환경
- 실시간 스케쥴링 정책과 우선순위
- CPU 친화도
- 능력
- 스택
3. Pthread 함수의 리턴값
시스템 호출과 일부 라이블러리 함수에서 상태를 리턴하는 전통적인 방법은 성공하면 '0'을 리턴하고 에러가 발생하면 음수를 리턴하고 errno를 설정해 에러를 알리는 구조이다.
하지만 Pthread API 함수는 성공하면 '0'을, 실패하면 양수를 리턴한다. 이 점이 주의해야 할 사항이다.
4. pthread 사용을 위한 선행 작업
1) -D_REENTRANT Define 정의
2) include pthread.h
3) -lpthread : 라이블러리 Link
* NPTL의 헤더와 라이블러리 Path
- /usr/include/nptl, /usr/lib/nptl (이 PATH는 CFLAGS나 LDFLAGS에 포함시킬 없다)
3. References
https://computing.llnl.gov/tutorials/pthreads/
http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Thread/Beginning/PthreadApiReference
System Program/pthread 2013. 1. 1. 13:54
1. 정의
쓰레들은 보통 pthread_exit() 함수를 호출하거나 콜백함수에서 리턴함으로써 종료될 때까지
병렬로 작업을 수행한다.
가끔은 특정 쓰레드의 동작을 취소하고 싶은 경우가 있다. 이 때 사용하는 함수이다.
2. 쓰레드 취소 함수
#include <pthread.h>
int pthread_cancel(pthread_t thread);
성공하면 '0'을 리턴, 에러가 발생하면 에러 번호(양수)를 리턴한다.
pthread_cancel() 함수는 즉시 리턴한다.
3. 취소 상태와 종류 설정 함수
pthread_cancel() 함수를 주 쓰레드나 어떤 쓰레드에서 취소하고자 하는 쓰레드 ID를 호출하면
그 쓰레드의 콜백함수는 취소되게 된다. 취소되는 지점은 취소 지점 가능 함수에 의해서 설정되는데
가능한 함수들의 목록은 너무 길어서 생락한다.
4. 예제
/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
void *threadFunc(void *arg)
{
int count = 0;
printf("new thread started ....\n");
int retval;
#ifdef TEST_PTHREAD_TESTCANCEL
while(1)
{
count++;
// 임의의 cancle point 지정
pthread_testcancel();
};
#else
#ifdef TEST_PTHREAD_SETCANCELSTATE
retval = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if(retval != 0)
{
perror("pthread_create : ");
exit(EXIT_FAILURE);
}
retval = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if(retval != 0)
{
perror("pthread_create : ");
exit(EXIT_FAILURE);
}
#endif
while(1)
{
printf("count = %d \n", count++);
sleep(1);
// for testing pthread_exit() return value
if(count == 10)
break;
}
#endif
pthread_exit((void *) count);
}
int main(int argc, char *argv[])
{
pthread_t tid;
int retval;
void *res;
retval = pthread_create(&tid, NULL, threadFunc, NULL);
if(retval != 0)
{
perror("pthread_create : ");
exit(EXIT_FAILURE);
}
sleep(5);
#ifndef EXIT_NORMAL
retval = pthread_cancel(tid);
if(retval != 0)
{
perror("pthread_cancle : ");
exit(EXIT_FAILURE);
}
#endif
retval = pthread_join(tid, &res);
if(retval != 0)
{
perror("pthread_join : ");
exit(EXIT_FAILURE);
}
if(res == PTHREAD_CANCELED)
{
printf("thread cancled\n");
}
else
{
printf("thread is normal exit retval = %ld \n", (long)res);
}
exit(EXIT_SUCCESS);
}
위의 예제에서 pthread_testcancel() 함수가 있는데, 이 함수는 pthread_cancel() 함수를 주 쓰레드에서 호출 후에
쓰레드가 취소될 수 있는 함수는 정해져 있다 (printf나 sleep 함수는 취소지점으로 가능한 함수이다).
만약 이런 취소 지점으로 가능한 함수가 존재하지 않을 경우, pthread_testcancel() 함수를 주기적으로 호출함으로써
쓰레드 취소지점으로 강제 설정하는 것이다.
TEST_PTHREAD_SETCANCELSTATE define 전처리 설정으로 묶어 있는 부분은 쓰레스 취소에 대한 설정을 변경할
수 있다. 자세한 내용은 참고 서적이나 사이트 참조바람.
|