国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《Linux高性能服務(wù)器編程》筆記02

這篇具有很好參考價(jià)值的文章主要介紹了《Linux高性能服務(wù)器編程》筆記02。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

Linux高性能服務(wù)器編程

參考

Linux高性能服務(wù)器編程源碼: https://github.com/raichen/LinuxServerCodes

豆瓣: Linux高性能服務(wù)器編程

第06章 高級(jí)I/O函數(shù)

Linux提供了很多高級(jí)的I/O函數(shù)。它們并不像Linux基礎(chǔ)I/O函數(shù)(比如open和read) 那么常用(編寫內(nèi)核模塊時(shí)一般要實(shí)現(xiàn)這些I/O函數(shù)),但在特定的條件下卻表現(xiàn)出優(yōu)秀的性 能。本章將討論其中和網(wǎng)絡(luò)編程相關(guān)的幾個(gè),這些函數(shù)大致分為三類:

用于創(chuàng)建文件描述符的函數(shù),包括pipe、dup/dup2函數(shù)。

用于讀寫數(shù)據(jù)的函數(shù),包括readv/writev、sendfile、mmap/munmap、splice和tee函數(shù)。

用于控制I/O行為和屬性的函數(shù),包括fcntl函數(shù)。

6.1 pipe函數(shù)

pipe函數(shù)可用于創(chuàng)建一個(gè)管道,以實(shí)現(xiàn)進(jìn)程間通信。我們將在13.4節(jié)討論如何使用管道 來(lái)實(shí)現(xiàn)進(jìn)程間通信,本章只介紹其基本使用方式。pipe函數(shù)的定義如下:

#include <unistd.h>
int pipe( int fd[2] );

pipe函數(shù)的參數(shù)是一個(gè)包含兩個(gè)int型整數(shù)的數(shù)組指針。該函數(shù)成功時(shí)返回0,并將一對(duì) 打開的文件描述符值填入其參數(shù)指向的數(shù)組。如果失敗,則返回-1并設(shè)置errno。

通過(guò)pipe函數(shù)創(chuàng)建的這兩個(gè)文件描述符fd[0]和fd[1]分別構(gòu)成管道的兩端,往fd[1]寫入的數(shù)據(jù)可以從fd[0]讀出。并且,fd[0]只能用于從管道讀出數(shù)據(jù),fd[1]則只能用于往管道 寫入數(shù)據(jù),而不能反過(guò)來(lái)使用。如果要實(shí)現(xiàn)雙向的數(shù)據(jù)傳輸,就應(yīng)該使用兩個(gè)管道。默認(rèn)情況下,這一對(duì)文件描述符都是阻塞的。此時(shí)如果我們用read系統(tǒng)調(diào)用來(lái)讀取一個(gè)空的管道, 則read將被阻塞,直到管道內(nèi)有數(shù)據(jù)可讀;如果我們用write系統(tǒng)調(diào)用來(lái)往一個(gè)滿的管道(見(jiàn) 后文)中寫入數(shù)據(jù),則write亦將被阻塞,直到管道有足夠多的空閑空間可用。但如果應(yīng)用 程序?qū)d[0]和fd[1]都設(shè)置為非阻塞的,則read和write會(huì)有不同的行為。關(guān)于阻塞和非阻 塞的討論,見(jiàn)第8章。如果管道的寫端文件描述符fd[1]的引用計(jì)數(shù)(見(jiàn)5.7節(jié))減少至0, 即沒(méi)有任何進(jìn)程需要往管道中寫入數(shù)據(jù),則針對(duì)該管道的讀端文件描述符fd[0]的read操作 將返回0,即讀取到了文件結(jié)束標(biāo)記(End Of File,EOF);反之,如果管道的讀端文件描述 符fd[0]的引用計(jì)數(shù)減少至0,即沒(méi)有任何進(jìn)程需要從管道讀取數(shù)據(jù),則針對(duì)該管道的寫端文 件描述符fd[1]的write操作將失敗,并引發(fā)SIGPIPE信號(hào)。關(guān)于SIGPIPE信號(hào),我們將在 第10章討論。

pipe 函數(shù)是用于創(chuàng)建管道的系統(tǒng)調(diào)用。管道是用于進(jìn)程間通信的一種機(jī)制,它可以在兩個(gè)進(jìn)程之間傳遞數(shù)據(jù)。pipe 函數(shù)的聲明如下:

#include <unistd.h>

int pipe(int fd[2]);
  • 參數(shù)

    • fd: 用于存儲(chǔ)管道兩端文件描述符的數(shù)組。fd[0] 是用于讀取的文件描述符,fd[1] 是用于寫入的文件描述符。
  • 返回值

    • 如果成功,返回 0;如果失敗,返回 -1,并設(shè)置 errno。

使用示例

#include <stdio.h>
#include <unistd.h>

int main() {
    int pipe_fd[2];

    // 創(chuàng)建管道
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        return 1;
    }

    // 管道創(chuàng)建成功,pipe_fd[0] 用于讀取,pipe_fd[1] 用于寫入

    // 關(guān)閉不需要的文件描述符
    close(pipe_fd[0]); // 關(guān)閉讀取端
    close(pipe_fd[1]); // 關(guān)閉寫入端

    return 0;
}

上述示例演示了如何使用 pipe 函數(shù)創(chuàng)建一個(gè)管道。創(chuàng)建成功后,pipe_fd[0] 用于讀取,pipe_fd[1] 用于寫入。通常,創(chuàng)建管道后,需要在進(jìn)程中關(guān)閉不需要的文件描述符。

管道內(nèi)部傳輸?shù)臄?shù)據(jù)是字節(jié)流,這和TCP字節(jié)流的概念相同。但二者又有細(xì)微的區(qū)別。應(yīng)用層程序能往一個(gè)TCP連接中寫入多少字節(jié)的數(shù)據(jù),取決于對(duì)方的接收通告窗口的大小和 本端的擁塞窗口的大小。而管道本身?yè)碛幸粋€(gè)容量限制,它規(guī)定如果應(yīng)用程序不將數(shù)據(jù)從管道讀走的話,該管道最多能被寫入多少字節(jié)的數(shù)據(jù)。自Linux2.6.11內(nèi)核起,管道容量的大 小默認(rèn)是65536字節(jié)。我們可以使用fcntl函數(shù)來(lái)修改管道容量(見(jiàn)后文)。此外,socket的基礎(chǔ)API中有一個(gè)socketpair 函數(shù)。它能夠方便地創(chuàng)建雙向管道。其定義如下:

