(1)IO multiplexing
(2)用在什么地方?多路非阻塞式IO。
(3)select和poll
(4)外部阻塞式,內部非阻塞式自動輪詢多路阻塞式IO
IO多路復用原理:
其實就是整個函數對外表現為阻塞式的,也就是我們調用這個函數,如果條件達不到一定
會被阻塞;但是其實內部並不是阻塞的,而是以一種非阻塞的方式工作的,內部能夠實現
自動輪詢,如果有任何一個IO設備達到條件即可返回到應用層。
用select函數實現IO多路復用:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
原理:通過阻塞監聽設備文件是否有數據可操作,如果有數據可讀則返回,當然也可以設
置超時時間,然后我們通過read函數去讀取/寫,所以其實這個就不涉及到read函數是否
是阻塞或者是非阻塞的了。因為select函數返回之后才能讀取,所以有數據我們就去讀取
,沒有我們就不去讀。
我們可以設置多路的IO操作,當調用select函數的時候進行阻塞,內部輪詢監聽是否可以
進行相應的操作,如果那個可以進行操作那么就操作哪個,然后返回到應用層去,結束
select函數的阻塞。
參數詳解:
第一個參數:其實就是表示多路IO時,文件描述符最大的值+1,因為我們的fd是從0開始
的。
第二個參數:是一個指向fd_set結構體的指針,結構體需要我們通過以下的函數來填充:
void FD_CLR(int fd, fd_set *set); 用來清除描述詞組set中相關fd 的位
int FD_ISSET(int fd, fd_set *set); 用來測試描述詞組set中相關fd 的位是否為真
void FD_SET(int fd, fd_set *set); 用來設置描述詞組set中相關fd的位
void FD_ZERO(fd_set *set); 用來清除描述詞組set的全部位
也就是表示在我們打開的所有設備文件中要進行讀操作的設備是那些,把他的fd填充進去
,如果所有的都不進行讀操作,則寫NULL。
第三個參數: 一般是NULL
第四個參數:一般是NULL
第五個參數:表示超時時間(阻塞時間),是一個結構體:如下:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
有一個秒和一個微秒,是共同配合使用的,xx秒xx微秒
返回值:失敗: -1 超時返回 0 成功:> 0
/*************************************************************************/
代碼如下:
#include <stdio.h>
//According to POSIX.1-2001
#include <sys/select.h>
//According to earlier standards
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FILE "/dev/input/mouse0"
int main(void)
{
int fd = -1;
int sele_ret = -1;
fd_set Fd_set;
struct timeval time = {0};
char buf[10] = {0};
//打開設備文件
fd = open(FILE, O_RDONLY);
if (-1 == fd)
{
perror("open error");
exit(-1);
}
//構建多路復用IO
FD_ZERO(&Fd_set); //清除全部fd
FD_SET(0, &Fd_set); //添加標准輸入
FD_SET(fd, &Fd_set); //添加鼠標
time.tv_sec = 10; //設置阻塞超時時間為10秒鍾
time.tv_usec = 0;
sele_ret = select(fd+1, &Fd_set, NULL, NULL, &time);
if (0 > sele_ret)
{
perror("select error");
exit(-1);
}
else if (0 == sele_ret)
{
printf("無數據輸入,等待超時.\n");
}
else
{
if (FD_ISSET(0, &Fd_set)) //監聽得到得到的結果若是鍵盤,則讓去讀取鍵盤的數據
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf)/2);
printf("讀取鍵盤的內容是: %s.\n", buf);
}
if (FD_ISSET(fd, &Fd_set)) //監聽得到得到的結果若是鼠標,則去讀取鼠標的數據
{
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf)/2);
printf("讀取鼠標的內容是: %s.\n", buf);
}
}
//關閉鼠標設備文件
close(fd);
return 0;
}
/********************************************************************************/
poll函數實現IO多路復用:
原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
參數詳解:
第一個參數 :struct pollfd {
int fd; //文件描述符
short events; //請求的事件
short revents; //返回的事件
};
其實就是我們多路復用IO的所有文件的fd,注意這里跟select不一樣,他是在這個結構體
內進行區分的, events就是表示我們是進行那種操作,是用宏定義的, revents是內核
構建的,返回一個事件,所以我們就是通過 events == revents來確定是不是該fd發生了
可操作。
宏定義如下:
POLLIN 普通或優先級帶數據可讀
POLLRDNORM 普通數據可讀
POLLRDBAND 優先級帶數據可讀
POLLPRI 高優先級數據可讀
POLLOUT 普通數據可寫
POLLWRNORM 普通數據可寫
POLLWRBAND 優先級帶數據可寫
POLLERR 發生錯誤
POLLHUP 發生掛起
POLLNVAL 描述字不是一個打開的文件
第二個參數:就是select的第一個參數
第三個參數:阻塞超時時間,以毫秒為單位
返回值:跟select是一樣的,失敗-1 超時0 >0成功
代碼如下:
/*****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define FILE "/dev/input/mouse0"
int main(void)
{
int fd = -1;
int poll_ret = 0;
struct pollfd poll_fd[2] = {0};
char buf[100] = {0};
//打開設備文件
fd = open(FILE, O_RDONLY);
if (-1 == fd)
{
perror("open error");
exit(-1);
}
//構建多路復用IO
poll_fd[0].fd = 0; //鍵盤
poll_fd[0].events = POLLIN; //定義請求的事件為讀數據
poll_fd[1].fd = fd; //鼠標
poll_fd[1].events = POLLIN; //定義請求的事件為讀數據
int time = 10000; //定義超時時間為10秒鍾
poll_ret = poll(poll_fd, fd+1, time);
if (0 > poll_ret)
{
perror("poll error");
exit(-1);
}
else if (0 == poll_ret)
{
printf("阻塞超時.\n");
}
else
{
if (poll_fd[0].revents == poll_fd[0].events) //監聽得到得到的結果若是鍵盤,則讓去讀取鍵盤的數據
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf)/2);
printf("讀取鍵盤的內容是: %s.\n", buf);
}
if (poll_fd[1].revents == poll_fd[1].events) //監聽得到得到的結果若是鼠標,則去讀取鼠標的數據
{
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf)/2);
printf("讀取鼠標的內容是: %s.\n", buf);
}
}
//關閉文件
close(fd);
return 0;
}