https://man7.org/linux/man-pages/man3/pthread_create.3.html
출처 : 리눅스 매뉴얼 페이지
1. pthread_create : create a new thread.
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg);
인자 뜯어보기
pthread_t *restrict thread
const pthrhead_attr_t * restrict
void *(*start_routine) (void *)
void *restrict arg
- restrict 가 뭐였더라 → 이걸 붙여서 매개변수로 들어오는 포인터는, 고유의 포인터이다. (컴파일러에게 두 포인터가 같은지 확인시키지 않아도 됨: 컴파일러의 최적화 작업을 줄여주는 효과. )사용법: 포인터 사용시에 주로 사용 . (*이 자료형과 restrict 사이에 위치.)
- *고유의 포인터라 함은: 그 포인터가 가리키는 값을 다른 포인터가 가리키지 않는다.
- -pthread와 컴파일과 링크 해주는 편.
- 현재 프로세스에서 pthread_create 함수를 쓰면 그 프로세스에서 쓰레드를 만들 수 있다.
- 쓰레드가 만들어지면 start_routine 함수 포인터가 사용되고,
- arg가 그 함수의 인자로 들어간다.
- thread가 종료되는 방법들:
- pthread_exit(1) : pthread_join(3)을 실행한 쓰레드(같은 프로세스 내에 있어야 함)에게 exit status value를 주면서 종료된다.
- start_routine에서 반환되는 것. (returns from start_routine). return을 반환받은 후 pthread_exit(3) 를 하는 것과 같은 효과
- canceled : (pthread_cancel (3))
- attr : pthread_attr_t 구조체를 가리키고 있음 :
- pthread를 만들어내는 시점에 여러 설정들을 결정하기 위해 갖고 있는 구조체.
- pthread_attr_init(3) 함수에 의해서 이 구조체가 초기화된다.
- NULL이 들어갈 경우 defalut attributes 상태로 쓰레드가 생성된다.
- 성공 시: (start_routine함수 반환 뒤)thread: pthred_t thread포인터가 가리키고 있는 버퍼에 new thread의 ID를 저장한다. 다른 pthread function을 호출하려고 할 때 사용되는 identifier이다.
- 처음 쓰레드가 생긴 경우 CPU-time은 0이다.
- 기타
The new thread inherits a copy of the creating thread's signal
mask (pthread_sigmask(3)). The set of pending signals for the
new thread is empty (sigpending(2)). The new thread does not
inherit the creating thread's alternate signal stack
(sigaltstack(2)).
The new thread inherits the calling thread's floating-point
environment (fenv(3)).
The initial value of the new thread's CPU-time clock is 0 (see
pthread_getcpuclockid(3)).
11. 반환 값: 성공시 0, error 시에 error number, *thread내의 내용은 undefined
12. 사용예시
: main에서 인자로 쓰레드 이름을 받아서 그 개수 만큼 쓰레드를 만들어낸다.
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct thread_info { /* Used as argument to thread_start() */
pthread_t thread_id; /* ID returned by pthread_create() */
int thread_num; /* Application-defined thread # */
char *argv_string; /* From command-line argument */
};
/* Thread start function: display address near top of our stack,
and return upper-cased copy of argv_string. */
static void *
thread_start(void *arg)
{
struct thread_info *tinfo = arg;
char *uargv;
printf("Thread %d: top of stack near %p; argv_string=%s\n",
tinfo->thread_num, (void *) &tinfo, tinfo->argv_string);
uargv = strdup(tinfo->argv_string);
if (uargv == NULL)
handle_error("strdup");
for (char *p = uargv; *p != '\0'; p++)
*p = toupper(*p);
return uargv;
}
int main(int argc, char *argv[])
{
int s, opt, num_threads;
pthread_attr_t attr;
ssize_t stack_size;
void *res;
/* The "-s" option specifies a stack size for our threads. */
stack_size = -1;
while ((opt = getopt(argc, argv, "s:")) != -1) {
switch (opt) {
case 's':
stack_size = strtoul(optarg, NULL, 0);
break;
default:
fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
argv[0]);
exit(EXIT_FAILURE);
}
}
num_threads = argc - optind;
/* Initialize thread creation attributes. */
s = pthread_attr_init(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
if (stack_size > 0) {
s = pthread_attr_setstacksize(&attr, stack_size);
if (s != 0)
handle_error_en(s, "pthread_attr_setstacksize");
}
/* Allocate memory for pthread_create() arguments. */
struct thread_info *tinfo = calloc(num_threads, sizeof(*tinfo));
if (tinfo == NULL)
handle_error("calloc");
/* Create one thread for each command-line argument. */
for (int tnum = 0; tnum < num_threads; tnum++) {
tinfo[tnum].thread_num = tnum + 1;
tinfo[tnum].argv_string = argv[optind + tnum];
/* The pthread_create() call stores the thread ID into
corresponding element of tinfo[]. */
s = pthread_create(&tinfo[tnum].thread_id, &attr,
&thread_start, &tinfo[tnum]);
if (s != 0)
handle_error_en(s, "pthread_create");
}
/* Destroy the thread attributes object, since it is no
longer needed. */
s = pthread_attr_destroy(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
/* Now join with each thread, and display its returned value. */
for (int tnum = 0; tnum < num_threads; tnum++) {
s = pthread_join(tinfo[tnum].thread_id, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
printf("Joined with thread %d; returned value was %s\n",
tinfo[tnum].thread_num, (char *) res);
free(res); /* Free memory allocated by thread */
}
free(tinfo);
exit(EXIT_SUCCESS);
}
* getopt함수:
#include <unistd.h>
int getopt(int argc, char *const argv[],
const char *optstring);
optstring:
* strtoul:
unsigned long int strtoul(const char *nptr, char **endptr, int base);
* thread_join 함수:
prototype:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
thread인자로 들어온 thread를 종료할 때까지 기다린다.
코드 입 출력 예시
: -s 옵션으로 0x100000 : 스택 의 최대 사이즈 지정(getopt, strtoul 함수 참고)
hola salut servus : thread_start 함수(만든 함수) 에서 인자로 받은 다음, thread_create()함수 사용시 start_routine의 인자로 들어간다.
그 다음 종료 시에 이 문자열을 각각 리턴하고 thread_join 함수에서 그 반환값을 받아서 출력할 수 있도록 한다.
* 직접 코드를 쳐보면서 이해하면 이해에 도움이 된다!
2. pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
- 스레드가 종료되었다고 해서 메모리가 해제되는 것이 아니기 때문에, 메모리 누수 방지를 위해 반드시 이 함수를 사용해주어야 한다.
pthread_join 함수의 첫번째 인자에 기다리려는. thread 의 아이디를 넣고, 입력받고 싶은 값을 저장할 버퍼를 두번째 인자에 넣어주면,
스레드가 종료되면서 반환하는 값이 버퍼 (retval)에 저장된다.
- 메모리 자원을 제대로 해주기 위해, 스레드 종료 시 반환 값을 사용하거나 알기 위해서 사용한다고 이해하면 된다!
'CS(computer science)' 카테고리의 다른 글
makefile 오류 - 'linker' input unused [-Werror,-Wunused-command-line-argument] (0) | 2022.12.10 |
---|---|
컴퓨터네트워크 - 애플케이션 계층 (간단 맛보기) (1) | 2022.12.04 |
ft_print 구현해보기(printf, 가변인자 이해하기) (0) | 2022.11.29 |
Makefile 뜯어보기 (0) | 2022.11.29 |
linux - 메모리 릭 확인하는 법[쉘, 리눅스, do leaks] (4) | 2022.11.29 |