【linux应用软件编程】文件操作学习3【目录IO、出错处理及framebuffer基础操作】
文章目录
- 前言
- 一、目录IO
- 1.1 打开目录文件:opendir()
- 1.2 读取目录文件:readdir()
- 1.3 关闭目录文件:closedir()
- 1.4 文件夹创建:mkdir()
- 1.5 使用示例:目录遍历
- 二、出错处理
- 2.1 strerror
- 2.2 perror
- 三、framebuffer
- 3.1 什么是framebuffer:
- 3.2 如何操作framebuffer
- 3.2.1 打开显示设备文件
- 3.2.2 获取显示设备的相关参数
- 3.2.3 建立内存映射
- 3.2.4 写入RGB颜色值;
- 3.2.5 解除内存映射与关闭显示设备文件
前言
上篇文章我们学习了文件相关基础知识及标准IO和文件IO,这篇文章,我们主要是对文件IO的一个补充即目录IO的学习,还有对出错处理的一个学习,以及对之前C语言五子棋项目实战中framebuffer帧缓存技术的补充学习。下面是之前相关知识的博客文章,感兴趣的可以看一看:
文件相关基础知识及【标准IO】
文件IO及文件IO与标准IO的区别
基于【C语言】和【framebuffer】的五子棋游戏项目
一、目录IO
- 打开目录文件:
opendir() - 读取目录文件:
readdir() - 关闭目录文件:
closedir()
1.1 打开目录文件:opendir()
- 函数原型:
#include<sys/types.h>#include<dirent.h>DIR*opendir(constchar*name); - 功能:
打开目录文件并获得目录流指针。
- 参数:
name:目录文件名。
- 返回值:
成功:返回目录流指针;
失败:返回NULL。
1.2 读取目录文件:readdir()
- 函数原型:
#include<dirent.h>structdirent*readdir(DIR*dirp); - 功能:
从目录流中读取数据。
- 参数:
dirp:目录流指针。
- 返回值:
成功:;
文件末尾:NULL;
失败:NULL。
1.3 关闭目录文件:closedir()
- 函数原型:
#include<sys/types.h>#include<dirent.h>intclosedir(DIR*dirp); - 功能:
关闭目录文件。
- 参数:
dirp:目录流指针。
- 返回值:
成功:返回0;
失败:返回-1.
1.4 文件夹创建:mkdir()
- 函数原型:
#include<sys/stat.h>#include<sys/types.h>intmkdir(constchar*pathname,mode_tmode);- 功能:
创建一个文件夹。
- 参数:
pathname:创建的文件夹的名称;
mode:权限设置。 - 返回值:
成功:返回0;
失败:返回-1。
1.5 使用示例:目录遍历
通过调用opendir打开上级目录,通过readdir循环读取目录项信息,打印每个文件的文件名、inode 编号,依据dirent结构体中的d_type字段识别并输出文件类型,最后调用closedir关闭目录,完成目录遍历。
#include<stdio.h>#include<dirent.h>voidget_type(structdirent*pinfo){switch(pinfo->d_type){caseDT_BLK:printf("%s : b\n",pinfo->d_name);break;caseDT_CHR:printf("%s : c\n",pinfo->d_name);break;caseDT_DIR:printf("%s : d\n",pinfo->d_name);break;caseDT_FIFO:printf("%s : p\n",pinfo->d_name);break;caseDT_LNK:printf("%s : l\n",pinfo->d_name);break;caseDT_REG:printf("%s : -\n",pinfo->d_name);break;caseDT_SOCK:printf("%s : s\n",pinfo->d_name);break;default:printf("%s : unknow\n",pinfo->d_name);break;}}intmain(){DIR*pdir=opendir("..");if(NULL==pdir){printf("opendir error!\n");return-1;}while(1){structdirent*pinfo=readdir(pdir);if(NULL==pinfo){break;}printf("%s : %ld\n",pinfo->d_name,pinfo->d_ino);get_type(pinfo);}closedir(pdir);return0;}二、出错处理
2.1 strerror
- 函数原型:
#include<string.h>char*strerror(interrnum); - 功能:
将错误码转化为对应的错误信息字符串。
- 参数:
errnum:错误码(errno)。
- 返回值:
成功:返回对应错误信息字符串的首地址;
失败:返回NULL。 - 注意:
使用errno需要包含
errno.h这个头文件。
2.2 perror
函数原型:
#include<stdio.h>voidperror(constchar*s);功能:
打印自己的出错信息和系统给出的出错原因。
参数:
自己的出错信息字符串。
返回值:
无返回值。
三、framebuffer
3.1 什么是framebuffer:
- 帧缓存、帧缓冲技术;
- 是Linux为显示设备提供的一个接口,把显存抽象后的一种设备文件,通过该设备文件可以实现应用层向显存空间的数据写入,从而实现像素点的绘制。
3.2 如何操作framebuffer
通过内存映射技术,可以实现显存空间和用户空间的内存映射,通过向用户空间写入RGB颜色值,从而实现显存数据的写入。
像素点位数/位深度的种类:
- RGB888 四个字节
- RGB565 两个字节
framebuffer的操作流程:
- 打开显示设备文件/dev/fb0;
- 获取显示设备的相关参数(分辨率、像素点位数/位深度);
- 建立内存映射;
- 写入RGB颜色值;
- 解除内存映射;
- 关闭显示设备文件。
3.2.1 打开显示设备文件
通过我们的文件IO的函数接口open()打开framebuffer的设备文件,它在/dev/fb0这个目录下,
//1. 打开fb=open("/dev/fb0",O_RDWR);if(-1==fb){perror("open /dev/fb0 error");return-1;}3.2.2 获取显示设备的相关参数
我们通过ioctl()这个系统调用的函数接口来获取设备的相关参数;
它的描述是这样的:
ioctl()系统调用用于操控特殊文件底层对应的设备参数。参数fd必须是一个已经成功打开的文件描述符。第二个参数是依赖具体设备的请求命令码(request)。不同驱动,命令码含义完全不一样,没有全局统一标准。第三个参数是一个无类型的内存指针。历史上C 语言还没有void*时,这里约定使用char *argp,所以手册后续讲解中将它命名为argp。也就是原型里的可变参数,你可以传任意类型指针。
在linux@ubuntu:/usr/include/linux$这个路径下的的fb.h文件是Linux 液晶屏帧缓冲驱动专用头文件,里面定义了 Framebuffer 设备 (/dev/fb0) 所有可用的ioctl命令码。
例如:
FBIOGET_VSCREENINFO:这个宏就是我们现在需要的获取虚拟屏幕可变信息的命令码(分辨率、色深、像素格式等)。- 还有这个:struct fb_var_screeninfo 可变屏幕信息结构体:
structfb_var_screeninfo{__u32 xres;/* visible resolution */__u32 yres;__u32 xres_virtual;/* virtual resolution */__u32 yres_virtual;__u32 xoffset;/* offset from virtual to visible */__u32 yoffset;/* resolution */__u32 bits_per_pixel;/* guess what */__u32 grayscale;/* 0 = color, 1 = grayscale, *//* >1 = FOURCC */structfb_bitfieldred;/* bitfield in fb mem if true color, */structfb_bitfieldgreen;/* else only length is significant */structfb_bitfieldblue;structfb_bitfieldtransp;/* transparency */__u32 nonstd;/* != 0 Non standard pixel format */__u32 activate;/* see FB_ACTIVATE_* */__u32 height;/* height of picture in mm */__u32 width;/* width of picture in mm */__u32 accel_flags;/* (OBSOLETE) see fb_info.flags */};成员变量 含义说明 xres/yres可见屏幕分辨率(实际 LCD 显示宽高像素)
例:800×480,xres=800,yres=480xres_virtual/yres_virtual虚拟显存分辨率
显存可以开辟一块比屏幕更大的画布,用于硬件滚屏、双缓冲。
例如虚拟高度设为yres*2实现页面翻页xoffset/yoffset显示偏移量:从虚拟显存的 (xoffset,yoffset)位置开始截取画面输出到屏幕
修改yoffset配合大的yres_virtual,即可实现硬件滚动 (Panning)bits_per_pixel每个像素占用比特数,色深
常见值:16 (RGB565)、24 (RGB888)、32 (RGBA8888)grayscale灰度模式标志
0= 彩色;1= 灰度;大于 1 代表使用 FOURCC 压缩图像格式red/green/blue/transpRGB 分量位域信息, struct fb_bitfield结构体
包含每个颜色通道:起始偏移位、占用位数。
👉RGB565:红占 5bit,绿占 6bit,蓝占 5bitnonstd非标准像素格式标记,非 0 代表不是标准 RGB 格式 activate参数生效时机。控制调用 FBIOPUT_VSCREENINFO后,新分辨率是立刻生效还是等到下一次 vsync 垂直同步height/width屏幕物理尺寸,单位:毫米 (mm),用于计算 DPI,不影响像素绘图 accel_flags硬件加速标志,已经废弃,不再使用
代码示例:
//2. 获取显示设备的相关参数structfb_var_screeninfovinfo;intret=ioctl(fb,FBIOGET_VSCREENINFO,&vinfo);if(-1==ret){perror("ioctl error");return-1;}printf("vinfo.xres = %d, vinfo.yres = %d\n",vinfo.xres,vinfo.yres);printf("vinfo.xres_virtual = %d, vinfo.yres_virtual = %d\n",vinfo.xres_virtual,vinfo.yres_virtual);printf("vinfo.bits_per_pixel = %d\n",vinfo.bits_per_pixel);3.2.3 建立内存映射
以这两张图为例:Linux帧缓冲通过mmap将位于内核空间的物理显存映射到用户进程虚拟地址空间,程序获得映射指针pmem后,便可利用公式*(pmem + y*w + x)直接计算像素偏移,通过指针赋值直接修改显存数据,无需调用read/write,显存更新后硬件自动刷新到800×600的显示屏上,实现屏幕绘图。显存可开辟一块大于实际屏幕xres × yres的虚拟画布xres_virtual × yres_virtual,屏幕仅截取画布的一部分画面进行显示,修改x_offset、y_offset显示偏移量,无需移动显存像素数据,就能快速切换可视区域,从而实现硬件滚屏、双缓冲翻页效果。
- mmap()
- 函数原型:
#include<sys/mman.h>void*mmap(void*addr,size_tlength,intprot,intflags,intfd,off_toffset); - 功能
建立文件和用户空间的内存映射。
- 参数
addr:要映射的用户空间的首地址,一般情况下填NULL,让内核自己寻找一块连续的内存空间,并将空间首地址返回;
length:要映射的用户空间大小;
prot:对映射空间的读写权限
1. PROT_READ
2. PROT_WRITE
flags:MAP_SHARED(共享映射区域)
fd:要映射的文件描述符;
offset:映射的偏移量 - 返回值
成功:返回映射到的用户空间首地址;
失败:返回MAP_FAILED((void *)-1)。
- 函数原型:
内存映射代码示例:
void*pmem=NULL;//3. 建立内存映射len=vinfo.xres_virtual*vinfo.yres_virtual*vinfo.bits_per_pixel/8;pmem=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);if(MAP_FAILED==pmem){perror("mmap error");return-1;}3.2.4 写入RGB颜色值;
首先校验像素坐标是否超出可视屏幕范围,随后基于虚拟显存宽度计算目标像素的内存偏移,通过显存映射指针pmem直接向帧缓冲写入指定颜色值,完成单点绘制。
voidshow_point(intx,inty,unsignedintcol){//4. 写入RGB颜色值if(x>=vinfo.xres||y>=vinfo.yres){return;}*((unsignedint*)pmem+y*vinfo.xres_virtual+x)=col;}3.2.5 解除内存映射与关闭显示设备文件
- munmap()
函数原型:
#include<sys/mman.h>intmunmap(void*addr,size_tlength);功能:
解除
mmap()建立的内存映射,释放用户空间与物理显存之间的映射关系;取消后不能再通过该指针访问显存,关闭帧缓冲文件前必须调用。参数:
addr:mmap返回的映射起始虚拟地址(如前面的pmem)length:需要解除映射的内存字节长度,显存映射大小(finfo.smem_len)
返回值:
成功:返回
0
失败:返回-1,并设置errno错误号
代码示例:
voiddeinit_fb(){//5. 解除内存映射;munmap(pmem,len);//6. 关闭显示设备文件。close(fb);}通过这些步骤我们就可以绘制一个点了,再借助这个点绘制的函数接口,我们就可以进一步封装其他绘制函数了,等下一篇文章我将进一步分享通过framebuffer绘制bmp格式图片的方法。
