실습1:

1초씩 대기 하며 'wait..'를 프로그램을 작성하고, 사용자가 Ctrl+C 를 누르면 "종료하시겠습니까?"를 묻고 y 혹은 Y를 입력하면 종료하는 프로그램 작성

sigaction 에 SIGINT 신호에 대한 액션 핸들러를 등록해서 처리한다.

액션핸들러에서 scanf()를 사용한다.

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
31
32
33
34
35
36
37
38
39
40
41
42
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
 
int num=0;
void int_handle(int);
main()
{
    static struct sigaction act;
 
    void int_handle(int);
 
    act.sa_handler=int_handle;
    sigfillset(&(act.sa_mask));
    sigaction(SIGINT,&act,NULL);
 
    while(1) {
        printf("i'm sleepy..\n");
        sleep(1);
 
        if(num>0)
            exit(0);
        }
    
}
 
void int_handle(int signum){
    char q;
    printf("SIGINT:%d\n",signum);
    printf("종료하시겠습니까?");
    scanf("%c",&q);
    getchar();
 
 
    if(q=='y' || q=='Y')
    {
        num++;
        //exit(0);
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

실습2:

실습1에 작성한 코드를 바탕으로, 어제 실습한 멀티프로세스 서버(echo_mpserv.c) 에서 서버가 실행된 후에 사용자가 Ctrl+C를 누르면 "종료하시겠습니까?"를 묻고 y or Y를 누르면  종료하는 프로그램 코드를 적용해서 실습한다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#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);
void int_handle(int signum); 
int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
 
    pid_t pid;
    struct sigaction act;
    struct sigaction act2;
    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);
 
    act2.sa_handler=int_handle;
    sigfillset(&(act2.sa_mask));
    sigaction(SIGINT,&act2,NULL);
 
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0sizeof(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(clnt_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 int_handle(int signum){
    char q;
    printf("SIGINT:%d\n",signum);
    printf("종료하시겠습니까?");
    scanf("%c",&q);
    getchar();
 
 
    if(q=='y' || q=='Y')
    {
        exit(0);
    }
}
 
void error_handling(char * message)
{
    fputs(message, stderr);
    fputc('\n',stderr);
    exit(1);
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

 

IO Multiplexing

I/O Model

 

멀티 프로세스 서버의 단점과 대안

멀티 프로세스 서버의 단점

  • 프로세스의 빈번한 생성은 성능의 저하로 이어진다.
  • 멀티 프로세스의 흐름을 고려해서 구현해야 하기 때문에 구현이 쉽지 않다.
  • 프로세스간 통신이 필요한 상황에서는 서버의 구현이 더 복잡해진다.

 

멀티 프로세스 서버의 대안

  • 하나의 프로세스가 다수의 클라이언트에게 서비스를 할 수 있도록 한다.
  • 이를 위해서는 하나의 프로세스가 여러 개의 소켓을 핸들링 할 수 있는 방법이 존재 해야 한다.
  • 바로 이것이 IO 멀티플렉싱이다.
  •  

다중 접속 서버

● 다중 프로세스 : 새로운 연결이 올때마다 새로운 프로세스 생성

● 다중 쓰레드 : 새로운 연결이 올때마다 새로운 쓰레이드 생성

● 멀티플렉싱 : 하나의 전송로를 효과적으로 사용

 

IO Model

● Blocking I/O

● Non-Blocking I/O

● I/O Multiplexing (select, poll ...)

● Signal Driven ( SIGIO)

● Asynchronous I/O

● - aio_* functions

● - io_* functions

 

 

입출력 모델

봉쇄 vs 비봉쇄

동기 vs 비동기

- 이들 조합을 이용한 4개의 입출력 모델

- 다양한 입출력 기술이 있지만 이들 4개 범주에 포함

 

봉쇄

동기

비봉쇄

동기

봉쇄

비동기

비봉쇄

비동기

 

봉쇄 vs 비봉

 

봉쇄 : 데이터가 준비될 때까지 봉쇄(Blocking)

  • 직관적이며, 결과를 예측하기 쉽다
  • 프로그램이 명확하다.
  • 영원히 Blocking 될 수 있으므로 주의해야 한다.
  • 봉쇄 되므로, 여러 파일을 처리하려면 다른 기술을 사용해야 한다.

비 봉쇄 : 즉시 반환 (NonBlocking)

  • 바로 반환 하므로, 여러 파일을 처리할 수 있다.
  • 일반적으로 프로그램 개발이 까다롭다.
  • Busy wait를 주의 해야 한다.
  • fcntl 함수로 소켓을 봉쇄 혹은 비봉쇄 지정

 

동기 vs 비동기

동기 : 데이터 입출력 시점을 아고 있다.

  • 입출력 함수를 호출 하는 시점에 데이터를 처리한다
  • 입출력 함수 호출 시점이 동기화 시점
  • 데이터 흐름이 간결하다

비동기 : 데이터의 입출력 시점을 모른다.

  • 프로세스 진행 중에, 이벤트를 발생함으로써 데이터를 처리한다.
  • 이벤트를 받은 시점에 입출력 함수를 호출
  • 데이터를 효율적으로 처리할 수 있다.
  • 이벤트 처리로 프로그램이 복잡해질 수 있다. (이벤트 처리에는 많은 주의가 필요)

 

 

 

 

 

Client/Server

다중화와 버퍼

(1) 큐에 넣어서 하나씩 처리하는 형태

  • 많은 처리 요구가 들어와도 하나씩 처리
  • 처리시간이 긴 요구가 있다면 뒤의 요구들의 처리는 오래 지연됨

(2) 멀티 프로세스

  • 요구 처리마다 프로세스를 생성하여 개별적으로 응답
  • 오랫동안 계속되야 하는 다수의 처리에 유리
  • 프로세스의 전환이 빈번히 일어나는 경우 처리가 조금 늦음

(3) 멀티 스레드

  • 요구를 접수할 때마다 새로운 스레드 생성
  • 하나의 프로세서로 병렬 실행하며 처리
  • 하나의 스레드가 에러가 발생하면 프로세서 전체에 영향을 미침
  • 전환이 빈번한 요구인 경우 프로세스의 전환이 비교적 빠름

(4) 단일 프로세스나 스레드에 의한 다중처리(select 시스템 콜 사용)

  • 수신한 메시지를 구별하면서 처리
  • 관계있는 메세지들로 처리

 

 

비동기 다중 I/O

Asyncronous I/O Multiplexing

한 프로세스(스레드) 내에서 이루어지는 다중 비동기 IO 처리 방법

  • 폴링 (Polling)
  • 셀렉팅 (Selecting)
  • 인터럽트 (Interrupt)

 

Polling

처리해야 할 작업들을 순차적으로 돌아가면서 처리하는 방법

- 서버가 각 클라이언트로부터의 데이터 수신을 순차적으로 처리

 

입출력 함수가 블록되지 않아야 하므로 소켓을 넌블록 모드로 설정

- 넌블록 모드 : 즉시 처리할 수 있으면 결과를 리턴하고, 처리할 수 없는 경우라도 리턴 됨

여러 클라이언트가 고르게 트래픽을 발생 시키는 경우에 적합

 

Selecting

폴링과의 반대 개념으로 동작함

데이터가 도착하면 해당 클라이언트와의 입출력을 처리

유닉스의 select() 함수와 함께 소켓을 비동기(asynchronous)모드로 변경하여 사용

클라이언트로부터 데이터 도착이 불규칙적인 경우 적합

 

 

비동기 I/O Multiple 사례

● 멀티프로세스형 서버 프로그램

- 새로운 클라이언트 접속시 해당 클라이언트와 통신을 담당하는 프로세스를 생성

- 프로그램 작성이 편리하지만 수백명 이상의 클라이언트에서는 프로세스 수가 많아지는 문제가 있음

● 폴링형 서버 프로그램

- 소켓을 넌블록 모드로 설정

- 서버 프로그램은 접속된 클라이언트의 입출력 상태 확인

● 셀렉팅형 서버 프로그램

- 소켓은 비동기 모드로 설정 - 비동기형 방식이라고도 함

● 인터럽트형 서버 프로그램

- 원하는 I/O 이벤트가 발생 하였을 때 서버 프로세스에게 시그널로 알리고 - 서버가 시그널 처리 루틴에서 통신 서비스를 수행

 

 

I/O Multiplexing?

멀티 프로세스 방법은 많은 비용을 필요로 한다.

멀티 쓰레드 방법도 많은 비용이 소모된다.

입출력을 사건(이벤트로)다룬다면, 어떨까 ?

 

I/O Multiplexing

하나의 프로세스에서 입력과 출력을 다룬다.

프로세스 혹은 스레드를 만들 필요가 없다.

다른 많은 기술들이 입출력 다중화에 기반한다.

- 관리할 파일의 그룹을 만들고

- 그룹의 파일에 입출력 이벤트가 있는지를 확인.

- 입출력 이벤트가 발생한 파일의 목록을 일괄 처리

 

I/O Multiplexing 매커니즘

입출력 이벤트를 검사할 파일의 정보를 가지는 비트 테이블을 준비

비트 테이블에 검사할 파일을 체크

체크한 파일에 이벤트가 발생하면, 이벤트가 발생한 비트 필드의 값을 1로 해서 반환

비트 테이블을 검사. 비트 필드가 1이면, 이에 대응하는 파일에 대해서 입출력 함수를 호출

 

select 함수의 기능과 호출 순서 

  • 수신한 데이터를 지니고 있는 소켓이 존재하는가?
  • 블로킹되지 않고 데이터의 전송이 가능한 소켓은 무엇인가?
  • 예외상황이 발생한 소켓은 무엇인가?

select 함수를 이용하면, 배열에 저장 된 다수의 파일 디스크립터를 대상으로 이와 같은 질문을 할 수 있다.

 

Step One.

파일 디스크립터의 설정

검사의 범위 지정

타임아웃의 설정

Step One에서는 관찰의 대상을 묶고, 관찰의 유형을 지정한다.

Step Two.

select 함수의 호출

Step Two에서는 관찰 대상의 변화를 묻는다.

 

Step Three.

호출결과 확인

Step Three에서는 물음에 대한 답을 듣는다.

 

select()

#include <sys/slect.h>

int select (int nfds, fd_set *readfds, fde_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  • nfds : 파일 테이블의 최대 크기
  • readfds : 읽기 이벤트를 검사할 파일정보를 포함한 비트 테이블
  • writefds : 쓰기 이벤트를 검사할 파일정보를 포함한 비트 테이블
  • exceptfds : 예외 이벤트를 검사할 파일정보를 포함한 비트 테이블
  • timeout : 이벤트를 기다릴 시간 제한
  • return : 이벤트가 발생한 파일의 갯수

 

select 매크로

  • FD_ZERO(fd_set * fdest) : 인자로 전달된 주소의 fd_set형 변수의 모든 비트를 0으로 초기화
  • FD_SET(int fd, fd_set *fdset) : 매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보를 등록한다.
  • FD_CLR(int fd, fd_set *fdset) : 매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보를 삭제한다.
  • FD_ISSET(int fd, fd_set *fdset) : 매개변수 fdset으로 전달된 주소의 변수에 매개변수 fd로 전달된 파일 디스크립터 정보가 있으면 양수를 반환한다.

 

select() 문제

  • nfds 크기 +1 필드를 모두 검사해야 함
  • fd_set은 이전 상태를 기억하지 못함
  • 매번 파일 테이블을 복사해야 함
  •  

select.c

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
 
#define BUF_SIZE 30
 
int main(int argc,char *argv[])
{
    fd_set reads, temps;
    int result, str_len;
    char buf[BUF_SIZE];
    struct timeval timeout;
 
    FD_ZERO(&reads);
    FD_SET(0,&reads); // 0 is standard input(console)
 
    /*
     timeout.tv_sec=5;
     timeout.tv_usec=5000;
     */
 
    while(1)
    {
        temps=reads;
        timeout.tv_sec=5;
        timeout.tv_usec=0;
        result=select(1,&temps,0,0,&timeout);
        if(result==-1)
        {
            puts("select() error!");
            break;
        }
        else if(result==0)
        {
            puts("Time-out!");
        }
        else
        {
            if(FD_ISSET(0,&temps))
            {
                str_len=read(0,buf,BUF_SIZE);
                buf[str_len]=0;
                printf("message from console: %s",buf);
            }
        }
    }
    return 0;
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

+ Recent posts