在Linux應用編程中的並發式IO的三種解決方案是:
(1) 多路非阻塞式IO
(2) 多路復用
(3) 異步IO
以下代碼將以操作鼠標和鍵盤為實例來演示。
1. 多路非阻塞式IO
多路非阻塞式IO訪問,主要是添加O_NONBLOCK標志和fcntl()函數。
代碼示例:
1 /* 2 * 並發式IO的解決方案1:多路非阻塞式IO處理鍵盤和鼠標同時讀取 3 */ 4 5 #include <stdio.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <fcntl.h> 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 12 #define MOUSEPATH "/dev/input/mouse1" 13 14 int main(void) 15 { 16 int fd = -1; 17 int ret = -1; 18 int flag = -1; 19 char buf[200] = {0}; 20 21 fd = open(MOUSEPATH, O_RDONLY | O_NONBLOCK); 22 if (fd < 0) 23 { 24 perror("open"); 25 _exit(-1); 26 } 27 28 // 把0的文件描述符變成非阻塞式的 29 flag = fcntl(0, F_GETFD); // 獲取stdin原來的flag 30 flag |= O_NONBLOCK; // 給stdin原來的flag添加非阻塞式屬性 31 fcntl(0, F_SETFL, flag); // 更新flag 32 33 while (1) 34 { 35 // 讀鼠標 36 memset(buf, 0, sizeof(buf)); 37 ret = read(fd, buf, 50); 38 39 if (ret > 0) 40 { 41 printf("鼠標讀出的內容是:[%s]\n", buf); 42 } 43 44 // 讀鍵盤 45 memset(buf, 0, sizeof(buf)); 46 ret = read(0, buf, 50); 47 if (ret > 0) 48 { 49 printf("鍵盤讀出的內容是:[%s]\n", buf); 50 } 51 } 52 53 return 0; 54 }
2. IO多路復用
(1) 多路非阻塞式IO
(2) select() 和 poll() 函數
(3) 外部式阻塞,內部非阻塞式自動輪詢多路阻塞式IO
代碼示例:
select() 函數實現:
1 /* 2 * 並發式IO的解決方案2:多路復用select()函數處理 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <unistd.h> 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 #include <fcntl.h> 11 #include <sys/select.h> 12 #include <sys/time.h> 13 #include <stdlib.h> 14 15 #define MOUSEPATH "/dev/input/mouse1" 16 17 int main(void) 18 { 19 int fd = -1, ret = -1; 20 char buf[300] = {0}; 21 fd_set myset; 22 struct timeval tmv; 23 24 fd = open(MOUSEPATH, O_RDONLY); 25 if (-1 == fd) 26 { 27 perror("open"); 28 _exit(-1); 29 } 30 31 // 處理myset 32 FD_ZERO(&myset); // 清零 33 FD_SET(fd, &myset); // 加載鼠標的文件描述符到myset集合中 34 FD_SET(0, &myset); 35 36 // struct timeval *timeout 超時處理 37 tmv.tv_sec = 10; 38 tmv.tv_usec = 0; 39 40 // 原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 41 ret = select(fd+1, &myset, NULL, NULL, &tmv); // fd+1 這裡是最大的fd加1 nfds是從0開始的 42 if (ret < 0) 43 { 44 perror("select"); 45 _exit(-1); 46 } 47 else if (ret == 0) 48 { 49 printf("Timeout.\n"); 50 exit(0); 51 } 52 else 53 { 54 /* 等到了一路IO,然後去監測哪個IO到了就處理哪個IO */ 55 if ( FD_ISSET(fd, &myset) ) 56 { 57 // 這裡處理鼠標 58 memset(buf, 0, sizeof(buf)); 59 read(fd, buf, 50); 60 printf("鼠標讀出的內容是:[%s]\n", buf); 61 } 62 63 if ( FD_ISSET(0, &myset) ) 64 { 65 // 這裡處理鍵盤 66 memset(buf, 0, sizeof(buf)); 67 read(0, buf, 50); 68 printf("鍵盤讀出的內容是:[%s]\n", buf); 69 } 70 } 71 72 return 0; 73 }
poll() 函數實現:
1 /* 2 * 並發式IO的解決方案2:多路復用poll()函數處理 3 */ 4 5 #include <stdio.h> 6 #include <string.h> 7 #include <unistd.h> 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 #include <fcntl.h> 11 #include <poll.h> 12 #include <stdlib.h> 13 14 #define MOUSEPATH "/dev/input/mouse1" 15 #define IO_MULTIPLEXING 2 16 #define MAXBUF 1024 17 #define MILLISECOND 1000 18 19 int main(void) 20 { 21 int fd = -1, ret = -1, i = 0; 22 char buf[MAXBUF] = {0}; 23 struct pollfd pfd[IO_MULTIPLEXING] = {0}; 24 25 fd = open(MOUSEPATH, O_RDONLY); 26 if (-1 == fd) 27 { 28 perror("open"); 29 _exit(-1); 30 } 31 32 // 初始化 pollfd 33 pfd[0].fd = 0; // 鍵盤 34 pfd[0].events = POLLIN; // 等待讀操作 35 36 pfd[1].fd = fd; // 鍵盤 37 pfd[1].events = POLLIN; // 等待讀操作 38 39 // 原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout); 40 ret = poll(pfd, fd+1, 10 * MILLISECOND); // fd+1 這裡是最大的fd加1 nfds是從0開始的 41 if (ret < 0) 42 { 43 perror("poll"); 44 _exit(-1); 45 } 46 else if (ret == 0) 47 { 48 printf("Timeout.\n"); 49 exit(0); 50 } 51 else 52 { 53 /* 等到了一路IO,然後去監測哪個IO到了就處理哪個IO */ 54 for (i = 0; i < IO_MULTIPLEXING; i++) 55 { 56 // 處理鍵盤和鼠標 57 if ( pfd[i].events == pfd[i].revents ) 58 { 59 memset(buf, 0, sizeof(buf)); 60 read(pfd[i].fd, buf, MAXBUF); 61 printf("Content:[%s].\n", buf); 62 } 63 } 64 } 65 66 close(fd); 67 68 return 0; 69 }
3. 異步IO
(1) 異步IO:就是操作系統用軟件實現的一套中斷響應系統
(2) 工作方法:進程注冊一個異步IO事件(使用signal注冊一個信號SIGIO的處理函數)
(3) 涉及函數:fcntl(F_GETFL, F_SETFL, O_ASYNC, F_SETOWN), signal(), sigaction()函數
代碼示例:
1 /* 2 * 並發式IO的解決方案3:異步IO處理 signal or sigaction and fcntl 3 */ 4 5 #include <stdio.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 #include <signal.h> 11 #include <string.h> 12 13 #define MAXBUFF 1024 14 #define MOUSEPATH "/dev/input/mouse1" 15 16 // 全局變理 17 int mousefd = -1; 18 19 // 定義signal的函數指針 20 typedef void (*sighandler_t)(int); 21 22 // 函數聲明 23 void func(int sig); 24 25 int main(void) 26 { 27 int flag = -1; 28 char buf[MAXBUFF]; 29 sighandler_t ret = (sighandler_t)-2; 30 31 // 操作鼠標文件 32 mousefd = open(MOUSEPATH, O_RDONLY); 33 if ( mousefd < 0 ) 34 { 35 perror("open"); 36 _exit(-1); 37 } 38 39 // 把鼠標的文件描述符設置為可以接受異步IO 40 flag = fcntl(mousefd, F_GETFL); 41 flag |= O_ASYNC; 42 fcntl(mousefd, F_SETFL, flag); 43 44 // 把異步IO事件的接收進程設置為當前進程 45 fcntl(mousefd, F_SETOWN, getpid()); 46 47 // 注冊當前進程的SIGIO信號捕獲函數 48 ret = signal(SIGIO, func); 49 if (SIG_ERR == ret) 50 { 51 perror("signal"); 52 _exit(-1); 53 } 54 55 // 操作鍵盤 56 while (1) 57 { 58 memset(buf, 0, sizeof(buf)); 59 read(0, buf, MAXBUFF); 60 printf("鍵盤讀取的內容是:[%s].\n", buf); 61 } 62 63 return 0; 64 } 65 66 // 綁定到SIGIO信號,在函數內處理異步通知事件 67 void func(int sig) 68 { 69 char buf[MAXBUFF] = {0}; 70 71 if ( sig != SIGIO ) 72 return; 73 74 read(mousefd, buf, MAXBUFF); 75 printf("鼠標讀取的內容是:[%s].\n", buf); 76 }