Linux系统高级IO
Linux系统高级IO
- 阻塞式IO的困境
程序中读取键盘
#include <stdio.h>
int main(void)
{
// 键盘就是标准输入,stdin,文件描述符是0
char buf[100];
memset(buf, 0, sizeof(buf));
read(0, buf ,2);
printf("%s",buf);
exit(0);
}
程序中读取鼠标
#include <stdio.h>
int main(void)
{
int fd = -1;
char buf[100];
fd = open("/dev/input/mouse1",O_RDONLY);
if (fd < 0)
{
perror("mouse fail");
exit(-1);
}
memset(buf, 0, sizeof(buf));
read(fd, buf ,50);
printf("%s",buf);
exit(0);
}
程序中同时读取键盘和鼠标
以下方式只能先读鼠标再读键盘!!!因为程序被鼠标读阻塞住了!解决方法是并发式IO!
#include <stdio.h>
int main(void)
{
int fd = -1;
char buf[100];
// 鼠标
fd = open("/dev/input/mouse1",O_RDONLY);
if (fd < 0)
{
perror("mouse fail");
exit(-1);
}
memset(buf, 0, sizeof(buf));
read(fd, buf ,50);
printf("mouse:%s",buf);
// 键盘
memset(buf, 0, sizeof(buf));
read(0, buf ,2);
printf("keyboard:%s",buf);
exit(0);
}
- 非阻塞式IO
没办法读取,直接结束!但可以在一个while循环里以轮询方式实现,但性能不好!
#include <stdio.h>
int main(void)
{
// 把文件描述符变成非阻塞的
int flag = -1;
flag = fcntl(0, F_GETFL);// 获取flag
flag |= O_NONBLOCK;//添加非阻塞属性
fcntl(0, F_SETFL, flag);//设置flag
char buf[100];
// 键盘
memset(buf, 0, sizeof(buf));
read(0, buf ,2);
printf("keyboard:%s",buf);
exit(0);
}
- IO多路复用
用来应对并发IO。
select / poll
select / poll 本身是阻塞式的,内部是非阻塞式的(以非阻塞式自动轮询多路阻塞式IO),但与while不同,可以让出CPU。
用select函数实现同时读取键盘鼠标
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main() {
int fd = -1;
char buf[100];
// 鼠标
fd = open("/dev/input/mouse1",O_RDONLY);
if (fd < 0)
{
perror("mouse fail");
exit(-1);
}
fd_set readfds; // 读集合
struct timeval timeout; // 超时时间
FD_ZERO(&readfds); // 清空集合
FD_SET(0, &readfds); // 键盘
FD_SET(fd, &readfds); // 鼠标
timeout.tv_sec = 5; // 超时时间为 5 秒
timeout.tv_usec = 0;
printf("等待输入(5 秒超时)...\n");
int ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &timeout);
if (ret == -1) {
perror("select 出错");
exit(-1);
} else if (ret == 0) {
printf("超时,没有输入\n");
exit(0);
} else {
// 处理键盘
if (FD_ISSET(0, &readfds)) {
memset(buf, 0, sizeof(buf));
read(0, buf ,2);
printf("keyboard:%s",buf);
}
// 处理鼠标
if (FD_ISSET(fd, &readfds)) {
memset(buf, 0, sizeof(buf));
read(fd, buf ,50);
printf("mouse:%s",buf);
}
}
return 0;
}
- 异步IO
异步IO就是操作系统用软件实现的一套中断响应系统。异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
void sig_fun() {
int data = 0;
int n = read(mousefd, &data, sizeof(data));
if (n < 0) {
printf("read mouse error\n");
}
else {
printf("%d\n", data);
}
}
struct sigaction sa;
struct sigaction od_sa;
sa.sa_handler = sig_fun;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGIO); // SIGIO添加进信号集
sa.sa_flags = 0;
// 1. 调用sigaction为SIGIO信号建立信号处理程序
sigaction(SIGIO, &sa, &od_sa); // 捕获SIGIO后, 处理信号时, 阻塞信号; 处理完毕后恢复
// 2. 以命令F_SETOWN调用fcntl来设置进程ID, 用于接收对该描述符的信号(SIGIO)
if (fcntl(mousefd, F_SETOWN, getpid()) < 0) {
perror("fcntl F_SETOWN error");
exit(1);
}
// 3. 以命令F_SETFL调用fcntl, 设置O_ASYNC文件状态标识, 使得在该描述符上可以进行异步IO
int flag = fcntl(mousefd, F_GETFL);
if (flag < 0) {
perror("fcntl F_GETFL error");
exit(1);
}
flag |= O_ASYNC;
ret = fcntl(mousefd, F_SETFL, flag);
if (ret < 0) {
perror("fcntl F_SETFL error");
exit(1);
}
while (1) {
usleep(50);
}
- 存储映射IO
不需要把内存复制到另一地址,直接映射。
(1)共享而不是复制,减少内存操作
(2)处理大文件时效率高,小文件不划算
mmap
使用mmap()函数将 LCD 显存的物理地址映射到进程的虚拟地址空间。
#include <sys/mman.h>
char *fbp = (char *)mmap(0, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fbp == MAP_FAILED) {
perror("mmap");
close(fd);
return -1;
}