본문 바로가기
Programming/열혈 TCP, IP 소켓 프로그래밍(저자 윤성우)

Ch 10. 내용 확인문제

by minjunkim.dev 2020. 8. 8.

    모든 내용은 [윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어] 를 기반으로 제 나름대로 이해하여 정리한 것입니다. 다소 부정확한 내용이 있을수도 있으니 이를 유념하고 봐주세요!


01. 다음 중 프로세스에 대한 설명으로 옳은 것은?

a. 프로세스는 운영체제의 관점에서 프로그램의 실행 단위가 된다.


b. 프로세스도 생성 방식에 따라서 부모와 자식의 관계를 갖는다.

# 틀린 설명

c. 프로세스는 다른 프로세스를 포함할 수 있다. 즉, 하나의 프로세스는 자신의 메모리 영역에 또 다른 프로세스를 포함할 수 있다.

=> 프로세스는 서로 독립적인 존재이므로, 다른 프로세스를 포함할 수 없다.

d. 자식 프로세스는 또 다른 자식 프로세스를 생성할 수 있고, 이렇게 생성된 자식 프로세스 역시 또 다른 자식 프로세스를 생성할 수 있지만, 이들은 모두 하나의 프로세스와만 부모-자식의 관계를 형성한다.
=> 모든 자식 프로세스에게 부모 프로세스는 하나지만, 부모는 여러 개의 자식을 둘 수 있다.

즉, 프로세스는 트리 형태로 구성된다. 그리고 자식 프로세스 역시 자식 프로세스를 생성할 수 있다.



02. fork 함수가 호출되면 자식 프로세스가 생성되는데, 이 자식 프로세스의 특징으로 옳은 것을 모두 고르면?

 

b. 자식 프로세스는 부모 프로세스의 모든 것을 복사해서 생성되는 프로세스이다.

# 틀린 설명
a. 부모 프로세스가 소멸되면 자식 프로세스도 소멸된다.
=> fork 함수호출 이후에는 부모 프로세스와 자식 프로세스가 서로 완전히 분리된 메모리 구조를 지닌다.

c. 부모 프로세스와 자식 프로세스는 전역으로 선언되는 변수를 공유한다.
=> fork 함수호출 이후에는 서로 완전히 분리된 메모리 구조를 지니므로, 자식 프로세스에서 전역변수를 증가시킨다 해도 부모 프로세스에 영향을 주지 않는다.

d. fork 함수 호출로 생성된 자식 프로세스는 부모 프로세스가 실행한 코드를 처음부터 fork 함수가 호출된 위치까지 실행해 온다.

=> 두 프로세스 모두 "fork 함수의 호출 이후" 문장을 실행하게 된다(정확히 표현하면 fork 함수의 반환 이후)


03. 자식 프로세스가 생성되면 부모 프로세스의 모든 것을 복사하는데, 이때 복사의 대상으로는 소켓의 파일 디스크립터도 포함이 된다. 그렇다면 복사된 파일 디스크립터의 정수 값은 원본 파일 디스크립터의 정수 값과 동일한지 확인하기 위한 프로그램을 작성해보자.

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

void error_handling(char *message);

int main(int argc, char * argv[])
{
    int sock;
    pid_t pid;

    sock=socket(PF_INET,SOCK_STREAM,0); // TCP 소켓 생성
    pid=fork(); // 자식 프로세스 생성

    if(pid==0)
        printf("Child Process sock's file descriptor : %d\n",sock);
    else
        printf("Parent Process sock's file descriptor : %d\n",sock);
    
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(EXIT_FAILURE); // -1
}

# 실행 결과


04. 프로세스가 좀비가 되는 경우에 대해서 설명하고, 이를 막기 위해서 어떠한 방법을 취해야 하는지 설명해 보자.

- 자식 프로세스가 종료하면서 exit로 값을 전달하거나, return문으로 반환값을 전달하게 되는데 이 값을 운영체제가 받는다. 이 값이 부모 프로세스로 전달되어야만 자식 프로세스가 완전히 종료되는데, 이를 위해서는 부모 프로세스가 운영체제에게 적극적인 요청이 있어야 한다.

- 이를 위한 방법으로는,

1) wait 함수 호출

2) waitpid 함수 호출

3) signal, sigaction 함수 호출을 통한 시그널 핸들링

등을 통해 좀비 프로세스 생성을 방지할 수 있다.


05. SIGINT에 대한 핸들러를 등록하지 않은 상태에서 Ctrl+C 키가 입력되면, 운영체제가 지정해 놓은 디폴트 이벤트 핸들러에 의해서 프로그램이 종료되어 버린다. 그러나 Ctrl+C 키에 대한 핸들러를 직접 등록하면 프로그램은 종료되지 않고 프로그래머가 지정한 이벤트 핸들러가 대신 호출된다. 그렇다면 일반적인 프로그램에서 다음과 같이 동작하도록 이벤트 핸들러 등록을 위한 코드를 구성해보자.

 

    "Ctrl+C 키가 눌리면, 정말로 프로그램을 종료할 것인지를 묻고, 이에 대한 대답으로 'Y'가 입력되면 프로그램을 종료한다."

 

그리고 간단히 문자열을 1초당 한번 정도 반복 출력하는 프로그램을 작성해서 위의 이벤트 핸들러 등록 코드를 적용시켜보자.

 

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

void keycontrol(int sig)
{
    static char buf[30];

    if(sig==SIGINT)
    {
        fputs("정말로 프로그램을 종료할까요? : ",stdout);
        fgets(buf,sizeof(buf),stdin);
        if(!strcmp(buf,"y\n")||!strcmp(buf,"Y\n"))
            exit(EXIT_SUCCESS);
    }
}

int main(int argc, char * argv[])
{
    int i;
    signal(SIGINT, keycontrol); // 시그널 등록

    for(i=0;i<100;++i)
    {
        puts("wait...");
        sleep(1);
    }

    return 0;
}

[출처] : 윤성우 저, "열혈강의 TCP/IP 소켓 프로그래밍", 오렌지미디어