Linux中文件描述符fb和文件指针FILE*的区别

本文介绍了Linux中文件描述符(fd)的概念及其用途,并解释了C语言中文件指针(FILE*)的工作原理。同时对比了文件描述符与文件指针之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.文件描述符:
linux中,当一个进程打开一个文件或者是创建一个新文件时,内核向进程返回一个文件描述符来标示该文件。文件描述符是一个非负整数,实际上它是一个索引,指向内核为进程所维护的一个文件记录表。
这里写图片描述
任何程序运行起来都会打开三个默认的流,标准输入流,标准输出流,标准错误流通常情况下对应的硬件为键盘,显示器,显示器。
三个流的文件标示符为 0,1,2
因为默认的三个流已经设置好,后面打开的文件,文件描述符从3,4,5开始依次向后。
文件描述符重定向。
当close调三个默认流后,文件的描述符会从当前位置最小的开始。
2.文件指针FILE*:
文件指针变量标示符,一般形式为大写,可以看出是系统定义的一个结构体,该结构体中含有一系列文件名,文件状态,当前位置信息,文件描述符fd,还有缓冲区等。
在编写源程序的时候不需要关系FILE结构的细节。
C语言定义一个FILE*的对象时:
FILE *fp;
表示fp是一个指向FILE结构体的指针,通过fp可以读写该文件,或者操作文件,习惯的称为fp为文件指针。
3.文件标示符fd和文件指针FILE*的区别
FILE*中的内容包含文件描述符。
使用fopen、fclose、fread、fwirte对文件进行操作时,在lib层。
而文件描述符在系统调用层。系统调用的接口为write/read、close/open
它们的返回值为fd。
这里写图片描述

