高级IO(文件的读写)——并发式IO的解决方案(解决多路阻塞式IO的方案)
以下內(nèi)容源于朱有鵬《物聯(lián)網(wǎng)大講堂》課程的學(xué)習(xí)整理,如有侵權(quán),請(qǐng)告知?jiǎng)h除。
 
一、并發(fā)式IO的解決方案
- 所謂并發(fā)式IO,即上節(jié)中提及的鼠標(biāo)和鍵盤都已經(jīng)啟動(dòng)。
1、非阻塞式IO
- 使用fcntl函數(shù), 將上節(jié)中阻塞式的鼠標(biāo)和鍵盤讀取改為非阻塞式的。
- 性能不是很好。
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(void) {// 讀取鼠標(biāo)int fd = -1;int flag = -1;char buf[200];int ret = -1;fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);if (fd < 0){perror("open:");return -1;}// 把0號(hào)文件描述符(stdin)變成非阻塞式的flag = fcntl(0, F_GETFL); // 先獲取原來的flagflag |= O_NONBLOCK; // 添加非阻塞屬性fcntl(0, F_SETFL, flag); // 更新flag// 這3步之后,0就變成了非阻塞式的了while (1){// 讀鼠標(biāo)memset(buf, 0, sizeof(buf));ret = read(fd, buf, 50);if (ret > 0){printf("鼠標(biāo)讀出的內(nèi)容是:[%s].\n", buf);}// 讀鍵盤memset(buf, 0, sizeof(buf));ret = read(0, buf, 5);if (ret > 0){printf("鍵盤讀出的內(nèi)容是:[%s].\n", buf);}}return 0; }/* int main(void) {// 讀取鼠標(biāo)int fd = -1;char buf[200];fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);if (fd < 0){perror("open:");return -1;}memset(buf, 0, sizeof(buf));printf("before read.\n");read(fd, buf, 50);printf("讀出的內(nèi)容是:[%s].\n", buf);return 0; } *//* int main(void) {// 讀取鍵盤// 鍵盤就是標(biāo)準(zhǔn)輸入,stdinchar buf[100];int flag = -1;// 把0號(hào)文件描述符(stdin)變成非阻塞式的flag = fcntl(0, F_GETFL); // 先獲取原來的flagflag |= O_NONBLOCK; // 添加非阻塞屬性fcntl(0, F_SETFL, flag); // 更新flag// 這3步之后,0就變成了非阻塞式的了memset(buf, 0, sizeof(buf));printf("before read.\n");read(0, buf, 5);printf("讀出的內(nèi)容是:[%s].\n", buf);return 0; } */
2、多路復(fù)用IO
3、異步通知(異步IO)
 
二、IO多路復(fù)用原理
1、何為IO多路復(fù)用?
(1)英文為:IO multiplexing
(2)用在什么地方?
- 用于解決并發(fā)式IO,多路阻塞式的。
(3)涉及select函數(shù)、poll函數(shù)。
- 兩個(gè)函數(shù)設(shè)計(jì)思想一樣,外部特征不一樣。
(4)實(shí)現(xiàn)原理:外部阻塞式(select函數(shù)本身是阻塞式的),內(nèi)部非阻塞式自動(dòng)輪詢(select自動(dòng)輪詢時(shí)A,B)多路阻塞式IO(鍵盤A,鼠標(biāo)B,這兩者本身是阻塞式的)。
- 只要AB至少有一個(gè)輸入,則select由阻塞返回。
2、select函數(shù)介紹
 
3、poll函數(shù)介紹
 
4、多路復(fù)用實(shí)踐
(1)用poll函數(shù)實(shí)現(xiàn)同時(shí)讀取鍵盤鼠標(biāo)
 
 
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <poll.h>int main(void) {// 讀取鼠標(biāo)int fd = -1, ret = -1;char buf[200];struct pollfd myfds[2] = {0};fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}// 初始化我們的pollfdmyfds[0].fd = 0; // 鍵盤myfds[0].events = POLLIN; // 等待讀操作myfds[1].fd = fd; // 鼠標(biāo)myfds[1].events = POLLIN; // 等待讀操作ret = poll(myfds, fd+1, 10000);if (ret < 0){perror("poll: ");return -1;}else if (ret == 0){printf("超時(shí)了\n");}else{// 等到了一路IO,然后去監(jiān)測(cè)到底是哪個(gè)IO到了,處理之if (myfds[0].events == myfds[0].revents){// 這里處理鍵盤memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("鍵盤讀出的內(nèi)容是:[%s].\n", buf);}if (myfds[1].events == myfds[1].revents){// 這里處理鼠標(biāo)memset(buf, 0, sizeof(buf));read(fd, buf, 50);printf("鼠標(biāo)讀出的內(nèi)容是:[%s].\n", buf);}}return 0; }
(2)用select函數(shù)實(shí)現(xiàn)同時(shí)讀取鍵盤鼠標(biāo)
- 設(shè)置超時(shí)時(shí)間,即阻塞的時(shí)間不能太長,如果很久都沒有IO來激活select,則表明超時(shí)了。
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h>int main(void) {// 讀取鼠標(biāo)int fd = -1, ret = -1;char buf[200];fd_set myset;struct timeval tm;//設(shè)置超時(shí)時(shí)間,即阻塞的時(shí)間不能太長,如果很久都沒有IO來激活select,則表明超時(shí)了。fd = open("/dev/input/mouse1", O_RDONLY);if (fd < 0){perror("open:");return -1;}// 當(dāng)前有2個(gè)fd,一共是fd一個(gè)是0// 處理mysetFD_ZERO(&myset);FD_SET(fd, &myset);FD_SET(0, &myset);tm.tv_sec = 10;tm.tv_usec = 0;ret = select(fd+1, &myset, NULL, NULL, &tm);//+1,是因?yàn)?~fd,則共有fd+1個(gè)文件描述符if (ret < 0)//錯(cuò)誤{perror("select: ");return -1;}else if (ret == 0)//表明超時(shí){printf("超時(shí)了\n");}else//>0表示有一路IO激活了{(lán)// 等到了一路IO,然后去監(jiān)測(cè)到底是哪個(gè)IO到了,處理之if (FD_ISSET(0, &myset)){// 這里處理鍵盤memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("鍵盤讀出的內(nèi)容是:[%s].\n", buf);}if (FD_ISSET(fd, &myset)){// 這里處理鼠標(biāo)memset(buf, 0, sizeof(buf));read(fd, buf, 50);printf("鼠標(biāo)讀出的內(nèi)容是:[%s].\n", buf);}}return 0; }
三、異步IO
1、何為異步IO?
(1)幾乎可以認(rèn)為,異步IO就是操作系統(tǒng)用軟件實(shí)現(xiàn)的一套中斷響應(yīng)系統(tǒng)。類比硬件中斷。
(2)異步IO的工作方法
- 當(dāng)前進(jìn)程注冊(cè)一個(gè)異步IO事件(使用signal注冊(cè)一個(gè)信號(hào)SIGIO的處理函數(shù)),然后當(dāng)前進(jìn)程可以正常處理自己的事情;
- 當(dāng)異步事件發(fā)生后,當(dāng)前進(jìn)程會(huì)收到一個(gè)SIGIO信號(hào),從而執(zhí)行綁定的處理函數(shù),來處理這個(gè)異步事件。
2、涉及的函數(shù)
(1)fcntl函數(shù),主要設(shè)置異步通知。
- 涉及的命令有F_GETFL(獲取flag)、F_SETFL、O_ASYNC(表明可以接收異步通知)、F_SETOWN(設(shè)置通知誰(一般都是通知當(dāng)前進(jìn)程));
 
(2)signal或sigaction函數(shù)(SIGIO)
3、代碼實(shí)踐
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h>int mousefd = -1;// 綁定到SIGIO信號(hào),在函數(shù)內(nèi)處理異步通知事件 void func(int sig) {char buf[200] = {0};if (sig != SIGIO)return;read(mousefd, buf, 50);printf("鼠標(biāo)讀出的內(nèi)容是:[%s].\n", buf); }int main(void) {// 讀取鼠標(biāo)char buf[200];int flag = -1;mousefd = open("/dev/input/mouse1", O_RDONLY);if (mousefd < 0){perror("open:");return -1;} // 把鼠標(biāo)的文件描述符設(shè)置為可以接受異步IOflag = fcntl(mousefd, F_GETFL);flag |= O_ASYNC;fcntl(mousefd, F_SETFL, flag);// 把異步IO事件的接收進(jìn)程設(shè)置為當(dāng)前進(jìn)程fcntl(mousefd, F_SETOWN, getpid());// 注冊(cè)當(dāng)前進(jìn)程的SIGIO信號(hào)捕獲函數(shù)signal(SIGIO, func);// 讀鍵盤,在這里是當(dāng)前進(jìn)程while (1){memset(buf, 0, sizeof(buf));read(0, buf, 5);printf("鍵盤讀出的內(nèi)容是:[%s].\n", buf);}return 0; }
四、存儲(chǔ)映射IO
 
1、反映在mmap函數(shù)
- 把一個(gè)文件和一段內(nèi)存映射起來。比如LCD設(shè)備文件和顯存的對(duì)應(yīng)。
2、例子
- LCD顯示,IPC之共享內(nèi)存
3、存儲(chǔ)映射IO的特點(diǎn)
(1)共享而不是復(fù)制,減少內(nèi)存操作。
(2)處理大文件時(shí)效率高(一般用于視頻處理),小文件不劃算。
 
總結(jié)
以上是生活随笔為你收集整理的高级IO(文件的读写)——并发式IO的解决方案(解决多路阻塞式IO的方案)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: android 1024 github,
- 下一篇: linux qt ping,Qt5.2中
