隨筆篇-Socket IO 問(wèn)題

0.中斷

0.1 簡(jiǎn)介

中斷是指計(jì)算機(jī)運(yùn)行過(guò)程中,出現(xiàn)某些意外情況需主機(jī)干預(yù)時(shí),機(jī)器能自動(dòng)停止正在運(yùn)行的程序并轉(zhuǎn)入處理新情況的程序,處理完畢后又返回原被暫停的程序繼續(xù)運(yùn)行
-------- 引用百度百科

例如, 你正在家看電影,突然外賣(mài)送到了,此時(shí)你不得不暫停電影,停止播放,去拿外賣(mài)

這個(gè)過(guò)程就是中斷,在計(jì)算中中斷分為兩種:

  • 硬中斷
  • 軟中斷

0.2 硬中斷

硬中斷通常是由硬件發(fā)起的中斷,電腦外設(shè),網(wǎng)卡等發(fā)起的中斷,例如最常見(jiàn)的鍵盤(pán)打字場(chǎng)景如下:

CPU正在運(yùn)行用戶程序,那么當(dāng)用戶在鍵盤(pán)輸入字符時(shí),那么鍵盤(pán)就會(huì)發(fā)起中斷,CPU的中斷引腳(INTR)就會(huì)接受到中斷信號(hào)(唯一的數(shù)字標(biāo)識(shí)),從而調(diào)用內(nèi)核程序?qū)⑤斎氲淖址故驹趯?duì)應(yīng)的地方。

當(dāng)然CPU不可能給每個(gè)硬件設(shè)備都弄一個(gè)中斷引腳,因此中斷信號(hào)(唯一的數(shù)字標(biāo)識(shí))時(shí)通過(guò)中斷控制器去發(fā)送到CPU的中斷引腳,如下圖所示:

image-20210723114357715

8259A是一個(gè)中斷控制器,每個(gè)接口都連接不同的硬件設(shè)備,當(dāng)硬件設(shè)備超過(guò)8個(gè)時(shí)可以再連接一個(gè)中斷控制器

中斷控制器大概工作流程就是 中斷請(qǐng)求寄存器保存多個(gè)硬件的中斷請(qǐng)求信息,接下來(lái)通過(guò)優(yōu)先級(jí)解析器對(duì) 中斷請(qǐng)求進(jìn)行排序,數(shù)字越小越先執(zhí)行,最后將正在執(zhí)行的中斷請(qǐng)求存入

正在服務(wù)寄存器中,如下圖:

image-20210723140324004

當(dāng)然每個(gè)中斷信號(hào)其實(shí)本質(zhì)就是一個(gè)位置的數(shù)字,那么每個(gè)數(shù)字即要執(zhí)行什么樣的程序,這個(gè)由中斷向量表,即 把系統(tǒng)中所有的中斷類(lèi)型碼及其對(duì)應(yīng)的中斷向量按一定的規(guī)律存放在一個(gè)區(qū)域內(nèi),這個(gè)存儲(chǔ)區(qū)域就叫中斷向量表

在內(nèi)核程序啟動(dòng)時(shí)就會(huì)去加載這個(gè)表,這樣就知道了,不同的硬件設(shè)備中斷就會(huì)執(zhí)行不同的程序

0.3 軟中斷

眾所周知,系統(tǒng)運(yùn)行速度越快越好,最好是實(shí)時(shí)系統(tǒng),因此,Liunx為了滿足這點(diǎn),當(dāng)中斷發(fā)生時(shí),將耗時(shí)較短的操作的操作交給硬中斷處理,而一些耗時(shí)較長(zhǎng)的的工作交給軟中斷處理。

例如,網(wǎng)卡接收到數(shù)據(jù)時(shí),就會(huì)發(fā)送一個(gè)硬中斷請(qǐng)求,CPU接收到了這個(gè)中斷,就會(huì)快速將網(wǎng)卡中的數(shù)據(jù)存放到內(nèi)存中,然后發(fā)送一個(gè)軟中斷,當(dāng)軟中斷信號(hào)被喚醒后,CPU就會(huì)處理內(nèi)存中的數(shù)據(jù),在處理期間還是能夠響應(yīng)其他的硬中斷,如下圖:

image-20210723142909629

1.FD

