成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

由Nodejs來(lái)說(shuō)I/O

adie / 3190人閱讀

摘要:調(diào)用一次獲得就緒文件描述符時(shí),返回的并不是實(shí)際的描述符,而是一個(gè)代表就緒描述符數(shù)量的值,拿到這些值去指定的一個(gè)數(shù)組中依次取得相應(yīng)數(shù)量的文件描述符即可,這里使用內(nèi)存映射技術(shù),避免了復(fù)制大量文件描述符帶來(lái)的開銷。

Nodejs定義

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

什么是IO

IO(Input & Output),顧名思義,輸入輸出即是IO。磁盤,網(wǎng)絡(luò),鼠標(biāo),鍵盤等都算IO;而大家通常說(shuō)的IO,大部分指磁盤和網(wǎng)絡(luò)的數(shù)據(jù)操作。
對(duì)于磁盤,IO=讀寫;對(duì)于網(wǎng)絡(luò),IO=收發(fā)。

Blocking I/O,從一個(gè)作業(yè)說(shuō)起

學(xué)習(xí)C語(yǔ)言時(shí),有個(gè)作業(yè),大意是寫一個(gè)server程序和client程序,實(shí)現(xiàn)TCP/UDP通信??雌饋?lái)代碼如下:

Client端
int ClientSend(SOCKET s, char* msg)
{
    char buf[BUF_SIZE] = {0};
    if (s && msg)
    {        
        int len = send(s, msg, strlen(msg), 0);
        if (len > 0)
        {
            println("Client send OK!");
            len = recv(s, buf, BUF_SIZE);
            if (len > 0)
            {
                println("Client receive: %s", buf);
            }
            // else socket recv error
        }
        // else socket send error
    }
    // else 
}

int main(char* argc, char* argv[])
{
    // 初始化socket
    SOCKET s = InitSocket();
    if (s != -1)
    {
        ClientSend(s, "Hi, I am Client");
    }
    // else socket init error
    return 0;
}
Server端
int main(char* argc, char* argv[])
{
    char buf[BUF_SIZE] = {0};
    const char* msg = "Roger that, I am Server";
     // 初始化socket,略
    SOCKET s = InitSocket();
    SOCKET cs;
    sockaddr_in addr;
    int nAddrLen = sizeof(addr);
    
    while ((cs = accept(s, &addr, &nAddrLen)) != -1)
    {
        int len = recv(cs, buf, BUF_SIZE, 0);
        if (len > 0)
        {
            len = send(cs, msg, strlen(msg), 0);
            if (len > 0)
            {
                println("Serve one client");
            }
            // else socket send error
        }
    }
    return 0;
}

在這個(gè)例子中,如果一個(gè)Client通信沒有結(jié)束,其它的Client是無(wú)法和Server通信的。原因就是代碼里面使用的是Blocking I/O,即同步IO。因?yàn)樵诖a中的recv或者send,都會(huì)阻塞住當(dāng)前代碼的執(zhí)行。單靠這種模型,是無(wú)法實(shí)現(xiàn)一個(gè)完善的服務(wù)器的。

Blocking I/O,多線程(多進(jìn)程)

為了讓Server能服務(wù)更多的Client,基于Blocking I/O,可以采用多線程(進(jìn)程)來(lái)處理,實(shí)現(xiàn)1對(duì)多的服務(wù)。

Server端
int ThreadProc(void* pParam)
{
   char buf[BUF_SIZE] = {0};
   const char* msg = "Roger that, I am Server";
   if (pParam)
   {
       int len = recv(cs, buf, BUF_SIZE, 0);
        if (len > 0)
        {
            len = send(cs, msg, strlen(msg), 0);
            if (len > 0)
            {
                println("Serve one client");
            }
            // else socket send error
        }
      // else socket recv error
   } 
   // else param error
   return 0;
}

int main(char* argc, char* argv[])
{
     // 初始化socket,略
    SOCKET s = InitSocket();
    SOCKET cs;
    sockaddr_in addr;
    int nAddrLen = sizeof(addr);
    
    while ((cs = accept(s, &addr, &nAddrLen)) != -1)
    {
        int pThread = CreateThread(NULL, 0, ThreadProc, cs);
      // serve on client
    }
    return 0;
}

