本文主要是介绍并发式IO的解决方案---非阻塞式、多路复用和异步通知(异步IO),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
---非阻塞式IO
阻塞式读取键盘和鼠标:
// 读取鼠标
int fd = -1;
char buf[200];
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 0)
{
perror("open:");
return -1;
}
memset(buf, 0, sizeof(buf));
printf("before 鼠标 read.\n");
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
// 读键盘
memset(buf, 0, sizeof(buf));
printf("before 键盘 read.\n");
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
return 0;
实现非阻塞IO访问:O_NONBLOCK和fcntl
int fcntl(int fd, int cmd, ... /* arg */ );
F_GETFL (void)
Get the file access mode and the file status flags; arg is ignored.
F_SETFL (int)
Set the file status flags to the value specified by arg. File access mode
(O_RDONLY, O_WRONLY, O_RDWR) and file creation flags (i.e., O_CREAT, O_EXCL,
O_NOCTTY, O_TRUNC) in arg are ignored. On Linux this command can change only the
O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK flags.
属性:
O_NONBLOCK //非阻塞
O_ASYNC //接收异步IO
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号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0, F_GETFL); // 先获取原来的flag
flag |= O_NONBLOCK; // 添加非阻塞属性
fcntl(0, F_SETFL, flag); // 更新flag
// 这3步之后,0就变成了非阻塞式的了
while (1)
{
// 读鼠标
memset(buf, 0, sizeof(buf));
//printf("before 鼠标 read.\n");
ret = read(fd, buf, 50);
if (ret > 0)
{
printf("鼠标读出的内容是:[%s].\n", buf);
}
// 读键盘
memset(buf, 0, sizeof(buf));
//printf("before 键盘 read.\n");
ret = read(0, buf, 5);
if (ret > 0)
{
printf("键盘读出的内容是:[%s].\n", buf);
}
}
---多路复用IO
select和poll
外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO
---select函数介绍
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
//nfds:
多路阻塞式IO中最大的文件描述符+1- //*readfds:要读的IO
- //*writefds:要写的IO
- //*exceptfds:出现错误的IO
- //*timeout:超时时间,在超时时间内,有IO激活函数,则立刻返回,没有IO激活,则在到达超时时间时返回
- //struct timeval {time_t tv_sec; /* seconds */suseconds_t tv_usec; /* microseconds */};
- //返回值:0-超时返回 >0-返回的是IO的返回路数 -1-返回错误
select的几个宏函数:
void FD_CLR(int fd, fd_set *set); //将fd从set里面拿出去
clearO(∩_∩)O~int FD_ISSET(int fd, fd_set *set); //判断fd有没有被置位,返回值是正的,说明这个fd的IO发生了,负的就没有发生
void FD_SET(int fd, fd_set *set); //将fd添加进set里面去
void FD_ZERO(fd_set *set); //将所有的fd_set全部清除出去
代码实践:用select函数实现同时读取键盘鼠标
int fd = -1, ret = -1;
char buf[200];
fd_set myset;
struct timeval tm;
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 0)
{
perror("open:");
return -1;
}
// 当前有2个fd,一共是fd一个是0
// 处理myset
FD_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); //如果两个都没有输入,程序就会在这阻塞住
if (ret < 0)
{
perror("select: ");
return -1;
}
else if (ret == 0)
{
printf("超时了\n");
}
else
{
// 等到了一路IO,然后去监测到底是哪个IO到了,处理之
if (FD_ISSET(0, &myset))
{
// 这里处理键盘
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
if (FD_ISSET(fd, &myset))
{
// 这里处理鼠标
memset(buf, 0, sizeof(buf));
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
}
return 0;
---poll函数介绍
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//使用前必须先设置 struct pollfd * fds
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */ //是内核设置的
};
events常用宏:
POLLIN There is data to read. //FD
要读- POLLOUT Writing now will not block.//FD要写
如果有事件发生,要判断是哪个IO发生的,就去判断每个IO的 events和revents是否相等 ,若相等就说明此IO发生
返回值和select一样;
代码实践:用poll函数实现同时读取键盘鼠标
// 读取鼠标
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;
}
// 初始化我们的pollfd
myfds[0].fd = 0; // 键盘
myfds[0].events = POLLIN; // 等待读操作
myfds[1].fd = fd; // 鼠标
myfds[1].events = POLLIN; // 等待读操作
ret = poll(myfds, fd+1, 10000);
if (ret < 0)
{
perror("poll: ");
return -1;
}
else if (ret == 0)
{
printf("超时了\n");
}
else
{
// 等到了一路IO,然后去监测到底是哪个IO到了,处理之
if (myfds[0].events == myfds[0].revents)
{
// 这里处理键盘
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
if (myfds[1].events == myfds[1].revents)
{
// 这里处理鼠标
memset(buf, 0, sizeof(buf));
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
}
return 0;
---异步通知(异步IO)
异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
3.6.6.2、涉及的函数:
(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)
(2)signal或者sigaction(SIGIO)
3.6.3.代码实践
int mousefd = -1;
// 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
char buf[200] = {0};
if (sig != SIGIO)
return;
read(mousefd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
int main(void)
{
// 读取鼠标
char buf[200];
int flag = -1;
mousefd = open("/dev/input/mouse1", O_RDONLY);
if (mousefd < 0)
{
perror("open:");
return -1;
}
// 把鼠标的文件描述符设置为可以接受异步IO
flag = fcntl(mousefd, F_GETFL);
flag |= O_ASYNC; //接受异步IO
fcntl(mousefd, F_SETFL, flag);
// 把异步IO事件的接收进程设置为当前进程
fcntl(mousefd, F_SETOWN, getpid());
// 注册当前进程的SIGIO信号捕获函数
signal(SIGIO, func);
// 读键盘
while (1)
{
memset(buf, 0, sizeof(buf));
//printf("before 键盘 read.\n");
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
return 0;
}
---存储映射IO
mmap函数
LCD显示和IPC之共享内存
存储映射IO的特点
(1)共享而不是复制,减少内存操作
(2)处理大文件时效率高,小文件不划算
mmap函数
LCD显示和IPC之共享内存
存储映射IO的特点
(1)共享而不是复制,减少内存操作
(2)处理大文件时效率高,小文件不划算
这篇关于并发式IO的解决方案---非阻塞式、多路复用和异步通知(异步IO)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!