Linux中,一切皆文件,內(nèi)核則是利用文件描述符來(lái)操作文件,當(dāng)新建了一個(gè)文件或者打開(kāi)現(xiàn)存文件都會(huì)返回一個(gè)文件描述符fd(整型數(shù)字),然后通過(guò)文件描述符來(lái)進(jìn)行操作。

例如可以使用exec文件來(lái)給文件創(chuàng)建fd,例如:

touch test.txt
exec 5<test.txt
exec 6>test.txt

數(shù)字5 就代表 test.txt這個(gè)文件的讀操作,操作fd5,即操作這個(gè)文件都操作

數(shù)字6 就代表 test.txt這個(gè)文件的寫(xiě)操作,操作fd5,即操作這個(gè)文件都操作

#代表將hello寫(xiě)入到test.txt文件中
echo 'hello' >& 6

當(dāng)然在linux中,任何程序都具備三個(gè)基礎(chǔ)文件描述符0,1,2,含義如下:

  • 0代表標(biāo)準(zhǔn)輸入
  • 1代表標(biāo)準(zhǔn)輸出
  • 2代表錯(cuò)誤輸出

可以以下命令查詢當(dāng)前進(jìn)程的文件描述符

# $$ 代表當(dāng)前進(jìn)程
ls /proc/$$/fd
image-20210801114938708

2. 狀態(tài)

在操作系統(tǒng)中CPU有兩種狀態(tài),分別是

  • 內(nèi)核態(tài)

    運(yùn)行內(nèi)核程序的狀態(tài)稱為內(nèi)核態(tài)

  • 用戶態(tài)

    運(yùn)行用戶程序成為用戶態(tài)

這種狀態(tài)會(huì)時(shí)常進(jìn)行切換,例如當(dāng)發(fā)生中斷時(shí),CPU就會(huì)從用戶態(tài)切換到內(nèi)核態(tài)

3. Server

每個(gè)服務(wù)端程序一旦運(yùn)行都會(huì)去創(chuàng)建socket,當(dāng)然socket也是文件描述符進(jìn)行表示的,

socket一旦創(chuàng)建,就會(huì)進(jìn)入bind階段,將地址綁定到socket上,綁定完成后就會(huì)開(kāi)始進(jìn)行監(jiān)聽(tīng),監(jiān)聽(tīng)完成并開(kāi)始進(jìn)行調(diào)用accpet方法

以下對(duì)上述步驟進(jìn)行詳細(xì)演示

3.1 socket

這里使用nc命令進(jìn)行創(chuàng)建服務(wù)端程序,如果沒(méi)有可以安裝

# 創(chuàng)建服務(wù)端程序 并且端口號(hào)為8989
nc -l 8989
image-20210801115547122

一旦執(zhí)行此命令,CPU就會(huì)調(diào)用內(nèi)核去執(zhí)行內(nèi)核的socket && bind && listen方法

當(dāng)然也可以先找到nc的進(jìn)程id,然后查看該進(jìn)程的所有文件描述符

可以另起一個(gè)tab頁(yè),之前的程序不要關(guān)閉

ps -ef | grep nc
image-20210801120542819

例如我這里是6040,那么我就需要去6040下去找對(duì)應(yīng)的文件描述符,如下:

ll /proc/6040/fd
image-20210801121039226

可以發(fā)現(xiàn)多了兩個(gè)文件描述符3,4,而且分別指向了socket

為了更加了解nc -l 8989運(yùn)行以后,內(nèi)核程序是如何執(zhí)行的,可以通過(guò)strace命令查看到應(yīng)用程序與內(nèi)核的交互過(guò)程,如下:

先停掉之前的程序

# strace 追蹤與內(nèi)核交互程序
# -ff 如果一個(gè)程序啟動(dòng)有多個(gè)線程,那么就會(huì)按照線程號(hào)記錄到每個(gè)文件中
# -o kk 將內(nèi)容輸出到kk文件中
strace -ff -o kk nc -l 8989
image-20210801121924541

另起一個(gè)tab頁(yè),就可以看到輸出的kk文件,如下圖所示:

image-20210801122112802

打開(kāi)文件,查看器文件內(nèi)容,可知,當(dāng)執(zhí)行完指令后創(chuàng)建了兩個(gè)socket,如下圖:

image-20210801122230399
image-20210801122418490

之所以有兩個(gè)socket,是因?yàn)橐粋€(gè)是IPV4,一個(gè)是IPV6

