본문 바로가기
대외활동/시스템프로그래밍

0828 프로세스, 블로킹/논블로킹

by hoshi03 2024. 8. 28.

• fork 메서드

 

fork 메서드를 이용해서 프로세스를 복제

fork가 불린 시점부터 프로세스가 분화하고

부모는 자식의 pid를 알게되고

자식은 fork의 결과가 0이 된다

fork == -1 이면 결과가 비정상적인 것

 

fork로 대부분의 정보가 복제되고

fork 리턴값, 파일 잠금, 알람,시그널, 프로세스 진행 시간은 초기화된다

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

static int g_Var = 1;
char str[] = "PID";

int main(){
    __pid_t  pid = fork();
    int cpid = pid;
    switch (cpid)
    {
    case -1:
        perror("Fork");
        exit(1);
        break;
    
    case 0: //자식
        printf("pid : %d, ppid : %ld\n", getpid(), getppid());
        exit(0);
    default: // -1,0이 아닌 양수면 부모 프로세스
        printf("cpid : %ld, pid : %d\n", cpid, getpid());
        break;
    }
}

 

• wait()

wait 시스템 콜을 사용해서 자식 프로세스를 종료시킨다

자식 프로세스가 종료될때까지 대기하다가 자식 프로세스가 종료디면 즉시 반환한다

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

static int g_Var = 1;
char str[] = "PID";

int main(){
    __pid_t  pid = fork();
    int cpid = pid;
    switch (cpid)
    {
    case -1:
        perror("Fork");
        exit(1);
        break;
    
    case 0: //자식
        printf("child! pid : %d, ppid : %ld\n", getpid(), getppid());
        sleep(2);
        exit(0);
    default: // -1,0이 아닌 양수면 부모 프로세스
        printf("parent ! cpid : %ld, pid : %d\n", cpid, getpid());
        wait(NULL);
        system("ps -l");
        break;
    }
}

 

• waitpid()

waitpid()를 사용해서 특정 pid를 가진 자식 프로세스가 종료될때까지 대기한다

waitpid의 세번째 인자를 WNOHANG으로 설정해두면 논 블로킹 모드가 되어서

대기중 부모 프로세스는 다른 작업 , 여기서는 printf를 할 수 있다

세번째 인자를 0으로 설정해두면 블로킹 모드가 되어 부모 프로세스가 자식 프로세스의 종료를 기다려야 하며, 자식이 종료된 후에만 다음 작업을 수행한다

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

static int g_Var = 1;
char str[] = "PID";

int main(){
    __pid_t  pid = fork();
    int cpid = pid;
    int status;
    switch (cpid)
    {
    case -1:
        perror("Fork");
        exit(1);
        break;
    
    case 0: //자식
        printf("child! pid : %d, ppid : %ld\n", getpid(), getppid());
        sleep(10);
        exit(0);
    default: // -1,0이 아닌 양수면 부모 프로세스
        printf("parent ! cpid : %ld, pid : %d\n", cpid, getpid());
        while(waitpid(cpid,&status,WNOHANG) == 0){
            printf("parent process\n");
            sleep(1);
        }
        if (WIFEXITED(status))
        {
            printf("exit code : %d\n", WEXITSTATUS(status));
        }

        else if(WIFSIGNALED(status)){
            printf("killed by signal\n");
        }
        
        system("ps -l");
        break;
    }
}

 

• 데몬 프로세스

백그라운드에서 돌아가면서 요청이 오면 응답해주는  리스너 기능을 한다

아래코드는 자식 프로세스를 sighup을 무시하고 

루트에서 실행, setsid로 새로운 세션으로, 표준 입출력,오류를 무시해서 데몬 프로세스로 만들어준다

자식 프로세스는 계속 백그라운드에 남아있게된다

ps -C daemon -o comm,pid,ppid,pgid,sid,user  명령어로 daemon 프로세스의 정보를 보면 부모가 1이 되어 있다

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
        int pid;
         pid = fork();
        printf("pid = [%d] \n", pid);
        if(pid < 0){
                printf("fork Error... : return is [%d] \n", pid );
                perror("fork error : ");
                exit(0);
        }else if (pid > 0){
                printf("child process : [%d] - parent process : [%d] \n", pid, getpid());
                exit(0);
        }else if(pid == 0){
                printf("process : [%d]\n", getpid());
        }

        //자식 프로세스를 데몬으로 만들기
        signal(SIGHUP, SIG_IGN);
        //close(0);
        //close(1);
        //close(2);
        chdir("/");
        setsid();
        open("/dev/null", O_RDWR);
        int cnt = 0;
        while(1) {
                printf("cnt = %d\n", cnt++);
                sleep(5);
        }
}

 

• exec ~ 메서드

 

--execl

중간에 다른 프로그램을 실행하고 싶으면 exec로 실행

쉘 스크립트, 기계어 코드 등을 넣어서 실행 가능하다

execl 부분이 실행되면 /bin/ls에서 ls -l 명령을 실행하고 그 밑에 코드는 실행하지 않는다

execl 부분이 실행 불가능(타이핑 오류)하면 아래 코드인 main end가 출력된다

int main(){
    printf("main start\n");
    execl("/bin/ls", "ls", "-l", NULL);
    printf("main end\n");
}

 

-- execv() 벡터에 경로를 넣어서 명령어 실행하는 코드

int main(){
    char * arglist[] = {"/bin/ls", "ls", "-l", NULL};
    printf("main start\n");
    execv(arglist[0], &arglist[1]);
    printf("main end\n");
}

 

--  fork & exec() : 자식 프로세스에서 execvp 메서드 실행

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

int main()
{
    int status;
    if (fork() == 0)
    {
        char *arglist[] = {"/bin/ls", "ls", "-l", NULL};
        printf("child start\n");
        execvp(arglist[0], &arglist[1]);
        //execvp 실패시 아래 코드 출력
        printf("child end\n");
        exit(0);
    }

    else{
        wait(&status);
        if (WIFEXITED(status))
        {
            printf("exit code : %d\n", WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status)){
            printf("killed by signal\n");
        }
    }
}

 

-- posix_spawn() 메서드 : fork & exec 대신 이 메서드를 사용하는걸 권장한다

스케줄링이나 우선 순위 등의 이유로 fork 함수 수행 후 exec가 바로 수행되면

메모리 해제 문제 등이 발생할수 있기에 추가된 메서드

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

extern char **environ;

int system(char *cmd)
{
    pid_t pid;
    int status;
    char *argv[] = {"sh", "-c", cmd, NULL};

    posix_spawn(&pid, "/bin/bash", NULL, NULL, argv, environ);
    waitpid(pid, &status, 0);
    return status;
}

int main(int argc, char **argv, char **envp)
{
    while (*envp)
    {
        printf("%s\n", *envp++);
    }

    system("who");
    system("nocommand");
    system("cal");
    return 0;
}

 

• 프로세스 간 통신