Running a new process

  • Executing a new program : binary program을 읽어서 자신을 호출한 process의 메모리 영역에 덮어 씀(기존 program은 중지)
  • Creating a new process(forking) : 자신을 호출한 process(parent process)를 복사하여, 새로운 process 생성
#include <unistd.h>

int excl(const char* path, const char* arg, ... /* (char*) NULL */);
int exclp(const char* file, const char* arg, ... /* (char*) NULL */);
int exele(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[]);
  • Exec family of call
    • path/file : 실행할 program binary
    • arg/argv[] : 실행할 program의 인자(마지막에는 NULL을 넣어주어야 함)
    • envp[] : program 실행 시, 새로 지정할 환경 변수(마지막에는 NULL을 넣어주어야 함)
    • return : 없으면 성공, -1 : error

Example

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

int main(void) {
        printf("--> Before exec function\n");

        if (execlp("ls", "ls", "-a", (char*) NULL) == -1) {
                perror("execlp");
                exit(1);
        }

        printf("--> After exec function\n"); // execlp가 실행되는 순간 process가 덮여쓰여져 이 구문은 실행되지 않음

        return 0;
}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        char* argv[3];

        printf("Before exec function\n");

        argv[0] = "ls";
        argv[1] = "-a";
        argv[2] = NULL;
        if (execv("/bin/ls", argv) == -1) {
                perror("execv");
                exit(1);
        }

        printf("After exec function\n");

        return 0;
}

creating a child process

#include <unistd.h>

pid_t fork(void);
  • fork() system call
    • 자신을 복사해 새로운 process 생성
    • 부모와 자식 프로세스의 수행은 동시 진행
      • 독립적으로 실행
      • 같은 program binary 수행
    • return
      • child process's pid : parent process
      • -1 : error
      • 0 : child process

Example

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        pid_t pid;

        switch (pid = fork()) {
                case -1:
                        perror("fork");
                        exit(1);
                        break;
                case 0:
                        printf("Child process - My PID:%d, My Parent's PID:%d\n",
                                        (int)getpid(), (int)getppid());
                        break;
                default:
                        printf("Parent process - My PID:%d, My Parent's PID:%d, "
                                        "My Child's PID:%d\n", (int)getpid(), (int)getppid(), (int)pid);
                        break;
        }
        printf("End of fork\n");

        return 0;
}
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        pid_t pid;

        switch (pid = fork()) {
                case -1:
                        perror("fork");
                        exit(1);
                        break;
                case 0:
                        printf("--> Child Process\n");
                        if (execlp("ls", "ls", "-a", (char*)NULL) == -1) {
                                perror("execlp");
                                exit(1);
                        }
                        exit(0);
                        break;
                default:
                        printf("--> Parent process - My PID:%d\n", (int)getpid());
                        break;
        }

        printf("End");
        return 0;
}

Terminating a process

#include <stdlib.h>

void exit(int status);
  • process가 종료되면, 어떻게 종료되었는지를 exit status에 저장
    • 일반적으로 0은 정상 종료를 나타냄
    • child process의 종료 상태를 parent process로 전달 가능
  • exit() system call
    • 기본적인 process 종료 과정 수행
    • status & 0377이 부모에게 전달됨
  • 종료 과정
    • atexit(3)에 등록된 함수들을 등록 역순으로 수행
    • Standard IO stream의 모든 내용을 모두 flush
    • 모든 temporal file들 삭제
    • _exit(2) 호출 : 이후 종료 과정은 kernel이 수행
      • 사용 중이던 file descriptor 닫기
      • 부모 프로세스에 exit status 전달
      • 부모 프로세스에게 SIGCHLD signal 전달
      • 자식 프로세스에게 SIGHUP signal 전달
      • Process가 사용하던 자원들 반납

registering functions for exit

#include <stdlib.h>

int atexit(void (*function)(void));
  • process 종료 시 호출할 함수들을 등록
  • void (*function)(void) : 등록할 function prointer(return과 인자가 없은 함수)
  • return
    • 0 : success
    • non-zero : error

Example

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

void cleanup1(void) {
        printf("Cleanup 1 is called.\n");
}

void cleanup2(void) {
        printf("Cleanup 2 is called.\n");
}

int main(void) {
        atexit(cleanup1);
        atexit(cleanup2);

        exit(0);
}

Process synchronization

  • 다중 프로그래밍 시스템
    • 여러 개의 프로세스들이 존재
    • 프로세스들은 서로 독립적으로 동작
    • 공유 자원 또는 데이터가 있을 때, 문제 발생 가능
  • 동기화
    • 프로세스들이 서로 동작을 맞추는 것
    • 프로세스들이 서로 정보를 공유하는 것

Zombie process / state

  • 종료되었지만, 아직 삭제되지 않은 프로세스
  • 부모 프로세스보다 먼저 종료된 경우, zombie state가 됨
    • 자원 등은 모두 반납
    • 하지만, kernel에 최소한의 정보가 남아 있음(ex. exit status)
  • 부모 프로세스가 exit status를 얻어갈 때까지 zombie process로 남아있음

acquiring exit status of a child

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

pid_t wait(int* status);
  • 자식 process가 종료할 때까지 대기
  • 자식 process가 종료하면 exit status를 얻어 옴(여러 개인 경우, 가장 빨리 종료된 것의 exit status를 얻어 옴)
  • return
    • -1 : 실행 중인 자식이 없음
    • child process id : success
  • status : exit status를 저장할 위치
  • status 값 읽기
    • exit으로 전달한 값(8bits, 비정상 종료 시 = 0), process를 종료시킨 signal(정상 종료 시 = 0)
    • system마다 다를 수 있음
    • Macro function 사용 추천
      • WIFEXITED(status) : 정상 종료 시, true
      • WEXITSTATUS(status) : 정상 종료 시 반환 값(exit()의 값)
      • WIFSIGNALED(status) : 시그널에 의해 종료 시, true

Example

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        int status;
        pid_t pid;

        switch (pid = fork()) {
                case -1:
                        perror("fork");
                        exit(1);
                        break;
                case 0:
                        printf("--> Child Process\n");
                        exit(2);
                        break;
                default:
                        while (wait(&status) != pid)
                                continue;

                        printf("--> Parent Process\n");
                        printf("Status : %d, %x\n", status, status);
                        printf("Child process Exit Status:%d\n", WEXITSTATUS(status));
                        break;
        }
        return 0;
}

wait for a specific child

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

pid_t waitpid(pid_t pid, int* status, int options);
  • option
    • WNOHANG : 자식이 종료하기 전이라도, 상태 값 바로 반환(대기하지 않고 수행을 계속 함)
    • WNOWAIT : 상태 값을 반환한 child process를 대기 상태로 유지(다시 exit status를 요청할 수 있음)

Example

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main(void) {
        int status;
        pid_t pid;

        if ((pid = fork()) < 0) {
                perror("fork");
                exit(1);
        }

        if (pid == 0) {
                printf("--> Child process\n");
                sleep(3);
                exit(3);
        }

        printf("--> Parent process\n");
        while (waitpid(pid, &status, WNOHANG) == 0) {
                printf("Parent still wait...\n");
                sleep(1);
        }
        printf("Child Exit Status : %d\n", WEXITSTATUS(status));

        return 0;
}

 

'💻 Computer Science > System' 카테고리의 다른 글

[System] 메모리 맵핑  (1) 2024.01.26
[System] 시그널  (1) 2024.01.25
[System] 프로세스  (0) 2024.01.25
[System] 시스템 정보  (0) 2024.01.24
[System] Files in Unix/Linux  (1) 2024.01.24