同時(shí)當(dāng)socket方法執(zhí)行完成以后,返回了文件描述符3,4


為了更加了解socket方法可以通過(guò)man指令查看socket的描述,如下:

# 2 代表查看2類(lèi)命令
man 2 socket
image-20210801123318871

從描述可以出,socket方法調(diào)用代表創(chuàng)建一個(gè)通信端點(diǎn),并且返回一個(gè)fd

調(diào)用時(shí)需給這個(gè)方法傳入一個(gè) 域名參數(shù),類(lèi)型參數(shù),和協(xié)議參數(shù)

因此只要是服務(wù)端程序一旦啟動(dòng),就一定會(huì)調(diào)用socket

調(diào)用內(nèi)核程序,也叫系統(tǒng)調(diào)用

3.2 bind

繼續(xù)查看kk文件內(nèi)容可知,當(dāng)socket創(chuàng)建完成以后,緊接著會(huì)調(diào)用bind內(nèi)核程序,如下圖所示:

image-20210801123941438

從圖中代碼可以知道,是將端口和地址綁定到了fd4這個(gè)socket上,為了更加了解清楚

可以繼續(xù)查看手冊(cè),了解其細(xì)節(jié) ,如下:

image-20210801124201289
image-20210801124219000

從上圖中可知,當(dāng)socket創(chuàng)建成功后,并沒(méi)有分配給scoket地址,因此借助bind需要給socket分配地址。

當(dāng)分配成功后就會(huì)返回一個(gè)0,注意這個(gè)0并不是fd

3.3 listen

繼續(xù)查看kk文件,發(fā)現(xiàn)當(dāng)bind過(guò)后緊接著調(diào)用了listen程序,如下圖:

image-20210801124701832

同理查看手冊(cè)去了解該方法的作用

image-20210801124746644

從上圖可知,該方法是用來(lái)監(jiān)聽(tīng)是否有客戶端來(lái)連接這個(gè)socket,如果一旦連接就會(huì)回調(diào)accept方法

至此可以發(fā)現(xiàn),任何一個(gè)服務(wù)端程序啟動(dòng)都會(huì)經(jīng)過(guò)socket bind listen三個(gè)步驟

4.client

4.1 accpet

當(dāng)使用nc程序連接服務(wù)端時(shí),從上面描述可知會(huì)先調(diào)用accpet方法,與服務(wù)端建立連接

nc localhost 8989
image-20210801125156809

繼續(xù)查看kk文件,可以發(fā)現(xiàn)調(diào)用了accpet方法建立了連接,如下:

image-20210801125251823

注意: 此時(shí)并沒(méi)有給服務(wù)端發(fā)消息,只是建立三次握手

同理查看手冊(cè)去了解該方法的說(shuō)明

image-20210801125425867

從圖中可知,該它提取掛起連接隊(duì)列上的第一個(gè)連接請(qǐng)求偵聽(tīng)套接字的連接sockfd創(chuàng)建一個(gè)新的已連接套接字,并返回引用該套接字的新文件描述符

意思就是創(chuàng)建一個(gè)客戶端連接的socket,然后返回給socket的文件描述符,從之前的圖可知,目前的socket描述為7

注意:這里說(shuō)的socket指的是客戶端連接的socket

4.2 recvfrom

當(dāng)客戶端給服務(wù)端發(fā)送消息,服務(wù)端就會(huì)調(diào)用recvfrom程序接受網(wǎng)卡中的消息,并且通過(guò)調(diào)用write方法去寫(xiě)入到用戶程序上,如下:

image-20210801130415636

因此整個(gè)流程客戶端與服務(wù)端通信過(guò)程是:

  1. 服務(wù)端啟動(dòng)后先系統(tǒng)調(diào)用socket,bind,listen

    多路復(fù)用器select后續(xù)再說(shuō)

  2. 客戶端與服務(wù)端建立三次握手時(shí),先將數(shù)據(jù)包發(fā)送到網(wǎng)卡,網(wǎng)卡發(fā)起硬中斷,然后由

    用戶態(tài)切換到內(nèi)核態(tài),調(diào)用accpet方法建立連接創(chuàng)建客戶端socket

  3. 客戶端發(fā)送消息時(shí),還是會(huì)發(fā)送到網(wǎng)卡,然后發(fā)起中斷,狀態(tài)切換,最后接收消息,然后通過(guò)write調(diào)用將消息寫(xiě)回用戶程序

