Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- PersistenceContext
- 코루틴 컨텍스트
- terminal
- 자원부족
- VARCHAR (1)
- Value too long for column
- 티스토리챌린지
- python
- JanusWebRTCGateway
- JanusGateway
- JanusWebRTCServer
- vfr video
- preemption #
- pytest
- addhooks
- tolerated
- 달인막창
- 오블완
- 코루틴 빌더
- mp4fpsmod
- Spring Batch
- taint
- 겨울 부산
- 개성국밥
- 깡돼후
- kotlin
- JanusWebRTC
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- table not found
- PytestPluginManager
Archives
너와 나의 스토리
[TCP/IP 소켓] Signal 함수, 핸들링, 좀비 프로세스 소멸, 다중접속 서버 본문
반응형
시그널이란?
- 특정 상황이 되었을 때 운영체제가 프로세스에게 해당 상황이 발생했음을 알리는 일종의 메시지를 가리켜 시그널이라고 한다.
시그널 등록이란?
- 특정 상황에서 운영체제로부터 프로세스가 시그널을 받기 위해서는 해당 상황에 대해서 등록의 과정을 거쳐야 한다.
시그널과 signal 함수
- 프로세스와 운영체가 하는 일
- ex) 자식 프로세스가 종료되면 프로세스가 zombie_handler라는 이름의 함수를 호출하도록 하고 싶을 때
- 프로세스: zombie_handler라는 함수 생성
- 운영체제: 자식 프로세스가 종료되면 대신 zombie_handler 함수 호출해줌
- 시그널 등록 함수(시그널 등록할 때 사용)
- 매개변수 선언: int signo, void(*func)(int)
- signo: 특정 상황에 대한 정보
- void(*func)(int): 특정 상황에서 호출될 함수의 주소 값(포인터)를 전달
- 즉, 첫 번째 인자를 통해 명시된 상황 발생시, 두 번째 인자로 전달된 주소 값의 함수가 호출된다.
- 반환형: 매개변수형이 int이고 반환형이 void인 함수 포인터
- 매개변수 선언: int signo, void(*func)(int)
- signal 함수를 통해서 등록 가능한 특정 상황과 그 상황에 할당된 상수
- signal(SIGCHLD,mychild);
- 자식 프로세스가 종료되면 mychild 함수를 호출해 달라
- signal(SIGALRM, timeout);
- alarm 함수 호출을 통해서 등록된 시간이 지나면 timeout 함수를 호출하라
- signal(SIGINT, keycontrol);
- CTRL+C 가 입력되면 keycontrol 함수를 호출해 달라
- signal(SIGCHLD,mychild);
- 시그널이 등록되면, 함께 등록된 함수의 호출을 통해서 운영체제는 시그널의 발생을 알린다.
시그널 핸들링 예제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 소켓 프로그래밍]
반응형
'Computer Networks > 실습' 카테고리의 다른 글
주소 할당 에러의 원인 - Time wait과 SO_REUSEADDR 옵션 (0) | 2019.11.08 |
---|---|
[CH.4-2,3] TCP기반 서버, 클리이언트의 구현 - Iterative 기반으로 구현 (0) | 2019.10.09 |
[CH.4-1] TCP와 UDP에 대한 이해 (0) | 2019.10.09 |
[실습] 주소체계와 데이터 정렬 - IP주소/PORT번호/주소정보 표현/인터넷 주소(Internet Address) (0) | 2019.09.25 |
리눅스 기반 파일 조작하기 (0) | 2019.09.25 |
Comments