一、文件偏移量。
1、什么是文件偏移量?
文件偏移量就是文件光标当前的定位,默认打开一个文件时,文件的定位都是在文件的最开头。
文件读操作/写操作都会使得文件偏移量往后偏移。
2、怎么才能使得文件偏移量发生偏移?
1)使用读写函数可以使得文件发生偏移?
fd = open("xxx.txt"); --> 当前文件偏移量:0
write(fd,"hello",5); --> 当前文件偏移量:5
2)如何使得不调用读写函数前提下发生偏移呢? --> lseek() --> man 2 lseek
功能: reposition read/write file offset
//重新定位读写文件偏移量
头文件:
#include
#include
函数原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
fd: 文件描述符
offset: 需要发生偏移的字节数
whence: 基准点
SEEK_SET --> 相对于文件开头
SEEK_CUR --> 相对于当前的位置
SEEK_END --> 相对于文件末尾
返回值:
成功: 当前位置距离开头的偏移量
失败: -1
例子: 验证一下lseek函数的参数。
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//helloworldyueqianappletree
int fd = open("test.txt",O_RDWR); //0
int ret = lseek(fd,8,SEEK_SET);
printf("ret = %dn",ret); //8
char buf[20] = {0};
read(fd,buf,10);
printf("buf = %sn",buf); //ldyueqiana
lseek(fd,-3,SEEK_CUR);
//写入之前文件内容:helloworldyueqianappletree
write(fd,"ggy",3); //写入之后文件内容:helloworldyueqiggyppletree
lseek(fd,-6,SEEK_END);
write(fd,"guanguoyuan",strlen("guanguoyuan")); //写入之前文件内容:helloworldyueqiggyppletree
//写入之后文件内容:helloworldyueqiggyppguanguoyuan
return 0;
}
练习1: 如果当前的定位在开头,还往前偏移会怎样?
答案:lseek函数会执行失败,返回-1
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
int fd = open("xxx.txt",O_RDWR); //开头
int ret = lseek(fd,-5,SEEK_SET);
printf("ret = %dn",ret);
//现在文件的定位依然在开头。
write(fd,"apple",5);
close(fd);
return 0;
}
练习2: 如果当前的定位在末尾,还往后偏移会怎样?
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//helloworld
int fd = open("xxx.txt",O_RDWR); //开头
int ret = lseek(fd,5,SEEK_END);
printf("ret = %dn",ret); //15 但是文件内容: helloworld
write(fd,"apple",5); //A、helloworldapple
//B、helloworld apple 正确
//C.
close(fd);
return 0;
}
练习3:学习完lseek函数之后,再做一次昨晚作业的第二题。
假设当前目录下有一个1.txt文件,里面有一些内容,但是内容是什么,我们不知道,文件多大,我们也不知道。
现在要求你写一个程序copy.c,当执行 ./copy 1.txt 2.txt时
2.txt就会自动创建,2.txt的内容与大小完全与1.txt一致。
#include
#include
#include
#include
#include
int main(int argc,char *argv[]) // ./copy 1.txt 2.txt
{
//1、打开文件
int fd1 = open(argv[1],O_RDONLY);
int fd2 = open(argv[2],O_WRonLY|O_CREAT,0777);
//2、使用lseek函数去计算第一个文件字节数
int n = lseek(fd1,0,SEEK_END); //n就是当前距离文件开头的字节数
//3、定义一个缓冲区
char buf[n];
//4、将文件定位设置到开头
lseek(fd1,0,SEEK_SET); //现在定义在开头
//5、将第一个文件的内容全部读取到文件中
read(fd1,buf,n);
//6、将数组写入到第二个文件中
write(fd2,buf,n);
//7、关闭文件
close(fd1);
close(fd2);
return 0;
}
二、linux系统IO应用实例。 ----> lcd液晶设备
1、在linux下,一切都是文件。
连lcd液晶设备都是一个文件来的,既然是一个文件,那么lcd液晶设备肯定有对应的文件名。
lcd液晶设备 ---> 硬件设备 ---> 去开发板中/dev下寻找。
[root@GEC6818 /]#ls /dev
ttySAC0 --> 外接串口1
ttySAC1 --> 外接串口2
ttySAC2 --> 外接串口3
ttySAC3 --> 外接串口4
fb0 --> lcd液晶设备
结论:这个fb0就是lcd液晶设备的文件名,也就是说,如果你想访问lcd液晶设备,那么只需要访问fb0这个文件即可。
2、已知lcd液晶设备名字是"/dev/fb0",就可以使用open函数去访问该设备。
如果这时候写一些数据到lcd液晶设备上,那么确实是可以显示内容到lcd液晶设备上的。
但是我们必须要先知道一些关于lcd液晶设备的参数。
1)lcd液晶设备分辨率:800 * 480 --> 像素点总数
2)每一个像素点都是由三原色组成,所以像素点可以显示任何一种颜色,那么每一个像素点是由多少个字节组成的呢?
在开发板中输入:
[root@GEC6818 /]#cat /sys//class/graphics/fb0/bits_per_pixel
32 ---> lcd液晶设备每一个像素点 = 32位
---> lcd液晶设备每一个像素点 = 4个字节,分别是ARGB (透明度、红色、绿色、蓝色)
例如:
红色:0x00FF0000
紫色:0x00FF00FF
黄色:0x00FFFF00
例子: 写一个程序,将紫色显示到lcd液晶设备上。
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1、打开lcd液晶设备
int lcd = open("/dev/fb0",O_WRONLY);
if(lcd < 0)
printf("open lcd error!n");
//2、将颜色写入到lcd液晶设备上。
int color = 0x00FF00FF;
int i;
for(i=0;i<800*480;i++)
{
write(lcd,&color,4);
}
//3、关闭lcd液晶设备
close(lcd);
return 0;
}
执行结果:显示全屏紫色。
练习4: 显示全屏红色。
练习5: 显示全屏黑色。 //0x00000000
练习6: 显示德国国旗。
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1、打开lcd液晶设备
int lcd = open("/dev/fb0",O_WRONLY);
if(lcd < 0)
printf("open lcd error!n");
//2、依次将颜色写入到lcd液晶设备上
int color1 = 0x00000000;
int color2 = 0x00FF0000;
int color3 = 0x00FFFF00;
int i;
for(i=0;i<800*160;i++)
{
write(lcd,&color1,4);
}//i=800*160
for(;i<800*320;i++)
{
write(lcd,&color2,4);
}//i=800*320
for(;i<800*480;i++)
{
write(lcd,&color3,4);
}
//3、关闭lcd液晶设备
close(lcd);
return 0;
}
练习7: 显示意大利国旗。
三、访问文件另外一种方式 --- 内存映射。
1、内存映射与普通IO方式有什么区别?
例子:想把一些数据写入到文件中。
普通IO:
open()访问文件 --- 得到一个文件描述符fd ---- 直接往文件描述符fd中写入数据就可以 --- 关闭文件描述符fd
内存映射:
open()访问文件 --- 得到一个文件描述符fd ---- 根据文件描述符fd去内存空间上映射一片空间,得到一个地址p --- 用户只需要将数据拷贝到这个地址p上的空间 ---- 对应的文件就会有相应的变化 --- 撤销映射 --- 关闭文件
2、内存映射步骤。
1)通过访问文件,得到文件描述符。
int fd = open("/dev/fb0",O_RDWR); //使用内存映射的时候,打开文件方式一定要写可读可写。
2)根据文件描述符fd去内存空间中映射出一片空间。 --> mmap() --> man 2 mmap
功能: mmap --- map files or devices into memory
//映射文件或者设备到内存中
#include
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
参数:
addr:映射空间的起始地址
不为NULL --> 用户选择内存空间起始地址 0.0000000001%
NULL --> 系统内核帮你选择空间 99.99999999%
length:映射的空间的长度,例如:lcd液晶 800*480*4
prot:
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
如果需要多个权限,则需要使用"|"连接在一起,例如:PROT_READ|PROT_WRITE
flags:
MAP_SHARED 共享的
MAP_PRIVATE 私有的
fd:文件描述符
offset:文件的偏移量(决定了从文件的哪个地方开始进行映射)
返回值:
成功:指向那片内存映射的内存空间的起始地址
失败:NULL
3)将数据拷贝到内存空间。 --> memcpy() --> man 3 memcpy
功能: copy memory area
//拷贝数据到内存空间中
#include
void *memcpy(void *dest, const void *src, size_t n);
参数:
dest:目标内存起始地址 --> 内存空间
src:需要拷贝的数据 --> 颜色
n:处理src的前n个字节
返回值:
成功:dest区域的地址
失败:NULL
4)撤销映射 --- munmap() --> man 2 munmap
功能:unmap files or devices into memory
#include
int munmap(void *addr, size_t length);
参数:
addr:指向那片内存映射的内存空间的起始地址
length:撤销的长度
返回值:
成功:0
失败:-1
5)关闭文件
close(fd);
例子: 使用内存映射方式显示全屏紫色。
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[])
{
//1、打开文件。
int lcd = open("/dev/fb0",O_RDWR);
if(lcd < 0)
printf("open lcd error!n");
//2、根据文件描述符lcd映射一片空间。
int *p = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd,0);
if(p == NULL)
printf("mmap error!n");
//3、将数据拷贝到内存空间中。
int color = 0x00FF00FF; //紫色
int i;
for(i=0;i<800*480;i++)
{
memcpy(p+i,&color,4);
}
//4、撤销映射
munmap(p,800*480*4);
//5、关闭文件。
close(lcd);
return 0;
}
练习8: 使用内存映射方式来显示德国国旗。