재진입
POSIX.1에 기반한 C언 어 함수들은 단일 쓰레드 프로세스 환경을 가정하고 만들어졌다. 재 진입(Reentrancy)는 디자인 이슈가 아니었다. 그러므로 멀티 쓰레드 프로그래밍 환경에서 POSIX 함수가 재진입 가능할지를 보장할 수 없다. 멀티 쓰레드 환경에서는 사용하려는 함수가 재진입가능한지를 검토해야 한다.
예를 들어서 asctime함 수는 프로세스의 메모리영역에 공간을 할당하고, 그에 대한 포인터를 돌려준다. 데이터 영역이 독립적이지 않기 때문에 다른 쓰레드에서 asctime함수를 호출하면, 프로세스 데이터가 변해 버리는 문제가 발생한다.
다음은 재진입하지 않는 함수들이다.
001 #include <stdio.h>멀티 쓰레드 프로그램에서, 재진입을 보장하지 않는 함수를 사용했을 때 발생하는 문제를 보여주고 있다. 018과 048에서 time_str의 출력결과가 다르다. t_function 쓰레드함수에서 asctime을 호출하면서 메모리 할당된 영역의 데이터를 변경해 버렸기 때문이다.
002 #include <unistd.h>
003 #include <stdlib.h>
004 #include <pthread.h>
005 #include <time.h>
006
007 #define MAX_THREAD_NUM 1
008 void *t_function(void *data)
009 {
010 time_t current_time ;
011 struct tm *mytm;
012 char *time_str;
013
014 current_time = time((time_t *)NULL);
015 mytm = localtime(¤t_time);
016
017 time_str = asctime(mytm);
018 printf("Child Thread Start Time : %s", time_str);
019 sleep(5);
020 }
021
022 int main(int argc, char **argv)
023 {
024 pthread_t p_thread[2];
025 int thr_id;
026 int status;
027 int i = 0;
028 time_t current_time ;
029 struct tm *mytm;
030 char *time_str;
031
032 current_time = time((time_t *)NULL);
033 mytm = localtime(¤t_time);
034 time_str = asctime(mytm);
035 printf("Main Thread Start Time 1 : %s", time_str);
036 sleep(10);
037
038 for( i = 0; i < MAX_THREAD_NUM; i++)
039 {
040 thr_id = pthread_create(&p_thread[i], NULL, t_function, (void *)&i);
041 if (thr_id < 0)
042 {
043 perror("thread create error : ");
044 exit(0);
045 }
046 }
047 pthread_join(p_thread[0], NULL);
048 printf("Main Thread Start Time 2 : %s", time_str);
049 return 0;
050 }
051
- POSIX.1 프로세스 환경 관련 함수들 : getlogin(), ttyname()
- C언어 함수들 : asctime(), gmtime(), localtime()
- POSIX.1 시스템 데이터 베이스 함수 getgrgid(), getgrnam(), getpwuid(), getpwnam()
errno
errno는 external 전역 변수다. 그러므로 멀티 쓰레드 환경에서 에러를 검사하기 위한 목적으로 사용하기 힘들다. 최근에 호출한 함수의 errno값인지를 장담할 수 없기 때문이다.
재진입을 보장하기 위한 방법
- 재진입 가능한 버전의 함수 즉, _r이 붙은 함수를 사용한다.
- 뮤 텍스등으로 전역 데이터에 대한 접근을 제어한다.
- gcc의 경우 _REENTRANT 정의해서 재진입을 보장할 수 있다. 재진입 가능한 함수와 그렇지 않은 함수가 존재 할때, 재진입 가능한 함수를 링크한다. 또한 errno와 같은 external 전역 변수를 각 쓰레드 마다 사용할 수 있도록 해준다.
# gcc -o myprog myprog.c -lpthread -D_REENTRANT
재진입 가능한 사용자 정의 함수 만들기
아래의 원칙을 지켜야 한다.
- 전역변수를 사용하지 않는다.
- 전역변수의 포인터를 반환하지 않는다. 대신 인자로 데이터를 넘겨받도록 한다. POSIX의 _r계 열 함수가 이런 방식을 사용한다.
- 공유되는 자원은 접근제어를 한다.
- 재진입하지 않는 함수는 호출하지 않는다.
- 데이터 공간을 함수가 아닌 호출자가 제공하도록 한다.
병렬 프로그래밍에서 주의 해야할 문제들
ABA 문제
A 쓰레드와 B 쓰레드가 있다.
- A 쓰레드가 메모리의 값을 읽는다. 이런 저런 연산을 한다.
- B 쓰레드가 메모리의 값을 읽는다. 이런 저런 연산을 한다.
- A 쓰레드가 메모리에 연산 값을 쓴다.
- B 쓰레드가 메모리에 연산 값을 쓴다.
- A 쓰레드가 메모리의 값을 읽는다. 이런 저런 연산을 한다.
- A 쓰레드가 연산 값을 메모리에 쓴다.
- B 쓰레드가 메모리의 값을 읽는다. 이런 저런 연산을 한다.
- B 쓰레드가 메모리에 여산 값을 쓴다.
'프로그램언어 > C/C++' 카테고리의 다른 글
유용하게 사용하는 매크로 함수 (0) | 2012.03.24 |
---|---|
[AIX]dynamic library & static library (0) | 2012.03.24 |
GCC 사용법 (0) | 2012.03.24 |
xlC 컴파일 옵션 - AIX (0) | 2012.03.24 |
GCC Compile 옵션 및 과정 (0) | 2012.03.24 |