日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux | 高级I/O函数

發(fā)布時(shí)間:2023/12/13 linux 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux | 高级I/O函数 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 創(chuàng)建文件描述符的函數(shù)
    • pipe函數(shù)
    • dup函數(shù)、dup2函數(shù)
  • 讀取或?qū)懭霐?shù)據(jù)
    • readv函數(shù)、writev函數(shù)
  • 零拷貝
    • sendfile函數(shù)
    • splice函數(shù)
    • tee函數(shù)
  • 進(jìn)程間通信——共享內(nèi)存
    • mmap函數(shù) 和 munmap函數(shù)
  • 控制文件描述符
    • fcntl函數(shù)


創(chuàng)建文件描述符的函數(shù)

pipe函數(shù)

不再贅述,詳情見(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, inf fd[2]); // domain只能使用 UNIX 本地域協(xié)議族 AF_UNIX,因?yàn)槲覀儍H能在本地使用這個(gè)雙向管道。 // 成功時(shí)返回0,失敗時(shí)返回-1并設(shè)置error。

dup函數(shù)、dup2函數(shù)

這兩個(gè)函數(shù)會(huì)在 CGI服務(wù)器 中用到。CGI服務(wù)器: 主要是通過(guò)把服務(wù)器本地標(biāo)準(zhǔn)輸入、輸出或者文件重定向到網(wǎng)絡(luò)連接中,以達(dá)到向標(biāo)準(zhǔn)輸入、輸出緩沖區(qū)中輸入的信息,能在網(wǎng)絡(luò)連接中發(fā)送的效果。

#include<unistd.h> int dup( int file_descriptor ); int dup2( int file_descriptor_one, int file_descriptor_two );
  • dup: 創(chuàng)建一個(gè)新的文件描述符,該文件描述符和原有文件描述符 file_descriptor 指向相同的文件、管道或網(wǎng)絡(luò)連接。返回的文件描述符總是取系統(tǒng)當(dāng)前可用的最小整數(shù)值。
  • dup2: 與 dup 類似,只是返回第一個(gè)不小于 file_descriptor_two 的整數(shù)值。

兩個(gè)系統(tǒng)調(diào)用失敗時(shí)都返回 -1,并設(shè)置 error。

通過(guò) dup 和 dup2 創(chuàng)建的文件描述符并不繼承原文件描述符的屬性。比如:close-on-exec 和 non-blocking 等。

用 dup 實(shí)現(xiàn)一個(gè)基本的 CGI服務(wù)器 的局部代碼:

close( STDOUT_FILENO ); dup( connfd ); printf( "hello\n" ); close( connfd );

