第十七篇,文件偏移量lseek函数,系统IO应用实例(LCD应用)和内存映射详细讲解。

一、文件偏移量。
1、什么是文件偏移量?
文件偏移量就是文件光标当前的定位,默认打开一个文件时,文件的定位都是在文件的最开头。
文件读操作/写操作都会使得文件偏移量往后偏移。

2、怎么才能使得文件偏移量发生偏移?
1)使用读写函数可以使得文件发生偏移?
   fd = open("xxx.txt");   --> 当前文件偏移量:0
   write(fd,"hello",5);    --> 当前文件偏移量:5

2)如何使得不调用读写函数前提下发生偏移呢?   -->  lseek()   -->  man 2 lseek
功能: reposition read/write file offset
    //重新定位读写文件偏移量

头文件:
    #include <sys/types.h>
        #include <unistd.h>

函数原型:
       off_t lseek(int fd, off_t offset, int whence);

参数:
    fd:  文件描述符
      offset: 需要发生偏移的字节数  
    whence: 基准点
        SEEK_SET   --> 相对于文件开头
              SEEK_CUR   --> 相对于当前的位置
                SEEK_END   --> 相对于文件末尾

返回值:
    成功: 当前位置距离开头的偏移量
    失败: -1
              
    例子: 验证一下lseek函数的参数。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <string.h>

int main(int argc,char *argv[])
{
    //helloworldyueqianappletree
    
    int fd = open("test.txt",O_RDWR); //0
    
    int ret = lseek(fd,8,SEEK_SET);
    printf("ret = %d\n",ret); //8
    
    char buf[20] = {0};
    read(fd,buf,10);
    printf("buf = %s\n",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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open

int main(int argc,char *argv[])
{
    int fd = open("xxx.txt",O_RDWR);  //开头
    
    int ret = lseek(fd,-5,SEEK_SET);
    printf("ret = %d\n",ret);
    
    //现在文件的定位依然在开头。
    
    write(fd,"apple",5);
    
    close(fd);
    
    return 0;
}


    练习2: 如果当前的定位在末尾,还往后偏移会怎样?

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open

int main(int argc,char *argv[])
{
    //helloworld
    int fd = open("xxx.txt",O_RDWR);  //开头
    
    int ret = lseek(fd,5,SEEK_END);
    printf("ret = %d\n",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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open

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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open

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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open

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 <sys/mman.h>

   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 <string.h>

   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 <sys/mman.h>

   int munmap(void *addr, size_t length);

参数:
    addr:指向那片内存映射的内存空间的起始地址
    length:撤销的长度

返回值:
    成功:0
    失败:-1


5)关闭文件
   close(fd);


     例子: 使用内存映射方式显示全屏紫色。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      
#include <sys/mman.h>
#include <string.h>

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: 使用内存映射方式来显示德国国旗。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖爱Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值