수정입니다

27. Interlude : Thread API 본문

전공/운영체제

27. Interlude : Thread API

nongdamgom 2024. 1. 15. 14:12

 

Thread Creation

#include <pthread.h>

pthread_t thread;  // 미리 thread 변수 생성

...

int
pthread_create(
pthread_t * thread  // 변수 주소값 넘겨서 기록
const pthread_attr_t* attr,
void* (*start_routine)(void*),
void* arg // void pointer type => argument에 아무 type이나 올 수 있게 해줌
);


/*example*/

// integer argument
void* (*start_routine)(int),
int arg

// return an integer
int (*start_routine)(void*)
void* arg
  • attr : 여러 속성 기록(보통 null)
  • start_routine : 이 함수 기준으로 thread가 시작 & 끝
  • arg : start_routine에 넘겨질 argument

 

 

Example : Creating a Thread

#include <pthread.h>

typedef struct __myarg_t {
	int a;
	int b;
} myarg_t;

void* mythread(void* arg) {
	myarg_t* m = (myarg_t*)arg;
	printf("%d %d\n", m->a, m->b);
	return NULL;
}

int main(int argc, char* argv[]) {
	pthread_t p;
	int rc;  // thread가 잘 생성 됐는지 / 아닌지 판별

	myarg_t args;
	args.a = 10;
	args.b = 20;
	rc = pthread_create(&p, NULL, mythread, &args);
}
  • mythread() 함수가 끝나면 thread가 죽음 => 죽게 하고 싶지 않다면 while(flag)로 제어 가능
  • mythread 내에서 arg의 주소를 받아 myarg_t 형으로 변환 => 멤버 변수 출력 == 10, 20 출력

 

 

Wait for a thread to complete

void *value_ptr; // return 값의 포인터를 받을 변수
...
int pthread_join(pthread_t thread, void **value_ptr);
// void **valule_ptr => thread의 함수 실행이 끝난 return 값의 포인터를 포인터 형식으로 받음
  • pthread_join : 특정 thread가 죽을 때까지 기다리는 함수, wait()랑 비슷
  • argument로 value_ptr의 double pointer를 받아야 함
  • => *value_ptr 이라는 >original pointer 변수 값<을 변경 가능하게 해줘야 하기 때문에, pass in value가 아닌 pass in pointer를 해줘야 하고, 때문에 포인터 변수 값의 포인터를 받아야함
  • 즉, thread를 만든 process가 pthread_join을 같이 부르면, 그 thread가 어떤 함수를 실행 하고, 끝날 때 그 return 값의 pointer를 받아서 나중에 수정 같은걸 할 수 있게 해야하기 때문에 pass in pointer로 해줘야 된다는 얘기...

 

 

Waiting for Thread Completion

#include <stdio.h>
#include <pthread.h>
#include <assert.h>
#include <stdlib.h>


typedef struct __myarg_t {
	int a;
	int b;
} myarg_t;

typedef struct __myret_t {
	int x;
	int y;
} myret_t;


void* mythread(void* arg) {
	myarg_t* m = (myarg_t*)arg;
	printf("%d %d\n", m->a, m->b);
	
	myret_t* r = malloc(sizeof(myret_t));
	r->x = 1;
	r->y = 2;
	return (void *) r;
}

int main(int argc, char* argv[]) {
	int rc;
	pthread_t p;
	myret_t* m; // 쓰레기값 담겨있음

	myarg_t args;
	args.a = 10;
	args.b = 20;
	rc = pthread_create(&p, NULL, mythread, &args);
	pthread_join(p, (void**)&m);
  
	printf("returned %d %d\n", m->x, m->y);
	return 0;
}
  • mythread() routine에서 arg로 받은 값 10, 20 출력 후 
  • myret_t 라는 구조체 변수 할당 => 구조체 pointer 로 return 하면 두개 이상의 값을 return 할 수 있는 장점이 있음
  • 구조체의 멤버 변수에 값 할당 후, 구조체 시작 주소 return 
  • main 함수에서 그 구조체 변수의 시작 pointer를 가리키는 pointer에 return 값 pointer의 pointer가 오면, 원래 쓰레기 값이 담겨 있던 m에, return 값 주소를 담게 변경을 해줌 

 

 

Example : Dangerous code

void* mythread(void* arg) {
	myarg_t* m = (myarg_t*)arg;
	printf("%d %d\n", m->a, m->b);
	
	myret_t* r; // heap에 할당 안되고, stack에 할당 됨
	r->x = 1;
	r->y = 2;
	return (void *) r;
}
  • heap에 할당(malloc 사용) 하면, mythread가 끝나고 나서도, 계속 정보가 남아있는데, stack에 할당이 되면 mythread가 끝난 후 사라지거나, 아무튼 어떻게 될 지 모르기 때문에 끝나고 다시 r에 접근하면 잘못 된 값이 나올 수 있게 됨

 

 

Locks

  • 항상 적절히 초기화 돼야 함
  • static way : 매크로 사용
  • dynamic way : assert(rc == 0) => 실행 중에 0으로 초기화

 

 

Compiling and Running

prompt> gcc -o main main.c -Wall -pthread
  • 뒤에 -pthread를 붙여야 linking이 잘 된다고 함 

 

'전공 > 운영체제' 카테고리의 다른 글

36. I/O Devices  (1) 2024.01.15
28. Locks  (1) 2024.01.15
26. Concurrency : An Introduction  (1) 2024.01.15
20. Paging: Smaller Tables  (1) 2024.01.14
19. Translation Lookaside Buffers  (1) 2024.01.14