#include<sys/types.h> 
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, int fd[2] );

socketpair 前三個(gè)參數(shù)的含義與socket系統(tǒng)調(diào)用的三個(gè)參數(shù)完全相同,但domain 只能使 用UNIX本地域協(xié)議族AF_UNIX,因?yàn)槲覀儍H能在本地使用這個(gè)雙向管道。最后一個(gè)參數(shù) 則和pipe系統(tǒng)調(diào)用的參數(shù)一樣,只不過(guò)socketpair創(chuàng)建的這對(duì)文件描述符都是既可讀又可寫 的。socketpair 成功時(shí)返回0,失敗時(shí)返回-1并設(shè)置errno。

6.2 dup函數(shù)和dup2函數(shù)

有時(shí)我們希望把標(biāo)準(zhǔn)輸入重定向到一個(gè)文件,或者把標(biāo)準(zhǔn)輸出重定向到一個(gè)網(wǎng)絡(luò)連接 (比如CGI編程)。這可以通過(guò)下面的用于復(fù)制文件描述符的dup或dup2函數(shù)來(lái)實(shí)現(xiàn):

#include <unistd.h>
int dup( int flle_descriptor );
int dup2( int file_descriptor_one, int file_descriptor_two );

dup函數(shù)創(chuàng)建一個(gè)新的文件描述符,該新文件描述符和原有文件描述符file_descriptor指 向相同的文件、管道或者網(wǎng)絡(luò)連接。并且dup返回的文件描述符總是取系統(tǒng)當(dāng)前可用的最小 整數(shù)值。dup2和dup類似,不過(guò)它將返回第一個(gè)不小于file_descriptor_two的整數(shù)值。dup和 dup2系統(tǒng)調(diào)用失敗時(shí)返回-1并設(shè)置errno。

6-1testdup.cpp

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

int main(int argc, char* argv[]) {
    // 檢查命令行參數(shù)是否足夠
    if (argc <= 2) {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }

    const char* ip = argv[1];
    int port = atoi(argv[2]);

    // 初始化服務(wù)器地址結(jié)構(gòu)
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    // 創(chuàng)建套接字
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    assert(sock >= 0);

    // 綁定地址
    int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    // 監(jiān)聽連接
    ret = listen(sock, 5);
    assert(ret != -1);

    // 等待客戶端連接
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    int connfd = accept(sock, (struct sockaddr*)&client, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
    } else {
        // 關(guān)閉標(biāo)準(zhǔn)輸出文件描述符
        close(STDOUT_FILENO);
        // 復(fù)制 connfd 到標(biāo)準(zhǔn)輸出文件描述符的位置
        dup(connfd);
        // 此后標(biāo)準(zhǔn)輸出將輸出到 connfd 關(guān)聯(lián)的套接字
        printf("abcd\n");
        // 關(guān)閉 connfd
        close(connfd);
    }

    // 關(guān)閉套接字
    close(sock);
    return 0;
}

這段代碼的主要作用是創(chuàng)建一個(gè)服務(wù)器程序,監(jiān)聽指定端口,并在接收到客戶端連接后將標(biāo)準(zhǔn)輸出重定向到與客戶端連接關(guān)聯(lián)的套接字。

  • socket 創(chuàng)建:通過(guò) socket 函數(shù)創(chuàng)建一個(gè)套接字,用于接收客戶端連接。

  • bind 和 listen:使用 bind 綁定地址,然后通過(guò) listen 監(jiān)聽連接。

  • accept:等待客戶端連接,一旦有客戶端連接,就會(huì)返回一個(gè)新的套接字 connfd。

  • dup 函數(shù):關(guān)閉標(biāo)準(zhǔn)輸出文件描述符 (STDOUT_FILENO),然后使用 dup 函數(shù)將 connfd 復(fù)制到標(biāo)準(zhǔn)輸出文件描述符的位置。這樣,之后所有的 printf 輸出都將寫入到與客戶端連接關(guān)聯(lián)的套接字。

  • 輸出到客戶端:通過(guò) printf 輸出 “abcd”,這將通過(guò)與客戶端連接的套接字發(fā)送給客戶端。

  • 關(guān)閉套接字:關(guān)閉套接字,釋放資源。

總體來(lái)說(shuō),這段代碼演示了如何將標(biāo)準(zhǔn)輸出重定向到與客戶端連接的套接字,從而實(shí)現(xiàn)通過(guò)網(wǎng)絡(luò)連接輸出信息到客戶端。

在代碼清單6-1中,我們先關(guān)閉標(biāo)準(zhǔn)輸出文件描述符STDOUT_FILENO(其值是1), 然后復(fù)制socket文件描述符connfd。因?yàn)閐up總是返回系統(tǒng)中最小的可用文件描述符,所以 它的返回值實(shí)際上是1,即之前關(guān)閉的標(biāo)準(zhǔn)輸出文件描述符的值。這樣一來(lái),服務(wù)器輸出到 標(biāo)準(zhǔn)輸出的內(nèi)容(這里是“abcd”)就會(huì)直接發(fā)送到與客戶連接對(duì)應(yīng)的socket上,因此printf 調(diào)用的輸出將被客戶端獲得(而不是顯示在服務(wù)器程序的終端上)。這就是CGl服務(wù)器的基 本工作原理。

這段話描述了CGI服務(wù)器的基本工作原理。下面是對(duì)每個(gè)步驟的解釋:

  1. 關(guān)閉標(biāo)準(zhǔn)輸出文件描述符 (STDOUT_FILENO): 通過(guò)調(diào)用close(STDOUT_FILENO)關(guān)閉標(biāo)準(zhǔn)輸出文件描述符。這是因?yàn)樵贑GI服務(wù)器的工作模式中,我們希望將動(dòng)態(tài)生成的內(nèi)容發(fā)送到與客戶端連接相關(guān)聯(lián)的套接字,而不是輸出到服務(wù)器程序的終端。

  2. 復(fù)制socket文件描述符 (connfd): 使用dup(connfd)將套接字文件描述符 connfd 復(fù)制到系統(tǒng)中最小的可用文件描述符,而這個(gè)最小的可用文件描述符實(shí)際上就是關(guān)閉的標(biāo)準(zhǔn)輸出文件描述符 STDOUT_FILENO 的值。這意味著現(xiàn)在套接字文件描述符 connfd 成為了標(biāo)準(zhǔn)輸出文件描述符的副本。

  3. 輸出到標(biāo)準(zhǔn)輸出: 使用 printf 輸出內(nèi)容(在這里是 “abcd”)。由于標(biāo)準(zhǔn)輸出文件描述符已經(jīng)被復(fù)制為與客戶端連接相關(guān)的套接字,所以 printf 的輸出實(shí)際上會(huì)被發(fā)送到客戶端而不是顯示在服務(wù)器程序的終端上。

  4. 客戶端接收: 因?yàn)闃?biāo)準(zhǔn)輸出已被重定向到與客戶端連接的套接字,所以客戶端將接收到服務(wù)器發(fā)送的 “abcd”。

