POSIX(Pthreads) 정의:Portable Operating System Interface의 약어로, 유닉스 계열 운영체제 간의 호환성을 보장하기 위해IEEE에서 책정한 표준 인터페이스 규격임.- 도입 목적:
- 이식성(
Portability): 다양한OS환경에서 동일한 스레드 코드를 수정 없이 컴파일 및 실행할 수 있도록 보장함. - 표준화: 스레드 생성 및 관리에 대한 일관된 함수 인터페이스를 제공하여 개발 안정성을 확보함.
- 이식성(
- 계층 구조: 운영체제(
OS) -> 프로세스(Process) -> 스레드(Thread) 순으로 이어지는 계층적 관리 및 실행 구조를 가짐.
- 자원 점유: 프로세스 생성 및 관리에 따른 시스템 리소스 소모량 높음.
Context Switch오버헤드: 프로세스 제어 블록(PCB) 정보의 저장 및 복원 과정에서CPU부하 수반.- 데이터 공유 한계: 독립적 주소 공간 운영으로 인해
IPC기법 도입 필수 및 통신 비용 발생.
- 실행 효율성: 프로세스 대비 경량화된 단위로
Context Switch속도 향상. - 자원 공유: 프로세스 내
Data및Heap영역을 공유하여 전역 변수 등에 직접 접근 가능. - 통신 오버헤드 감소: 별도
IPC나 파이프 없이 스레드 간 효율적인 데이터 교환 수행. - 병렬 실행 흐름: 단일 프로세스 내 복수의 실행 흐름(
Execution Flow)을 생성하여 처리량 증대.
- 실행 단위: 프로세스는 운영체제 수준의 독립적 실행 단위이며, 스레드는 프로세스 내부에 종속된 실행 흐름임.
- 메모리 구조:
- 프로세스:
Code,Data,Stack,Heap영역을 독립적으로 할당 및 운영함. - 스레드:
Stack영역만 개별 할당하며, 그 외 영역은 부모 프로세스의 자원을 공유함.
- 프로세스:
- OS 커널 직접 관리: 현대 리눅스(
NPTL) 환경에서 스레드는 커널 수준 스레드(LWP)와 1:1 매핑되므로, 실제 문맥 교환의 결정 및 수행은 운영체제 커널 스케줄러가 담당함. pthread역할: 라이브러리 차원에서 교환을 직접 수행하지 않으며, 커널 스케줄러에 정책 및 우선순위를 전달하는 인터페이스 역할을 수행함.
SCHED_OTHER(기본값): 완전 공평 스케줄러(CFS) 기반의 시분할 방식. 정적 우선순위가 아닌 시스템 부하 및 가중치(nice)에 따른 공평한 자원 배분.SCHED_FIFO/SCHED_RR: 실시간 정책으로, 명시적인 우선순위 설정이 가능함 (루트 권한 필요).
- 상속 (
Inheritance): 기본적으로 부모 스레드의 정책과 우선순위를 상속받음 (PTHREAD_INHERIT_SCHED). - 직접 지정:
pthread_attr_t객체를 초기화하여 생성 시점에 정책 및 우선순위 값을 명시적으로 부여 가능함 (PTHREAD_EXPLICIT_SCHED).
- 최초 실행 흐름: 프로세스 생성 시 운영체제에 의해 자동 생성되는 첫 번째 스레드.
- 프로세스 수명 제어: 메인 스레드 종료 시 프로세스 내 모든 하위 스레드 강제 종료됨.
pthread_t *thread: 생성된 스레드의 식별값(ID)을 저장할 변수의 주소.const pthread_attr_t *attr: 스레드 속성 지정 (기본 설정 시NULL).void *(*start_routine)(void *): 스레드가 실행할 함수(Main Routine) 포인터.void *arg: 실행 함수로 전달할 매개변수 주소 (다중 인자 전달 시 구조체 활용).
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
void* thread_main(void *arg) {
int cnt = *((int*)arg);
for (int i = 0; i < cnt; i++) {
sleep(1);
puts("running thread");
}
return NULL;
}
int main() {
pthread_t t_id;
int thread_param = 5;
// 스레드 생성 수행
if (pthread_create(&t_id, NULL, thread_main, (void*)&thread_param) != 0) {
perror("pthread_create error");
return -1;
}
sleep(10); // 실행 흐름 유지를 위한 지연
return 0;
}- 자원 소멸 특성: 스레드 함수가 반환(
return)되더라도 시스템 자원이 자동으로 완전히 소멸되지 않으므로, 명시적인 회수 절차가 필요함. pthread_join(블로킹 방식): 대상 스레드가 종료될 때까지 호출부(주로 메인 스레드)를 블로킹하며, 종료 후 자원을 회수함. 다른 작업을 병행할 수 없음.pthread_detach(비블로킹 방식): 스레드를 분리 상태로 설정하여, 스레드 종료 시 별도의 대기 없이 운영체제가 즉시 자원을 회수하도록 함. 호출부는 블로킹되지 않고 즉시 다음 코드를 수행함.
- 빌드 커맨드:
gcc thread.c -o thread -lpthread - 전처리기 지침 (
#elif):#ifdef와#endif사이에서 다중 조건을 판별할 때 사용하며,#elif defined(MACRO)형식을 권장함. - 기술 노트 (포인터 크기): 포인터 변수의 크기는 아키텍처(32bit: 4byte, 64bit: 8byte)에 따라 결정되며,
void*를 통한 인자 전달 시 시스템 아키텍처를 고려해야 함.
- 코드 영역 공유: 프로세스 내 모든 스레드는 동일한
Code영역을 공유하므로 단일 함수를 동시 실행 가능함. - 스택 분리 기반 독립성: 각 스레드는 고유한
Stack영역을 할당받아 지역 변수를 개별 관리함. - 재진입성(
Re-entrancy): 지역 변수만 사용하여 동시 호출 시에도 안전하게 동작하는 특성.
- 광범위한 락 (
Coarse-grained): 루프 전체를 잠금. 오버헤드 적으나 동시성 저하. - 세밀한 락 (
Fine-grained): 연산 단위 잠금. 동시성 높으나 시스템 콜 오버헤드 증가. - 권장: 임계 구역은 최소화하되, 동기화 함수 호출 비용과의 균형을 고려하여 설계.
// 재진입 가능: 지역 변수만 활용
int add_reentrant(int a, int b) {
int result = a + b;
return result;
}
// 뮤텍스 동기화: 전역 자원 보호
int global_sum = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void add_with_mutex(int value) {
pthread_mutex_lock(&mutex);
global_sum += value;
pthread_mutex_unlock(&mutex);
}sem_init(sem, pshared, value): 세마포어 초기화.pshared가 0이면 단일 프로세스 내 공유,value는 초기 카운트 값임.sem_destroy(sem): 할당된 세마포어 자원 해제.sem_post(sem): 세마포어 값을 1 증가시킴 (신호 송신).sem_wait(sem): 세마포어 값을 1 감소시킴. 값이 0인 경우 양수가 될 때까지 대기(블로킹).
두 개의 세마포어(sem_one: 초기값 0, sem_two: 초기값 1)를 사용하여 입력과 연산의 순차적 실행을 보장함.
read스레드 (입력 담당):- 초기 상태에서
sem_two가 1이므로sem_wait(&sem_two)를 즉시 통과하여 입력을 수행함. - 입력 완료 후
sem_post(&sem_one)을 호출하여 연산 스레드에 신호를 보냄.
- 초기 상태에서
sem_wait(&sem_two); // 입력 권한 획득 (1 -> 0)
scanf("%d", &num);
sem_post(&sem_one); // 연산 스레드에 신호 송신 (0 -> 1)accu스레드 (연산 담당):- 초기 상태에서
sem_one이 0이므로sem_wait(&sem_one)에서 대기함. read스레드로부터 신호를 받으면 연산을 수행하고sem_post(&sem_two)를 호출하여 다음 입력을 허용함.
- 초기 상태에서
sem_wait(&sem_one); // 연산 권한 대기 (1 -> 0)
sum += num;
sem_post(&sem_two); // 입력 스레드에 신호 송신 (0 -> 1)- 구분 기준: 동시 호출 시 공유 자원(전역/정적 변수) 오염 여부에 따라 결정됨.
_REENTRANT매크로: 표준 라이브러리 함수를 스레드 안전 버전(예:_r버전)으로 자동 대체 및errno등의 스레드 개별화 수행.- 워커 스레드 모델: 메인 스레드가 작업을 분배하고 하위 워커 스레드들이 병렬 연산을 수행하는 패턴. 임계 구역을 통한 결과 취합이 필수적임.
ps -efT | grep [Keyword] | wc -l: 실행 중인 스레드 개수 실시간 확인.- 숫자가 감소하는 것은 스레드가 작업을 마치고 정상 종료되었음을 의미함.
[새로운내용]