[리눅스 프로그래밍] 멀티 프로세스

Yoon Yeoung-jin·2022년 6월 19일
0

Linux

목록 보기
2/13

프로그램 vs 프로세스 vs 스레드

  • 프로그램
    • 실행 가능한 코드, 실행되기를 기다리는 명령어와 정적인 데이터의 묶음.
  • 프로세스
    • 실행 중인 프로그램
    • 최소 한개의 스레드를 무조건 가지고 있다.
    • 플프로그램 이미지, 메모리 인스턴스, 커널 리소스 등의 정보
  • 스레드
    • 프로세스 내의 실행 단위를 의미.
    • 가상화 된 프로세서, 스택, 레지스터, 명령어 포인터 등 프로세서의 상태를 포함한다.
    • 프로세스 내의 모든 스레드는 같은 주소 공간을 공유한다.

프로세스 내의 메모리 공간 구조.

  • 힙 영역과 스텍 영역 사이의 빈 공간이 있는 이유는 malloc 과 같이 코드 상에서 동적으로 할당하기 위해서 비워진 메모리이다.
  • 참고 URL: https://bowbowbow.tistory.com/16

Multi-process

  • 부모 프로세스와 자식 프로세스
    • 하나의 프로세스가 다른 프로세스를 생성할때, 생성한 프로세스를 부모 프로세스, 생성된 프로세스를 자식 프로세스라고 한다.
    • 부모 프로세스는 자식 프로세스가 자식 프로세스에 대해서 종료 처리를 해주어야 한다. (좀비 프로세스 생성 방지를 위해)

사용 헤더

  • sys/types.h
  • sys/wait.h
  • unistd.h

