zoukankan      html  css  js  c++  java
  • 《文件IO》

      在Linux系统中,一切都是“文件”:普通文件、驱动程序、网络通信等等。所有的操作,都是通过“文件IO”来操作的。

    1.文件有哪些?

    1.1磁盘、Flash、SD卡、U盘这样的真实文件,以某种格式(FAT32、EXT4等)保存在某个设备上,要先mount

    mount /dev/sda1  /mnt

    通过cat /proc/mounts或直接mount可以查看已经挂载的文件。

    挂载TF卡:

    通过fdisk -l查看是哪个设备:/dev/sda1

    格式化TF卡:mkfs.ext4   -m   0  /dev/sda1

    mount -t ext4   /dev/sda1   /TFDISK 

    1.2linux内核提供的虚拟文件系统

     

       sysfs是一个虚拟文件,挂载在/sys下

       通过/sys就可以查看内核有哪些设备、固件等

    mount -t sysfs none  /mnt

      本来none这边是设备节点,但是虚拟文件系统没有设备节点,所以这边用none。

    1.3设备节点/dev/xxx,设备文件还分为字符设备、块设备。还有网络文件以及FIFO等

     

      其中c表示字符设备,b表示块设备。

      主设备号表示哪个驱动,次设备号表示哪个硬件。

    2.怎么访问文件

      open/read/write/lseek/close

      ioctl/mmap

    例1:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    
    /*
     * ./copy 1.txt 2.txt
     * argc    = 3
     * argv[0] = "./copy"
     * argv[1] = "1.txt"
     * argv[2] = "2.txt"
     */
    int main(int argc, char **argv)
    {
        int fd_old, fd_new;
        char buf[1024];
        int len;
        
            //判断参数
        if (argc != 3) 
        {
            printf("Usage: %s <old-file> <new-file>
    ", argv[0]);
            return -1;
        }
            
            //打开老文件
        fd_old = open(argv[1], O_RDONLY);
        if (fd_old == -1)
        {
            printf("can not open file %s
    ", argv[1]);
            return -1;
        }
    
            //打开新文件
        fd_new = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (fd_new == -1)
        {
            printf("can not creat file %s
    ", argv[2]);
            return -1;
        }
    
           //从老文件将数据读取出来写入新文件
        while ((len = read(fd_old, buf, 1024)) > 0)
        {
            if (write(fd_new, buf, len) != len)
            {
                printf("can not write %s
    ", argv[2]);
                return -1;
            }
        }
    
        close(fd_old);
        close(fd_new);
        
        return 0;
    }

      ./copy 1.txt 2.txt

      我们运行这个程序的时候,传入这个参数,

      argc    = 3

      argv[0] = "./copy"
      argv[1] = "1.txt"
      argv[2] = "2.txt"

      例1中是打开两个文件,把一个文件的数据读取出来,然后再写入另一个文件中。

    例2:

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <sys/mman.h>
    
    /*
     * ./copy 1.txt 2.txt
     * argc    = 3
     * argv[0] = "./copy"
     * argv[1] = "1.txt"
     * argv[2] = "2.txt"
     */
    int main(int argc, char **argv)
    {
        int fd_old, fd_new;
        struct stat stat;
        char *buf;
        
        /* 1. 判断参数 */
        if (argc != 3) 
        {
            printf("Usage: %s <old-file> <new-file>
    ", argv[0]);
            return -1;
        }
    
        /* 2. 打开老文件 */
        fd_old = open(argv[1], O_RDONLY);
        if (fd_old == -1)
        {
            printf("can not open file %s
    ", argv[1]);
            return -1;
        }
    
        /* 3. 确定老文件的大小*/
        if (fstat(fd_old, &stat) == -1)
        {
            printf("can not get stat of file %s
    ", argv[1]);
            return -1;
        }
    
        /* 4. 映射老文件 */
        buf = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd_old, 0);
        if (buf == MAP_FAILED)
        {
            printf("can not mmap file %s
    ", argv[1]);
            return -1;
        }
    
        /* 5. 创建新文件 */
        fd_new = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
        if (fd_new == -1)
        {
            printf("can not creat file %s
    ", argv[2]);
            return -1;
        }
    
        /* 6. 写入新文件*/
        if (write(fd_new, buf, stat.st_size) != stat.st_size)
        {
            printf("can not write %s
    ", argv[2]);
            return -1;
        }
    
        /* 5. 关闭文件*/
        close(fd_old);
        close(fd_new);
        
        return 0;
    }

      这里通过mmap直接映射,就可以像操作内存一样操作buf。

    3.怎么知道上面函数的用法

    Linux下有3大帮助方法:help、man、info。

    想查看某个命令的用法时,比如查看ls命令的用法,可以执行:

    ls  --help

      help只能用于查看某个命令的用法,而man手册既可以查看命令的用法,还可以查看函数的详细介绍等等。它含有9大分类,如下:

    1   Executable programs or shell commands       // 命令
    2   System calls (functions provided by the kernel)  // 系统调用,比如 man 2 open
    3   Library calls (functions within program libraries)  // 函数库调用
    4   Special files (usually found in /dev)             // 特殊文件, 比如 man 4 tty 
    5   File formats and conventions eg /etc/passwd  // 文件格式和约定, 比如man 5 passwd
    6   Games  // 游戏
    7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) //杂项
    8   System administration commands (usually only for root) // 系统管理命令
    9   Kernel routines [Non standard]  // 内核例程

      比如想查看open函数的用法时,可以直接执行“man open”,发现这不是想要内容时再执行“man  2  open”。  

      在man命令中可以及时按“h”查看帮助信息了解快捷键。常用的快捷键是:

      f  往前翻一页

      b  往后翻一页

      /patten 往前搜

      ?patten 往后搜

      就内容来说,info手册比man手册编写得要更全面,但man手册使用起来更容易些。

       可以直接执行“info”命令后,输入“H”查看它的快捷键,在info手册中,某一节被称为“node”,常用的快捷键如下:

    Up          Move up one line.
    Down        Move down one line.
    PgUp        Scroll backward one screenful.
    PgDn        Scroll forward one screenful.
    Home        Go to the beginning of this node.
    End         Go to the end of this node.
    
    TAB         Skip to the next hypertext link.
    RET         Follow the hypertext link under the cursor.
    l           Go back to the last node seen in this window.
    
    [           Go to the previous node in the document.
    ]           Go to the next node in the document.
    p           Go to the previous node on this level.
    n           Go to the next node on this level.
    u           Go up one level.
    t           Go to the top node of this document.
    d           Go to the main 'directory' node.

    4.系统调用函数怎么进入内核?

      常用的几个C库有glibc和uclibc,其中glibc更加完善,uclibc是经过裁剪给嵌入式用的。但是现在嵌入式性能提高,因为大部分都用glibc。

      

       以ARM64位为例,当我们当用open函数的时候,glibc最终就会调用svc指令(以前的32位是用swi指令),cpu就会触发中断,cpu读取R8寄存器中的数值,然后以这个数值为下标通过数组sys_call_table查看到对应的系统调用。

    5  内核的sys_open、sys_read会做什么?

      以sys_open为例:

      首先会去分辨是字符设备还是块设备。 

      如果是字符设备,会通过主设备号去找到相对应的驱动程序,然后驱动程序中有相对应的open、read函数。

      如果是块设备,那么会通过FAT32或者EXT4等文件系统去操作文件,然后保存调用块设备驱动程序,保存在相对应的磁盘或者SD卡中。 

  • 相关阅读:
    DB2 关联更新
    postgresql 开启审计日志
    Delphi D10.X中Tpath引发的单元引用及代码编写的思考
    自己写的函数或者过程与Delphi提供的重名了怎么办?(50分)
    技巧四 Delphi XE3 代码自动提示bug解决
    想开发经典界面吗?
    初涉Delphi下Windows消息机制——同一程序内自定义消息实例
    Delphi的DirectShow开发概述
    delphi接口(抄自万一)
    fkInternalCalc和fkCalculated有何区别?
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11791459.html
Copyright © 2011-2022 走看看