總體而言,CGI服務(wù)器通過(guò)關(guān)閉標(biāo)準(zhǔn)輸出,將套接字文件描述符復(fù)制到標(biāo)準(zhǔn)輸出的位置,然后通過(guò)標(biāo)準(zhǔn)輸出輸出內(nèi)容,實(shí)現(xiàn)了將動(dòng)態(tài)生成的內(nèi)容發(fā)送到與客戶端連接相關(guān)的套接字,從而向客戶端提供實(shí)時(shí)的動(dòng)態(tài)內(nèi)容。這是基本的CGI服務(wù)器工作原理。

6.3 readv 函數(shù)和writev 函數(shù)

readv函數(shù)將數(shù)據(jù)從文件描述符讀到分散的內(nèi)存塊中,即分散讀;writev函數(shù)則將多塊分散的內(nèi)存數(shù)據(jù)一并寫入文件描述符中,即集中寫。它們的定義如下:

#include <sys/uio.h>
ssize_t readv( int fd, const struct iovec* vector, int count); 
ssize_t writev( int fd, const struct iovec* vector, int count );

fd參數(shù)是被操作的目標(biāo)文件描述符。vector參數(shù)的類型是iovec結(jié)構(gòu)數(shù)組。我們?cè)诘? 章討論過(guò)結(jié)構(gòu)體iovec,該結(jié)構(gòu)體描述一塊內(nèi)存區(qū)。count參數(shù)是vector數(shù)組的長(zhǎng)度,即有多 少塊內(nèi)存數(shù)據(jù)需要從fd讀出或?qū)懙絝d。readv和writev在成功時(shí)返回讀出/寫入fd的字節(jié)數(shù),失敗則返回-1并設(shè)置errno。它們相當(dāng)于簡(jiǎn)化版的recvmsg和sendmsg函數(shù)。

考慮第4章討論過(guò)的Web服務(wù)器。當(dāng)Web服務(wù)器解析完一個(gè)HTTP請(qǐng)求之后,如果目標(biāo)文檔存在且客戶具有讀取該文檔的權(quán)限,那么它就需要發(fā)送一個(gè)HTTP應(yīng)答來(lái)傳輸該文檔。這個(gè)HTTP應(yīng)答包含1個(gè)狀態(tài)行、多個(gè)頭部字段、1個(gè)空行和文檔的內(nèi)容。其中,前3部分的內(nèi)容可能被Web服務(wù)器放置在一塊內(nèi)存中,而文檔的內(nèi)容則通常被讀入到另外一塊單 獨(dú)的內(nèi)存中(通過(guò)read函數(shù)或mmap函數(shù))。我們并不需要把這兩部分內(nèi)容拼接到一起再發(fā)送,而是可以使用writev函數(shù)將它們同時(shí)寫出,如代碼清單6-2所示。

6-2testwritev.cpp

這段代碼是一個(gè)簡(jiǎn)單的HTTP服務(wù)器,根據(jù)客戶端請(qǐng)求的文件名,在響應(yīng)中返回相應(yīng)的文件內(nèi)容。以下是對(duì)代碼的注釋和解釋:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#define BUFFER_SIZE 1024
static const char* status_line[2] = { "200 OK", "500 Internal server error" };

int main( int argc, char* argv[] )
{
    // 檢查命令行參數(shù)
    if( argc <= 3 )
    {
        printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );
    const char* file_name = argv[3];

    // 創(chuàng)建套接字
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );
    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    // 綁定地址
    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    // 監(jiān)聽
    ret = listen( sock, 5 );
    assert( ret != -1 );

    // 接受客戶端連接
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        // 處理HTTP響應(yīng)
        char header_buf[ BUFFER_SIZE ];
        memset( header_buf, '\0', BUFFER_SIZE );
        char* file_buf;
        struct stat file_stat;
        bool valid = true;
        int len = 0;
        
        // 檢查文件狀態(tài)
        if( stat( file_name, &file_stat ) < 0 )
        {
            valid = false;
        }
        else
        {
            // 檢查是否為目錄,是否有讀權(quán)限
            if( S_ISDIR( file_stat.st_mode ) || !(file_stat.st_mode & S_IROTH) )
            {
                valid = false;
            }
            else
            {
                // 讀取文件內(nèi)容
                int fd = open( file_name, O_RDONLY );
                file_buf = new char [ file_stat.st_size + 1 ];
                memset( file_buf, '\0', file_stat.st_size + 1 );
                if ( read( fd, file_buf, file_stat.st_size ) < 0 )
                {
                    valid = false;
                }
            }
        }
        
        if( valid )
        {
            // 構(gòu)建HTTP響應(yīng)頭
            ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[0] );
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, 
                             "Content-Length: %d\r\n", file_stat.st_size );
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
            struct iovec iv[2];
            iv[ 0 ].iov_base = header_buf;
            iv[ 0 ].iov_len = strlen( header_buf );
            iv[ 1 ].iov_base = file_buf;
            iv[ 1 ].iov_len = file_stat.st_size;
            
            // 使用 writev 函數(shù)將響應(yīng)頭和文件內(nèi)容一并寫入套接字
            ret = writev( connfd, iv, 2 );
        }
        else
        {
            // 發(fā)送500錯(cuò)誤響應(yīng)
            ret = snprintf( header_buf, BUFFER_SIZE-1, "%s %s\r\n", "HTTP/1.1", status_line[1] );
            len += ret;
            ret = snprintf( header_buf + len, BUFFER_SIZE-1-len, "%s", "\r\n" );
            send( connfd, header_buf, strlen( header_buf ), 0 );
        }
        
        // 關(guān)閉連接并釋放資源
        close( connfd );
        delete [] file_buf;
    }

    // 關(guān)閉服務(wù)器套接字
    close( sock );
    return 0;
}