根据lcd.c #include "lcd.h" #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> #include <string.h> #include <stdlib.h> //全局变量 int fd_lcd; //帧缓冲设备文件文件描述符 int fb_size = 800*480*4; //帧缓冲设备文件的大小 unsigned int *pmap; //映射区首地址 /******************************************** * 功能:初始化LCD显示屏 * 返回值: * -1:失败 * 0:成功 * 参数:无 ********************************************/ int lcdInit() { //打开帧缓冲设备文件(/dev/fb0) fd_lcd = open("/dev/fb0",O_RDWR); if (-1 == fd_lcd) { perror("open lcd error"); return -1; } //映射 pmap = mmap(NULL, fb_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_lcd, 0); if (MAP_FAILED == pmap) { //映射失败 perror("mmap error"); close(fd_lcd); return -1; } return 0; } /******************************************** * 功能:关闭LCD显示屏 * 返回值:无 * 参数:无 ********************************************/ void lcdClose() { //解除映射,释放内存 munmap(pmap, fb_size); //关闭帧缓冲设备文件 close(fd_lcd); } /******************************************** * 功能:显示背景颜色 * 返回值:无 * 参数: * color:要显示的颜色值 ********************************************/ void lcdBrushBG(unsigned int color) { for (int y = 0; y < 480; y++) { for (int x = 0; x < 800; x++) { *(pmap + y*800 + x) = color; } } } /******************************************** * 功能:在指定的位置像素点 * 返回值:无 * 参数: * x,y:像素点的坐标 * color:像素点显示的颜色值 ********************************************/ void lcdDrawPoint(int x,int y,unsigned int color) { if (x >= 0 && x < 800 && y >= 0 && y < 480) { //是屏幕范围内的点,画像素点 *(pmap + y*800 + x) = color; } } /******************************************** * 功能:在指定的位置画矩形(实心) * 返回值:无 * 参数: * x0,y0:矩形显示的位置(左上顶点的坐标) * w,h:矩形的宽度高度 * color:矩形的颜色值 ********************************************/ void lcdDrawRect(int x0,int y0,int w,int h,unsigned int color) { for (int y = y0; y < y0+h; y++) { for (int x = x0; x < x0+w; x++) { lcdDrawPoint(x,y,color); } } } /******************************************** * 功能:在指定的位置画圆形(实心) * 返回值:无 * 参数: * x0,y0:圆心的坐标 * r:半径 * color:圆的颜色值 ********************************************/ void lcdDrawCircle(int x0,int y0,int r,unsigned int color) { for (int y = y0-r; y < y0+r; y++) { for (int x = x0-r; x < x0+r; x++) { if ((y-y0)*(y-y0)+(x-x0)*(x-x0) <= r*r) { //这个坐标到圆心的距离小于等于半径(是圆内的点) lcdDrawPoint(x,y,color); } } } } /******************************************** * 功能:在指定的位置显示文字 * 返回值:无 * 参数: * x0,y0:文字显示的位置(左上顶点的坐标) * word:文字取模结果的首地址 * w,h:文字的宽度高度(像素点个数) * color:文字的颜色值 ********************************************/ void lcdDrawWord(int x0,int y0,unsigned char *word,int w,int h,unsigned int color) { //遍历取模结果 int bytes_line = w/8; //每一行数据需要用多少个字节来保存 for (int i = 0; i < w/8*h; i++) { //每一个字节的数据对应8个像素点的状态 for (int j = 0; j <= 7; j++) { if(word[i]>>7-j & 0x01) { //是文字上的点,画像素点 int x = j + (i%bytes_line)*8 + x0; int y = i/bytes_line + y0; lcdDrawPoint(x,y,color); } } } } /******************************************** * 功能:在指定的位置显示字符串(每个汉字占3个字节) * 返回值:无 * 参数: * x0,y0:字符串显示的位置(第一字符左上顶点的坐标) * count:要显示的字符个数 * word:字符串取模结果的首地址 * w,h:字符的宽度高度(像素点个数) * color:字符的颜色值 ********************************************/ void lcdDrawStr(int x0,int y0,int count,unsigned char *word,int w,int h,unsigned int color) { //逐个显示字符 for (int i = 0; i < count; i++) { lcdDrawWord(x0+i*w,y0,word+i*w*h/8,w,h,color); } } /******************************************** * 功能:在指定的位置显示数字(正整数) * 返回值:无 * 参数: * x0,y0:数字显示的位置(左边第一个数字左上顶点的坐标) * number:要显示的数字值 * word:数字0~9取模结果的首地址 * w,h:数字的宽度高度(像素点个数) * color:数字的颜色值 ********************************************/ void lcdDrawNumber(int x0,int y0,int number,unsigned char *word,int w,int h,unsigned int color) { if (number < 0) { return; } //提取数字各位的值 char bits[20] = {0}; //保存数字各位的值 int wei = 0; //记录数字的位数 //如果数字是0,那么就是一个一位数 if (number == 0) { wei = 1; } //循环获取数字个位的数,直到number为0 while (number) { bits[wei++] = number%10; //提取个位数 number /= 10; //去除个位数 } //显示数字 for (int i = wei-1; i >= 0; i--) { lcdDrawWord(x0+(wei-1-i)*w,y0,word+bits[i]*w*h/8,w,h,color); } } /******************************************** * 功能:在指定的位置显示BMP图片 * 返回值:无 * 参数: * x0,y0:图片显示的位置(左上顶点的坐标) * pic_path:要显示的图片文件的路径名 ********************************************/ void lcdDrawBMP(int x0,int y0,char *pic_path) { //打开图片文件 int fd_pic = open(pic_path,O_RDONLY); if (-1 == fd_pic) { perror("open pic error"); return; } //读取图片的参数(魔数、像素数组文件偏移量、宽度、高度、色深) short MS,depth; //魔数、色深 int offset,w,h; //像素数组的文件偏移量、图片宽度、图片高度 read(fd_pic,&MS,2); //判断当前图片是不是BMP图片 if (0x4D42 != MS) { //不是BMP图片 printf("this pic is not BMP!\n"); close(fd_pic); return; } lseek(fd_pic,0x0A,SEEK_SET); read(fd_pic,&offset,4); lseek(fd_pic,0x12,SEEK_SET); read(fd_pic,&w,4); read(fd_pic,&h,4); lseek(fd_pic,0x1C,SEEK_SET); read(fd_pic,&depth,2); if (depth < 24) { //该BMP图片是24位以下的BMP图片,当前代码不支持显示 printf("The depth of this image is less than 24 bits and thus cannot be displayed.\n"); close(fd_pic); return; } // printf("MS=0x%x,offset=0x%x,w=%d,h=%d,deepth=%d\n",MS,offset,w,h,depth); //读取像素数组数据 int full_bytes_line = (4-(w*depth/8)%4)%4; //每一行填充的字节数 = (4 - 一行像素点颜色值的有效字节数%4)%4 int buf_size = (w*depth/8+full_bytes_line)*abs(h); //像素数组的大小 = 像素点颜色值的有效字节数+填充字节数 unsigned char buf[buf_size]; //用来保存像素数组数据 lseek(fd_pic,offset,SEEK_SET); read(fd_pic,buf,buf_size); //遍历图片上的每个像素点,将其颜色值提取出来按照argb的顺序重新排列组合,并显示到屏幕上去 unsigned char a,r,g,b; //4个颜色分量 unsigned char *p = buf; //为了方便访问数据,通过指针来访问 for (int y = 0; y < abs(h); y++) { for (int x = 0; x < w; x++) { //获取一个像素点的4个颜色分量值 b = *p++; g = *p++; r = *p++; if (depth == 24) { //是一个24位的BMP图片,只有3个颜色分量,a需要手动赋值 a = 0; } else if (depth == 32) { //是一个32位的BMP图片,有4个颜色分量,a可以直接获取 a = *p++; } //将这4个颜色分量按照 a r g b 的顺序重新排列组合成一个32位的颜色值 // a: 1001 1101 // r: 0101 0000 // g: 1010 0010 // b: 0000 1111 // a << 24: // 1010 1101 0000 0000 0000 0000 0000 0000 // r << 16: // 0101 0000 0000 0000 0000 0000 // g << 8: // 1010 0010 0000 0000 // b << 0: // 0000 1111 // a << 24 | r << 16 | g << 8 | b: // 1010 1101 0101 0000 1010 0010 0000 1111 unsigned int color = a << 24 | r << 16 | g << 8 | b; //将该像素点颜色值显示到屏幕上去 if (h > 0) { //图像在保存的时候是从下往上保存的,显示也要从下往上显示 lcdDrawPoint(x+x0,h-1-y+y0,color); } else { lcdDrawPoint(x+x0,y+y0,color); } } //每显示完一行像素点的颜色值,就需要跳过后面的填充数据 p += full_bytes_line; } //关闭图片文件 close(fd_pic); } main.c: #include <string.h> #include <termios.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> //初始化串口 //file: 串口所对应的文件名 //baudrate:波特率 //返回值:成功返回串口设备文件描述符,失败返回-1 int init_serial(const char *file, int baudrate) { int fd; fd = open(file, O_RDWR); if (fd == -1) { perror(“open device error:”); return -1; } struct termios myserial; //清空结构体 memset(&myserial, 0, sizeof (myserial)); //O_RDWR myserial.c_cflag |= (CLOCAL | CREAD); //设置控制模式状态,本地连接,接受使能 //设置 数据位 myserial.c_cflag &= ~CSIZE; //清空数据位 myserial.c_cflag &= ~CRTSCTS; //无硬件流控制 myserial.c_cflag |= CS8; //数据位:8 myserial.c_cflag &= ~CSTOPB;// //1位停止位 myserial.c_cflag &= ~PARENB; //不要校验 //myserial.c_iflag |= IGNPAR; //不要校验 //myserial.c_oflag = 0; //输入模式 //myserial.c_lflag = 0; //不激活终端模式 switch (baudrate) { case 9600: cfsetospeed(&myserial, B9600); //设置波特率 cfsetispeed(&myserial, B9600); break; case 115200: cfsetospeed(&myserial, B115200); //设置波特率 cfsetispeed(&myserial, B115200); break; case 19200: cfsetospeed(&myserial, B19200); //设置波特率 cfsetispeed(&myserial, B19200); break; } /* 刷新输出队列,清除正接受的数据 / tcflush(fd, TCIFLUSH); / 改变配置 */ tcsetattr(fd, TCSANOW, &myserial); return fd; } int main() { //初始化串口(如:设备文件/dev/ttySAC1,波特率9600) int fd_gy39 = init_serial(“/dev/ttySAC1”,9600); if (-1 == fd_gy39) { printf(“GY39 init error!\n”); return -1; } //发送命令(如:0xA5 0x83 0x28) unsigned char cmd[3] = {0xA5,0x83,0x28}; //同时获取光照强度数据温湿度数据 //往设备文件中写入命令(发送命令) write(fd_gy39,cmd,3); //循环读取传感器数据 ssize_t ret; while (1) { sleep(1); unsigned char data[9+15] = {0}; //光照强度数据9个字节,温湿度数据15个字节 ret = read(fd_gy39,data,sizeof(data)); if (-1 == ret) { //读取GY39数据失败 perror(“read data from GY39 error”); close(fd_gy39); return -1; } else if (ret == 0) { //没有读到数据 printf(“No data was read.\n”); sleep(1); continue; } else if (sizeof(data) != ret) { //读取到的数据长度不正确,数据不完整,放弃这条数据,重新读 printf(“The data is incomplete.\n”); printf(“sizeof(data)=%d,ret=%ld\n”,sizeof(data),ret); continue; } //分析计算传感器数据 if (data[2] == 0x15) { //光照强度数据在前面 if (data[0] != 0x5A && data[1] != 0x5A && data[9] != 0x5A && data[10] != 0x5A) { //数据不是以0x5A开头,数据不完整,放弃这条数据,重新读 printf(“Error in data: data[0]=0x%x data[1]=0x%x,data[9]=0x%x,data[10]=0x%x”); continue; } //计算光照强度数据 float LUX = (data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7])/100; //计算温度 float T = (data[13] << 8 | data[14])/100; //打印或者显示数据 printf(“LUX=%.2f,T=%.2f\n”,LUX,T); } else { //温湿度等数据在前面 if (data[0] != 0x5A && data[1] != 0x5A && data[15] != 0x5A && data[16] != 0x5A) { //数据不是以0x5A开头,数据不完整,放弃这条数据,重新读 // printf(“data[0]=0x%x data[1]=0x%x,data[9]=0x%x,data[10]=0x%x”); continue; } } } //关闭串口文件 close(fd_gy39); return 0; } 在此程序上修改获取传感器数据,并显示到GEC6818开发板屏幕上去,要显示名称单位, 实现手控灯自控灯
最新发布
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值