這樣的方案,的確能同時(shí)處理多個(gè)Client請(qǐng)求,實(shí)現(xiàn)并發(fā)。但由于創(chuàng)建線程的成本很高(需要分配內(nèi)存,調(diào)度CPU等),受Server硬件條件的限制,這種方案不能服務(wù)很多Client,即服務(wù)器性能很低下。
另外,如果把ThreadProc里面的代碼增加邏輯:

// recive data from buf
setenv(buf);
CreateProcess(NULL, 0 ...);
// parse env in child process

這就是一個(gè)簡(jiǎn)單的CGI模型了。
在一些簡(jiǎn)單的http服務(wù)器代碼中,見到過(guò)這樣的模型。(比如一些嵌入式系統(tǒng)服務(wù)器)。

Non-blocking I/O,你完事兒沒有?

因?yàn)锽locking I/O的特點(diǎn),所以系統(tǒng)提供了另外的方法,Non-blocking I/O,即調(diào)用send,recv等接口時(shí),不會(huì)阻塞線程,但調(diào)用者需要自己去輪訓(xùn)IO的狀態(tài)來(lái)判定操作;就像一個(gè)監(jiān)工不停的問(wèn)工人,你完事兒沒有。

int main(char * argc, char * argv[])
{
    // 初始化socket,略
    SOCKET s = InitSocket();
    SOCKET cs;
    sockaddr_in addr;
    int fd;
    int nAddrLen = sizeof(addr);
    SetNonblocking(s);

    while (running) 
    {
        int ret = select(FD_SETSIZE, ...);
        if (ret == -1) break;
        if (ret == 0) continue;

        for (fd = 0; fd < FD_SETSIZE; fd++)
        {
            if (FD_ISSET(fd, ...)
            { 
                // 有新的client進(jìn)來(lái)   
                if (fd == s)
                {
                    cs = accept(s, & addr, & nAddrLen, 0);
                    FD_SET(cs, ...);
                }
                else // cs中的一個(gè)里面有變化
                {
                    ioctl(fd, FIONREAD, & nread); 
                    // 處理完畢
                    if (nread == 0)
                    {
                        close(fd);
                        FD_CLR(fd, ...);
                    }
                    else
                    {
                        // 處理Client邏輯,這里可能會(huì)創(chuàng)建線程。
                        ......
                    }
                }
            }
            // serve on client
        }
    }
    return 0;
}

在這種模型中,while和for循環(huán)不停的檢查fd_set的狀態(tài),并做相應(yīng)的處理,類似Apache的解決方案。
但是,這個(gè)模型里面還有一個(gè)block,就是select,當(dāng)有fd發(fā)生變化時(shí),select才會(huì)返回。
還有,select中的FD_SETSIZE有限制(一般是2048),就表明單進(jìn)程還是不能支持更大量級(jí)的并發(fā)。Apache采用多進(jìn)程的方式來(lái)解決這個(gè)問(wèn)題。
后期有了epoll,這個(gè)限制放的更寬,很多http服務(wù)器是用epoll來(lái)實(shí)現(xiàn)的(Nginx)。

epoll主要有兩個(gè)優(yōu)點(diǎn):

基于事件的就緒通知方式 ,select/poll方式,進(jìn)程只有在調(diào)用一定的方法后,內(nèi)核才會(huì)對(duì)所有監(jiān)視的文件描述符進(jìn)行掃描,而epoll事件通過(guò)epoll_ctl()注冊(cè)一個(gè)文件描述符,一旦某個(gè)文件描述符就緒時(shí),內(nèi)核會(huì)采用類似call back的回調(diào)機(jī)制,迅速激活這個(gè)文件描述符,epoll_wait()便會(huì)得到通知。

調(diào)用一次epoll_wait()獲得就緒文件描述符時(shí),返回的并不是實(shí)際的描述符,而是一個(gè)代表就緒描述符數(shù)量的值,拿到這些值去epoll指定的一個(gè)數(shù)組中依次取得相應(yīng)數(shù)量的文件描述符即可,這里使用內(nèi)存映射(mmap)技術(shù), 避免了復(fù)制大量文件描述符帶來(lái)的開銷。

Nodejs,也采用了和Nginx類似的思路,可以再深入了解下libuv。

Asynchronous I/O

有些人說(shuō)Nodejs是Asynchronous I/O,其實(shí)不然。Asynchronous I/O是說(shuō)用戶發(fā)起read等IO操作后,去做其它的事情了,而系統(tǒng)在完成IO操作后,用signal的方式通知用戶完成。目前使用此模型的http服務(wù)器有asyncio等。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64571.html

相關(guān)文章

  • 【譯】node js event loop part 1.1

    原文 先說(shuō)1.1總攬: Reactor模式 Reactor模式中的協(xié)調(diào)機(jī)制Event Loop Reactor模式中的事件分離器Event Demultiplexer 一些Event Demultiplexer處理不了的復(fù)雜I/O接口比如File I/O、DNS等 復(fù)雜I/O的解決方案 未完待續(xù) 前言 nodejs和其他編程平臺(tái)的區(qū)別在于如何去處理I/O接口,我們聽一個(gè)人介紹nodejs,總是...

    macg0406 評(píng)論0 收藏0
  • nodejs 異步I/O和事件驅(qū)動(dòng)

    摘要:異步和事件驅(qū)動(dòng)注本文是對(duì)眾多博客的學(xué)習(xí)和總結(jié),可能存在理解錯(cuò)誤。接觸有兩個(gè)月,對(duì)的兩大特性一直有點(diǎn)模糊,即異步和事件驅(qū)動(dòng)。 nodejs 異步I/O和事件驅(qū)動(dòng) 注:本文是對(duì)眾多博客的學(xué)習(xí)和總結(jié),可能存在理解錯(cuò)誤。請(qǐng)帶著懷疑的眼光,同時(shí)如果有錯(cuò)誤希望能指出。 接觸nodejs有兩個(gè)月,對(duì)nodejs的兩大特性一直有點(diǎn)模糊,即異步IO和事件驅(qū)動(dòng)。通過(guò)對(duì)《深入淺出nodejs》和幾篇博客的閱...

    binaryTree 評(píng)論0 收藏0
  • JavaScript 單線程不簡(jiǎn)單.md

    摘要:對(duì)于而言,單線程指的是它的執(zhí)行線程是單線程。對(duì)于來(lái)說(shuō),單線程不僅不是劣勢(shì),它對(duì)于降低編程復(fù)雜度還有很重要的作用,單線程避免了多線程編程模型多線程死鎖狀態(tài)同步等問(wèn)題。單線程的應(yīng)用是脆弱了,但群體的力量是強(qiáng)大的。 我們常聽說(shuō) JavaScript 是單線程的,那這個(gè)單線程是什么意思呢?單線程是否意味 JavaScript 存在性能缺陷呢? 在瀏覽器端,JavaScript 單線程指的是 J...

    Lowky 評(píng)論0 收藏0
  • 快速學(xué)習(xí)nodejs系列:六、nodejs特性3--事件驅(qū)動(dòng)

    摘要:事件驅(qū)動(dòng)在中,當(dāng)某個(gè)執(zhí)行完畢后,會(huì)以事件的形式通知執(zhí)行操作的線程而線程去執(zhí)行對(duì)應(yīng)事件的回調(diào)函數(shù)。為了處理異步,線程必須要有事件循環(huán),不斷的檢查有沒有事件要處理,并依次處理。其實(shí)在底層中,有一半的代碼,都是在處理事件隊(duì)列回調(diào)函數(shù)。 事件驅(qū)動(dòng) 上一節(jié)中,我們提到異步I/O;當(dāng)I/O處理完畢后,nodejs是怎樣知道I/O已經(jīng)完成了呢?又是怎樣去處理的呢?答案是:事件驅(qū)動(dòng)(事件循環(huán))機(jī)制。 ...

    ashe 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<