流程:

  • 先關(guān)閉標(biāo)準(zhǔn)輸出文件描述符 STDOUT_FILENO (其值是1);
  • 通過(guò) dup 復(fù)制 socket 文件描述符 connfd ;
  • 由于 dup 總是返回系統(tǒng)中最小的可用文件描述符,所以它的返回值實(shí)際上是 1 ,即之前關(guān)閉的標(biāo)準(zhǔn)輸出文件描述符的值;
  • 服務(wù)器輸出到標(biāo)準(zhǔn)輸出的內(nèi)容(“hello”)就會(huì)直接發(fā)送到與客戶端對(duì)應(yīng)的 socket 上,因此 printf 調(diào)用的輸出將被客戶端獲得,而不是顯示在服務(wù)器程序的終端上。

  • 讀取或?qū)懭霐?shù)據(jù)

    readv函數(shù)、writev函數(shù)

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

    相當(dāng)于簡(jiǎn)化版的 recvmsg 和 sendmsg 。

    #include<sys/uio.h> ssize_t readv( int fd, const struct iovec* vector, int count ); ssize_t weitev( int fd, const struct iovec* vector, int count ); // fd:被操作的目標(biāo)文件描述符 // vector:iovec結(jié)構(gòu)數(shù)組,iovec封裝了一塊內(nèi)存的起始位置和長(zhǎng)度 // count:vector數(shù)組的長(zhǎng)度,即有多少塊內(nèi)存數(shù)據(jù)需要從fd讀出或?qū)懭氲絝d // 成功時(shí)返回讀出/寫入fd的字節(jié)數(shù),失敗則返回-1并試著errno。

    零拷貝

    sendfile函數(shù)

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

    #include<sys/sendfile.h> ssize_t sendfile( int out_fd, int in_fd, off_t* offset, size_t count ); // out_fd:待寫入內(nèi)容的文件描述符,必須是一個(gè)socket // in_fd:待讀出內(nèi)容的文件描述符,必須是一個(gè)支持類似mmap函數(shù)的文件描述符,即必須指向真實(shí)的文件,不能是socket和管道 // offset:指定從讀入文件流的哪個(gè)位置開(kāi)始讀,如果為空,則使用讀入文件流默認(rèn)的起始位置 // count:傳輸?shù)淖止?jié)數(shù) // 成功時(shí)返回傳輸?shù)淖止?jié)數(shù),失敗返回-1,并設(shè)置errno

    sendfile 幾乎是專門為在網(wǎng)絡(luò)上傳輸文件而設(shè)計(jì)的。


    splice函數(shù)

    splice函數(shù): 本質(zhì)就是借助管道描述符在兩個(gè)文件之間移動(dòng)數(shù)據(jù),也是零拷貝

    #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ù)據(jù)的文件描述符 // off_in:如果fd_in是一個(gè)管道描述符,那么off_in必須被設(shè)置為NULL;反之,如果fd_in不是一個(gè)管道描述符(如socket),那么off_in表示從輸入數(shù)據(jù)流開(kāi)始讀取數(shù)據(jù)的起始位置。總而言之,若off_in不為NULL,則它將指出具體的偏移位置。 // fd_out/off_out:與fd_in/off_in類似,不過(guò)用于輸出數(shù)據(jù)流。 // len:移動(dòng)數(shù)據(jù)的長(zhǎng)度 // flags:控制數(shù)據(jù)的移動(dòng)方式

    使用 splic函數(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í)(fd_out 不是管道文件描述符)。splice函數(shù)失敗時(shí)返回 -1 并設(shè)置 errno 。

    常見(jiàn)的 errno :

    errno含義
    EBADF參數(shù)所指文件描述符有錯(cuò)
    ENOMEM內(nèi)存不夠
    EINVAL目標(biāo)文件系統(tǒng)不支持splice,或者目標(biāo)文件以追加方式打開(kāi),或者兩個(gè)文件描述符都不是管道文件描述符,或者某個(gè) offset 參數(shù)被用于不支持隨機(jī)訪問(wèn)的設(shè)備(如字符設(shè)備)
    ESPIPE參數(shù) fd_in(或fd_out) 是管道文件描述符,而 off_in(或off_out) 不為NULL

    tee函數(shù)

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

    #include<fcntl.h> ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags); // 參數(shù)含義同splice,但 fd_in 和 fd_out 必須同時(shí)是管道文件描述符 // 成功時(shí)返回兩個(gè)文件之間復(fù)制的數(shù)據(jù)數(shù)量(字節(jié)數(shù)),返回0表示沒(méi)有復(fù)制任何數(shù)據(jù),tee失敗返回-1并設(shè)置errno。

    tee 常和 splice 一起用,splice 將非管道管道綁定,tee 將 splice 操作后得到的管道綁定在一起。


    進(jìn)程間通信——共享內(nèi)存

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

    • mmap函數(shù): 申請(qǐng)一段內(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:起始地址,為NULL時(shí)由系統(tǒng)自動(dòng)分配一個(gè)地址。 // length:指定內(nèi)存段的長(zhǎng)度。 // prot:設(shè)置內(nèi)粗段的訪問(wèn)權(quán)限,可取以下值的按位或: // PROT_READ,內(nèi)存段可讀。 // PROT_WRITE,內(nèi)存段可寫。 // PROT_EXEC,內(nèi)存段可執(zhí)行。 // PROT_NONE,內(nèi)存段不能被訪問(wèn)。 // flags:控制內(nèi)存段內(nèi)容被修改后程序的行為。 // fd:被映射文件對(duì)應(yīng)的文件描述符,一般通過(guò)open調(diào)用獲得。 // offset:參數(shù)設(shè)置從文件的何處開(kāi)始映射(對(duì)于不需要讀入整個(gè)文件的情況)。 // 成功時(shí)返回0,失敗返回-1并設(shè)置errno。

    mmap 的 flags 參數(shù)的常用值及其含義:

    常用值含義
    MAP_SHARED進(jìn)程間共享這段內(nèi)存,對(duì)該內(nèi)存段的修改將反映到被映射的文件中。提供了進(jìn)程間共享內(nèi)存的 POSIX 方法
    MAP_PRIVATE內(nèi)存段為調(diào)用進(jìn)程所私有,所有修改不會(huì)反映到被映射的文件中
    MAP_ANONYMOUS這段內(nèi)存不是從文件映射而來(lái)的,其內(nèi)容被初始化為全0。此時(shí) mmap 最后兩個(gè)參數(shù)將被忽略。
    MAP_FIXED內(nèi)存段必須位于 start 指定的地址處,start 必須是內(nèi)存頁(yè)面大小(4096字節(jié)=4KB)的整數(shù)倍
    MAP_HUGETLB按照“大內(nèi)存頁(yè)面”來(lái)分配內(nèi)存空間,“大內(nèi)存頁(yè)面”由 /proc/meminfo 文件來(lái)擦查看

    控制文件描述符

    fcntl函數(shù)

    fcntl函數(shù): 全名 file control ,提供了對(duì)文件描述符的各種控制操作(類似于系統(tǒng)調(diào)用 ioctl,但 ioctl 比 fcntl 提供的控制更多。)但是 fcntl 是 POSIX 規(guī)定的首選方法。

    #include<fcntl.h> int fcntl(int fd, int cmd, ...); // fd:被操作的文件描述符 // cmd:執(zhí)行何種操作 // 有可能需要第三個(gè)可選參數(shù) arg // 成功時(shí)返回值根據(jù)操作不同有所不同,失敗時(shí)返回-1并設(shè)置errno

    將文件描述符設(shè)置為非阻塞的:

    int setnonblocking( int fd ){// F_GETFL 獲取 fd 的標(biāo)志,成功時(shí)返回 fd 的標(biāo)志int old_option = fcntl(fd, F_GETFL); // 獲取文件描述符舊的狀態(tài)標(biāo)志int new_option = old_option | O_NONBLOCK; // 設(shè)置非阻塞標(biāo)志fcntl(fd, F_SETFL, new_option); // F_SETFL 設(shè)置 fd 的標(biāo)志return old_option; // 返回文件描述符舊的狀態(tài)標(biāo)志,以便日后恢復(fù)該狀態(tài)標(biāo)志 }

    題外話:SIGIO 和 SIGURG 這兩個(gè)信號(hào)與其他 Linux 信號(hào)不同,他們必須與某個(gè)文件描述符相關(guān)聯(lián)方可使用:

    • 被關(guān)聯(lián)文件描述符可讀可寫時(shí),系統(tǒng)將觸發(fā) SIGIO 信號(hào)。
    • 被關(guān)聯(lián)文件描述符是一個(gè) socket 且有帶外數(shù)據(jù)可讀時(shí),系統(tǒng)將觸發(fā) SIGURG 信號(hào)。

    這兩個(gè)信號(hào)就是通過(guò) fcntl函數(shù) 與文件描述符關(guān)聯(lián)的,具體做法是:fcntl函數(shù) 為目標(biāo)文件描述符指定宿主進(jìn)程進(jìn)程組,被指定的宿主進(jìn)程進(jìn)程組去捕獲這兩個(gè)信號(hào)。

    特別的,使用 SIGIO 時(shí),還需利用 fcntl 設(shè)置其 O_ASYNC標(biāo)志(異步I/O標(biāo)志,不過(guò)SIGIO信號(hào)模型并非真正意義上的異步I/O模型)。

    總結(jié)

    以上是生活随笔為你收集整理的Linux | 高级I/O函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。