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에 포함시킬 없다)
쓰레들은 보통 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);
}
#endifwhile(1)
{
printf("count = %d \n", count++);
sleep(1);
// for testing pthread_exit() return valueif(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 전처리 설정으로 묶어 있는 부분은 쓰레스 취소에 대한 설정을 변경할