linux 아래 서버 프로그램의 몇 가지 기본 모델 - [1] 단일/다중 프로세스 모드/다중 프로세스 탱크 (prefork) 모델...

6272 단어
서버의 기본 모델은 이렇게 맞는지 모르겠지만, 사실은 linux 아래 서버와 클라이언트의 통신 모델이다.즉, 클라이언트가 늑대처럼 갈망하는 방문 수요에 직면하여 서버가 어떻게 신속하게 응답해야 하는가이다.
내가 총결해 보니 이렇게 몇 가지가 있다.
  • 단일 프로세스로 서비스를 제공합니다
  • 다중 프로세스 제공 서비스
  • 다중 프로세스 풀 서비스(prefork)
  • io 복용 제공 서비스(select,poll)
  • epoll(사실은 IO 복용이기도 하다)
  • 다중 스레드 제공 서비스
  • 다중 스레드 탱크는 서비스를 제공한다
  • 신호 구동 서비스 제공

  • 하나하나 자신의 생각에 따라 쓰고 여러분과 다음에 비저지io, 비동기io, 공유 메모리, 프로세스 간 통신 등 서버 상용 기술을 함께 연구하고 싶습니다.쓸데없는 말은 하지 않고 바로 시작하다.
     
    단일 프로세스 제공 서비스
    이런 모델은 우리의 학습에만 존재한다. 한 클라이언트가 서버에 응답을 요청한 후에 이 고객은 서버를 완전히 점유했다. 이번에 어떻게 새로운 고객을 추가할지 그는 반드시 서버가 기존의 이 고객을 모실 때까지 기다려야 한다.서버는 새로운 고객을 위해 서비스를 제공하지 않을 것이다. 이것이 바로 완전한 점유이다.
    서버 및 클라이언트의 비헤이비어:
    server : bind -> listen -> accept one request -> do request, send response -> close accept fd  -> accept next request ....
    client  :  connect -> send request  -> wait response -> recieve response -> close connect
     
    서버의 행위는 일반적으로 socket을 만들고 관련 포트를 ip는 bind를 사용하여 socket에 묶고listen을 통해 이 포트를 감청하며 고객의 요청이 도착할 때 요청을 처리하고 응답합니다.고객 요청을 처리한 후 이 요청을 닫고 다음 요청을 처리합니다.
    클라이언트의 행동은connect를 통해 서버에 연결되어 요청을 보내고 응답을 기다리며 답변을 받고 연결을 닫습니다.
    이런 방식의 폐단은 한 고객이 요청을 처리하지 않았을 때, 다른 고객은 accept가 될 때까지 기다려야 한다는 것을 명백히 알 수 있다.웹 서비스와 같은 높은 병행 요청에서 이런 서버 모델은 분명히 안 된다.
    하나의 매개 변수가 있는데 여기서 나는 줄곧 매우 이상하다고 느꼈다. 바로listen의 두 번째 매개 변수인backlog이다. 설명에 따르면 이 매개 변수는 세 차례의 악수 중의 연결 수와 네트워크 연결을 완성했지만 아직 accept되지 않은 연결 수의 합이 가장 큰 값이지만 각 핵에서 실제로 나타나는 것과 차이가 있는 것 같다.자신의 본에서 실현된 바와 같이, 초과 또는connect ok를 할 것 같다. 
    내 실험용 서버 코드를 붙여서 코드 양을 절약하기 위해 모든 오류 처리가 무시되었다
     
    /*
     * auther : [email protected]
     */
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #define MAX_CONNECTION 2
    
    int main(int argc, char** argv){
        int fd;
        time_t ticks;
        int port = 99999;
        fd = socket(AF_INET,SOCK_STREAM,0);
        struct sockaddr_in addr;
        memset(&addr,0,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        int size = sizeof(struct sockaddr);
        bind(fd,(struct sockaddr*)&addr,size);
        listen(fd,MAX_CONNECTION);
        struct sockaddr_in client_addr;
        while (1){
            memset(&client_addr, 0, sizeof (client_addr));
            char buf[1024];
            memset(&buf,0,sizeof(buf));
            int client_fd = accept(fd, (struct sockaddr *) &client_addr, &size);
            ticks = time (0);
            snprintf(buf, sizeof(buf), "%s", ctime(&ticks));
            write(client_fd, buf, sizeof(buf));
            sleep(3600);
            close(client_fd);
        }
        close(fd);
        return 0;
    }
    

    텔넷 127.0.0.1 99999로 테스트할 수 있지만 클라이언트가 텔넷 요청을 사용할 때 다음을 기다려야 합니다.
     
    다중 프로세스 제공 서비스
    하나의 프로세스가 서비스를 제공하는 것은 이미 대처할 수 없으니, 차라리 몇 명의 아들을 더 낳아서 요구를 처리하고, 노자로서 아들에 대한 감시만 하면 된다.다중 프로세스가 바로 이런 이치일 것이다.모든 요청은 fork 하위 프로세스로 처리됩니다.
    이 모델은 첫 번째 장점에 비해 여러 개의 요청을 처리하고 신속하게 응답할 수 있다는 것이다.
    단점은 요청할 때마다 새로운 프로세스를 생성하고 처리가 완료되며 삭제해야 한다는 것이다.원가가 좀 높아서 합병 요청이 비교적 높을 때 cpu를 다 소모합니다.아무래도 진행이라는 건 좀 무거운 거니까.
     
    수정 프로그램은 간단합니다. accept 뒤에 코드를 삽입하면 됩니다. 삽입된 코드는
            int client_fd = accept(fd, (struct sockaddr *) &client_addr, &size);
            
            pid = fork();
            if(pid > 0){
                close(client_fd);
                continue;
            }
            close(fd);
            ticks = time (0);
    

    하위 프로세스가 좀비 프로세스가 되는 것을 방지하기 위해 신호 SIGCHLD를 처리하여 이 신호를 받은 후waitpid 함수를 호출하여 하위 프로세스를 회수하는 것이 부족합니다.
     
    다중 프로세스 풀 서비스
    매번 요청이 새로운 프로세스를 생성하는 것은 사실 필요성이 크지 않다. 대부분의 병발 서버가 처리하는 초당 병발량은 보통 수백 정도이다. 따라서 일반적인 몇 개 또는 10여 개의 프로세스가 순환하여 서비스를 제공하면hold가 살 수 있다. 매번 요청이 새로운 프로세스를 구축하는 비용을 줄이기 위해 우리 선배는 다중 프로세스 탱크(prefork)의 모델을 발명했고 몇몇 프로세스로 요청을 처리했다.
    두 가지 다중 프로세스 탱크의 실현을 보았는데, 하나는 부모 프로세스는listen만 관리하고, 하위 프로세스는 모든 요청에 대해 accept를 요청한다.다른 하나는 부모 프로세스가 accept를 맡고 accept를 받은 confd 핸들을 하위 프로세스에 전달하는 것이다.
    여기서 제가 먼저 다음 두 번째 실현을 말씀드리겠습니다. 두 번째 실현에 관해서select모델에서 말하자면 두 번째 모델의 실현과 select의 효과가 더욱 좋기 때문입니다.
     
    첫 번째 테스트 코드는 다음과 같습니다. (잘못된 프로세서가 추가되지 않았습니다.)
    /*
     * auther : [email protected]
     */
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #define MAX_CONNECTION 2
    
    int main(int argc, char** argv){
        int fd = -1;
        time_t ticks;
        pid_t pid; 
        pid_t pids[10]; 
        int port = 99999;
        fd = socket(AF_INET,SOCK_STREAM,0);
        struct sockaddr_in addr;
        memset(&addr,0,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        int size = sizeof(struct sockaddr);
        bind(fd,(struct sockaddr*)&addr,size);
        listen(fd,MAX_CONNECTION);
    
        int i;
        for(i = 0; i< 10;i++){
            pid = fork();
            if(pid > 0){
                continue;
            }
            pids[i] = pid;
            struct sockaddr_in client_addr;
            while (1){
                memset(&client_addr, 0, sizeof (client_addr));
                char buf[1024];
                memset(&buf,0,sizeof(buf));
                printf("wait
    "); int client_fd = accept(fd, (struct sockaddr *) &client_addr, &size); close(fd); ticks = time (0); snprintf(buf, sizeof(buf), "%s", ctime(&ticks)); write(client_fd, buf, sizeof(buf)); sleep(3600); close(client_fd); } } close(fd); for(i = 0; i< 10;i++){ int status; if(pids[i] < 0){ continue; } waitpid(pids[i],&status,0); } return 0; }

      
    여기에는 원래 비교적 얽힌 부분이 하나 있는데 그것이 바로 accept이다. 원래의 linux 버전은 놀라움 현상이 있을 것이다. 즉, 요청이 왔을 때 여러 개의 하위 프로세스가 accept 막힘에서 깨어나 자원 소모가 너무 크다. 이 비교적 얽힌 문제는 현재의 비교적 새로운 linux 호스트에서 이미 해결되었다. 다른 얽힌 문제가 있을 때 select의 충돌, 이것은 우리가 select에서 다시 이어가자.여기까지만 얘기하자.
     
     
     
     
     
     
     
     
     
    다음으로 전송:https://www.cnblogs.com/wully/archive/2011/12/17/forksserver.html

    좋은 웹페이지 즐겨찾기