5.BIO

BIO(阻塞式IO),即socket返回的文件描述符都是阻塞的,如果說(shuō)連接的數(shù)據(jù)沒(méi)有到達(dá)那么就會(huì)一直阻塞在那,等到數(shù)據(jù)到達(dá)。這種效率可想而知是非常低的

5.1 案例

該案例簡(jiǎn)單模擬一下BIO

在系統(tǒng)中安裝java環(huán)境,并且準(zhǔn)備一個(gè)java程序,內(nèi)容如下:

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        System.out.println("create server socket");
        while(true) {
            Socket socket = ss.accept();
            System.out.println("client port:" + socket.getPort());
            BufferedReader  br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while(true) {
                System.out.println(br.readLine());
                if(br.readLine().equals("exit")) break;
            }
        }
    }
}

從上圖程序可知,當(dāng)接收到一個(gè)socket后,會(huì)開(kāi)啟一個(gè)死循環(huán)讀取這個(gè)socket發(fā)送過(guò)來(lái)的消息

如果消息沒(méi)有發(fā)送過(guò)來(lái)就會(huì)一直等待,這樣即使有第二個(gè)socket發(fā)送消息過(guò)來(lái)也只能是等待

輸入以下指令執(zhí)行程序

strace -ff -o bbb java Server

發(fā)現(xiàn)程序一直阻塞著等待客戶端的連接,如下圖:

image-20210723161307075

此時(shí)可以再起一個(gè)tab頁(yè),查看用戶內(nèi)核交互記錄

image-20210723161331038

發(fā)現(xiàn)產(chǎn)生了很多文件,這是因?yàn)?code>jvm啟動(dòng)時(shí)會(huì)創(chuàng)建一些線程去處理一些事情,例如垃圾回收等等之類(lèi)


輸入jps指令,可以發(fā)現(xiàn)Server程序運(yùn)行在2039進(jìn)程id上,如下圖所示:

image-20210723162434020

通過(guò)以下命令,進(jìn)入進(jìn)程對(duì)應(yīng)的目錄中

cd /proc/2039/fd

查看內(nèi)容詳情,如下圖:

image-20210723162914851
  • 3 是因?yàn)槌绦騿?dòng)時(shí)會(huì)加載rt.jar,所以會(huì)有一個(gè)文件描述符 3
  • 4,5 代表創(chuàng)建的socket,之所以有兩個(gè)是因?yàn)橐粋€(gè)是IPV4 Socket,一個(gè)是IPV6 Socket

nc 命令可以與任何服務(wù)端程序建立連接

通過(guò)以下命令與服務(wù)端程序(Server)建立通信,如下:

# 注意:在兩個(gè)tab頁(yè)同時(shí)輸入該命令
nc localhost 8888
image-20210801132156203

可以從輸出的消息可以看出,只輸出了一個(gè)客戶端的消息,而第二個(gè)客戶端的消息并沒(méi)有輸出,如下:

image-20210801132300141

這種通信模型即BIO模型,也就是說(shuō)其socket是阻塞的,一直等待客戶端消息發(fā)送過(guò)來(lái)

如果不發(fā)送一直阻塞,其他客戶端也無(wú)法連接過(guò)來(lái)

5.2 thread

從上圖也可知,當(dāng)在高并發(fā)的情況下并不是適合,不可能10w個(gè)客戶端一直要等服務(wù)端挨個(gè)處理完成

為了提高程序的并發(fā)能力,采用一些人采取了多線程的方式,如下:

import java.io.*;
import java.net.*;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        System.out.println("create server socket");
        while(true) {
            Socket socket = ss.accept();
            new Thread(() -> {
                try {
                         System.out.println("client port:" + 
                                            socket.getPort());
                        BufferedReader  br = 
                            new BufferedReader(
                            new InputStreamReader(
                                socket.getInputStream()));
                         while(true) {
                            System.out.println(br.readLine());
                        }
                } catch(Exception e) {
                  e.printStackTrace();
                }

           }).start();
        }

    }
}

輸入以下指令運(yùn)行程序

# 最好是將之前的bbb文件刪除嗎,防止引起混亂
strace -ff -o mm  java Server

運(yùn)行成功以后再通過(guò)nc程序去連接服務(wù)端,這里還是啟動(dòng)兩個(gè)客戶端去連接服務(wù)端程序

nc localhost 8888