這個(gè)程序根據(jù)客戶端請(qǐng)求的文件名,返回相應(yīng)的HTTP響應(yīng)。它能處理的請(qǐng)求包括:

  • 如果請(qǐng)求的文件存在且可讀,返回一個(gè)包含文件內(nèi)容的200 OK響應(yīng)。
  • 如果請(qǐng)求的文件是目錄或者不可讀,返回一個(gè)500 Internal Server Error響應(yīng)。

代碼清單6-2中,我們省略了HTTP請(qǐng)求的接收及解析,因?yàn)楝F(xiàn)在關(guān)注的重點(diǎn)是HTTP 應(yīng)答的發(fā)送。我們直接將目標(biāo)文件作為第3個(gè)參數(shù)傳遞給服務(wù)器程序,客戶telnet到該服務(wù) 器上即可獲得該文件。關(guān)于HTTP請(qǐng)求的解析,我們將在第8章給出相關(guān)代碼。

6.4 sendfile 函數(shù)

sendfile函數(shù)在兩個(gè)文件描述符之間直接傳遞數(shù)據(jù)(完全在內(nèi)核中操作),從而避免了內(nèi)核緩沖區(qū)和用戶緩沖區(qū)之間的數(shù)據(jù)拷貝,效率很高,這被稱為零拷貝。sendfile函數(shù)的定義如下:

#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count ); 

in_fd參數(shù)是待讀出內(nèi)容的文件描述符,out_fd參數(shù)是待寫入內(nèi)容的文件描述符。offset參數(shù)指定從讀入文件流的哪個(gè)位置開始讀,如果為空,則使用讀入文件流默認(rèn)的起始位置。 count參數(shù)指定在文件描述符in_fd和out_fd之間傳輸?shù)淖止?jié)數(shù)。sendfile 成功時(shí)返回傳輸?shù)?字節(jié)數(shù),失敗則返回-1并設(shè)置errno。該函數(shù)的man手冊(cè)明確指出,in_fd必須是一個(gè)支持 類似mmap函數(shù)的文件描述符,即它必須指向真實(shí)的文件,不能是socket和管道;而out fd 則必須是一個(gè)socket。由此可見(jiàn),sendfile幾乎是專門為在網(wǎng)絡(luò)上傳輸文件而設(shè)計(jì)的。下面的 代碼清單6-3利用sendfile函數(shù)將服務(wù)器上的一個(gè)文件傳送給客戶端。

以下是一個(gè)簡(jiǎn)單的使用 sendfile 函數(shù)的代碼示例。該示例將一個(gè)文件的內(nèi)容寫入到套接字中。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <unistd.h>

int main() {
    // 打開源文件
    int in_fd = open("source.txt", O_RDONLY);
    if (in_fd == -1) {
        perror("Error opening source file");
        return 1;
    }

    // 創(chuàng)建套接字并綁定端口
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    // 省略套接字創(chuàng)建和綁定的代碼

    // 打開目標(biāo)文件(套接字)
    int out_fd = accept(sock, NULL, NULL);
    if (out_fd == -1) {
        perror("Error accepting connection");
        close(in_fd);
        close(sock);
        return 1;
    }

    // 獲取源文件的大小
    struct stat stat_buf;
    fstat(in_fd, &stat_buf);

    // 使用 sendfile 將文件內(nèi)容傳輸?shù)教捉幼?/span>
    off_t offset = 0;
    ssize_t sent_bytes = sendfile(out_fd, in_fd, &offset, stat_buf.st_size);

    if (sent_bytes == -1) {
        perror("Error using sendfile");
    }

    // 關(guān)閉文件和套接字
    close(in_fd);
    close(out_fd);
    close(sock);

    return 0;
}

請(qǐng)注意,上述代碼是一個(gè)簡(jiǎn)化的示例,實(shí)際應(yīng)用中可能需要更多的錯(cuò)誤檢查和處理。

上述代碼中的

struct stat stat_buf;
fstat(in_fd, &stat_buf);

解釋如下:

struct stat stat_buf; 聲明了一個(gè)結(jié)構(gòu)體變量 stat_buf,該結(jié)構(gòu)體用于存儲(chǔ)文件的狀態(tài)信息,包括文件大小、權(quán)限、最后訪問(wèn)時(shí)間等。fstat(in_fd, &stat_buf); 通過(guò)文件描述符 in_fd 獲取文件狀態(tài)信息,并將其保存在 stat_buf 中。

具體而言,fstat 函數(shù)的作用是獲取與文件描述符相關(guān)聯(lián)的文件的狀態(tài)信息,并將這些信息填充到傳入的結(jié)構(gòu)體中。在這里,fstat 函數(shù)用于獲取打開的源文件 in_fd 的狀態(tài)信息,以便后續(xù)操作,如獲取文件大小等。

struct stat 結(jié)構(gòu)體的定義通常包含了很多字段,例如:

struct stat {
    dev_t         st_dev;      /* 文件所在設(shè)備的 ID */
    ino_t         st_ino;      /* 文件的 inode 號(hào) */
    mode_t        st_mode;     /* 文件的類型和權(quán)限信息 */
    nlink_t       st_nlink;    /* 文件的硬鏈接數(shù)量 */
    uid_t         st_uid;      /* 文件的用戶 ID */
    gid_t         st_gid;      /* 文件的組 ID */
    off_t         st_size;     /* 文件的大?。ㄗ止?jié)數(shù))*/
    time_t        st_atime;    /* 最后訪問(wèn)時(shí)間 */
    time_t        st_mtime;    /* 最后修改時(shí)間 */
    time_t        st_ctime;    /* 最后狀態(tài)改變時(shí)間 */
    blksize_t     st_blksize;  /* 文件系統(tǒng) I/O 緩沖區(qū)大小 */
    blkcnt_t      st_blocks;   /* 分配給文件的塊數(shù)量 */
};

在上述代碼中,st_size 字段用于獲取文件的大?。ㄗ止?jié)數(shù)),這對(duì)于確定文件的長(zhǎng)度非常有用。

6-3testsendfile.cpp

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>