사용 함수

  • fork(): 자식 프로세스를 만드는 함수
    pid_t fork(void)
    파라미터
    	* 없음
    반환값
    	* 성공: 부모 프로세스 -> 자식 프로세스의 PID값, 자식 프로세스 -> 0
    	* 실패: 부모 프로세스 -> -1, 자식 프로세스 -> 생성되지 않음.
    
    -> fork의 경우 자식 프로세스를 생성할 떄 모든 데이터를 복제하여 생성한다. 
  • getpid, getppid: PID 값 얻는 함수
    pid_t getpid(void);
    pid_t getppid(void);
    파라미터
    	* 없음
    반환값
    	* getpid: 현재 프로세스의 PID
    	* getppid: 현재 프로세스의 부모 프로세스 PID
  • exec: 실행 함수
    • exec 함수의 경우 fork에서 생성된 데이터를 지우고 실행한 명령어의 새로운 데이터로 구성한다.

      int execl(const char *path, const char *arg, .../* (char  *) NULL */);
      int execlp(const char *file, const char *arg, ... /* (char  *) NULL */);
      int execle(const char *path, const char *arg, ... /*, (char *) NULL, char * const envp[] */);
      int execv(const char *path, char *const argv[]);
      int execvp(const char *file, char *const argv[]);
      int execvpe(const char *file, char *const argv[], char *const envp[]);
      
    • execl: list base arguments
      • 마지막에 NULL 값 필수 입력.
    • ececv: vector based arguments
    • suffix 'p': $PATH에서 경로 찾음
    • suffix 'e': 새로운 환경 변수 설정
      반환값
      * 실패: -1
  • exit: 종료 함수
    void exit(int status);
    파라미터
    	* status: exit status
    	* 성공: 0
    	* 실패: None Zero
  • wait(): 자식 프로세스 종료 대기 함수
    pid_t wait(int *wstatus);
    파라미터
    - wstatus: child process의 종료 상태
    반환값
    - 성공시 : terminated 된 자식 프로세스의 pid 
    - 실패시 : -1
    ======================================
    사용 가능 메크로
    WIFEXITED(wstatus)   : 0이 아닌 값을 리턴하면 자식프로세스가 정상종료했다는 의미.
    WEXITSTATUS(wstatus) : 정상 종료했음을 확인하면 해당 메크로를 통해여 종료 코드를 확인할 수 있다. 
    WIFSIGNALED(wstatus) : 이 매크로가 참이면 Signal을 통해 프로세스가 비정상 종료 했다는 뜻
    WTERMSIG(wstatus)    : SIFSIGNALED(status)매크로가 참일 경우 자식 프로세스를 종료시킨 시그널 번호를 얻는 매크로
    WCOREDUMP(wstatus)   : 시스템에 따라서는 WIFSIGNALED(status)가 참일 경우 자식 프로세스가 core덤프 파일을 생성했는지를 확인하는 이 매크로를 제공
    WIFSTOPPED(wstatus)  : 이 매크로가 참이면 자식 프로세스는 현재 멈춰있는(stopped) 상태이다.
    WSTOPSIG(wstatus)    : WIFSTOPPED(status)매크로가 참일 경우 자식 프로세스를 멈춤상태로 만든 시그널번호를 얻는다.
  • waitpid(): PID 기준으로 프로세스 종료 대기 함수
    pid_t waitpid(pid_t pid, int *status, int options);
    파라미터
    - pid: 종료 대기할 프로세스의 pid
    - status: 자식 프로세스의 종료 코드
    - options
    	- WNOHANG.   : waitpid를 실행했을 떄, 자식 프로세스가 종료되어 있지 않으면, 블록상태가 되지 않고 바로 리턴하게 해준다.
    	- WUNTRACED  : pid에 해당하는 자식 프로세스가 멈춤 상태일 경우 그 상태를 리턴한다.
    	- WCONTINUED : 중단 되었다가 재개된 자식 프로세스의 상태를 받는다.
    반환값
    - 양수: 상태가 바뀐 child process의 pid
    - 0: WNOHANG 지정시
    - -1:실패
  • wait4(): 자식 프로세스가 종료되는 것을 기다리며, 종료된 프로세스의 상태와 자원 사용량을 알려주는 함수
    pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage);
    파라미터
    - pid: 종료 대기할 프로세스의 pid
    - status: 자식 프로세스의 종료 코드
    - options
    	- WNOHANG.   : 자식 프로세스가 종료되어 있지 않으면, 블록상태가 되지 않고 바로 리턴하게 해준다.
    	- WUNTRACED  : pid에 해당하는 자식 프로세스가 멈춤 상태일 경우 그 상태를 리턴한다.
    	- WCONTINUED : 중단 되었다가 재개된 자식 프로세스의 상태를 받는다.
    - rusage: 리소스 사용량
    반환값
    - 양수: 상태가 바뀐 child process의 pid
    - 0: WNOHANG 지정시
    - -1:실패
    • struct rusage 구조체
      struct rusage {
                     struct timeval ru_utime; /* user CPU time used */
                     struct timeval ru_stime; /* system CPU time used */
                     long   ru_maxrss;        /* maximum resident set size */
                     long   ru_ixrss;         /* integral shared memory size */
                     long   ru_idrss;         /* integral unshared data size */
                     long   ru_isrss;         /* integral unshared stack size */
                     long   ru_minflt;        /* page reclaims (soft page faults) */
                     long   ru_majflt;        /* page faults (hard page faults) */
                     long   ru_nswap;         /* swaps */
                     long   ru_inblock;       /* block input operations */
                     long   ru_oublock;       /* block output operations */
                     long   ru_msgsnd;        /* IPC messages sent */
                     long   ru_msgrcv;        /* IPC messages received */
                     long   ru_nsignals;      /* signals received */
                     long   ru_nvcsw;         /* voluntary context switches */
                     long   ru_nivcsw;        /* involuntary context switches */
                 };
      
      struct timeval {
      	long tv_sec;
      	long tv_usec;
      }

예제 코드

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>

#include<sys/types.h>
#include<sys/time.h>
#include<sys/wait.h>
#include<sys/resource.h>

