并发式IO的解决方案:多路非阻塞式IO、多路复用、异步IO

时间:2023-03-09 18:24:44
并发式IO的解决方案:多路非阻塞式IO、多路复用、异步IO

在Linux应用编程中的并发式IO的三种解决方案是:

(1) 多路非阻塞式IO

(2) 多路复用

(3) 异步IO

以下代码将以操作鼠标和键盘为实例来演示。

1. 多路非阻塞式IO

多路非阻塞式IO访问,主要是添加O_NONBLOCK标志和fcntl()函数。

代码示例:

 /*
* 并发式IO的解决方案1:多路非阻塞式IO处理键盘和鼠标同时读取
*/ #include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h> #define MOUSEPATH "/dev/input/mouse1" int main(void)
{
int fd = -;
int ret = -;
int flag = -;
char buf[] = {}; fd = open(MOUSEPATH, O_RDONLY | O_NONBLOCK);
if (fd < )
{
perror("open");
_exit(-);
} // 把0的文件描述符变成非阻塞式的
flag = fcntl(, F_GETFD); // 获取stdin原来的flag
flag |= O_NONBLOCK; // 给stdin原来的flag添加非阻塞式属性
fcntl(, F_SETFL, flag); // 更新flag while ()
{
// 读鼠标
memset(buf, , sizeof(buf));
ret = read(fd, buf, ); if (ret > )
{
printf("鼠标读出的内容是:[%s]\n", buf);
} // 读键盘
memset(buf, , sizeof(buf));
ret = read(, buf, );
if (ret > )
{
printf("键盘读出的内容是:[%s]\n", buf);
}
} return ;
}

2. IO多路复用

(1) 多路非阻塞式IO

(2) select() 和 poll() 函数

(3) 外部式阻塞,内部非阻塞式自动轮询多路阻塞式IO

代码示例:

select() 函数实现:

 /*
* 并发式IO的解决方案2:多路复用select()函数处理
*/ #include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <stdlib.h> #define MOUSEPATH "/dev/input/mouse1" int main(void)
{
int fd = -, ret = -;
char buf[] = {};
fd_set myset;
struct timeval tmv; fd = open(MOUSEPATH, O_RDONLY);
if (- == fd)
{
perror("open");
_exit(-);
} // 处理myset
FD_ZERO(&myset); // 清零
FD_SET(fd, &myset); // 加载鼠标的文件描述符到myset集合中
FD_SET(, &myset); // struct timeval *timeout 超时处理
tmv.tv_sec = ;
tmv.tv_usec = ; // 原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
ret = select(fd+, &myset, NULL, NULL, &tmv); // fd+1 这里是最大的fd加1 nfds是从0开始的
if (ret < )
{
perror("select");
_exit(-);
}
else if (ret == )
{
printf("Timeout.\n");
exit();
}
else
{
/* 等到了一路IO,然后去监测哪个IO到了就处理哪个IO */
if ( FD_ISSET(fd, &myset) )
{
// 这里处理鼠标
memset(buf, , sizeof(buf));
read(fd, buf, );
printf("鼠标读出的内容是:[%s]\n", buf);
} if ( FD_ISSET(, &myset) )
{
// 这里处理键盘
memset(buf, , sizeof(buf));
read(, buf, );
printf("键盘读出的内容是:[%s]\n", buf);
}
} return ;
}

poll() 函数实现:

 /*
* 并发式IO的解决方案2:多路复用poll()函数处理
*/ #include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <stdlib.h> #define MOUSEPATH "/dev/input/mouse1"
#define IO_MULTIPLEXING 2
#define MAXBUF 1024
#define MILLISECOND 1000 int main(void)
{
int fd = -, ret = -, i = ;
char buf[MAXBUF] = {};
struct pollfd pfd[IO_MULTIPLEXING] = {}; fd = open(MOUSEPATH, O_RDONLY);
if (- == fd)
{
perror("open");
_exit(-);
} // 初始化 pollfd
pfd[].fd = ; // 键盘
pfd[].events = POLLIN; // 等待读操作 pfd[].fd = fd; // 键盘
pfd[].events = POLLIN; // 等待读操作 // 原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
ret = poll(pfd, fd+, * MILLISECOND); // fd+1 这里是最大的fd加1 nfds是从0开始的
if (ret < )
{
perror("poll");
_exit(-);
}
else if (ret == )
{
printf("Timeout.\n");
exit();
}
else
{
/* 等到了一路IO,然后去监测哪个IO到了就处理哪个IO */
for (i = ; i < IO_MULTIPLEXING; i++)
{
// 处理键盘和鼠标
if ( pfd[i].events == pfd[i].revents )
{
memset(buf, , sizeof(buf));
read(pfd[i].fd, buf, MAXBUF);
printf("Content:[%s].\n", buf);
}
}
} close(fd); return ;
}

3. 异步IO

(1) 异步IO:就是操作系统用软件实现的一套中断响应系统

(2) 工作方法:进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数)

(3) 涉及函数:fcntl(F_GETFL, F_SETFL, O_ASYNC, F_SETOWN), signal(), sigaction()函数

代码示例:

 /*
* 并发式IO的解决方案3:异步IO处理 signal or sigaction and fcntl
*/ #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h> #define MAXBUFF 1024
#define MOUSEPATH "/dev/input/mouse1" // 全局变理
int mousefd = -; // 定义signal的函数指针
typedef void (*sighandler_t)(int); // 函数声明
void func(int sig); int main(void)
{
int flag = -;
char buf[MAXBUFF];
sighandler_t ret = (sighandler_t)-; // 操作鼠标文件
mousefd = open(MOUSEPATH, O_RDONLY);
if ( mousefd < )
{
perror("open");
_exit(-);
} // 把鼠标的文件描述符设置为可以接受异步IO
flag = fcntl(mousefd, F_GETFL);
flag |= O_ASYNC;
fcntl(mousefd, F_SETFL, flag); // 把异步IO事件的接收进程设置为当前进程
fcntl(mousefd, F_SETOWN, getpid()); // 注册当前进程的SIGIO信号捕获函数
ret = signal(SIGIO, func);
if (SIG_ERR == ret)
{
perror("signal");
_exit(-);
} // 操作键盘
while ()
{
memset(buf, , sizeof(buf));
read(, buf, MAXBUFF);
printf("键盘读取的内容是:[%s].\n", buf);
} return ;
} // 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
char buf[MAXBUFF] = {}; if ( sig != SIGIO )
return; read(mousefd, buf, MAXBUFF);
printf("鼠标读取的内容是:[%s].\n", buf);
}

相关文章