在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 }