system함수가 thread-safe한가요??

hatbary의 이미지

안녕하세요..

스레드 풀을 만들어 놓고, 각각의 클라이언트들에게 서비스를 제공하는 서버를 만들고 있는데요.

서버는 클라이언트로부터 파일도 전송을 받는데, 이게 압축된 파일이라서 압축된 파일중 특정파일만 골라서 다른 파일과 압축하여 다시 전송해줘야 할 필요가 있거든요..

그래서 zip알고리즘을 구현해서 쓰려구 했는데, 그냥 system함수 써서, 직접 zip명령어를 호출하려구 합니다..(원래 system쓰는거 안조아하지만 실력이 딸려서..ㅠㅠ)

근데, 제가 알기로는 system함수가 fork->exec->sh...머..이런식으로 동작한다고 알고 있는데, 만약 스레드 내에서 이 system함수를 호출한다면 문제가 될 소지가 있지 않을까 라는 생각이 들어서 질문 올립니다..

어떤 용어를 써야 할지 좀 난감해서 그냥 system함수가 thread-safe한가라구 물어본거거든요..

이에 대한 답변을 부탁드립니다..

그럼 답변에 미리 감사 드리면서...

bw001730의 이미지

동작하고 있는 스레드가 열개라고 치고..
fork()를 하면..문제가 발생합니다.

즉, 현재 프로세스에서 10개의 스레드들은 각각 저마다의
메모리를 차지하고 있습니다.
그리고 10개의 스레드중 한 스레드가 fork()를 호출하게 되면요
10개의 스레드를 가지고 있는 부모 프로세스와 동일한 개수의
스레드가 있는 자식 프로세스가 생성됩니다.

그러면 자식 스레드가 10개 동작하는가 하면, 모두 죽은 상태의
스레드입니다. 동작하지도 않는 스레드들이 메모리만 차지하고 있습니다.
(또 락객체 같은 것 땜에 문제가 발생할 수도 있습니다.)
자식 프로세스에서 동작하는 스레드는 부모프로세스에서
fork()를 호출한 스레드뿐입니다.

그리고 이들의 메모리는 2가지 경우에만
자동소멸될수 있고.어떤 함수호출같은걸로 해제할수 없습니다.

자동소멸되는 첫번째 경우는
프로세스가 종료할때 자동소멸됩니다.
두번째 경우는 exec()계열의 함수를 호출할때입니다.
exec()하면 프로세스의 메모리이미지가 모두 사라지고, 새로운 프로그램의
이미지로 바뀌니깐요. 그러니 이땐 죽은 스레드가 차지하는 메모리가
해제됩니다.

system()은 fork()한담에, exec()하는 것이니깐 문제가 안될것 같은
생각이 듭니다.
직접 코딩은 못해보고 이론적인 것입니다만. 될것 같네요
님이 해보시고 답글좀 올려주세요 ^^

pynoos의 이미지

문제 없습니다.
fork 하면, 현재 thread를 제외한 모든 thread는 닫혀지게 되며,
thread unsafe한 데이터를 내부에 가지고 있지 않으니.. 그다지 문제 없어 보입니다.

signal을 masking 해두었다면 문제가 있을지도 모르겠습니다만, 아래 코드를 보니 __sigprocmask를 사용하여 masking을 잠시 푸는 군요.

