網絡編程實戰學習筆記(十四)-epoll

Epoll

C10k問題

非阻塞IO

1.是否使用了select poll epoll等就是非阻塞了?
非阻塞IO指的是套接字類型,而不是網絡事件模型(select,poll,epoll)

阻塞非阻塞對應數據準備過程,同步異步對應數據從內核到應用程序緩衝區過程

阻塞IO+多進程模型

1.多進程採用fork實現,程序調用 fork 一次,在父、子進程裏各返回一次。在調用該函數的進程(即爲父進程)中返回的是新派生的進程 ID 號,在子進程中返回的值爲 0。想要知道當前執行的進程到底是父進程,還是子進程,只能通過返回值來進行判斷。
2.當一個子進程退出時,系統內核還保留了該進程的若干信息,比如退出狀態。這樣的進程如果不回收,就會變成殭屍進程。在 Linux 下,這樣的“殭屍”進程會被掛到進程號爲 1 的 init 進程上。所以,由父進程派生出來的子進程,也必須由父進程負責回收,否則子進程就會變成殭屍進程。殭屍進程會佔用不必要的內存空間,如果量多到了一定數量級,就會耗盡我們的系統資源。

demo

#include<iostream>
#include <poll.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <error.h>
#include <unistd.h>
#include "message_objecte.h"
#include    <signal.h>
#include <wait.h>

#define    SERV_PORT      43211
#define    MAXLINE        4096
#define    UNIXSTR_PATH   "/var/lib/unixstream1.sock"
#define    LISTENQ        1024
#define    BUFFER_SIZE    4096
#define MAXLINE 4096

int tcp_server_listen(u_int32_t port)
{
    //1.create
    int fd=socket(AF_INET,SOCK_STREAM,0);

    //2.init
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_port=htons(port);
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=INADDR_ANY;

    //3.set reuse(SOL_SOCKET 套接字級別)
    int on=1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    //4.bind
    socklen_t len= sizeof(server_addr);
    int rt1=bind(fd,(struct sockaddr*)&server_addr,len);
    if(rt1<0){
        error(1,errno,"bind failed");
    }

    //5.listen
    int rt2=listen(fd,1024);
    if(rt2<0){
        error(1,errno,"listen failed");
    }
    return fd;
}
void child_run(int fd){
    char outbuf[MAXLINE+1];
    size_t outbuf_used=0;
    ssize_t result;

    while(1){
        char ch;
        //這裏的recv是阻塞模式的
        result = recv(fd,&ch,1,0);//一次接收一個字符
        //如果recv函數在等待協議接收數據時網絡中斷了,那麼它返回0。
        if(result == 0){
            printf("client closed\n");
            break;
        }else if(result==-1){
            perror("read");
            break;
        }
        if(outbuf_used< sizeof(outbuf)){
            outbuf[outbuf_used++]=ch;
        }
        if(ch=='\n'){
            send(fd,outbuf,outbuf_used,0);
            outbuf_used=0;
            continue;
        }
    }
}
//處理子進程退出的方式一般是註冊一個信號處理函數,
// 捕捉信號 SIGCHILD 信號,然後再在信號處理函數裏
// 調用 waitpid 函數來完成子進程資源的回收。
void sigchld_handler(int sig)
{
    //這裏選項 WNOHANG 用來告訴內核,即使還有未終止的子進程也不要阻塞
    // 在 waitpid 上。注意這裏不可以使用 wait,
    // 因爲 wait 函數在有未終止子進程的情況下,沒有辦法不阻塞。
    printf("quit child process\n");
    while(waitpid(-1,0,WNOHANG)>0){
        return;
    }
}

int main(int c,char **v){
    int listen_fd=tcp_server_listen(SERV_PORT);
    signal(SIGCHLD,sigchld_handler);
    while(1){
        struct sockaddr_storage ss;
        socklen_t slen= sizeof(ss);
        int conn_fd=accept(listen_fd,(struct sockaddr*)&ss,&slen);
        if(conn_fd<0){
            error(1,errno,"accept failed");
            exit(1);
        }
        /*
         * 從父進程派生出的子進程,同時也會複製一份描述字,也就是說,連接套接字和
         * 監聽套接字的引用計數都會被加 1,而調用 close 函數則會對引用計數進行減 1 操作,
         * 這樣在套接字引用計數到 0 時,纔可以將套接字資源回收。所以,這裏的 close
         * 函數非常重要,缺少了它們,就會引起服務器端資源的泄露。*/
        //fork先返回非零值,再返回零值
        if(fork()==0){//子進程,只負責連接套接字
            //子進程不需要關心監聽套接字,故而在這裏關閉掉監聽套接字 listen_fd
            close(listen_fd);
            printf("child process\n");
            child_run(conn_fd);
            exit(0);
        }else{   //父進程,只負責監聽套接字
            printf("this is main process\n");
            //父進程不需要關心連接套接字,所以在這裏關閉連接套接字
            close(conn_fd);
        }
    }
    return 0;
}

輕量級方式:阻塞IO+線程程模型

事件驅動模型

事件驅動模型,也被叫做反應堆模型(reactor),或者是 Event loop 模型。這個模型的核心有兩點。
第一,它存在一個無限循環的事件分發線程,或者叫做 reactor 線程、Event loop 線程。這個事件分發線程的背後,就是 poll、epoll 等 I/O 分發技術的使用
第二,所有的 I/O 操作都可以抽象成事件,每個事件必須有回調函數來處理。acceptor 上有連接建立成功、已連接套接字上發送緩衝區空出可以寫、通信管道 pipe 上有數據可以讀,這些都是一個個事件,通過事件分發,這些事件都可以一一被檢測,並調用對應的回調函數加以處理。

同步/異步IO、阻塞/非阻塞IO 30 | 真正的大殺器:異步I/O探索

在這裏插入圖片描述
同步異步和阻塞非阻塞完全是不同的概念啊。
同步異步指的是,做完一件事再做另一件事還是兩件事互不干擾的做。比如同步IO指的是從發起讀寫操作到完成讀寫操作是同步完成的,即發起-執行-完成。異步IO:發起讀寫然後就OK了,內核完成後通知完成的狀態。

阻塞:就是停在這裏不往下進行了。非阻塞:就是不停,繼續執行。

**阻塞非阻塞是一個動作。同步異步是一套過程。**可以這麼理解嗎?
**另外:**平時所說的多線程下保持數據同步,意思應該是保持數據在多線程下的一致。

PS :怎麼感覺脫離了這一章節理解的還是不到位,上面寫的還是不夠清楚。30 | 真正的大殺器:異步I/O探索

可以這麼理解: 阻塞非阻塞對應數據準備過程,同步異步對應數據從內核到應用程序緩衝區過程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章