CS(computer science)

linux - pthread_create 매뉴얼 뜯어보기[pthread]

ebang 2022. 12. 1. 23:00
반응형

https://man7.org/linux/man-pages/man3/pthread_create.3.html

출처 : 리눅스 매뉴얼 페이지

 

pthread_create(3) - Linux manual page

pthread_create(3) — Linux manual page PTHREAD_CREATE(3) Linux Programmer's Manual PTHREAD_CREATE(3) NAME         top pthread_create - create a new thread SYNOPSIS         top #include int pthread_create(pthread_t *restrict thread, const pthread_a

man7.org

 

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

  1. restrict 가 뭐였더라 → 이걸 붙여서 매개변수로 들어오는 포인터는, 고유의 포인터이다. (컴파일러에게 두 포인터가 같은지 확인시키지 않아도 됨: 컴파일러의 최적화 작업을 줄여주는 효과. )사용법: 포인터 사용시에 주로 사용 . (*이 자료형과 restrict 사이에 위치.)
    1. *고유의 포인터라 함은: 그 포인터가 가리키는 값을 다른 포인터가 가리키지 않는다.
    2. -pthread와 컴파일과 링크 해주는 편.
  2. 현재 프로세스에서 pthread_create 함수를 쓰면 그 프로세스에서 쓰레드를 만들 수 있다.
  3. 쓰레드가 만들어지면 start_routine 함수 포인터가 사용되고,
  4. arg가 그 함수의 인자로 들어간다.
  5. thread가 종료되는 방법들:
    1. pthread_exit(1) : pthread_join(3)을 실행한 쓰레드(같은 프로세스 내에 있어야 함)에게 exit status value를 주면서 종료된다.
    2. start_routine에서 반환되는 것. (returns from start_routine). return을 반환받은 후 pthread_exit(3) 를 하는 것과 같은 효과
    3. canceled : (pthread_cancel (3))
  6. attr : pthread_attr_t 구조체를 가리키고 있음 :
    1. pthread를 만들어내는 시점에 여러 설정들을 결정하기 위해 갖고 있는 구조체.
    2. pthread_attr_init(3) 함수에 의해서 이 구조체가 초기화된다.
    3. NULL이 들어갈 경우 defalut attributes 상태로 쓰레드가 생성된다.
  7. 성공 시: (start_routine함수 반환 뒤)thread: pthred_t thread포인터가 가리키고 있는 버퍼에 new thread의 ID를 저장한다. 다른 pthread function을 호출하려고 할 때 사용되는 identifier이다.
  8. 처음 쓰레드가 생긴 경우 CPU-time은 0이다.
  9. 기타
       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);
 
main문에서 받은 인자에서 옵션을 파싱하는 함수.
반복적으로 호출할 때마다 옵션 하나씩을 반환한다.

 

 
- optind: argv인자에서 처리할 인덱스(다음 인덱스).
처음에는 1로 시스템이 초기화한다.
호출할 때 다시 1로 리셋할 수도 있고 (다시 옵션 탐색하려고) 또는 다시 처음으로 돌려서 다른 인자에서 옵션을
추출하려고 사용한다.

 

->getopt함수가 옵션을 찾아내면 그 문자형을 반환하고 optind를 하나 올린다.
(optind, nextchar를 업데이트.)
-> 옵션을 못 찾아내면 -1을 반환.

 

optstring:

optstring is a string containing the legitimate option characters. (그렇다고 한다.)

 

* strtoul:

unsigned long int strtoul(const char *nptr, char **endptr, int base);
 
첫번째 인자로 들어온 const char *형을 base를 이용해서 unsigned long int 값으로 변경한다 .
2~36값의 base가 들어온다. 또는 스페셜하게 0이 들어올 수도 .



-> str to unsigned long 함수로 atoi처럼 생각하되 범위가 더 넓어졌다고 보면 된다.
base가 0으로 들어오면 문자열이 0x로 시작할 수 있고, 16진수로 이해해서 간다.
그렇지 않으면 그 다음 char가 '0'이 아닌이상 10진수로 처리된다. (그 다음 char가 '0'이라면 8진수)

 

he strtoul() function converts the initial part of the string in nptr to an unsigned long int value according to the given base, which must be between 2 and 36 inclusive, or be the special value 0.

 

The string may begin with an arbitrary amount of white space (as determined by isspace(3)) followed by a single optional '+' or '-' sign. If base is zero or 16, the string may then include a "0x" prefix, and the number will be read in base 16; otherwise, a zero base is taken as 10 (decimal) unless the next character is '0', in which case it is taken as 8 (octal).
 
 
num_threads = argc - optind;  <-  왜 이렇게 되는지 이제 이해할 수 있어야 한다.

 

* thread_join 함수:

prototype: 
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);

thread인자로 들어온 thread를 종료할 때까지 기다린다.

이미 종료되어있으면 바로 종료된다.

 

retval이NULL이 아니라면
pthread_join()은 retval 포인터에 'thread'의 exit status를 전달한다.

 

'thread'가 취소되면, retval에 PTHREAD_CANCELED 가 저장되는 형식이다.

 

pthread_"join ㅎ마수가 성공하면 0을 반환하고, error가 있다면 error number를 반환한다.
(데드락이 보인다든지 ...)

 

 

코드 입 출력 예시

: -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)에 저장된다. 

 

- 메모리 자원을 제대로 해주기 위해, 스레드 종료 시 반환 값을 사용하거나 알기 위해서 사용한다고 이해하면 된다!

 

 

반응형