其它IO類型的實現(xiàn)在這篇之后的三篇
1、IO
input,output。調(diào)用read或recv接口時,如果對方長時間不向我方接收緩沖區(qū)拷貝數(shù)據(jù),我們的進程就只能阻塞,這是讀取條件不滿足。阻塞的時間成本最后會體現(xiàn)在用戶上。因此可以說,IO = 等 + 數(shù)據(jù)拷貝。高效IO則是單位事件內(nèi),等的比重越低,IO效率越高。
可以看出IO是有條件的,滿足條件就叫IO時間就緒。
IO有五種模型
2、同、異步IO(5種IO類型)
調(diào)用讀取用的接口后,對于有自己的接收緩沖區(qū),有5種IO方式
阻塞IO:如果緩沖區(qū)內(nèi)沒有好數(shù)據(jù),系統(tǒng)調(diào)用就一直阻塞等待,不做別的事情,直到準備好數(shù)據(jù),才開始讀取并返回。所有的套接字默認都是阻塞IO。
非阻塞IO:單次檢驗緩沖區(qū)內(nèi)沒有數(shù)據(jù),recv函數(shù)就返回EWOULDBLOCK,每隔一段時間再調(diào)用recv接口來查看緩沖區(qū),如果有就讀取并返回數(shù)據(jù),沒有就還返回上面的。這也就是非阻塞輪詢。
阻塞與非阻塞的區(qū)別在于等的方式不同,不過都要自己去拷貝數(shù)據(jù)。非阻塞在沒得到結果前就不阻塞當前進程,直接返回,過一會再來查看;阻塞就是一直阻塞等待,直到讀取數(shù)據(jù)再返回。
上面兩個等的方式不同,但拷貝方式相同。
信號驅(qū)動IO:緩沖區(qū)內(nèi)有數(shù)據(jù)時,就發(fā)送SIGIO信號通知上層調(diào)用接口來讀取。信號是告知上層什么時候可以讀取的,接口是在信號處理了之后才調(diào)用的。
recv函數(shù)調(diào)用后會先檢測有沒有數(shù)據(jù),也就是條件檢測,沒有返回,有才讀取再返回。像這樣的接口只使用一個文件描述符。
多路復用/多路轉接IO:執(zhí)行一個進程,傳多個文件描述符,調(diào)用多個接口來等待數(shù)據(jù),進程只負責等待數(shù)據(jù),等到有了數(shù)據(jù),也就是IO條件就緒,就通知上層調(diào)用recv接口來讀取并返回。
多路復用IO是效率最高的,因為并不只是一個進程在等,而是很多個進程在等,那么等待成功的可能性就更高。單個進程等待的概率和多個進程等待的概率相比。
上面4個都是同步IO。
異步IO:不參與等待過程,只是發(fā)起一個進程,給一個緩沖區(qū)來接收數(shù)據(jù)。至于結果怎么樣,由操作系統(tǒng)來決定。多路復用是進程在等待,異步IO是系統(tǒng)在等待。
兩種IO方式的區(qū)別是有沒有參與等以及讀取的過程。實際上,對應到系統(tǒng),應當是一個進程通過特定的文件描述符,等待IO事件就緒,通過系統(tǒng)調(diào)用來讀取數(shù)據(jù)拷貝到上層。
recv函數(shù)就是在做條件檢測,條件通過再讀取。
3、其它高級IO
記錄鎖、系統(tǒng)V流機制。存儲映射IO(mmap),readv和writev函數(shù)等
4、非阻塞IO
int main()
{
char buffer[64];
while(true)
{
printf(">>> ");
fflush(stdout);
ssize_t n = read(0, buffer, sizeof(buffer) - 1);
if(n > 0)
{
buffer[n - 1] = 0;
std::cout << "echo# " << buffer << std::endl;
}
}
}
這就是一個等待的過程,如果我們不輸入,就一直卡在read那里。這是阻塞,如果要變成非阻塞,用fcntl接口。
#include <fcntl.h>
int fcntl(int fd, int cmd, …/* arg */);
cmd值有5種
復制一個現(xiàn)有的描述符(cmd=F_DUPFD)
獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD)
獲得/設置文件狀態(tài)標記(cmd=F_GETFL或F_SETFL)//GET獲得文件狀態(tài)標記位,SET設置這個文件新的狀態(tài)
獲得/設置異步I/O所有權(cmd=F_GETOWN或F_SETOWN)
獲得/設置記錄鎖(cmd=F_GETLK,F(xiàn)_SETLK或F_SETLKW)
void SetNonBlock(int fd) {
int fl = fcntl(fd, F_GETFL);//使用F_GETFL將當前的文件描述符的屬性取出來(這是一個位圖)
if (fl < 0) {
perror("fcntl");
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK);//然后再使用F_SETFL將文件描述符設置回去. 設置回去的同時, 加上一個O_NONBLOCK參數(shù)
}
非阻塞和阻塞都是文件的讀取方式,在文件結構體中就是一個變量設為1或0,這里的O_NONBLOCK是宏,只有一個比特位,通過fcntl將這個數(shù)設置進文件結構體。
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL);//使用F_GETFL將當前的文件描述符的屬性取出來(這是一個位圖)
if (fl < 0)
{
std::cerr << "error string: " << strerror(errno) << " error code: " << errno << std::endl;
return ;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK);//然后再使用F_SETFL將文件描述符設置回去. 設置回去的同時, 加上一個O_NONBLOCK參數(shù)
}
int main()
{
char buffer[64];
//SetNonBlock(0);//0表示非阻塞
while(true)
{
printf(">>> ");
fflush(stdout);
ssize_t n = read(0, buffer, sizeof(buffer) - 1);
if(n > 0)
{
buffer[n - 1] = 0;
std::cout << "echo# " << buffer << std::endl;
}
else if(n == 0)
{
std::cout << "end file" << std::endl;
break;
}
else
{
std::cout << "read error??" << std::endl;
break;
}
}
}
不設置成0,就是阻塞。read那里不是0而是4,當前沒有打開文件描述符為4的文件,就會read error。加上錯誤原因
else
{
std::cerr << "read error?" << "error string: " << strerror(errno) << " error code: " << errno << std::endl;
break;
}
現(xiàn)在再用上SetNonBlock,設置為0,那么即使不輸入,也會出錯,因為它不阻塞,直接去判斷,沒輸入也沒讀取完,所以就打印錯誤語句
那么這里就在每一次循環(huán)后sleep一會。只有在輸入時才會打印正常語句,不輸入就一直打印錯誤語句。非阻塞IO在底層沒有數(shù)據(jù)時,就以出錯形式返回,不過不算正式地出錯。
區(qū)分一下
else
{
if(errno == EAGAIN || errno == EWOULDBLOCK)//也就是錯誤碼為11的兩個,表示非阻塞式的出錯返回
{
//底層數(shù)據(jù)沒有準備好,下次繼續(xù)檢測
sleep(1);
std::cout << "data not ready" << std::endl;
continue;
}
else if(errno == EINTR)
{
//這次IO被信號中斷,需要重新讀取
continue;
}
std::cerr << "read error? " << "error string: " << strerror(errno) << " error code: " << errno << std::endl;
break;
}
非阻塞IO在沒有得到數(shù)據(jù)之前,可以做別的工作,模擬一下:
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#include <vector>
#include <functional>
void PrintLog()
{
std::cout << "這個是一個日志例程" << std::endl;
}
void OpenMysql()
{
std::cout << "這個是一個操作數(shù)據(jù)庫的例程" << std::endl;
}
void CheckNet()
{
std::cout << "這個是一個檢測網(wǎng)絡狀態(tài)的例程" << std::endl;
}
using func_t = std::function<void (void)>;
std::vector<func_t> funcs;
void LoadTask()
{
funcs.push_back(PrintLog);
funcs.push_back(OpenMysql);
funcs.push_back(CheckNet);
}
void HandlerAllTask()
{
for(const auto& func: funcs) func();
}
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL);//使用F_GETFL將當前的文件描述符的屬性取出來(這是一個位圖)
if (fl < 0)
{
std::cerr << "error string: " << strerror(errno) << " error code: " << errno << std::endl;
return ;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK);//然后再使用F_SETFL將文件描述符設置回去. 設置回去的同時, 加上一個O_NONBLOCK參數(shù)
}
int main()
{
char buffer[64];
SetNonBlock(0);//0表示非阻塞
LoadTask();
while(true)
{
printf(">>> ");
fflush(stdout);
ssize_t n = read(0, buffer, sizeof(buffer) - 1);
if(n > 0)
{
buffer[n - 1] = 0;
std::cout << "echo# " << buffer << std::endl;
}
else if(n == 0)
{
std::cout << "end file" << std::endl;
break;
}
else
{
if(errno == EAGAIN || errno == EWOULDBLOCK)//也就是錯誤碼為11的兩個,表示非阻塞式的出錯返回
{
//底層數(shù)據(jù)沒有準備好,下次繼續(xù)檢測
HandlerAllTask();
sleep(1);
std::cout << "data not ready" << std::endl;
continue;
}
else if(errno == EINTR)
{
//這次IO被信號中斷,需要重新讀取
continue;
}
std::cerr << "read error? " << "error string: " << strerror(errno) << " error code: " << errno << std::endl;
break;
}
sleep(3);
}
}
本篇gitee文章來源:http://www.zghlxwxcb.cn/news/detail-812646.html
結束。文章來源地址http://www.zghlxwxcb.cn/news/detail-812646.html
到了這里,關于Linux學習記錄——?? 高級IO(1)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!