Linux 学习笔记(十五)—— 基础IO

发布于:2024-10-13 ⋅ 阅读:(51) ⋅ 点赞:(0)

引子:

  • 文件 = 内容 + 属性
  • 文件分为:打开的文件 (进程打开)、 没打开的文件(存储在磁盘中)
  • 打开的文件一定在内存中,因为CPU只能和内存进行互动。如果要管理打开的文件,那么必定存在包含文件属性等信息的文件结构体

一、C语言文件接口回顾

#include <stdio.h>    ——  标准输入/输出头文件

FILE * fopen(const char* path, const char* mode);        ——  打开文件,返回文件指针,输入文件路径(如果只有文件名,默认为当前进程的当前路径cwd),打开方式”w“,"r","a"等;

  • 如果以"w"方式打开,如果路径下该文件不存在,会自动创建这个文件,并且会清空该文件从头开始写; ——  本质上和输出重定向">"是一样的;
  • 改变工作路径"#include<unistd.h>  int chdir(const char * path)"
  • "a":(writing at end of file)文件不存在也会创建文件;——  本质上和追加重定向">>"是一样的;

int fclose(FILE * fp);          ——  关闭文件,关闭成功返回0;

size_t fwrite(const void* ptr[写入内容的起始地址], size_t size[基本大小,如果是字符串最后的'\0'不要传进去,以‘\0’作为字符串结尾是C语言字符串的特殊规定,文件不是这样规定的,加进去会乱码], size_t nmemb[基本大小的个数], FILE* stream[写入的文件指针]);   

二、系统IO理解

文件其实是在磁盘上的,磁盘是外部设备,因此访问任意磁盘文件本质上就是在访问硬件;而用户不可能越过操作系统和驱动直接访问硬件,因此所有的语言的库函数,只要是涉及到访问硬件的,函数内部一定都封装了系统调用接口;

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

int open(const char* pathname, int flags[模式,O_RDONLY  / O_WRONLY / O_RDWR / O_CREAT / O_APPEND], mode_t mode[文件权限]);       

  • 系统接口:open a file or device

  • O_RDONLY  / O_WRONLY / O_RDWR / O_CREAT / O_APPEND:这些对应的是宏,具体就是一串二进制序列,只有一个为1,这样就可以实现用一个数字同时传递多个选项(通过按位或的方式);

  • int fd = open("log.txt", O_WRONLY);如果“log.txt”这个文件不存在,那么就会打开失败(返回“-1”)不会自行创建;int fd = open("log.txt", O_WRONLY | O_CREAT);这样就会自行创建了;但是这样创建出来的文件的权限不会是你想要的,如果想要创建想要的文件权限,就要用上“mode”这个参数;int fd = open("log.txt", O_WRONLY | O_CREAT,0666);这样就可以自行设置创建文件的权限了;

  • 但上述文件权限设置方法,会被权限掩码“umask”过滤,如果不想要设置的权限被掩码过滤,可以使用

    #include <sys/types.h>    #include <sys/stat.h>    mode_t umask(mode_t mode)

    修改当前进程的文件权限掩码,umask(0);

  • open()的返回值,fd(int)是文件描述符;

#include <unistd.h>

ssize_t write(int fd, const void* buf, size_t count);

  • 系统接口:write to a file descriptor;
  • 示例:const char* message = "hello file system call \n";    write(fd, message, strlen(message));
  • 此时的写入是从文件的起点开始写,但并不会清空原文件的内容;如果想要清空原文件,需要在open()函数的flag参数再加上O_TRUNC;同理,如果想要在后面加上内容就用“O_APPEND” ;

#include <unistd.h>

ssize_t read(int fd, const void* buf, size_t count);

  • 系统接口:read from a file descriptor;
  • count是缓冲区的大小;

#include <unistd.h>

int close(int fildes);

  • 系统接口:close a file descriptor;

三、访问文件的本质

操作系统会使用“struct file”描述一个被打开文件的信息,以便进行管理,类似于进程的PCB;该结构体直接或间接包含:文件路径、文件基本属性(权限、大小、读写位置、打开用户)、文件的内核缓冲区信息、struct file* next;

进程PCB中会有一个“struct file_struct* files”的指针,它指向一个包含指针数组的结构体(文件描述符表),该数组中的指针指向所有被该进程打开的文件的文件描述结构体“struct file”,而上述系统接口中的返回值“int fd”就是这个数组的下标;该数组元素第0,1,2个指针默认指向标准输入/输出/错误流;

C语言里面的“FILE”是C库自己封装的结构体,因为操作系统进程访问文件是通过文件描述符“int fd”的,因此可以肯定FILE结构体里包含了fd(FILE->_fileno);

一个文件可以被多个进程打开,因此struct file结构体里会包含一个叫“引用计数count”的字段;进程关闭文件,只会将文件的count减1,并将文件描述符表中的对应指针置空,如果count不为0就不需要其它操作了,如果count为0系统才需要回收资源;