int main( int argc, char* argv[] )
{
    // 檢查命令行參數(shù)
    if( argc <= 3 )
    {
        printf( "usage: %s ip_address port_number filename\n", basename( argv[0] ) );
        return 1;
    }
    
    // 獲取命令行參數(shù)
    const char* ip = argv[1];
    int port = atoi( argv[2] );
    const char* file_name = argv[3];

    // 打開文件
    int filefd = open( file_name, O_RDONLY );
    assert( filefd > 0 );
    
    // 獲取文件狀態(tài)信息
    struct stat stat_buf;
    fstat( filefd, &stat_buf );

    // 創(chuàng)建服務(wù)器地址結(jié)構(gòu)
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    // 創(chuàng)建監(jiān)聽socket
    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    // 綁定地址
    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    // 監(jiān)聽
    ret = listen( sock, 5 );
    assert( ret != -1 );

    // 接受客戶端連接
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        // 使用sendfile發(fā)送文件內(nèi)容
        sendfile( connfd, filefd, NULL, stat_buf.st_size );
        
        // 關(guān)閉連接
        close( connfd );
    }

    // 關(guān)閉監(jiān)聽socket
    close( sock );
    return 0;
}

代碼清單6-3中,我們將目標(biāo)文件作為第3個(gè)參數(shù)傳遞給服務(wù)器程序,客戶telnet到該服 務(wù)器上即可獲得該文件。相比代碼清單6-2,代碼清單6-3沒(méi)有為目標(biāo)文件分配任何用戶空間 的緩存,也沒(méi)有執(zhí)行讀取文件的操作,但同樣實(shí)現(xiàn)了文件的發(fā)送,其效率顯然要高得多。

6.5 mmap 函數(shù)和munmap函數(shù)

mmap函數(shù)用于申請(qǐng)一段內(nèi)存空間。我們可以將這段內(nèi)存作為進(jìn)程間通信的共享內(nèi)存, 也可以將文件直接映射到其中。munmap函數(shù)則釋放由mmap創(chuàng)建的這段內(nèi)存空間。它們的 定義如下:

#include <sys/mman.h>
void* mmap( void *start, size_t length, int prot, int flags, int fd, off_t offset );
int munmap( void *start, size_t length );

start參數(shù)允許用戶使用某個(gè)特定的地址作為這段內(nèi)存的起始地址。如果它被設(shè)置成 NULL,則系統(tǒng)自動(dòng)分配一個(gè)地址。length參數(shù)指定內(nèi)存段的長(zhǎng)度。prot參數(shù)用來(lái)設(shè)置內(nèi)存段的訪問(wèn)權(quán)限。它可以取以下幾個(gè)值的按位或:

PROT_READ,內(nèi)存段可讀。

PROT_WRITE,內(nèi)存段可寫。

PROT_EXEC,內(nèi)存段可執(zhí)行。

PROT NONE,內(nèi)存段不能被訪問(wèn)。

flags參數(shù)控制內(nèi)存段內(nèi)容被修改后程序的行為。它可以被設(shè)置為表6-1中的某些值(這 里僅列出了常用的值)的按位或(其中MAP_SHARED和MAP_PRIVATE是互斥的,不能同時(shí)指定)。

《Linux高性能服務(wù)器編程》筆記02,Linux Server,服務(wù)器,linux

fd參數(shù)是被映射文件對(duì)應(yīng)的文件描述符。它一般通過(guò)open系統(tǒng)調(diào)用獲得。offset參數(shù)設(shè) 置從文件的何處開始映射(對(duì)于不需要讀入整個(gè)文件的情況)。mmap函數(shù)成功時(shí)返回指向目標(biāo)內(nèi)存區(qū)域的指針,失敗則返回MAP_FAILED((void*)-1)并設(shè)置errno。munmap函數(shù)成功時(shí)返回0,失敗則返回-1并設(shè)置errno。我們將在第13章進(jìn)一步討論如何利用mmap 函數(shù)實(shí)現(xiàn)進(jìn)程間共享內(nèi)存。

mmap 函數(shù)用于將一個(gè)文件或者其它對(duì)象映射到調(diào)用進(jìn)程的地址空間,而 munmap 函數(shù)用于解除這種映射關(guān)系。

  • mmap 函數(shù)參數(shù)解釋:

    • start: 指定映射的起始地址,通常設(shè)置為0,由系統(tǒng)自動(dòng)分配。
    • length: 映射的長(zhǎng)度。
    • prot: 保護(hù)標(biāo)志,指定映射區(qū)的保護(hù)方式,可以是PROT_NONE(不可訪問(wèn)),PROT_READ(可讀),PROT_WRITE(可寫),PROT_EXEC(可執(zhí)行)等。
    • flags: 映射區(qū)的類型和映射對(duì)象的處理方式,可以是MAP_SHARED(共享映射)或MAP_PRIVATE(私有映射)等。
    • fd: 文件描述符,映射的文件。
    • offset: 文件映射的起始位置。
  • munmap 函數(shù)參數(shù)解釋:

    • start: 映射區(qū)的起始地址。
    • length: 映射區(qū)的長(zhǎng)度。

以下是一個(gè)簡(jiǎn)單的代碼示例:

