관리 메뉴

너와 나의 스토리

[TCP/IP 소켓] Signal 함수, 핸들링, 좀비 프로세스 소멸, 다중접속 서버 본문

Computer Networks/실습

[TCP/IP 소켓] Signal 함수, 핸들링, 좀비 프로세스 소멸, 다중접속 서버

노는게제일좋아! 2019. 11. 8. 18:52
반응형
시그널이란?
  • 특정 상황이 되었을 때 운영체제가 프로세스에게 해당 상황이 발생했음을 알리는 일종의 메시지를 가리켜 시그널이라고 한다.

시그널 등록이란?
  • 특정 상황에서 운영체제로부터 프로세스가 시그널을 받기 위해서는 해당 상황에 대해서 등록의 과정을 거쳐야 한다.

 

시그널과 signal 함수
  • 프로세스와 운영체가 하는 일
    • ex) 자식 프로세스가 종료되면 프로세스가 zombie_handler라는 이름의 함수를 호출하도록 하고 싶을 때
    • 프로세스: zombie_handler라는 함수 생성 
    • 운영체제: 자식 프로세스가 종료되면 대신 zombie_handler 함수 호출해줌
  • 시그널 등록 함수(시그널 등록할 때 사용)
    • 매개변수 선언: int signo, void(*func)(int)
      • signo: 특정 상황에 대한 정보
      • void(*func)(int): 특정 상황에서 호출될 함수의 주소 값(포인터)를 전달
      • 즉, 첫 번째 인자를 통해 명시된 상황 발생시, 두 번째 인자로 전달된 주소 값의 함수가 호출된다. 
    • 반환형: 매개변수형이 int이고 반환형이 void인 함수 포인터

  • signal 함수를 통해서 등록 가능한 특정 상황과 그 상황에 할당된 상수
    • signal(SIGCHLD,mychild); 
      • 자식 프로세스가 종료되면 mychild 함수를 호출해 달라
    • signal(SIGALRM, timeout);
      • alarm 함수 호출을 통해서 등록된 시간이 지나면 timeout 함수를 호출하라
    • signal(SIGINT, keycontrol);
      • CTRL+C 가 입력되면 keycontrol 함수를 호출해 달라
  • 시그널이 등록되면, 함께 등록된 함수의 호출을 통해서 운영체제는 시그널의 발생을 알린다.

 

 

시그널 핸들링 예제1 - 시그널을 발생시켜 sleep함수의 호출로 블로킹 상태에 있는 프로세스를 깨우자
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

void timeout(int sig) {
	if (sig == SIGALRM) {
		puts("Tiem out!");
	}
	alarm(2);
}
void keycontrol(int sig) {
	if (sig == SIGINT)
		puts("CTRL+C pressed");
}
int main(int argc, char *argv[])
{
	int i;
	signal(SIGALRM, timeout);
	signal(SIGINT, keycontrol);
	alarm(2);  // 2초 뒤에 SIGALRM 시그널 발생
	for (i = 0; i < 3; i++) {
		puts("wait....");
		sleep(100);
	}
	return 0;
}

그냥 두면 sleep(100)하다가 2초가 지나면 timeout 함수가 실행됨 -> sleep 깨짐

CTRL+C를 누르면 "CTRL+C pressed"가 뜸 -> sleep 깨짐

 

 

 

 

 

 

  • sigaction 함수
    • signal 함수 대체 가능, 훨씬 안정적
    • signal 함수는 유닉스 계열의 운영체제 별로 동작 방식에 있어 약간의 차이를 보일 수 있지만
    • sigaction 함수는 차이를 보이지 않음

  • sigaction 구조체 변수를 선언해서, 스그널 등록 시 호출될 함수의 정보를 채워서 위의 함수 호출 시 인자로 전달한다.
  • sa_mask의 모든 비트는 0, sa_flags는 0으로 초기화! 이 둘은 시그널 관련 정보의 추가 전달에 사용되는데, 좀비의 소멸을 목적으로는 사용되지 않는다.

 

 

시그널 핸들링 예제2 - 시그널 핸들링을 통한 좀비 프로세스의 소멸 
// 좀비 프로세스가 생성되지 않도록 하자

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

void read_childproc(int sig) {
	int status;
	pid_t id = waitpid(-1, &status, WNOHANG);
	if (WIFEXITED(status)) {
		printf("Removed proc id: %d \n", id);
		printf("Child send: %d \n", WEXITSTATUS(status));
	}
}

int main(int argc, char *argv[])
{
	pid_t pid;
	struct sigaction act;
	act.sa_handler = read_childproc;  // 자식 프로세스가 끝나면 read_childproc 함수 호출하라
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	sigaction(SIGCHLD, &act, 0);

	pid = fork();
	if (pid == 0) {
		puts("Hi! I'm child process");
		sleep(10);
		return 12;
	}
	else {
		printf("Child proc id: %d \n", pid);
		pid = fork();
		if (pid == 0) {
			puts("Hi! I'm child process");
			sleep(10);
			exit(24);
		}
		else {
			int i;
			printf("Child proc id: %d \n", pid);
			for (i = 0; i < 5; i++) {
				puts("wait....");
				sleep(5);   // 시그널이 발생하면 깨어남
			}
		}
	}
	return 0;
}

 

 

 

프로세스 기반 다중접속 

 

 

연결이 하나 생성될 때마다 프로세스를 생성해서 해당 클라이언트에 대해 서비스를 제공하는 것

 

 

 

 

 

  • 1단계: 에코 서버(부모 프로세스)는 accept 함수 호출을 통해서 연결요청을 수락한다.
  • 2단계: 이때 얻게 되는 소켓의 파일 디스크립터를 자식 프로세스를 생성해서 넘겨준다.
  • 3단계: 자식 프로세스는 전달받은 파일 디스크립터를 바탕으로 서비스를 제공한다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);
void read_childproc(int sig);


int main(int argc, char *argv[]) {
	int serv_sock, clnt_sock;
	struct sockaddr_in serv_adr, clnt_adr;

	pid_t pid;

	struct sigaction act;
	socklen_t adr_sz;
	int str_len, state;
	char buf[BUF_SIZE];

	if (argc != 2) {
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}

	act.sa_handler = read_childproc;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	state = sigaction(SIGCHLD, &act, 0);
	serv_sock = socket(PF_INET, SOCK_STREAM, 0);
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1) {
		error_handling("bind() error");
	}
	if (listen(serv_sock, 5) == -1) {
		error_handling("listen() error");
	}
	
	while (1) {
		adr_sz = sizeof(clnt_adr);
		clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
		if (clnt_sock == -1) continue;
		else puts("new client connected....");
		pid = fork();
		if (pid == -1) {
			close(clnt_sock);
			continue;
		}
		if (pid == 0) {
			close(serv_sock);
			while ((str_len = read(clnt_sock, buf, BUF_SIZE) != 0))
				write(clnt_sock, buf, str_len);

			close(clnt_sock);
			puts("client disconnected....");
			return 0;
		}
		else {
			close(serv_sock);
		}
	}
	close(serv_sock);
	return 0;
}

void read_childproc(int sig) {
	pid_t pid;
	int status;
	pid = waitpid(-1, &status, WNOHANG);
	printf("removed proc id: %d \n", pid);
}
void error_handling(char *message) {
	fputs(message, stderr);
	fputc( '\n', stderr);
	exit(1);
}

 

 

 

 

 

 

 

 

 

출처: [윤성우 열혈 TCP/IP 소켓 프로그래밍]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처: [윤성우 열혈 TCP/IP 소켓 프로그래밍]

 

반응형
Comments