回顧同時讀鍵盤、鼠標的方法
①多進程 參考:高級IO——非阻塞IO
②多線程 參考:高級IO——非阻塞IO
③將“讀鼠標”和“讀鍵盤”設置為非阻塞 參考:高級IO——非阻塞IO
④多路IO(select、poll機制) 參考:高級IO——多路IO
⑤異步IO
③④都是單線的
異步IO的原理
①~④都是主動的去讀,對於read函數來說它並不知道是不是一定有數據,如果有數據就讀到數據,沒有數據要么阻塞直到讀到數據為止,要么就不阻塞。實際上除了以上描述的方式外,還有另外一種聰明的方式,那就是使用異步IO的方式來實現。異步IO的原理就是,底層把數據准備好后,內核就會給進程發送一個“異步通知的信號”通知進程,表示數據准備好了,然后調用信號處理函數去讀數據,在沒有准備好時,進程忙自己的事情。比如使用異步IO讀鼠標,底層鼠標驅動把數據准備好后,會發一個“SIGIO”(異步通知信號)給進程,進程調用捕獲函數讀鼠標,讀鼠標的SIGIO捕獲函數需要我們自己定義。
舉個例子:到吃飯時間了,你可以去飯店排隊買飯,也可以提前給老板打電話,飯做好了叫我過去吃
使用異步IO方式讀鼠標和鍵盤
進程正常阻塞讀鍵盤,然后將讀鼠標設置為異步IO方式。進程正常阻塞讀鍵盤時,如果鼠標沒有數據的話,進程不關心讀鼠標的事情,如果鼠標數據來了,底層鼠標驅動就會向進程發送一個SIGIO信號,然后調用注冊的SIGIO信號捕獲函數讀鼠標數據。當然也可以反過來,進程正常阻塞讀鼠標,然后將讀鍵盤設置為異步IO方式。
異步IO這個名字怎么理解?
答:比如以異步IO方式讀鼠標數據為例,如果知道什么時間數據會來,等這個時間到時再去讀數據,這就是步調統一的同步讀。如果不知道什么時候會有數據來,這種就只能是什么時候數據來了就什么時候讀,這種就是異步的讀。之所叫異步,是因為我不知道你什么時候來,沒辦法統一步調(異步的),只能是隨時來是隨時讀。
不過使用異步IO有兩個前提:
(1)底層驅動必須要有相應的發送SIGIO信號的代碼,只有這樣當底層數據准備好后,底層才會發送SIGIO信號給進程。我們之所以可以對鼠標設置異步IO,是因為人家在實現鼠標驅動時,有寫發送SIGIO信號的代碼,如果驅動程序是我們自己寫的,發送SIGIO的代碼就需要我們自己來寫。
(2)應用層必須進行相應的異步IO的設置,否者無法使用異步IO應用層進行異步IO設置時,使用的也是fcntl函數。
使用異步IO時,應用層的設置步驟
①調用signal函數對SIGIO信號設置捕獲函數。在捕獲函數里面實現讀操作,比如讀鼠標。
②使用fcntl函數,將接收SIGIO信號的進程設置為當前進程。如果不設置的,底層驅動並不知道將SIGIO信號發送給哪一個進程。
fcntl(mousefd, F_SETOWN, getpid());
③使用fcntl函數,對文件描述符增設O_ASYNC的狀態標志,讓fd支持異步IO
mouse_fd = open("/dev/input/mouse1", O_RDONLY); flag = fcntl(mouse_fd, F_GETFL); flag |= O_ASYNC; //補設O_ASYNC fcntl(mouse_fd, F_SETFL, flag);
代碼演示
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <strings.h> 6 #include <errno.h> 7 #include <sys/time.h> 8 #include <sys/types.h> 9 #include <unistd.h> 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <poll.h> 14 #include <signal.h> 15 16 void print_err(char *str, int line, int err_no) 17 { 18 printf("%d, %s: %s\n", line, str, strerror(err_no)); 19 exit(-1); 20 } 21 22 int mousefd = 0; 23 void signal_fun(int signo) 24 { 25 int buf; 26 int ret = 0; 27 28 if(SIGIO == signo) 29 { 30 bzero(&buf, sizeof(buf)); 31 ret = read(mousefd, &buf, sizeof(buf)); 32 if(ret > 0) printf("%d\n", buf); 33 } 34 } 35 36 int main(void) 37 { 38 int ret = 0; 39 char buf[100] = {0}; 40 struct pollfd fds[2]; 41 42 mousefd = open("/dev/input/mouse0", O_RDONLY); 43 if(mousefd == -1) print_err("open mouse0 fail", __LINE__, errno); 44 45 //為SIGIO設置捕獲函數,在捕獲函數里面讀鼠標 46 signal(SIGIO, signal_fun); 47 48 //告訴鼠標驅動,他發送的SIGIO信號由當前進程接收 49 fcntl(mousefd, F_SETOWN, getpid()); 50 51 //對mousefd進行設置,讓其支持異步IO 52 int flg = fcntl(mousefd, F_GETFL); 53 flg |= O_ASYNC; 54 fcntl(mousefd, F_SETFL, flg); 55 56 while(1) 57 { 58 bzero(buf, sizeof(buf)); 59 ret = read(0, buf, sizeof(buf)); 60 if(ret > 0) printf("%s\n", buf); 61 } 62 63 return 0; 64 }