glibc-2.2.2/sysdeps/posix/system.c

     1  /* Copyright (C) 1991-1999, 2000 Free Software Foundation, Inc.
     2     This file is part of the GNU C Library.
     3
     4     The GNU C Library is free software; you can redistribute it and/or
     5     modify it under the terms of the GNU Library General Public License as
     6     published by the Free Software Foundation; either version 2 of the
     7     License, or (at your option) any later version.
     8
     9     The GNU C Library is distributed in the hope that it will be useful,
    10     but WITHOUT ANY WARRANTY; without even the implied warranty of
    11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    12     Library General Public License for more details.
    13
    14     You should have received a copy of the GNU Library General Public
    15     License along with the GNU C Library; see the file COPYING.LIB.  If not,
    16     write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    17     Boston, MA 02111-1307, USA.  */
    18
    19  #include <stddef.h>
    20  #include <stdlib.h>
    21  #include <unistd.h>
    22  #include <sys/wait.h>
    23  #include <signal.h>
    24  #include <sys/types.h>
    25  #include <errno.h>
    26
    27
    28  #ifndef HAVE_GNU_LD
    29  #define __environ       environ
    30  #endif
    31
    32  #define SHELL_PATH      "/bin/sh"       /* Path of the shell.  */
    33  #define SHELL_NAME      "sh"            /* Name to give it.  */
    34
    35  /* Execute LINE as a shell command, returning its status.  */
    36  int
    37  __libc_system (const char *line)
    38  {
    39    int status, save;
    40    pid_t pid;
    41    struct sigaction sa, intr, quit;
    42  #ifndef WAITPID_CANNOT_BLOCK_SIGCHLD
    43    sigset_t block, omask;
    44  #endif
    45
    46    if (line == NULL)
    47      /* Check that we have a command processor available.  It might
    48         not be available after a chroot(), for example.  */
    49      return __libc_system ("exit 0") == 0;
    50
    51    sa.sa_handler = SIG_IGN;
    52    sa.sa_flags = 0;
    53    __sigemptyset (&sa.sa_mask);
    54
    55    if (__sigaction (SIGINT, &sa, &intr) < 0)
    56      return -1;
    57    if (__sigaction (SIGQUIT, &sa, &quit) < 0)
    58      {
    59        save = errno;
    60        (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
    61        __set_errno (save);
    62        return -1;
    63      }
    64
    65  #ifndef WAITPID_CANNOT_BLOCK_SIGCHLD
    66
    67  /* SCO 3.2v4 has a bug where `waitpid' will never return if SIGCHLD is
    68     blocked.  This makes it impossible for `system' to be implemented in
    69     compliance with POSIX.2-1992.  They have acknowledged that this is a bug
    70     but I have not seen nor heard of any forthcoming fix.  */
    71
    72    __sigemptyset (&block);
    73    __sigaddset (&block, SIGCHLD);
    74    save = errno;
    75    if (__sigprocmask (SIG_BLOCK, &block, &omask) < 0)
    76      {
    77        if (errno == ENOSYS)
    78          __set_errno (save);
    79        else
    80          {
    81            save = errno;
    82            (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
    83            (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
    84            __set_errno (save);
    85            return -1;
    86          }
    87      }
    88  # define UNBLOCK __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)
    89  #else
    90  # define UNBLOCK 0
    91  #endif
    92
    93    pid = __fork ();
    94    if (pid == (pid_t) 0)
    95      {
    96        /* Child side.  */
    97        const char *new_argv[4];
    98        new_argv[0] = SHELL_NAME;
    99        new_argv[1] = "-c";
   100        new_argv[2] = line;
   101        new_argv[3] = NULL;
   102
   103        /* Restore the signals.  */
   104        (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
   105        (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
   106        (void) UNBLOCK;
   107
   108        /* Exec the shell.  */
   109        (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);
   110        _exit (127);
   111      }
   112    else if (pid < (pid_t) 0)
   113      /* The fork failed.  */
   114      status = -1;
   115    else
   116      /* Parent side.  */
   117      {
   118  #ifdef  NO_WAITPID
   119        pid_t child;
   120        do
   121          {
   122            child = __wait (&status);
   123            if (child <= -1 && errno != EINTR)
   124              {
   125                status = -1;
   126                break;
   127              }
   128            /* Note that pid cannot be <= -1 and therefore the loop continues
   129               when __wait returned with EINTR.  */
   130          }
   131        while (child != pid);
   132  #else
   133        int n;
   134
   135        do
   136          n = __waitpid (pid, &status, 0);
   137        while (n == -1 && errno == EINTR);
   138
   139        if (n != pid)
   140          status = -1;
   141  #endif
   142      }
   143
   144    save = errno;
   145    if ((__sigaction (SIGINT, &intr, (struct sigaction *) NULL) |
   146         __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL) |
   147         UNBLOCK) != 0)
   148      {
   149        if (errno == ENOSYS)
   150          __set_errno (save);
   151        else
   152          return -1;
   153      }
   154
   155    return status;
   156  }
   157  weak_alias (__libc_system, system)
hatbary의 이미지

여러분들의 의견을 듣고 간단하게 코딩했더니 별 문제 없어보이는군요..

아래는 그냥 후다닥 만든 코드와 결과 입니다.

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

void* call(void*);

int main()
{
        pthread_t tid[10];

        for(int i=0; i<10; i++)
        {
                printf("[+] thread created : %d\n",i);
                pthread_create(&tid[i],NULL,call,(void*)i);
        }

        sleep(20);
        return 0;

}

void* call(void* arg)
{
        pthread_detach(pthread_self());

        sleep(10-(int)arg);
        char cmd_unzip[132] = {'\0',};
        snprintf(cmd_unzip, sizeof(cmd_unzip), "unzip -qqu test.zip *.txt -d./tmp%d", (int)arg);

        int res = system(cmd_unzip);

        printf("return : %d\n",res);

        if(res == 127 || res == -1)
        {
                printf("[-] fail to call system\n");
                return NULL;
        }
        else if( res != 0)
        {
                printf("[-] fail to unzip\n");
                return NULL;
        }
        printf("[+] done\n");
        return NULL;
}

Quote:

[root@mars dummy]# ./test
[+] thread created : 0
[+] thread created : 1
[+] thread created : 2
[+] thread created : 3
[+] thread created : 4
[+] thread created : 5
[+] thread created : 6
[+] thread created : 7
[+] thread created : 8
[+] thread created : 9
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done
return : 0
[+] done

그런데..잠깐 또 의심가는게 바로 위에 분께서 현재 스레드를 제외한 다른 스레드가 닫힌다구 하셨는데, 스레드 풀을 만들어놓구 pthread_cont_wait()명령을 써서 스레드를 깨우는 방식이면 만들어놓은 스레드가 죽는건가요? 제가 님의 설명을 잘 이해 못하겠네요..지송^^a

암튼 여러분들의 도움을 받으니 그저 고마울 따름임다..^^

pynoos의 이미지

아.. 오해가 있을 것 같군요.

fork 하고 나면, 자식 프로세스에서는 다른 쓰레드는 모두 닫히고 하나만 남는다는 것이었습니다.

hatbary의 이미지

아하~~그렇군요..답변 감사드립니다..^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.