1.為什麼有阻塞IO
(1)常見的阻塞:wait、pause、sleep等函數,read和write某些特殊檔案時
(2)阻塞式的好處,在阻塞等待的程序不消耗CPU運作,提高性能。
2.如何實作非阻塞IO通路
(1)O_NONBLOCK和fcntl
3.阻塞式IO的困境
(1)程式中讀取鍵盤
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
char buf[100];
memset(buf,0,sizeof(buf));
printf("before read\n");
//讀取鍵盤鍵盤就是标準輸入,stdin
read(0,buf,2);//read預設是阻塞的
printf("讀出的内容是:[%s]\n",buf);
return 0;
}
(2)程式中讀取滑鼠
這裡的滑鼠輸入在/dev/input/mouse1有的是在mouse0下面通過
cat /dev/input/mouse0或者mouse1然後移動滑鼠出現亂碼則就是滑鼠輸入裝置。
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = -1;
char buf[200];
fd = open("/dev/input/mouse0",O_RDONLY);
if(fd < 0)
{
perror("open");
return -1;
}
memset(buf,0,sizeof(buf));
printf("before read\n");
//讀取滑鼠
read(fd,buf,5);//read預設是阻塞的
printf("讀出的内容是:[%s]\n",buf);
return 0;
}
(3)程式中同時讀取鍵盤滑鼠
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = -1;
char buf[200];
fd = open("/dev/input/mouse0",O_RDONLY);//打開滑鼠
if(fd < 0)
{
perror("open");
return -1;
}
memset(buf,0,sizeof(buf));
printf("before 滑鼠 read\n");
//讀取滑鼠
read(fd,buf,5);//read預設是阻塞的
printf("滑鼠讀出的内容是:[%s]\n",buf);
memset(buf,0,sizeof(buf));
printf("before 鍵盤 read\n");
//讀取鍵盤鍵盤就是标準輸入,stdin
read(0,buf,2);//read預設是阻塞的
printf("鍵盤讀出的内容是:[%s]\n",buf);
return 0;
}
(4)問題分析
像這種情況就隻能先讀滑鼠再讀鍵盤這就是阻塞式的缺陷
4.并發式IO
(1)非阻塞式IO
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int fd = -1;
int ret = -1;
int flag = -1;
char buf[200];
fd = open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK);//打開滑鼠
if(fd < 0)
{
perror("open");
return -1;
}
//把0号檔案描述符(stdin)變成非阻塞式
//int fcntl(int fd, int cmd, ... * arg * );
flag = fcntl(0,F_GETFL); //先擷取原來的flag
flag |= O_NONBLOCK; //添加非阻塞屬性
fcntl(0,F_SETFL,flag); //更新flag
//上面三步将0這個檔案描述符變為非阻塞式的
while(1)
{
memset(buf,0,sizeof(buf));
//printf("before 滑鼠 read\n");
//讀取滑鼠
ret = read(fd,buf,5);//read預設是阻塞的
if(ret > 0)
{
printf("滑鼠讀出的内容是:[%s]\n",buf);
}
memset(buf,0,sizeof(buf));
//printf("before 鍵盤 read\n");
//讀取鍵盤鍵盤就是标準輸入,stdin
ret = read(0,buf,5);//read預設是阻塞的
if(ret > 0)
{
printf("鍵盤讀出的内容是:[%s]\n",buf);
}
}
return 0;
}
5.多路複用IO
(1)何為IO多路複用(IO multiplexing)
(2)用在什麼地方:多路非阻塞式IO
(3)select和poll(本身是阻塞式函數)
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
參數1:檔案描述符的個數(檔案IO中最大檔案描述符+1)
參數2:要讀的檔案描述符的集合(相當于結構體,放多個需要讀的檔案描述符)
參數3:要寫的多個檔案描述符
參數4:看檔案描述符是否出錯
參數5:設定逾時時間(如果這段事間一直沒有IO事件發生就傳回0)
傳回值:傳回發生了幾次IO事件
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
參數1:多個檔案描述符的集合
參數2:放入這個集合的檔案描述符個數+1
參數3:逾時時間
傳回值:傳回發生了幾次IO事件
select函數實作
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main(void)
{
int fd = -1;
int ret = -1;
fd_set myset;
char buf[200];
struct timeval tm;
fd = open("/dev/input/mouse0",O_RDONLY);//打開滑鼠
if(fd < 0)
{
perror("open");
return -1;
}
//目前有兩個fd,1個是fd,1個是0
//處理myset
FD_ZERO(&myset);//先清零
FD_SET(fd,&myset);//設定fd
FD_SET(0,&myset);//設定0這個fd
tm.tv_sec = 10;//設定秒
tm.tv_usec = 0;//設定微妙(兩個秒數是相加的)
//int select(int nfds, fd_set *readfds, fd_set *writefds,
//fd_set *exceptfds, struct timeval *timeout);
ret = select(fd+1,&myset,NULL,NULL,&tm);
if(ret < 0)
{
perror("select");
return -1;
}
else if(ret == 0)
{
printf("逾時了\n");
}
else
{
//等到了一路IO,然後去監測是哪個IO到了
if(FD_ISSET(0,&myset))
{
//這裡處理鍵盤
memset(buf,0,sizeof(buf));
//讀取鍵盤鍵盤就是标準輸入,stdin
read(0,buf,5);//read預設是阻塞的
printf("鍵盤讀出的内容是:[%s]\n",buf);
}
else if(FD_ISSET(fd,&myset))
{
//這裡處理滑鼠
memset(buf,0,sizeof(buf));
read(fd,buf,5);//read預設是阻塞的
printf("滑鼠讀出的内容是:[%s]\n",buf);
}
}
return 0;
}
poll函數實作:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <poll.h>
int main(void)
{
int fd = -1;
int ret = -1;
struct pollfd myfds[2] = {0};
char buf[200];
struct timespec tm;
fd = open("/dev/input/mouse0",O_RDONLY);//打開滑鼠
if(fd < 0)
{
perror("open");
return -1;
}
//初始化我們的pollfd
myfds[0].fd = 0; //鍵盤
myfds[0].events = POLLIN;//等待讀操作
myfds[1].fd = fd; //滑鼠
myfds[1].events = POLLIN;//等待讀操作
tm.tv_sec = 10000;
//int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//ret = poll(myfds,2,tm.tv_sec);//2個檔案描述符,10000毫秒
ret = poll(myfds,fd,10000);//2個檔案描述符,10000毫秒,這裡寫fd檔案運作>直接退出
if(ret < 0)
{
perror("poll:");
return -1;
}
else if(ret == 0)
{
printf("逾時了\n");
}
else
{
//等到了一路IO,然後去監測是哪個IO到了
if(myfds[0].events == myfds[0].revents)
{
//這裡處理鍵盤
memset(buf,0,sizeof(buf));
//讀取鍵盤鍵盤就是标準輸入,stdin
read(0,buf,5);//read預設是阻塞的
printf("鍵盤讀出的内容是:[%s]\n\r",buf);
}
if(myfds[1].events == myfds[1].revents)
{
//這裡處理滑鼠
memset(buf,0,sizeof(buf));
read(fd,buf,50);//read預設是阻塞的
printf("滑鼠讀出的内容是:[%s]\n",buf);
}
}
return 0;
}
(4)外部阻塞式,内部非阻塞式自動輪詢多路阻塞式IO
6.異步通知(異步IO)
(1)異步IO就是作業系統用軟體實作的一套中斷系統。
(2)異步IO的工作方法:我們目前程序注冊一個異步IO事件(使用signal注冊一個信号SIGIO的處理函數),然後目前程序可以正常處理自己的事情,當異步事件發生後目前程序會收到一個SIGIO信号進而執行綁定的處理函數去處理這個異步事件。
(3)涉及函數(fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN))、signal或sigaction(SIGIO)
(4)代碼實作
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mousefd = -1;
//異步通知函數,綁定SIGIO信号,在函數内處理異步通知事件
void func(int sig)
{
char buf[200] = {0};
if(sig != SIGIO)
return;
//讀取滑鼠
read(mousefd,buf,5);//read預設是阻塞的
printf("滑鼠讀出的内容是:[%s]\n",buf);
}
int main(void)
{
char buf[200];
int flag = -1;
mousefd = open("/dev/input/mouse0",O_RDONLY);//打開滑鼠
if(mousefd < 0)
{
perror("open");
return -1;
}
//注冊異步通知(把滑鼠的檔案描述符設定為可以接受異步IO)
flag = fcntl(mousefd,F_GETFL);
flag |= O_ASYNC;
fcntl(mousefd,F_SETFL,flag);
//把異步IO事件的接收程序設定為目前程序
fcntl(mousefd,F_SETOWN,getpid());
//注冊目前程序的SIGIO信号捕捉函數
signal(SIGIO,func);
while(1)
{
memset(buf,0,sizeof(buf));
//讀取鍵盤鍵盤就是标準輸入,stdin
read(0,buf,5);//read預設是阻塞的
printf("鍵盤讀出的内容是:[%s]\n",buf);
}
return 0;
}
7.存儲映射IO
(1)mmap函數
(2)LCD顯示和IPC之共享記憶體
(3)存儲映射的特點:共享而不是複制,減少記憶體操作。處理大檔案時效率高,小檔案不劃算
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);