현재강좌 : UNIX BSD 소켓 시스템 콜 이전: 2.4 서버 프로그램 작성절차


2.5 소켓 관련 UNIX 시스템 콜

▶ 본 절에서는 소켓 프로그램 작성시 많이 사용되는 UNIX 시스템 콜을 소개한다.

▶ 먼저 UNIX에서 시그널(signal)이 발생했을 때 이를 처리하는 내용을 임의로 바꿀 때 사용되는 signal() 시스템 콜을 설명하고, 프로세스를 복제하는 데 사용되는 fork() 시스템 콜을 설명한다.

2.5.1 signal()

▶ UNIX 운영체제에서 시그널은 시스템내에서 어떤 사건(event)이 발생한 것을 프로세스 사이에 알리는 수단으로 사용되는데 UNIX에서는 31개의 사건에 대한 시그널이 정의되어 있다.

▶ 소켓 프로그램에서 주로 사용되는 시그널은 SIGFPE, SIGIO, SIGURG 세 가지이다(표 2-3 참조).

SIGFPE 부동소수점 연산 에러를 나타냄.
SIGIO I/O가 가능한 상태를 나타냄.
SIGURG
Out-of-band 데이터 도착과 같은 긴급한(urgent) 소켓 상태를 나타냄.

표 2-3 소켓 관련 시그널 종류

▶ 시그널이 발생했을 때 이를 디폴트로 처리하는 내용은 UNIX 커널에 미리 정의되어 있다(예를들어 부동소수점 에러가 발생하면 프로그램이 종료된다).

▶ 그러나 프로그래머는 시그널이 발생하였을 때 이와같은 UNIX 커널이 제공하는 디폴트 처리기 대신 시그널 처리 함수를 임의로 지정할 수 있는데 이 때 signal() 시스템 콜을 사용해야 한다.

▶ 아래의 프로그램 코드에서 signal(SIGIO, sigio_func)이 호출된 이후에 시스템에서 SIGIO 시그널이 발생하면 UNIX 커널의 디폴트 처리기가 동작하지 않고 sigio_func() 함수가 수행된다.

#include <signal.h>

main() {

int sigio_func(); /* 사용자 정의 함수 선언 */

signal(SIGIO, sigio_func); /* 시그널 처리 함수 지정 */

:

}

int sigio_func() {

/* SIGIO 시그널 발생시 처리 부분 */

}

▶ 한편 응용 프로그램에서 앞으로 발생할 시그널을 단순히 무시하려면 다음과 같이 무시할 시그널의 종류(예를들면 SIGIO)를 지정하고 옵션으로 시그널 무시(SIG_IGN)를 선택하여 signal() 시스템 콜을 호출하면 된다.

signal(SIGIO, SIG_IGN);

▶ 다음에는 signal()을 사용하는 예제 프로그램으로 1초마다 카운터를 증가시키고 이를 화면에 출력하는 프로그램 signal_test.c를 소개한다.

▶ UNIX 프로그램이 실행되는 도중에 사용자가 Ctrl-C를 입력하면 시그널 SIGINT가 발생한다.

▶ 예제 프로그램 signal_test.c에서는 SIGINT 시그널이 발생하면 프로그램이 바로 종료되는 것이 아니라 my_signal ()이라는 함수가 호출되게 하였다.

▶ my_signal()내에서 Ctrl-C가 입력된 사실을 화면에 출력하고 Ctrl-C가 세 번 입력되면 그 때 프로그램을 종료하도록 하였다.

▶ signal_test.c의 실행 예는 다음과 같다.

# signal_test

0

1

^C

Ctrl-C pressed.

2

3

^C

Ctrl-C pressed.

4

^C

Ctrl-C pressed.

5

#

▶ signal_test.c에서는 먼저 시그널 SIGINT가 발생했을 때 사용자 정의함수 my _singal()가 수행되도록 다음과 같이 signal() 시스템 콜을 호출해 두어야 한다.

signal(SIGINT, my_signal);

▶ 새로 지정된 시그널 처리 함수 my_signal()에서는 SIGINT가 발생했음을 화면에 출력하고 Cntl-C가 입력된 횟수를 세기 위한 카운터 count를 1 증가시킨다.

int my_signal() {

printf("\nCtrl-C pressed.\n");

count++;

return 0;

}

signal_test.c 전체 프로그램 리스트

2.5.2 fork()

