前言:爲什麼要使用IO多路轉接模型?
試想一下,如果A想接收B的消息,那麼A就需要創建一個進程(或線程),用while(1)循環一直監視B是否給A發來消息,同時B也一直需要監視A,這會讓系統的開銷變得很大。
IO多路轉接模型的函數select, 可以幫助我們同時監視多個文件句柄, 這樣極大地降低了 內存 與 CPU 的開銷。
IO多路轉接模型
int select(int maxFd, fdset* rdset, fdset* wrset, fdset* exceptionset, struct timeval*)
select函數的本質:將加入的文件句柄進行監視, 如果讀集合有可讀的情況 或者 寫集合有可寫的情況,那麼將向下執行
出錯時返回 -1
超時返回 0
正常情況下返回一個 大於0 的數
參數說明:
maxfd
: 文件句柄的最大值+1,如0, 1, 2分別代表輸入,輸出,錯誤輸出,那麼maxfd應該是現有的fd的最大值+1, select的本質是循環
rdset
: 讀文件句柄的集合,當檢測的文件句柄中有可讀的情況時,開始往下執行
wrset
: 寫文件句柄的集合,當檢測的文件句柄中有可寫的情況時,開始往下執行,一般來說文件句柄均爲可寫的情況
exceptionset
: 異常文件句柄的集合
struct timeval
:等待時間 NULL表示select函數阻塞並無限等待,0表示select函數非阻塞不等待,傳入timeval則表示阻塞等待的時間
文件句柄的函數:
FD_ZERO(fd_set* sets)
清空sets集合中原來的文件句柄FD_SET(int fd, fd_set* sets)
添加文件句柄到集合中FD_ISSET(int fd, fd_set* sets)
判斷文件句柄在sets集合中是否有設定
實例 :兩個文件句柄,同時對pipe文件進行讀取和寫入
char *GenerateStr(char *str, int strlen)
{
int i, flag;
srand(time(NULL)); //通過時間函數設置隨機數種子,使得每次運行結果隨機。
for (i = 0; i < strlen; i++)
{
flag = rand() % 3;
switch (flag)
{
case 0:
str[i] = rand() % 26 + 'a';
break;
case 1:
str[i] = rand() % 26 + 'A';
break;
case 2:
str[i] = rand() % 10 + '0';
break;
}
}
str[strlen] = 0;
return str;
}
int test1()
{
int fdr = open("pipe", O_RDWR);
ERROR_CHECK(fdr, -1, "pipe");
int fdw = open("pipe", O_RDWR);
ERROR_CHECK(fdw, -1, "pipe");
fd_set rdset, wrset;
struct timeval timeout;
while (1)
{
FD_ZERO(&rdset);
FD_ZERO(&wrset);
FD_SET(fdr, &rdset);
FD_SET(fdw, &wrset); //爲了防止文件句柄與初始狀態相比發生變化,所以每一次都要重新清空再設置
timeout.tv_sec = 3;
timeout.tv_usec = 0;
int ret = select(5, &rdset, &wrset, NULL, &timeout);
if (ret > 0)
{
if (FD_ISSET(fdw, &wrset))
{
cout << "fdw write:" << endl;
char buf[20] = {0};
GenerateStr(buf, 10);
write(fdw, buf, strlen(buf));
cout << endl;
}
if (FD_ISSET(fdr, &rdset))
{
cout << "fd1 read:" << endl;
char buf[20] = {0};
read(fdr, buf, sizeof(buf));
sleep(1);
cout << buf << endl;
}
}
else
cout << "timeout" << endl;//因爲設置了寫集合,所以實際上不會出現超時的情況,因爲文件句柄隨時都是可寫的狀態
}
close(fdw);
close(fdr);
return 0;
}