马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
异步IO
异步 I/O 答应应用步伐在发起 I/O 利用后,继续实行其他使命,而不必要期待 I/O 利用完成。与非壅闭 I/O 差异的是,异步 I/O 不必要应用步伐轮询查抄 I/O 利用的状态,而是由利用体系在 I/O 利用完成后通过某种方式(如信号、回调函数等)关照应用步伐。比方,在 Linux 体系中,可以利用aio_*系列函数(如aio_read、aio_write等)来实现异步 I/O。当调用aio_read函数发起一个异步读利用后,应用步伐可以继续实行其他代码,当读取利用完成后,利用体系会向应用步伐发送一个信号(默认是SIGIO),应用步伐可以通过信号处置惩罚函数来处置惩罚读取到的数据。异步 I/O 大大进步了应用步伐的并发性能和相应本事,但编程模子相对复杂,必要细致处置惩罚 I/O 利用的完成关照和数据处置惩罚逻辑。- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <signal.h>
- #include <string.h>
- #define BUFFER_SIZE 1024
- int mouse_fd = -1;
- // 统一的信号处理函数
- void signal_handler(int signum) {
- if (signum == SIGIO) {
- char buffer[BUFFER_SIZE];
- ssize_t bytes_read;
- // 读取鼠标输入
- memset(buffer, 0, sizeof(buffer));
- bytes_read = read(mouse_fd, buffer, BUFFER_SIZE);
- if (bytes_read > 0) {
- printf("鼠标输入: 读取到 %zd 字节\n", bytes_read);
- }
- // 读取键盘输入
- memset(buffer, 0, sizeof(buffer));
- bytes_read = read(STDIN_FILENO, buffer, BUFFER_SIZE);
- if (bytes_read > 0) {
- buffer[bytes_read] = '\0';
- printf("键盘输入: %s", buffer);
- }
- }
- }
- int main() {
- int flags;
- struct sigaction sa;
- // 初始化 sigaction 结构体
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = signal_handler;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
- // 为 SIGIO 信号设置信号处理函数
- if (sigaction(SIGIO, &sa, NULL) == -1) {
- perror("sigaction");
- return 1;
- }
- // 设置当前进程为标准输入的拥有者
- fcntl(STDIN_FILENO, F_SETOWN, getpid());
- // 获取标准输入的当前文件状态标志
- flags = fcntl(STDIN_FILENO, F_GETFL);
- if (flags == -1) {
- perror("fcntl F_GETFL");
- return 1;
- }
- // 设置标准输入为异步 I/O 模式
- flags |= O_ASYNC;
- if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) {
- perror("fcntl F_SETFL");
- return 1;
- }
- // 对鼠标设备文件进行相同的操作
- mouse_fd = open("/dev/input/mouse0", O_RDONLY);
- if (mouse_fd == -1) {
- perror("无法打开鼠标设备文件");
- return 1;
- }
- fcntl(mouse_fd, F_SETOWN, getpid());
- flags = fcntl(mouse_fd, F_GETFL);
- if (flags == -1) {
- perror("fcntl F_GETFL (鼠标)");
- close(mouse_fd);
- return 1;
- }
- flags |= O_ASYNC;
- if (fcntl(mouse_fd, F_SETFL, flags) == -1) {
- perror("fcntl F_SETFL (鼠标)");
- close(mouse_fd);
- return 1;
- }
- printf("开始异步 I/O 监听...\n");
- // 进入无限循环,等待信号
- while (1) {
- pause();
- }
- return 0;
- }
复制代码
存在bug , 多次利用键盘后,没有进入回调函数,接着移动鼠标,才会触发!!!
存储映射IO
存储映射 I/O(Memory - Mapped I/O)是一种将文件或装备的内容映射到历程地点空间的技能。通过这种方式,应用步伐可以像访问内存一样访问文件或装备的数据,而不必要利用传统的read和write体系调用。在 Linux 体系中,可以利用mmap函数来实现存储映射 I/O。- #include <sys/mman.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- int main() {
- int fd = open("test.txt", O_RDWR);
- if (fd == -1) {
- perror("open");
- exit(EXIT_FAILURE);
- }
- off_t file_size = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
- // 直接传递 fd,并且进行显式类型转换
- char *map = static_cast<char*>(mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
- if (map == MAP_FAILED) {
- perror("mmap");
- close(fd);
- exit(EXIT_FAILURE);
- }
- pid_t pid = fork();
- if (pid == -1) {
- perror("fork");
- munmap(map, file_size);
- close(fd);
- exit(EXIT_FAILURE);
- } else if (pid == 0) {
- // 子进程
- printf("子进程读取文件内容:\n%s", map);
- // 修改映射内存中的内容
- map[0] = 'Y';
- } else {
- // 父进程
- wait(NULL);
- printf("父进程读取修改后的文件内容:\n%s", map);
- }
- // 解除映射
- if (munmap(map, file_size) == -1) {
- perror("munmap");
- }
- close(fd);
- return 0;
- }
复制代码
通过mmap函数,将文件test.txt映射到历程的地点空间,map指向映射后的内存地区。对map的读写利用就相称于对文件的读写利用,而且对映射内存的修改会主动同步到文件中。存储映射 I/O 进步了 I/O 利用的服从,特殊是对于大文件的读写利用,淘汰了数据在用户空间和内核空间之间的复制次数。同时,多个历程可以共享同一个文件的映射,实现历程间通讯和数据共享。
LCD 显存的内存映射
根本概念
LCD 显存:LCD(Liquid Crystal Display,液晶体现器)显存是专门用于存储要体如今屏幕上的图像数据的内存地区。体现器会按照肯定的革新频率从显存中读取数据,并将其转换为对应的像素点体如今屏幕上。
内存映射:内存映射是一种将物理内存地点(如 LCD 显存的物理地点)映射到历程的捏造地点空间的技能。通过内存映射,应用步伐可以像访问平常内存一样访问物理装备的内存,从而实现对装备的直接读写利用。
实现步调
- 打开装备文件
在 Linux 体系中,LCD 装备通常以文件的情势存在于/dev目次下。应用步伐必要利用open()函数打开对应的装备文件,以获取装备文件形貌符。
- #include <fcntl.h>
- #include <stdio.h>
- int fd = open("/dev/fb0", O_RDWR);
- if (fd == -1) {
- perror("open");
- return -1;
- }
复制代码
- 获取 LCD 显存的物理地点和巨细
通过ioctl()函数向装备驱动步伐发送特定的下令,获取 LCD 显存的物理地点和巨细等信息。
- #include <sys/ioctl.h>
- #include <linux/fb.h>
- struct fb_fix_screeninfo finfo;
- struct fb_var_screeninfo vinfo;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
- perror("ioctl FBIOGET_FSCREENINFO");
- close(fd);
- return -1;
- }
- if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
- perror("ioctl FBIOGET_VSCREENINFO");
- close(fd);
- return -1;
- }
- size_t screen_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
复制代码 #include <sys/ioctl.h>:该头文件提供了 ioctl 函数的声明。ioctl(Input/Output Control)是一个用于装备输入输出控制利用的体系调用,它答应用户空间步伐向装备驱动步伐发送特定的控制下令,以实现对装备的各种利用。
#include <linux/fb.h>:此头文件界说了与 Linux 帧缓冲装备(Frame Buffer)相干的数据结构和常量。帧缓冲装备是 Linux 体系中用于管理图形体现的一种抽象装备,通过它可以直接访问体现器的显存。
struct fb_fix_screeninfo finfo;:界说了一个 fb_fix_screeninfo 范例的结构体变量 finfo。fb_fix_screeninfo 结构体用于存储帧缓冲装备的固定信息,这些信息在装备初始化后通常不会改变,比方帧缓冲装备的物理地点、一行像素数据的字节数等。
struct fb_var_screeninfo vinfo;:界说了一个 fb_var_screeninfo 范例的结构体变量 vinfo。fb_var_screeninfo 结构体用于存储帧缓冲装备的可变信息,这些信息可以通过 ioctl 下令举行修改,比方屏幕的分辨率、像素深度等。
ioctl(fd, FBIOGET_FSCREENINFO, &finfo):调用 ioctl 函数向帧缓冲装备发送 FBIOGET_FSCREENINFO 下令,该下令用于获取帧缓冲装备的固定屏幕信息。fd 是帧缓冲装备文件的文件形貌符,&finfo 是存储固定屏幕信息的结构体变量的地点。
if (ioctl(…) == -1):查抄 ioctl 函数的返回值。如果返回值为 -1,表现 ioctl 利用失败。
perror(“ioctl FBIOGET_FSCREENINFO”);:如果 ioctl 利用失败,利用 perror 函数输堕落误信息,提示用户在获取固定屏幕信息时发生了错误。
close(fd);:关闭帧缓冲装备文件的文件形貌符,开释相干资源。
return -1;:返回 -1 表现步伐实行失败。
ioctl(fd, FBIOGET_VSCREENINFO, &vinfo):调用 ioctl 函数向帧缓冲装备发送 FBIOGET_VSCREENINFO 下令,该下令用于获取帧缓冲装备的可变屏幕信息。&vinfo 是存储可变屏幕信息的结构体变量的地点。
后续的错误处置惩罚逻辑与获取固定屏幕信息时类似,如果 ioctl 利用失败,输堕落误信息,关闭文件形貌符,并返回 -1 表现步伐实行失败。
vinfo.xres:表现屏幕的水中分辨率,即屏幕一行的像素数目。
vinfo.yres:表现屏幕的垂直分辨率,即屏幕一列的像素数目。
vinfo.bits_per_pixel:表现每个像素占用的位数,比方 8 位、16 位、24 位等。
vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8:通过水中分辨率、垂直分辨率和每个像素占用的位数盘算出屏幕显存的总字节数,并将结果存储在 screen_size 变量中。
- 举行内存映射
利用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;
- }
复制代码
- 访问 LCD 显存
应用步伐可以像访问平常内存一样访问映射后的捏造地点,从而实现对 LCD 屏幕的控制。
- // 在屏幕左上角绘制一个红色像素点
- int x = 0;
- int y = 0;
- long location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
- (y+vinfo.yoffset) * finfo.line_length;
- *(fbp + location) = 255; // 蓝色通道
- *(fbp + location + 1) = 0; // 绿色通道
- *(fbp + location + 2) = 0; // 红色通道
- *(fbp + location + 3) = 0; // 透明度通道
复制代码
- 清除内存映射并关闭装备文件
在利用完 LCD 显存后,必要利用munmap()函数清除内存映射,并利用close()函数关闭装备文件。
- if (munmap(fbp, screen_size) == -1) {
- perror("munmap");
- }
- close(fd);
复制代码 优点
高效性:内存映射克制了数据在用户空间和内核空间之间的频仍拷贝,进步了数据传输的服从。
简化编程:应用步伐可以利用平常的内存访问指令来访问 LCD 显存,无需利用复杂的装备驱动接口,低落了编程难度。
革新显存
全部革新
- 原理
全部革新指的是每次革新时把整个显存地区的数据都更新一遍。也就是不管屏幕上的图像有没有厘革,都会将新的一帧完备图像数据写入显存。这种方式简单直接,不外大概会斲丧较多的带宽和盘算资源,尤其是在画面厘革较小时。
- 实现步调
天生新的图像数据:在应用步伐里,按照需求天生一帧完备的图像数据。这大概涉及到图形渲染、图像处置惩罚等利用。
写入显存:把天生的新图像数据写入到显存对应的地点空间。在差异的体系中,写入方式会有所差异,常见的有内存映射、直接内存访问(DMA)等。
期待体现器革新:体现器会按照肯定的革新频率从显存读取数据并体现出来。在完成数据写入后,必要期待体现器举行下一次革新,如许新的图像才华体如今屏幕上。
- #include <fcntl.h>
- #include <sys/mman.h>
- #include <sys/ioctl.h>
- #include <linux/fb.h>
- #include <stdio.h>
- #include <stdlib.h>
- int main() {
- int fd = open("/dev/fb0", O_RDWR);
- if (fd == -1) {
- perror("open");
- return -1;
- }
- struct fb_fix_screeninfo finfo;
- struct fb_var_screeninfo vinfo;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) {
- perror("ioctl FBIOGET_FSCREENINFO");
- close(fd);
- return -1;
- }
- if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
- perror("ioctl FBIOGET_VSCREENINFO");
- close(fd);
- return -1;
- }
- size_t screen_size = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
- 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;
- }
- // 生成新的图像数据(这里简单地将整个显存清零)
- for (size_t i = 0; i < screen_size; i++) {
- fbp[i] = 0;
- }
- // 解除映射并关闭文件
- if (munmap(fbp, screen_size) == -1) {
- perror("munmap");
- }
- close(fd);
- return 0;
- }
复制代码 更新革新
- 原理
更新革新只更新显存中发生厘革的部分,而不是整个显存地区。如许可以淘汰数据传输量和处置惩罚时间,进步服从,特殊是在画面只有部分地区厘革的情况下。
- 实现步调
检测厘革地区:**应用步伐必要检测出屏幕上哪些地区的图像发生了厘革。**这可以通过比力前后两帧图像的差异来实现,也可以根据应用步伐的逻辑直接确定厘革的地区。
天生厘革地区的新数据:针对检测到的厘革地区,天生新的图像数据。
更新显存:把厘革地区的新数据写入到显存对应的位置。
期待体现器革新:和全部革新一样,期待体现器举行下一次革新,让厘革的部分体如今屏幕上。
- # 假设 old_frame 和 new_frame 是前后两帧图像数据
- old_frame = get_old_frame()
- new_frame = get_new_frame()
- # 检测变化区域
- changed_rects = detect_changed_rects(old_frame, new_frame)
- # 遍历变化区域并更新显存
- for rect in changed_rects:
- x, y, width, height = rect
- for i in range(y, y + height):
- for j in range(x, x + width):
- # 计算显存中的位置
- index = calculate_index(i, j)
- # 更新显存中的数据
- update_framebuffer(index, new_frame[i][j])
复制代码 总结
全部革新实现简单,但服从较低;更新革新能进步服从,但实现复杂度较高,必要额外的处置惩罚来检测厘革地区。在现实应用中,要根据具体的需求和场景选择符合的革新方式。
|