#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char* file_path = "example.txt";
    const size_t file_size = 4096;

    // 打開文件
    int fd = open(file_path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 調(diào)整文件大小
    if (ftruncate(fd, file_size) == -1) {
        perror("ftruncate");
        close(fd);
        return 1;
    }

    // 映射文件到內(nèi)存
    void* mapped_data = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (mapped_data == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // 將數(shù)據(jù)寫入映射區(qū)
    const char* message = "Hello, Memory-mapped File!";
    strncpy(mapped_data, message, strlen(message));

    // 解除映射關(guān)系
    if (munmap(mapped_data, file_size) == -1) {
        perror("munmap");
    }

    // 關(guān)閉文件
    close(fd);

    return 0;
}

此示例創(chuàng)建了一個(gè)文件,通過(guò) mmap 將文件映射到內(nèi)存中,然后寫入數(shù)據(jù),最后通過(guò) munmap 解除映射關(guān)系。

6.6 splice 函數(shù)

splice函數(shù)用于在兩個(gè)文件描述符之間移動(dòng)數(shù)據(jù),也是零拷貝操作。splice函數(shù)的定義如下:

#include <fcntl.h>
ssize_t splice( int fd_in, loff_t* off_in, int fd_out, loff_t* off_out,size_t len, unsigned int flags );

fd_in參數(shù)是待輸入數(shù)據(jù)的文件描述符。如果fd_in是一個(gè)管道文件描述符,那么off_in 參數(shù)必須被設(shè)置為NULL。如果fd_in不是一個(gè)管道文件描述符(比如socket),那么off_in表示從輸入數(shù)據(jù)流的何處開始讀取數(shù)據(jù)。此時(shí),若off_in被設(shè)置為NULL,則表示從輸入數(shù)據(jù)流的當(dāng)前偏移位置讀入;若off_in不為NULL,則它將指出具體的偏移位置。fd_out/off_ out參數(shù)的含義與fd_in/off_in相同,不過(guò)用于輸出數(shù)據(jù)流。len參數(shù)指定移動(dòng)數(shù)據(jù)的長(zhǎng)度; flags參數(shù)則控制數(shù)據(jù)如何移動(dòng),它可以被設(shè)置為表6-2中的某些值的按位或。

《Linux高性能服務(wù)器編程》筆記02,Linux Server,服務(wù)器,linux

使用 splice 函數(shù)時(shí), fd_in和fd_out 必須至少有一個(gè)是管道文件描述符。splice 函數(shù)調(diào) 用成功時(shí)返回移動(dòng)字節(jié)的數(shù)量。它可能返回0,表示沒(méi)有數(shù)據(jù)需要移動(dòng),這發(fā)生在從管道中 讀取數(shù)據(jù)(fd_in是管道文件描述符)而該管道沒(méi)有被寫入任何數(shù)據(jù)時(shí)。splice函數(shù)失敗時(shí)返 回-1并設(shè)置errmo。常見(jiàn)的errno如表6-3所示。

《Linux高性能服務(wù)器編程》筆記02,Linux Server,服務(wù)器,linux

下面我們使用splice函數(shù)來(lái)實(shí)現(xiàn)一個(gè)零拷貝的回射服務(wù)器,它將客戶端發(fā)送的數(shù)據(jù)原樣 返回給客戶端,具體實(shí)現(xiàn)如代碼清單6-4所示。

6-4testsplice.cpp

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main( int argc, char* argv[] )
{
    if( argc <= 2 )
    {
        printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );

    // 創(chuàng)建套接字
    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sock >= 0 );

    // 綁定套接字
    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret != -1 );

    // 監(jiān)聽套接字
    ret = listen( sock, 5 );
    assert( ret != -1 );

    // 接受客戶端連接
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client );
    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
    if ( connfd < 0 )
    {
        printf( "errno is: %d\n", errno );
    }
    else
    {
        // 創(chuàng)建管道
        int pipefd[2];
        ret = pipe( pipefd );
        assert( ret != -1 );

        // 從套接字讀取數(shù)據(jù)并通過(guò) splice 復(fù)制到管道
        ret = splice( connfd, NULL, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE ); 
        assert( ret != -1 );

        // 從管道讀取數(shù)據(jù)并通過(guò) splice 復(fù)制到套接字
        ret = splice( pipefd[0], NULL, connfd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
        assert( ret != -1 );

        // 關(guān)閉連接套接字
        close( connfd );
    }

    // 關(guān)閉監(jiān)聽套接字
    close( sock );
    return 0;
}

注釋和解釋

  1. 創(chuàng)建套接字、綁定、監(jiān)聽:通過(guò) socket、bindlisten 創(chuàng)建并設(shè)置服務(wù)器套接字。

  2. 接受客戶端連接:使用 accept 函數(shù)等待客戶端連接,得到連接套接字 connfd。

  3. 創(chuàng)建管道:使用 pipe 創(chuàng)建一個(gè)管道,pipefd[0] 是讀取端,pipefd[1] 是寫入端。

  4. 通過(guò) splice 實(shí)現(xiàn)數(shù)據(jù)傳輸:使用兩次 splice 函數(shù),第一次從連接套接字 connfd 中讀取數(shù)據(jù)并寫入管道,第二次從管道讀取數(shù)據(jù)并寫入連接套接字。這樣實(shí)現(xiàn)了零拷貝,避免了數(shù)據(jù)在用戶空間和內(nèi)核空間之間的復(fù)制。

  5. 關(guān)閉連接套接字:關(guān)閉已經(jīng)處理完的連接套接字。

  6. 關(guān)閉監(jiān)聽套接字:關(guān)閉服務(wù)器監(jiān)聽套接字。

我們通過(guò)splice函數(shù)將客戶端的內(nèi)容讀入到pipefd[1]中,然后再使用splice 函數(shù)從 pipefd[0]中讀出該內(nèi)容到客戶端,從而實(shí)現(xiàn)了簡(jiǎn)單高效的回射服務(wù)。整個(gè)過(guò)程未執(zhí)行recv/ send操作,因此也未涉及用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝。

6.7 tee函數(shù)

tee函數(shù)在兩個(gè)管道文件描述符之間復(fù)制數(shù)據(jù),也是零拷貝操作。它不消耗數(shù)據(jù),因此 源文件描述符上的數(shù)據(jù)仍然可以用于后續(xù)的讀操作。tee函數(shù)的原型如下:

#include <fcntl.h>
ssize_t tee( int fd_in, int fd_out, size_t len, unsigned int flags );

該函數(shù)的參數(shù)的含義與splice相同(但fd_in和fd_out 必須都是管道文件描述符)。tee 函數(shù)成功時(shí)返回在兩個(gè)文件描述符之間復(fù)制的數(shù)據(jù)數(shù)量(字節(jié)數(shù))。返回0表示沒(méi)有復(fù)制任 何數(shù)據(jù)。tee失敗時(shí)返回-1并設(shè)置errno。

代碼清單6-5利用tee 函數(shù)和splice函數(shù),實(shí)現(xiàn)了Linux下tee程序(同時(shí)輸出數(shù)據(jù)到終 端和文件的程序,不要和tee函數(shù)混淆)的基本功能。

6-5testtee.cpp

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