▶ UNIX에서 임의의 프로세스는 fork()를 이용해서 자신과 똑같은 기능을 수행하는 프로세스를 하나 복제할 수 있다.

▶ fork()를 호출한 프로세스를 부모(parent) 프로세스라 하고 새로 생긴 프로세스를 자식(child) 프로세스라고 한다.

▶ 자식 프로세스는 부모 프로세스의 코드, 스택, 파일기술자, 소켓번호 등을 공유하게 되어 프로세스 수행 환경이 부모 프로세스와 같게 된다.

▶ 단, 새로 생긴 자식 프로세스의 id 번호(PID: Process Identication)는 부모 프로세스의 PID와 구별되며 각종 변수들은 공유하지 않는다.

▶ fork() 시스템 콜이 성공적으로 수행되면 그 순간에 하나의 프로세스가 두 개의 프로세스로 되는데 두 프로세스는 수행할 일을 구분하기 위하여 두 프로세스의 fork() 리턴문이 서로 다르다는 것을 이용한다.

▶ 부모 프로세스에게는 fork()의 리턴값으로 새로 만들어진 자식 프로세서의 PID가 리턴되며, 자식 프로세스에게는 fork()의 리턴값이 0이 된다.

▶ 한편 fork() 문이 실패한 경우 부모 프로세스에서는 -1을 리턴하고, 자식 프로세스는 생성되지 않는다.

▶ 그림 2-14에 PID가 135인 어떤 프로세스가 fork()를 수행하여 자식 프로세스를 만드는 것을 보였다.

그림 2-14 fork()의 수행과정

▶ 예를들어 아래와 같은 프로그램 코드를 수행하면 fork() 문을 만나는 순간에 두 개의 프로세스가 생성되는데 그 중 자식 프로세스는 fork()의 리턴값이 0이 되므로 child_ work()를 수행하고, 부모 프로세스에서는 fork()의 리턴값이 자식 프로세스의 PID 값(즉 0이 아님)이 되므로 parent_work()를 수행하게 된다.

int PID;

PID = fork();

if(PID == 0) {

child_work(); /* 자식 프로세스용 코드 */

} else {

parent_work(); /* 부모 프로세스용 코드 */

}

▶ 다음은 fork()의 사용 예로 부모와 자식 프로세스가 같은 이름의 변수를 각각 증가시키고 그 결과를 확인해 보는 예제 프로그램 fork_test.c를 소개한다.

▶ 자식 프로세스는 두 개의 변수 global_var과 local_var를 각각 1씩 증가시키며, 부모 프로세서는 2초 후에 이 두 변수를 각각 5씩 증가시킨다.

/*----------------------------------------------------------------------------------------------

파일명 : fork_test.c

: fork() 시스템 사용 연습

컴파일 : cc -o fork_test fork_test.c

실행예 : fork_test

----------------------------------------------------------------------------------------------*/

#include <sys/types.h>

#include <unistd.h>

int global_var = 0; /* 전역 변수 선언 */

int main(void) {

pid_t pid;

int local_var = 0; /* 지역 변수 선언 */

if((pid = fork()) < 0) {

printf("fork error\n");

return -1;

/* 자식 프로세스 */

} else if (pid == 0) {

global_var++;

local_var++;

printf("CHILD - my pid : %d, parent's pid : %d\n", getpid(), getppid());

} else {

/* 부모 프로세스 */

sleep(2); /* 2 쉰다 */

global_var += 5;

local_var += 5;

printf("PARENT - my pid is %d, child's pid is %d\n", getpid(), pid);

}

printf("\t global var : %d\n", global_var);

printf("\t local var : %d\n", local_var);

}

▶ 이 프로그램 실행 결과는 다음과 같다.

# fork_test

CHILD - my pid is 12557 and parent's pid is 12556

global var : 1

local var : 1

PARENT - my pid is 12556 and child's pid is 12557

global var : 5

local var : 5

#

▶ 이 프로그램 실행 결과에서 2초 후에 부모 프로세스가 main() 함수 밖과 안에서 정의된 변수 global_var와 local_var를 각각 5씩 증가시켰는데 그 결과가 모두 6이 되지 않고 5가 되어 있는 것을 주목할 필요가 있다.

▶ 즉, 자식 프로세스와 부모 프로세스는 파일이나 소켓은 공유하지만 변수는 공유하지 않는다는 것을 알 수 있다.


현재강좌 : UNIX BSD 소켓 시스템 콜 이전: 2.4 서버 프로그램 작성절차