從服務(wù)端輸出的結(jié)果可以看出完美解決了上述問(wèn)題,如下圖

image-20210801133549543

5.3 缺點(diǎn)

上述多線程的方式,解決了阻塞問(wèn)題,但是從本質(zhì)來(lái)講socket還是阻塞的,只不過(guò)是換成了多線程的方式

查看與內(nèi)核交互的日志,去了解當(dāng)創(chuàng)建線程時(shí),內(nèi)核干了什么事情

image-20210801134627178

從上圖可知,文件有多個(gè),我們并不知道主線程對(duì)應(yīng)的是哪個(gè)文件,因此可以篩選以下,因?yàn)橹骶€程啟動(dòng)時(shí)會(huì)輸出create server socket,這樣可以通過(guò)以下命令進(jìn)行篩選

 grep 'create server socket' ./mm.* 
image-20210801134223266

從圖中可以看出,主線程相關(guān)內(nèi)容是在6341文件上,因此查看該文件內(nèi)容

vim mm.6341

從文件內(nèi)容上可以看出,創(chuàng)建線程其本質(zhì)是調(diào)用內(nèi)核的clone方法,如下圖所示:

image-20210801134528961

且與線程的文件一一對(duì)應(yīng),如下圖:

image-20210801134725283

在此我們得出結(jié)論,每創(chuàng)建一個(gè)線程就會(huì)發(fā)生一個(gè)系統(tǒng)調(diào)用,假設(shè)10W個(gè)客戶端連接進(jìn)來(lái),那么就意味著要?jiǎng)?chuàng)建10W個(gè)線程,發(fā)生10W次系統(tǒng)調(diào)用

且不說(shuō)能不能能不能支持這么多線程數(shù),即使能支持,CPU的線程調(diào)度,系統(tǒng)調(diào)用也會(huì)大大消耗資源

因此如果這種方式并不適合在并發(fā)的情況下使用

3.NIO

基于上述原因,因在在Linux中提供了另外一個(gè)socket,即非阻塞式socket,主要是為了解決BIO 問(wèn)題,如下:

image-20210730170634325

有了非阻塞Socket,那么就意味著不用向之前那樣進(jìn)行等待阻塞又或者是創(chuàng)建多個(gè)線程去處理客戶端,即一個(gè)線程也可以去處理多個(gè)客戶端程序,而不阻塞

此時(shí)工作方式就變成了以下方式:

socket(...) = 4
bind(4,...) = 0
listen(4)
accpet(4,...) = -1 # 返回的socket不再阻塞等待客戶端連接,沒(méi)有數(shù)據(jù)到達(dá)返回 -1

# 當(dāng)有下一個(gè)客戶端連接過(guò)來(lái)會(huì)繼續(xù) accpet
accpet(4,...) = 5
listen(5)
recvfrom(5) = -1 # 沒(méi)有數(shù)據(jù)到達(dá)返回 -1

因此修改之前的程序代碼,如下:

public static void main(String[] args) throws IOException {
    List<SocketChannel> socketChannels = new ArrayList<>(10);

    ServerSocketChannel ss = ServerSocketChannel.open();
    ss.bind(new InetSocketAddress(8888));

    while (true) {
        // 不會(huì)阻塞 如果客戶端沒(méi)有發(fā)送數(shù)據(jù)那么channel為空
        SocketChannel channel = ss.accept();
        if (channel == null) {
            System.out.println("發(fā)數(shù)據(jù)為空");
        } else {
            ss.configureBlocking(false);
            socketChannels.add(channel);
        }

        socketChannels.forEach(t -> {
            // 讀取客戶端消息
        });
    }
}

從上述偽代碼可知,這種工作模型一個(gè)線程可以接收多個(gè)客戶端請(qǐng)求,然后在程序中即用戶態(tài)中判斷客戶端發(fā)送的消息是否到達(dá)。到達(dá)則處理數(shù)據(jù),不到搭達(dá)則進(jìn)行下一次循環(huán)

每次查詢是否到達(dá)都需要調(diào)用recv...方法,也就是系統(tǒng)調(diào)用,而用戶態(tài)與內(nèi)核態(tài)的切換時(shí)比較小號(hào)資源的

當(dāng)有10W個(gè)客戶端,意味著每次循環(huán)都要經(jīng)歷10W次,那么這個(gè)效率肯定時(shí)非常地下的

4. 多路復(fù)用

