
Posix library의 pthread_create를 사용할 때 가끔 argument를 여러 개 넘겨야 할 상황에 부딪힐 수 있습니다. 이때 Global variable로 넘길 수 있겠지만, 이는 Data race를 발생 시킬 수 있으므로 argument로 넘기는 게 더 안정적일 거 같다는 생각입니다.
pthread_create의 문법 상 하나의 argument만 넘길 수 있으니 메모리가 연속적으로 이어져있는 데이터 구조를 넘기는 게 방법인 거 같습니다. C를 기준으로 구조체라고 생각합니다.
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
구조체를 넘기고 싶을 때는 Structure Padding을 주의해야합니다. Structure Padding은 간단히 설명하고 넘어가겠습니다.
예를 들어 아래의 structure가 있다고 가정하겠습니다.
struct test{
char a;
int b;
};
char는 1byte이고 int는 4byte를 할당 받기에 이러한 구조체를 pass했다면 1byte와 4byte로 쪼개면 되겠지라는 생각을 할 것입니다. 하지만 우리의 컴퓨터는 흔히 알듯이 32bit or 64bit mechine입니다. 즉, 하나의 instruction이 4byte or 8byte라는 겁니다(해당 글은 4byte 기준으로 설명하겠습니다). 그래서 한번의 CPU cycle 당 4byte 씩 읽게 되는데, 이렇게 되면 char 1byte랑 int는 3byte만 읽게 되어서 문제가 생깁니다. 이러한 배경에서 char의 크기는 Padding에 힘입어 instruction의 크기ㅔ 맞춰지게 됩니다. 즉, char는 4byte(1byte+empty 3byte), int도 4byte로 총 8byte가 되는 것이죠. 그래서 pass받은 곳에서 데이터를 쪼갤 때는 4byte씩 접근해야하는 것입니다. 이러한 Memory Level의 이해가 필요합니다. padding을 피하는 방법도 있는데, 이 이상의 자세한 사항을 추가적으로 찾아보시기를 바랍니다.
Anyway, Stucture Padding에 주의하며 pass하였다면 해당 routine에서 다시 parsing하는 것이 중요합니다. parameter의 data type은 void*입니다. 그렇기 때문에 위 test구조체를 예로 들자면,
char a = (char)*((int *)a)
이렇게 parsing해야합니다. 이를 위해서는 당연히 pointer에 대한 이해가 있어야합니다. 그리고 int와 char와 같은 data type은 그저 box, 즉 문자열이니 정수이니 그런 해석이 아니라, 메모리에서 1byte와 4byte 단위로 읽겠다면 box같은 개념이라는 것도 이해해야 합니다. 저도 이거 처음 parsing할 때는 애 많이 먹었습니다,,,
+글 쓰다가 32bit과 64bit mechine의 word size가 다른데 과연 int size 어떻게 될까? 여전히 4byte인가?라는 의문이 생겼는데 추후에 공부해서 작성해보겠습니다.
오늘도 읽어주셔서 감사합니다. 지적과 훈수는 저를 성장하게 합니다. 감사합니다!