안녕하세요.
읽고 있는 책(하단 참고 도서 참고)에 내용 중 pthread 내용 조금 수정이 필요한 코드 부분이 있어 수정과 확인을 위해서 공유합니다.
1. POSIX Threads (pthread)란?
- POSIX Threads(pthreads)는 POSIX (Portable Operating System Interface) 표준을 따르는 멀티스레딩 API입니다.
- 멀티스레딩(multi-threading)을 지원하여 하나의 프로세스 내에서 여러 스레드가 동시에 실행될 수 있도록 설계되었습니다.
- Unix 계열 시스템(Linux, macOS 등)에서 널리 사용되며, C 및 C++에서 멀티스레드를 구현하는 표준 방법입니다.
2. 주요 특징
- 경량 프로세스: 스레드는 동일한 프로세스 내에서 실행되므로 메모리 공간과 리소스를 공유합니다.
- 독립 실행: 각 스레드는 독립적인 실행 흐름을 가지며, 별도의 스택을 사용합니다.
- 효율성: 프로세스 간 통신보다 스레드 간 통신이 더 빠릅니다.
- 표준화: POSIX 표준에 기반하여 작성되어 플랫폼 간 이식성이 높습니다.
3. 스레드와 프로세스의 차이
항목 | 프로세스 | 스레드 |
---|---|---|
독립성 | 서로 독립적 | 동일 프로세스 내에서 실행 |
메모리 사용 | 각 프로세스는 독립된 메모리 공간을 가짐 | 메모리와 자원을 공유 |
생성 속도 | 상대적으로 느림 | 빠름 |
통신 방식 | IPC (파이프, 소켓 등) 사용 | 공유 메모리를 통해 직접 통신 |
컨텍스트 스위칭 비용 | 비교적 높음 | 낮음 |
4. pthread 기본 함수
- pthread_create
- 새로운 스레드를 생성합니다.
- 프로토타입:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
- 매개변수:
pthread_t *thread
: 생성된 스레드의 ID를 저장할 변수const pthread_attr_t *attr
: 스레드의 속성을 지정 (NULL이면 기본 속성 사용)void *(*start_routine)(void *)
: 스레드에서 실행할 함수void *arg
: 실행 함수에 전달할 인자
- pthread_exit
- 스레드의 실행을 종료합니다.
- 프로토타입:
void pthread_exit(void *retval);
- pthread_join
- 특정 스레드의 종료를 기다립니다.
- 프로토타입:
int pthread_join(pthread_t thread, void **retval);
- pthread_self
- 현재 실행 중인 스레드의 ID를 반환합니다.
- 프로토타입:
pthread_t pthread_self(void);
- pthread_equal
- 두 스레드 ID가 같은지 비교합니다.
- 프로토타입:
int pthread_equal(pthread_t t1, pthread_t t2);
- pthread_cancel
- 특정 스레드의 실행을 취소합니다.
- 프로토타입:
int pthread_cancel(pthread_t thread);
5. 스레드 동기화
멀티스레드 환경에서는 공유 자원 접근 시 동기화가 필요합니다. POSIX 스레드는 다음과 같은 동기화 메커니즘을 제공합니다:
- 뮤텍스 (Mutex)
- 상호 배제를 통해 공유 자원에 대한 동시 접근을 방지합니다.
- 주요 함수:
pthread_mutex_init
: 뮤텍스 초기화pthread_mutex_lock
: 뮤텍스를 잠금pthread_mutex_unlock
: 뮤텍스를 해제pthread_mutex_destroy
: 뮤텍스를 해제 및 제거
- 조건 변수 (Condition Variable)
- 스레드가 특정 조건이 만족될 때까지 대기하거나 다른 스레드에 신호를 보낼 수 있게 합니다.
- 주요 함수:
pthread_cond_wait
: 조건 변수에서 대기pthread_cond_signal
: 조건 변수에 신호를 보냄pthread_cond_broadcast
: 모든 대기 중인 스레드에 신호를 보냄
- 읽기-쓰기 잠금 (Read-Write Lock)
- 읽기와 쓰기를 구분하여 동기화를 제공합니다.
- 주요 함수:
pthread_rwlock_init
: 읽기-쓰기 잠금 초기화pthread_rwlock_rdlock
: 읽기 잠금pthread_rwlock_wrlock
: 쓰기 잠금pthread_rwlock_unlock
: 잠금 해제pthread_rwlock_destroy
: 잠금 제거
6. 스레드 속성
스레드 속성은 pthread_attr_t
를 통해 설정할 수 있습니다. 주요 속성:
- 분리 상태 (Detached State)
- 스레드가 종료된 후 리소스를 자동으로 반환할지 여부를 설정합니다.
pthread_attr_setdetachstate
함수로 설정 가능:PTHREAD_CREATE_JOINABLE
(기본값):pthread_join
을 통해 종료 대기 가능PTHREAD_CREATE_DETACHED
: 자동으로 리소스 반환
- 스택 크기 (Stack Size)
- 스레드 스택 크기를 설정합니다.
pthread_attr_setstacksize
함수로 설정 가능
- 우선순위 스케줄링
- 스레드의 우선순위를 설정할 수 있습니다.
pthread_attr_setschedpolicy
및pthread_attr_setschedparam
함수로 설정 가능
7. 스레드 ID (pthread_t)
- pthread_t는 스레드 ID를 나타내는 opaque object(불투명 객체)입니다.
- 플랫폼에 따라 정수형, 포인터, 또는 구조체로 구현될 수 있습니다.
- 두 스레드 ID를 비교할 때는
pthread_equal()
을 사용해야 합니다.
8. 예제 코드 1
간단한 멀티스레드 프로그램:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* threadFunc(void* arg) {
int id = *(int*)arg;
printf("Thread %d is running\n", id);
sleep(1);
printf("Thread %d is exiting\n", id);
return NULL;
}
int main() {
pthread_t threads[3];
int thread_ids[3] = {1, 2, 3};
for (int i = 0; i < 3; i++) {
pthread_create(&threads[i], NULL, threadFunc, &thread_ids[i]);
}
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("All threads have finished\n");
return 0;
}
8. 예제 코드 2 ( 도서 내용 예제 수정 )
사용자가 특정 문자를 입력했을 때, 스레드가 생성하여 스레드 ID를 보여주고 스레드 취소 및 종료하는 프로그램
PS 출력 결과를 보면 부모 프로세서에 다른 스레드 LWP(Light Weight Process)가 생성된 것을 확인할 수 있습니다 :
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/syscall.h>
#define MAX_THREAD_NUM 100
void* newThread(void *arg){
pthread_t tid;
tid = pthread_self();
//printf("thread[%lu] start\n", tid);
pid_t lwp = syscall(SYS_gettid);
printf("pthread_self() ID: %lu, LWP (syscall): %d\n", (unsigned long)tid, lwp);
if ((unsigned long)tid == (unsigned long)lwp) {
printf("pthread_t and LWP are identical!\n");
} else {
printf("pthread_t and LWP differ.\n");
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); //
while(1){
sleep(1);
printf("thread[%lu] work\n", tid);
}//while
}//newThread
void main(void) {
int result;
pthread_t thread[MAX_THREAD_NUM];
int thread_num = 0;
printf("n: make new thread\n"
"d: delete last created thread\n"
"h: help\n"
"e: exit\n");
while(1) {
char inputChar;
scanf("%c", &inputChar);
switch(inputChar) {
case 'n': {
printf("create thread");
result = pthread_create(&thread[thread_num],NULL,newThread,NULL);
if(result) {
printf(" ERROR [%d]\n", result);
} //if
else {
printf(" SUCCESS [%lu]\n", thread[thread_num]);
thread_num++;
}//else
}//n
break;
case 'd': {
int i;
if(thread_num){
printf("delte thread");
result = pthread_cancel(thread[thread_num-1]);
if(result) {
printf(" ERROR [%d]\n", result);
} //if
else {
printf(" SUCCESS [%lu]\n", thread[thread_num-1]);
thread_num--;
}//else
}//if
else {
printf("There is no thread to delete.\n");
}//else
}//d
case 'h' : {
printf("n: make new thread\n"
"d: delete last created thread\n"
"h: help\n"
"e: exit\n");
}// h
break;
case 'e' : {
printf("Exit Program\n");
return;
}
break;
default:
break;
}//switch
}//while
}//main
2개의 터미널을 실행시켜 터미널 창 1은 프로그램 빌드와 프로그램 실행을 터미널 창 2는 프로세서와 스레드가 생성 확인을 합니다.
<터미널 창 1>
$ gcc ex_pthread.c -o ex_pthread -lpthread
$ ./ex_pthread
<터미널 창 2>
$ ps -eLf | grep ex_thread
9. 참고 사항
- 스레드는 가벼운 실행 단위이지만, 올바른 동기화 없이는 경쟁 상태(race condition)가 발생할 수 있습니다.
- 리소스 공유 시 동기화 메커니즘(뮤텍스, 조건 변수 등)을 적절히 사용해야 합니다.
- 스레드 생성 및 관리 시, 적절한 리소스 해제가 중요합니다.
<참고 자료>
1. [도서] 임베디드 소프트웨어 베이직, 12.1 쓰레드 실습 - pthread
반응형
'Embedded > RaspberryPI' 카테고리의 다른 글
자원을 공유하는 다중 쓰레드 실습 소스코드 (뮤텍스-mutex 적용) (0) | 2025.01.18 |
---|---|
라즈베리파이(Raspberry Pi) 리눅스 커널 구성 확인 및 빌드(build) 해보기 (4) | 2025.01.10 |
리눅스(Raspberry Pi)에서 램 디스크(RAMdisk) 장단점 확인 및 램 디스크 테스트 하기 (0) | 2025.01.09 |
Raspberry Pi CM4에 ModbusTCP Server 실행해 보기 - 4편(ModbusTCP server와 유량센서와 연동) (6) | 2024.12.06 |
pyinstaller로 실행파일을 만들고 강제종료(ctrl + c) 시 PYI-3170:ERROR 에러 발생 메시지 해결 방법(?) (0) | 2024.12.02 |