4.1 select

為了解決上述在用戶態(tài)循環(huán)多次系統(tǒng)調(diào)用問(wèn)題,內(nèi)核增加了一個(gè)系統(tǒng)調(diào)用select,通過(guò)命令man 2 select 查看其介紹可知:

image-20210801141843761
image-20210801141923089
image-20210801141934771

從上圖中可以看出,select函數(shù),可以一次監(jiān)聽(tīng)多個(gè)文件描述符,一旦調(diào)用就會(huì)進(jìn)入阻塞狀態(tài),當(dāng)客戶端的消息到達(dá)時(shí),就會(huì)返回對(duì)應(yīng)客戶端的fd

也就是說(shuō)以前需要在程序中循環(huán)判斷消息是否到達(dá),而現(xiàn)在只需要將多個(gè)socketfd傳給select,在內(nèi)核內(nèi)部就會(huì)判斷,如果到達(dá)就會(huì)返回fd,用戶態(tài)就可以再次操作,如下:

image-20210730174707240

這種相當(dāng)于以前是在用戶態(tài)即用戶程序員那邊去判斷,現(xiàn)在只需要調(diào)用一次select即可判斷,這樣系統(tǒng)調(diào)用次數(shù)也就降低了

這樣由n次系統(tǒng)調(diào)用降低為了1,調(diào)用一次select就知道哪些文件描述符時(shí)可用的,之前的poll函數(shù)就是類(lèi)似功能

同時(shí)從參數(shù)select方法參數(shù)可知,如下圖

image-20210801142946239
  • nfds待監(jiān)聽(tīng)的最大fd指+1,最大值為1024
  • *readfds 待監(jiān)聽(tīng)的可讀fd集合
  • *writefds 待監(jiān)聽(tīng)可寫(xiě)fd集合
  • exceptfds 帶監(jiān)聽(tīng)異常fd集合
  • timeout 超時(shí)時(shí)間

select雖然降低了NIO或者BIO過(guò)程中的多次系統(tǒng)調(diào)用,但是有以下缺點(diǎn)無(wú)法解決:

  • select是直接在readfdswritefds操作,導(dǎo)致這兩個(gè)數(shù)組不可重用,每次都需要重新賦值
  • 每次select都要便利全量的fds

5.EPOLL

epoll 是Linux所特有的

epoll本質(zhì)是一個(gè)組合系統(tǒng)掉i用,由三部分組成

  • epoll_create
  • epoll_ctl
  • epoll_waite

當(dāng)然也可以從其手冊(cè)上看出,如下圖所示:

image-20210801105335237

5.1 epoll_create

當(dāng)服務(wù)端程序,調(diào)用epoll_create時(shí),就會(huì)創(chuàng)建一個(gè)epoll實(shí)例,在內(nèi)核中開(kāi)辟一塊空間,并且返回一個(gè)文件描述符,這個(gè)文件描述符就是用來(lái)描述這塊開(kāi)辟的空間,當(dāng)然也可以從其手冊(cè)中可以看出來(lái),如下:

image-20210801105648914

從圖中的描述可以看出,當(dāng)調(diào)用epoll_create方法時(shí),需要傳入一個(gè)size,但是這個(gè)size2.6.8版本后已經(jīng)忽略掉了

開(kāi)辟的內(nèi)核空間,其本質(zhì)就是一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體主要是是由紅黑樹(shù) + 就緒鏈表組成

5.2 epoll_ctl

從手冊(cè)中可以看出,當(dāng)調(diào)用該方法需要傳入之前epoll_create返回的文件描述符,如下圖:

image-20210801110806729

關(guān)于參數(shù)解釋如下:

  • epfd epoll實(shí)例的fd

  • op 對(duì)空間紅黑數(shù)進(jìn)行操作,操作參數(shù)如下圖

    image-20210801110859014
    • epoll_ctl_add表示將fd添加到之前在內(nèi)核開(kāi)辟的內(nèi)存空間,也就是將fd添加到紅黑樹(shù)上
    • 同時(shí)moddel,則表示修改和刪除
  • fd socketfd

5.3 epoll_wait

當(dāng)調(diào)用epoll_wait方法時(shí),就會(huì)直接獲取到達(dá)消息文件描述符,應(yīng)用程序進(jìn)行讀寫(xiě)操作

整個(gè)工作流程如下:

image-20210801151058612
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容