int main( int argc, char* argv[] )
{
    // 檢查命令行參數(shù)是否合法
    if ( argc != 2 )
    {
        printf( "usage: %s <file>\n", argv[0] );
        return 1;
    }

    // 打開文件,若文件不存在則創(chuàng)建
    int filefd = open( argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0666 );
    assert( filefd > 0 );

    // 創(chuàng)建兩個(gè)管道,分別用于標(biāo)準(zhǔn)輸入、文件寫入和標(biāo)準(zhǔn)輸出
    int pipefd_stdout[2];
    int ret = pipe( pipefd_stdout );
    assert( ret != -1 );

    int pipefd_file[2];
    ret = pipe( pipefd_file );
    assert( ret != -1 );

    // 使用 splice 將標(biāo)準(zhǔn)輸入的內(nèi)容寫入 pipefd_stdout[1] 管道
    ret = splice( STDIN_FILENO, NULL, pipefd_stdout[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    assert( ret != -1 );

    // 使用 tee 函數(shù)將 pipefd_stdout[0] 管道的內(nèi)容同時(shí)寫入 pipefd_file[1] 管道和標(biāo)準(zhǔn)輸出
    ret = tee( pipefd_stdout[0], pipefd_file[1], 32768, SPLICE_F_NONBLOCK );
    assert( ret != -1 );

    // 使用 splice 將 pipefd_file[0] 管道的內(nèi)容寫入文件
    ret = splice( pipefd_file[0], NULL, filefd, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    assert( ret != -1 );

    // 使用 splice 將 pipefd_stdout[0] 管道的內(nèi)容寫入標(biāo)準(zhǔn)輸出
    ret = splice( pipefd_stdout[0], NULL, STDOUT_FILENO, NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE );
    assert( ret != -1 );

    // 關(guān)閉文件和所有使用的管道
    close( filefd );
    close( pipefd_stdout[0] );
    close( pipefd_stdout[1] );
    close( pipefd_file[0] );
    close( pipefd_file[1] );

    return 0;
}

作用: 該程序通過(guò) splicetee 函數(shù)實(shí)現(xiàn)了將標(biāo)準(zhǔn)輸入的內(nèi)容同時(shí)寫入文件和標(biāo)準(zhǔn)輸出的功能。使用管道和文件描述符傳輸數(shù)據(jù),無(wú)需用戶空間和內(nèi)核空間之間的數(shù)據(jù)拷貝,從而提高了效率。

6.8 fcntl函數(shù)

fcntl 函數(shù),正如其名字(file control)描述的那樣,提供了對(duì)文件描述符的各種控制操 作。另外一個(gè)常見(jiàn)的控制文件描述符屬性和行為的系統(tǒng)調(diào)用是ioctl,而且ioctl比f(wàn)cntl能夠 執(zhí)行更多的控制。但是,對(duì)于控制文件描述符常用的屬性和行為,fcntl函數(shù)是由POSIX規(guī) 范指定的首選方法。所以本書僅討論fcntl 函數(shù)。fcntl函數(shù)的定義如下:

#include <fcntl.h>
int fcntl( int fd, int cmd,);

fd參數(shù)是被操作的文件描述符,cmd參數(shù)指定執(zhí)行何種類型的操作。根據(jù)操作類型的不 同,該函數(shù)可能還需要第三個(gè)可選參數(shù)arg。

#include <fcntl.h>

int fcntl( int fd, int cmd, ... );

解釋:

  • fd 文件描述符,是需要進(jìn)行操作的文件或套接字的標(biāo)識(shí)符。
  • cmd 控制命令,指定對(duì)文件描述符 fd 進(jìn)行的操作。

fcntl 函數(shù)用于對(duì)文件描述符進(jìn)行各種控制操作,取決于 cmd 參數(shù)的值。該函數(shù)的第三個(gè)參數(shù) arg 的具體含義取決于 cmd 的值。

常見(jiàn)的 cmd 可選值:

  1. F_DUPFD 復(fù)制文件描述符。arg 為新的文件描述符的最小允許值。
  2. F_GETFL 獲取文件狀態(tài)標(biāo)志。arg 為無(wú)符號(hào)整數(shù),表示文件的狀態(tài)標(biāo)志。
  3. F_SETFL 設(shè)置文件狀態(tài)標(biāo)志。arg 為要設(shè)置的狀態(tài)標(biāo)志的位掩碼。
  4. F_GETLK 獲取文件鎖信息。arg 為指向 struct flock 結(jié)構(gòu)的指針,用于存儲(chǔ)鎖信息。
  5. F_SETLK 設(shè)置文件鎖。arg 為指向 struct flock 結(jié)構(gòu)的指針,用于設(shè)置鎖信息。
  6. F_SETLKW 設(shè)置文件鎖,如果無(wú)法獲取鎖則阻塞。arg 為指向 struct flock 結(jié)構(gòu)的指針。

示例:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 獲取文件狀態(tài)標(biāo)志
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl");
        close(fd);
        return 1;
    }

    // 設(shè)置文件狀態(tài)標(biāo)志,添加 O_APPEND 標(biāo)志
    flags |= O_APPEND;
    int result = fcntl(fd, F_SETFL, flags);
    if (result == -1) {
        perror("fcntl");
        close(fd);
        return 1;
    }

    // 其他操作...

    close(fd);
    return 0;
}

上述示例中,通過(guò) fcntl 函數(shù)獲取文件的狀態(tài)標(biāo)志,然后設(shè)置了 O_APPEND 標(biāo)志,將文件設(shè)置為以追加方式打開。

后記

截至2024年1月20日11點(diǎn)21分,學(xué)習(xí)完《Linux高性能服務(wù)器編程》第六章的內(nèi)容,主要介紹Linux的基礎(chǔ)I/O函數(shù)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-812123.html

到了這里,關(guān)于《Linux高性能服務(wù)器編程》筆記02的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Linux高性能服務(wù)器編程——學(xué)習(xí)筆記①

    Linux高性能服務(wù)器編程——學(xué)習(xí)筆記①

    第一章有一些概念講的很好,值得好好關(guān)注一下?。?! 1.1 主要的協(xié)議 1.1.1 數(shù)據(jù)鏈路層 ? 數(shù)據(jù)鏈路層實(shí)現(xiàn)了網(wǎng)卡接口的網(wǎng)絡(luò)驅(qū)動(dòng)程序,以處理數(shù)據(jù)在物理媒介(以太網(wǎng)、令牌環(huán))上的傳輸。 ? 常用的協(xié)議有兩種: ARP協(xié)議(Address Resolve Protocol,地址解析協(xié)議) RARP(Reverse

    2024年01月20日
    瀏覽(34)
  • Linux高性能服務(wù)器編程——ch10筆記

    信號(hào)是由用戶、系統(tǒng)或者進(jìn)程發(fā)送給目標(biāo)進(jìn)程的信息,以通知目標(biāo)進(jìn)程某個(gè)狀態(tài)的改變或系統(tǒng)異常。 :::tips int kill(pid_t pid, int sig); ::: kill函數(shù):一個(gè)進(jìn)程給其他進(jìn)程發(fā)送信號(hào)的API。 sig一般大于0,如果設(shè)為0則表示不發(fā)送信號(hào),可以用來(lái)檢測(cè)進(jìn)程或進(jìn)程組是否存在。由于進(jìn)程P

    2024年02月06日
    瀏覽(24)
  • Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第五章 Linux網(wǎng)絡(luò)編程基礎(chǔ)API

    Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第五章 Linux網(wǎng)絡(luò)編程基礎(chǔ)API

    我們將從以下3方面討論Linux網(wǎng)絡(luò)API: 1.socket地址API。socket最開始的含義是一個(gè)IP地址和端口對(duì)(ip,port),它唯一表示了使用TCP通信的一端,本書稱其為socket地址。 2.socket基礎(chǔ)API。socket的主要API都定義在sys/socket.h頭文件中,包括創(chuàng)建socket、命名socket、監(jiān)聽socket、接受連接、發(fā)

    2024年02月07日
    瀏覽(41)
  • Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第二章 IP協(xié)議詳解

    Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第二章 IP協(xié)議詳解

    本章從兩方面探討IP協(xié)議: 1.IP頭部信息。IP頭部出現(xiàn)在每個(gè)IP數(shù)據(jù)報(bào)中,用于指定IP通信的源端IP地址、目的端IP地址,指導(dǎo)IP分片和重組,指定部分通信行為。 2.IP數(shù)據(jù)報(bào)的路由和轉(zhuǎn)發(fā)。IP數(shù)據(jù)報(bào)的路由和轉(zhuǎn)發(fā)發(fā)生在除目標(biāo)機(jī)器外的所有主機(jī)和路由器上,它們決定數(shù)據(jù)報(bào)是否應(yīng)

    2024年02月09日
    瀏覽(31)
  • 【linux高性能服務(wù)器編程】項(xiàng)目實(shí)戰(zhàn)——仿QQ聊天程序源碼剖析

    【linux高性能服務(wù)器編程】項(xiàng)目實(shí)戰(zhàn)——仿QQ聊天程序源碼剖析

    hello !大家好呀! 歡迎大家來(lái)到我的Linux高性能服務(wù)器編程系列之項(xiàng)目實(shí)戰(zhàn)——仿QQ聊天程序源碼剖析,在這篇文章中, 你將會(huì)學(xué)習(xí)到如何利用Linux網(wǎng)絡(luò)編程技術(shù)來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天程序,并且我會(huì)給出源碼進(jìn)行剖析,以及手繪UML圖來(lái)幫助大家來(lái)理解,希望能讓大家更能了

    2024年04月28日
    瀏覽(33)
  • Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第一章 TCP/IP協(xié)議族

    Linux高性能服務(wù)器編程 學(xué)習(xí)筆記 第一章 TCP/IP協(xié)議族

    現(xiàn)在Internet使用的主流協(xié)議族是TCP/IP協(xié)議族,它是一個(gè)分層、多協(xié)議的通信體系。 TCP/IP協(xié)議族包含眾多協(xié)議,我們只詳細(xì)討論IP協(xié)議和TCP協(xié)議,因?yàn)樗鼈儗?duì)編寫網(wǎng)絡(luò)應(yīng)用程序有最直接的影響。如果想系統(tǒng)學(xué)習(xí)網(wǎng)絡(luò)協(xié)議,RFC(Request For Comments,評(píng)論請(qǐng)求)是首選資料。 TCP/IP協(xié)議

    2024年02月09日
    瀏覽(40)
  • Linux高性能服務(wù)器編程|閱讀筆記:第6章 - 高級(jí)I/O函數(shù)

    Linux高性能服務(wù)器編程|閱讀筆記:第6章 - 高級(jí)I/O函數(shù)

    Hello! 非常感謝您閱讀海轟的文章,倘若文中有錯(cuò)誤的地方,歡迎您指出~ ? ?(?ˊ?ˋ)? 昵稱:海轟 標(biāo)簽:程序猿

    2024年02月03日
    瀏覽(21)
  • Linux高性能服務(wù)器編程|閱讀筆記:第1章 - TCP/IP協(xié)議族

    Linux高性能服務(wù)器編程|閱讀筆記:第1章 - TCP/IP協(xié)議族

    Hello! 非常感謝您閱讀海轟的文章,倘若文中有錯(cuò)誤的地方,歡迎您指出~ ? ?(?ˊ?ˋ)? 昵稱:海轟 標(biāo)簽:程序猿|C++選手|學(xué)生 簡(jiǎn)介:因C語(yǔ)言結(jié)識(shí)編程,隨后轉(zhuǎn)入計(jì)算機(jī)專業(yè),獲得過(guò)國(guó)家獎(jiǎng)學(xué)金,有幸在競(jìng)賽中拿過(guò)一些國(guó)獎(jiǎng)、省獎(jiǎng)…已保研 學(xué)習(xí)經(jīng)驗(yàn):扎實(shí)基礎(chǔ) + 多做

    2024年02月01日
    瀏覽(44)
  • 強(qiáng)推Linux高性能服務(wù)器編程, 真的是后端開發(fā)技術(shù)提升, 沉淀自身不容錯(cuò)過(guò)的一本經(jīng)典書籍

    強(qiáng)推Linux高性能服務(wù)器編程, 真的是后端開發(fā)技術(shù)提升, 沉淀自身不容錯(cuò)過(guò)的一本經(jīng)典書籍

    目錄 第1章 TCP/IP協(xié)議 1.1 TCP/IP協(xié)議族體系結(jié)構(gòu)以及主要協(xié)議 1.1.1 數(shù)據(jù)鏈路層 1.1.2 網(wǎng)絡(luò)層 1.1.3 傳輸層 1.1.4 應(yīng)用層 1.2 封裝 1.3 分用 1.5 ARP協(xié)議工作原理 1.5.1 以太網(wǎng)ARP請(qǐng)求/應(yīng)答報(bào)文詳解 1.5.2 ARP高速緩存的查看和修改 1.5.3 使用tcpdump觀察ARP通信過(guò)程所得結(jié)果如下 本篇核心關(guān)鍵所在

    2024年02月07日
    瀏覽(118)
  • 【網(wǎng)絡(luò)編程】高性能并發(fā)服務(wù)器源碼剖析

    【網(wǎng)絡(luò)編程】高性能并發(fā)服務(wù)器源碼剖析

    ? hello !大家好呀! 歡迎大家來(lái)到我的網(wǎng)絡(luò)編程系列之洪水網(wǎng)絡(luò)攻擊,在這篇文章中, 你將會(huì)學(xué)習(xí)到在網(wǎng)絡(luò)編程中如何搭建一個(gè)高性能的并發(fā)服務(wù)器,并且我會(huì)給出源碼進(jìn)行剖析,以及手繪UML圖來(lái)幫助大家來(lái)理解,希望能讓大家更能了解網(wǎng)絡(luò)編程技術(shù)?。?! 希望這篇文章能

    2024年04月15日
    瀏覽(42)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包