• Linux进阶-文件


    目录

    文件管理常用途径

    系统调用原理 

    文件系统类型

    文件IO五大模式

    系统IO和标准IO

    系统IO编程 

    常用头文件

    文件描述符

    open函数

    close函数

    read函数

    write函数

    lseek函数:设置文件读写位置

    sync函数:页缓存和回写,强制把修改过的页缓存区数据写入磁盘 

    读取文件状态和进行属性操作函数

    目录操作

    错误处理errno

    标准IO编程


    文件管理常用途径

            直接进行文件系统的底层操作(需要程序员熟悉文件系统的结构,并编写大量的代码完成)。

            调用shell程序实现,c语言提供了访问shell程序的接口,但shell的返回信息不便于在程序中进行分析。

            借助系统调用实现(如系统IO等)。

    系统调用原理 

    Linux内核:屏蔽硬件区别,把所有的硬件设备抽象成文件,提供统一的接口给用户使用,是一系列设备的驱动程序。

    文件操作必须通过物理存储设备的驱动程序访问驱动器,如硬盘、光盘驱动器的驱动程序,这些驱动程序存放在Linux内核

    系统调用函数是Linux内核中实现的,是应用程序和linux内核交互的接口。

    在linux中,大部分的头文件在系统的“/usr/include”目录下可以找到,它是系统自动的GCC编译器默认的头文件目录在预定义语句中包含系统调用函数库需要在头文件前加上相对路径sys/。可以用locate来定位头文件的位置,比如locate sys/stat.h。

    系统调用并非是ANSI C标准,所有不同的操作系统或不同的linux内核版本的系统调用函数可能不同。

    Linux提供的系统调用包含以下内容:

    进程控制:如fork、clone、exit、setpriority等创建、克隆、中止、设置进程优先级的操作。

    文件系统控制:如open、read、write等对文件的打开、读取、写入操作。

    系统控制:如reboot、stime、init_module等重启、调整系统时间、初始化模块的系统操作。

    内存管理:如mlock、mremap等内存也上锁、重映射虚拟内存操作。

    网络管理:如sethostname、getthostname设置或获取本主机名操作。

    socket控制:如socket、bind、send等进行TCP、UDP的网络通讯操作。

    用户管理:如setid、getid等设置或获取用户ID的操作。

    进程间通信:包括信号量、管道、共享内存等操作。

    文件系统类型

    虚拟文件系统:抽象层,对文件的访问实际上是对抽象层的访问。

            抽象对象:封装了底层读写细节,使用c语言的多态来实现具体文件系统的接口。

    普通文件系统:ext4、fat32、ubifs

    特殊文件系统

    进程文件系统:procfs,挂载在/proc,存放进程相关信息,任务管理器。

    设备文件系统:devfs,挂载在/dev。存放硬件操作接口。

    文件IO五大模式

    阻塞模式、非阻塞模式、IO多路复用、异步IO、信号驱动IO。

    系统IO和标准IO

    非缓冲文件操作,适合于小规模文件的读写和对实时性要求很高的设备的数据通信,这类操作是系统调用提供的。比如:调制解调器、连接于串口的工业设备。read()函数和write()函数。

    缓冲文件操作,适合于大规模非实时性数据的处理,这类操作是标准输入输出库提供的。

    系统IO编程 

    常用头文件

    stdio.h:C标准输入输出头文件,常使用printf函数。

    stdlib.h:C标准库头文件,常用malloc、free等函数。

    sys/stat.h:包含了关于文件权限定义,如S_IRWXU、S_IWUSR以及函数fstat用于查询文件状态。涉及系统调用文件相关的操作,通常都需要用到sys/stat.h文件。

    unistd.h:UNIX C标准库头文件,unix、linux系列的操作系统相关的C库,定义了unix类系统POSIX标准的符合常量头文件,比如Linux标准的输入文件描述符(STDIN)、标准输出文件描述符(STDOUT)和read、write等系统调用的声明。

    fcntl.h:unix标准中通用的头文件,其中包含的相关函数有open、fcntl、close等操作。 

    sys/types.h:包含Unix/Linux系统的数据类型的头文件,常用的有size_t、time_t、pid_t等类型。

    文件描述符

    文件描述符:小的非负整数,内核用以标识某一特定进程正在存访的文件。

    当内核打开一个现存文件或创建一个新文件时,它会返回一个文件描述符给该进程,这样该进程就能通过内核访问目标文件。

    int fd;                                                //定义文件描述符
    fd = open(filename, flags, mode);  //open(文件名,模式,权限(可选))
    lseek(fd, offset, whence);               //lseek(文件描述符,读写位置的偏移量,文件读写位置的基准值)
    write(fd, buf, write_len);
    read(fd, buf, read_len);
    close(fd);

    主模式(互斥,只选其一)描述副模式(兼容,可多选)描述
    O_RDONLY只读模式O_CREAT当文件不存在,需要去创建文件
    O_WRONLY只写模式O_APPEND追加模式
    O_RDWR读写模式O_DIRECT直接IO模式,读写数据跳过页缓存区,直接在磁盘操作
    O_SYNC同步模式,不需要手动添加sync函数
    O_NOBLOCK非阻塞模式

    open函数

    //所需头文件
    #include    //提供mode_t类型
    #include        //提供open()函数的符号
    #include              //提供open()函数

    //函数原型
    int open(const char* pathname,int flags);                    //当文件存在时
    int open(const char* pathname,int flags,int perms);    //当文件不存在时

    //返回值
    成功:文件描述符
    失败:-1

    close函数

    //所需头文件
    #include     //提供close()函数

    //函数原型
    int close(int fd);

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main()
    7. {
    8. int fd;
    9. fd = open("./a.txt", O_RDONLY|O_CREAT, 0666);
    10. if(fd < 0)
    11. {
    12. printf("open error!\r\n");
    13. }
    14. printf("fd:%d\r\n", fd);
    15. close(fd);
    16. return 0;
    17. }

    read函数

    //所需头文件
    #include

    //函数原型
    ssize_t read(int fd, void *buff, size_t count); 

    //返回值
    成功:
    count:成功读取全部字节
    0~count:
        剩余文件长度小于count
        读取期间被异步信号打断
    失败:-1,读取错误

    write函数

    //所需头文件
    #include

    //函数原型
    ssize_t write(int fd,void *buff,size_t count); 

    //返回值
    成功:
    count:成功写入全部字节
    0~count:写入期间被异步信号打断
    失败:-1,写入错误 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main(int argc,char** argv)
    7. {
    8. int fd1, fd2;
    9. char buf[512];
    10. int read_size;
    11. if(argc != 3)
    12. {
    13. printf("param error!\r\n");
    14. return -1;
    15. }
    16. fd1 = open(argv[1], O_RDONLY);
    17. fd2 = open(argv[2], O_WRONLY | O_CREAT, 0666);
    18. if( fd1 < 0 || fd2 < 0 )
    19. {
    20. printf("open error!\r\n");
    21. return -1;
    22. }
    23. while(1)
    24. {
    25. read_size = read(fd1, buf, 512);
    26. if(read_size == 0)
    27. break;
    28. write(fd2, buf, read_size);
    29. }
    30. close(fd1);
    31. close(fd2);
    32. return 0;
    33. }

     

    lseek函数:设置文件读写位置

    whence描述
    SEEK_SET基准点为文件开头
    SEEK_CUR基准点为当前位置
    SEEK_END基准点为文件末尾

    //所需头文件
    #include  

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

    //返回值
    成功:文件偏移位置值
    失败:-1

    sync函数:页缓存和回写,强制把修改过的页缓存区数据写入磁盘 

    //所需头文件
    #include  

    //函数原型
    void sync(void); 

    //返回值
    无 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main(int argc, char** argv)
    7. {
    8. int fd=open("lseek.txt", O_RDWR | O_CREAT, 0666);
    9. write(fd, "123", 3);
    10. lseek(fd, 3, SEEK_CUR);
    11. write(fd, "abc", 3);
    12. sync();
    13. close(fd);
    14. return 0;
    15. }

    读取文件状态和进行属性操作函数

    //所需头文件

    #include
    #include
    #include

    //函数原型

    int fstat(int fd, struct stat *statbuf);                                   //返回一个已打开文件的状态和属性信息
    int lstat(const char *pathname, struct stat *statbuf);      //返回一个未打开的符号链接文件本身的信息
    int stat(const char *pathname, struct stat *statbuf);     
      //返回一个未打开的符号链接文件指向文件的信息

    struct stat {
                   dev_t              st_dev;         //包含文件的设备ID
                   ino_t               st_ino;          //Inode号
                   mode_t           st_mode;      //文件类型和模式
                   nlink_t            st_nlink;       //硬链接数量
                   uid_t               st_uid;          //所有者的用户ID
                   gid_t               st_gid;          //所有者的群组ID
                   dev_t              st_rdev;        //设备ID(如果是特殊文件)
                   off_t                st_size;         //总大小,以字节为单位
                   blksize_t         st_blksize;    //IO文件系统的块大小
                   blkcnt_t           st_blocks;    //申请的块数(一块等于512B)

     

                   //从Linux 2.6开始,内核支持以下时间戳字段的纳秒精度。有关Linux 2.6之前的详细信息,请参见NOTES
                   struct timespec st_atim;     //最后访问时间
                   struct timespec st_mtim;    //最后修改时间
                   struct timespec st_ctim;     //上次状态更改时间

                   #define st_atime st_atim.tv_sec      /* Backward compatibility */
                   #define st_mtime st_mtim.tv_sec  

                   #define st_ctime st_ctim.tv_sec
    };

    //返回值
    成功:0
    失败:-1,错误置于errno

    chmod(path, 权限);                  //属性操作

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(int argc,char** argv)
    8. {
    9. struct stat buf;
    10. int fd = open("stat.txt", O_RDWR|O_CREAT, 0111);
    11. fstat(fd, &buf);
    12. if(buf.st_mode & S_IRUSR)
    13. puts("所有者拥有读权限");
    14. if(buf.st_mode & S_IRGRP)
    15. puts("群组拥有读权限");
    16. close(fd);
    17. chmod("stat.txt", 0777);
    18. stat("stat.txt", &buf);
    19. if(buf.st_mode & S_IWUSR)
    20. puts("所有者拥有写权限");
    21. if(buf.st_mode & S_IWGRP)
    22. puts("群组拥有写权限");
    23. return 0;
    24. }

    目录操作

    //所需头文件
    #include

    mkdir(path, 权限);                            //新建目录,返回0为成功,返回-1为失败

    getcwd(char *buf, size_t size);       //获取当前工作目录,返回当前目录的字符串长度,当大于size时返回NULL

    chdir(path);                                      //相当于cd命令,改变执行程序的工作目录

    rmdir(path);                                      //删除目录,该函数必须在该目录下没有子目录或文件的情况才能运行

    unlink(path);                                    //删除文件

    扫描子目录函数封装在dirent.h文件,使用DIR结构体,这个结构的指针所指向的内存空间被称为子目录流。

    struct dirent {
                   ino_t         d_ino;                       //Inode号
                   off_t          d_off;                       //该文件相对于文件夹的偏移量
                   unsigned  short d_reclen;       //d_name的长度
                   unsigned  char  d_type;          //文件类型, 例如管道, Socket , Block等
    ;并非所有文件系统类型都支持
                   char          d_name[256];          //文件名
    };

    函数名描述
    DIR *opendir(const char *name)打开路径并建立子目录流,返回子目录流指针
    struct dirent *readdir(DIR *dirp)

    函数返回一个指针,指针指向的结构体保存着子目录流dirp中下一个目录数据项的有关资料。

    后续的readdir调用将返回后续的目录数据。如果错误或到达子目录尾,将返回NULL值。

    long int telldir(DIR *dirp)函数返回值里记录着子目录流里的当前位置
    void seekdir(DIR *dirp, long int loc)

    对dirp指定的子目录流中的目录数据项的指针进行设置。

    loc的值用于设置指针位置,它应该通过前一个telldir函数调用获得。

    int *closedir(DIR *dirp)关闭子目录流,返回关闭操作结果

    可遍历子目录下所有文件的函数,scan_dir.c 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int scan_dir(char *dir, int depth)
    10. {
    11. DIR *dp;
    12. struct dirent *entry;
    13. struct stat statbuf;
    14. if( (dp = opendir(dir)) == NULL )
    15. {
    16. puts("无法打开该目录");
    17. return -1;
    18. }
    19. chdir(dir);
    20. //readdir是逐一读取文件(目录也属于文件的一种),会读取.和..
    21. while( (entry = readdir(dp)) != NULL )
    22. {
    23. /* 读取文件属性 */
    24. lstat(entry->d_name, &statbuf);
    25. if(statbuf.st_mode & S_IFDIR) //目录文件
    26. {
    27. if( strcmp(".", entry->d_name) == 0 || strcmp("..", entry->d_name) == 0 )
    28. continue;
    29. //printf,%*s,depth指定缩进宽度(目前为0),“”表示缩进的内容是空格
    30. printf("%*s%s/\n", depth, "", entry->d_name);
    31. scan_dir(entry->d_name, depth + 4);
    32. }else //普通文件
    33. {
    34. printf("%*s%s/\n", depth, "", entry->d_name);
    35. }
    36. }
    37. chdir("..");
    38. closedir(dp);
    39. }

     使用readdir函数读取目录中的每一个文件(包括目录和文件,因为目录是一个特殊文件),并逐一进行处理。首先使用lstat函数获取文件属性,然后判断是否为目录文件。如果是目录文件,则进行递归调用scan_dir函数,同时将深度+4,以便缩进显示。如果是普通文件,则直接输出文件名。

    scan_dir.h

    1. #ifndef __MP3_H
    2. #define __MP3_H
    3. void scan_dir(char *dir, int depth);
    4. #endif

    main.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int main()
    10. {
    11. puts("扫描该工程目录:");
    12. scan_dir("/home/couvrir/桌面/make_testfile1", 0);
    13. puts("扫描结束!");
    14. return 0;
    15. }

    错误处理errno

    在进行文件操作的过程中可能会因各种原因而失败,错误信息将以代码的形式保存在系统变量errno中。很多函数通过改变errno变量的值输出标准错误信息编码,这些错误信息保存在头文件errno.h中。

    进行错误处理的函数:sterror()、perror()。perror函数内也调用了sterror函数,将标准错误信息字符串输出到终端,并为其增加一个说明。

    因为很多函数都是以errno变量,当另一个函数操作完成后可能会改变该变量的值。所有要获得正确的错误信息,应该将取得errno变量值的语句或sterror()函数放在出错语句最近的位置。

    比如:perror("文件操作");

    终端显示可能是:

            文件操作:No such file or directory

    标准IO编程

    C标准库实现了一个IO缓存区。

    常见标准IO函数(跟上述系统IO函数使用差不多):

    fopen

    fclose

    fread

    fwrite

    fseek

    fflush:强制把IO缓存区的数据写入到页缓存区

  • 相关阅读:
    java计算机毕业设计web扶贫产品物资管理平台MyBatis+系统+LW文档+源码+调试部署
    【华为OD机试python】数字反转打印【2023 B卷|100分】
    新课标、新考法,猿辅导创新教育研究院全面拆解新课标
    力扣--动态规划1027.最长等差数列
    Selenium自动化测试 —— 通过cookie绕过验证码的操作!
    知识点小记:echarts折线图负数展示不一样的颜色
    【网络安全】跨站脚本攻击(XSS)
    NumPy 切片和索引
    Minecraft 1.18.1、1.18.2模组开发 23.3D动画盔甲制作
    户外运动耳机如何选择、最优秀的五款户外运动耳机推荐
  • 原文地址:https://blog.csdn.net/weixin_47077788/article/details/127988163