#define TEST_PATH "./"
#define AUTHER "Yoon Yeoung Jin "
#define USE_WAIT 0
#define USE_WAIT4 1

void yj_print_pid(void)
{
    pid_t pid;
    pid = getpid();
    printf("    * current process pid : %d\n", pid);
}

void yj_print_ppid(void)
{
    pid_t pid;
    pid = getppid();
    printf("    * parent process pid : %d\n", pid);
}

void yj_print_ruseage(const char *leader, const struct rusage *ru)
{
    const char *ldr = (leader == NULL) ? "" : leader;

    printf("%sCPU time (secs):         user=%.3f; system=%.3f\n", ldr,
            ru->ru_utime.tv_sec + ru->ru_utime.tv_usec / 1000000.0,
            ru->ru_stime.tv_sec + ru->ru_stime.tv_usec / 1000000.0);
    printf("%sMax resident set size:   %ld\n", ldr, ru->ru_maxrss);
    printf("%sIntegral shared memory:  %ld\n", ldr, ru->ru_ixrss);
    printf("%sIntegral unshared data:  %ld\n", ldr, ru->ru_idrss);
    printf("%sIntegral unshared stack: %ld\n", ldr, ru->ru_isrss);
    printf("%sPage reclaims:           %ld\n", ldr, ru->ru_minflt);
    printf("%sPage faults:             %ld\n", ldr, ru->ru_majflt);
    printf("%sSwaps:                   %ld\n", ldr, ru->ru_nswap);
    printf("%sBlock I/Os:              input=%ld; output=%ld\n",
            ldr, ru->ru_inblock, ru->ru_oublock);
    printf("%sSignals received:        %ld\n", ldr, ru->ru_nsignals);
    printf("%sIPC messages:            sent=%ld; received=%ld\n",
            ldr, ru->ru_msgsnd, ru->ru_msgrcv);
    printf("%sContext switches:        voluntary=%ld; "
            "involuntary=%ld\n", ldr, ru->ru_nvcsw, ru->ru_nivcsw);
}

int main(int argc, char **argv)
{
    int wstatus = 0;
    pid_t child_pid = 0;
    pid_t current_pid = 0;

    struct rusage usage;

    current_pid = fork();
    if(current_pid == -1){
        printf("fork() fail : %s\n", strerror(errno));
        return -1;
    } else if(current_pid > 0){
        /* parent process */
        printf(" [*] THIS IS PARENT PROCESS\n");
        yj_print_pid();
    } else {
        /* child process */
        printf(" [*] THIS IS CHILD PROCESS\n");
        yj_print_pid();
        yj_print_ppid();
        sleep(1);
        if(execl("/bin/ls", "ls", "-al", TEST_PATH, NULL) == -1){
            printf("execl() fail : %s\n", strerror(errno));
            exit(-1);
        }
    }

#if USE_WAIT
    child_pid = wait(&wstatus);
    if(child_pid == -1){
        printf("wait() fail: %s\n", strerror(errno));
        return -1;
    }
    if(WIFEXITED(wstatus)){
        printf("    * The child process(%d) terminated successfully. (CODE: %d)\n ", child_pid, WEXITSTATUS(wstatus));
    } else {
        printf("    * The child process(%d) is not exited\n ", child_pid);
    }
#elif USE_WAIT4
    child_pid = wait4(current_pid, &wstatus, 0, &usage);
    if(child_pid == -1){
        printf("wait4() fail: %s\n", strerror(errno));
        return -1;  
    }
    if(WIFEXITED(wstatus)){
        printf("    * The child process(%d) terminated successfully. (CODE: %d)\n ", child_pid, WEXITSTATUS(wstatus));
        yj_print_ruseage(AUTHER, (const struct rusage *)&usage);
    } else {
        printf("    * The child process(%d) is not exited\n ", child_pid);
    }
#endif

    return 0;
}
profile
신기한건 다 해보는 사람

0개의 댓글