理解 Linux 文件结构:一份简单易懂的入门教程

embedded/2025/2/8 13:09:41/

个人主页:chian-ocean

文章专栏-Linux

前言:

Linux 文件系统是指 Linux 操作系统用于组织和管理文件、目录及其元数据(如权限、时间戳等)的系统。文件系统定义了文件的存储、访问和管理的方式,并提供了数据持久性和组织结构。

在这里插入图片描述

C语言文件操作

C语言文件

  • FILE* 是一个指向文件的结构体,通过它,程序可以与文件进行交互。

  • 文件指针由标准库函数如 fopen() 创建和返回,文件的读写操作通过这个指针来执行。

  • 文件指针常用于文件操作函数,如 fopen()fread()fwrite()fclose() 等。

#include <stdio.h>       
#include <string.h>    int main()    
{    // 打开名为 "log.txt" 的文件以进行写入("w" 模式)FILE* fd = fopen("log.txt", "w"); //路径默认在当前工作路径if (fd == NULL)    // 如果fopen失败(例如文件无法打开){    perror("fopen");  // 输出错误信息,指示fopen失败return 1;          // 返回错误代码(1),表示程序失败}    const char* msg = "hello linux!\n"; // 定义要写入文件的字符串消息// 使用strlen计算消息长度,并将消息写入文件fwrite(msg, strlen(msg), 1, fd);    fclose(fd);   // 写入完成后关闭文件return 0;     // 返回0,表示程序成功执行
}

理解当前工作路径:

  • 在进程文件读写的时候会进程会记录当前的工作目录(cwd),如图:在/home/ocean/linux/file/filetest路径下创建文件。

在这里插入图片描述

  • 我们在代码上加上
chdir("/home/ocean/linux/file");

就会更改当前的工作路径,如图查看到的cwd是: /home/ocean/linux/file

在这里插入图片描述

再次执行代码后文件会创建在 /home/ocean/linux/file这个路径下

在这里插入图片描述

文件打开方式

r

  • 打开文件进行读取,文件指针定位在文件的开始位置。
  • 如果文件不存在,打开失败。

r+

  • 打开文件进行读取和写入,文件指针定位在文件的开始位置。
  • 如果文件不存在,打开失败。

w

  • 打开文件进行写入,如果文件存在,则截断文件至零长度;如果文件不存在,创建文件。
  • 文件指针定位在文件的开始位置。

w+

  • 打开文件进行读取和写入,如果文件不存在,创建文件;如果文件存在,截断文件至零长度。
  • 文件指针定位在文件的开始位置。

a

  • 打开文件进行附加写入,写入的内容会追加到文件的末尾;如果文件不存在,创建文件。
  • 文件指针定位在文件的末尾。

a+

  • 打开文件进行读取和附加写入,读取时从文件的开始位置开始,写入时追加到文件末尾;如果文件不存在,创建文件。

在这里插入图片描述

本质上没有必要记那么多,如果需要直接去看官方的文档即可

文件的系统调用

在操作系统中,文件操作通常通过系统调用(system calls)进行。这些系统调用直接与操作系统的内核交互,以进行文件的创建、读取、写入、删除等操作。

open()

  • 功能:打开文件,返回文件描述符。
  • 语法int open(const char *pathname, int flags, mode_t mode);
  • 参数
    • pathname:要打开的文件路径。
    • flags:指定文件的打开模式(如只读、写入、追加等)。
    • mode:文件权限,如果文件是新建的,指定文件权限。
  • 返回值:成功返回文件描述符(一个非负整数),失败返回 -1,并设置 errno

在这里插入图片描述

理解flags

flags 参数允许通过按位“或”运算符(|)将多个标志组合在一起。这样,可以灵活地指定多种行为。例如,你可以使用 O_WRONLY | O_CREAT | O_TRUNC 来实现“以写模式打开文件,如果文件不存在则创建文件,如果文件已存在则截断文件”的行为。

int fd = open("file.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);

为什么会用 |

#include <stdio.h>#define ONE (1<<0)    
#define TWO (1<<1)    
#define THREE (1<<2)    
#define FOUR (1<<3)    // 用于根据标志位显示相应功能的函数
void show (int flags)    
{    // 检查 flags 的每一位,如果相应的位被设置,则输出对应的信息if(flags & ONE) cout << "func 1" << endl;    // 如果 flags 的最低位(0)被设置,则输出 "func 1"if(flags & TWO) cout << "func 2" << endl;    // 如果 flags 的第二位(1)被设置,则输出 "func 2"if(flags & THREE) cout << "func 3" << endl;  // 如果 flags 的第三位(2)被设置,则输出 "func 3"if(flags & FOUR) cout << "func 4" << endl;   // 如果 flags 的第四位(3)被设置,则输出 "func 4"
}    int main()    
{      // 调用 show 函数,传递不同的 flags,分别测试不同的功能组合show(ONE | TWO);        // 传递 ONE | TWO,即 flags = 0001 | 0010 = 0011 -> func 1 和 func 2show(ONE | THREE);      // 传递 ONE | THREE,即 flags = 0001 | 0100 = 0101 -> func 1 和 func 3show(ONE | TWO | THREE); // 传递 ONE | TWO | THREE,即 flags = 0001 | 0010 | 0100 = 0111 -> func 1, func 2 和 func 3return 0;   // 程序正常结束
}
代码解释:
  1. 宏定义:使用 #define 来定义常量 ONETWOTHREEFOUR,它们分别表示不同的二进制位:
    • ONE 代表二进制的第 1 位(0001)。
    • TWO 代表二进制的第 2 位(0010)。
    • THREE 代表二进制的第 3 位(0100)。
    • FOUR 代表二进制的第 4 位(1000)。
  2. show 函数:这个函数接受一个整数 flags,然后检查每一位是否被设置。如果某一位被设置(即该位为 1),则打印相应的功能名:
    • 使用按位与操作符 & 来检查每个标志位是否被设置。
    • 如果某个标志位被设置,if (flags & ONE) 等判断条件为 true,执行相应的 cout 输出。
  3. main 函数:调用 show() 函数并传递不同的 flags 值,展示不同的功能组合:
    • show(ONE | TWO):将 ONETWO 进行按位“或”运算,得到 0011,所以会显示 "func 1""func 2"
    • show(ONE | THREE):将 ONETHREE 进行按位“或”运算,得到 0101,所以会显示 "func 1""func 3"
    • show(ONE | TWO | THREE):将 ONETWOTHREE 进行按位“或”运算,得到 0111,所以会显示 "func 1""func 2""func 3"

生成:

在这里插入图片描述

这个大致就是flag的 |的本质

常用的falg:
FlagDescription
O_RDONLY以只读方式打开文件
O_WRONLY以只写方式打开文件
O_RDWR以可读可写方式打开文件
O_CREAT如果文件不存在,则创建文件
O_TRUNC如果文件已存在,则将文件内容截断为零长度
O_APPEND以追加模式打开文件(所有写入都添加到文件末尾)

理解mode详细:参考

open() 函数中,mode 参数是用于指定新创建文件的权限和访问控制。该参数仅在使用 O_CREAT 标志时才需要提供,表示当文件不存在时,open() 会创建新文件并根据 mode 参数设置文件的权限。mode 参数通常是一个三位八进制数字,表示文件所有者、文件所属组和其他用户的权限。

mode 参数的基本结构

mode 参数采用 三位八进制数 的形式,表示文件的权限。每一位代表不同用户类别的访问权限,分别是:

  • 第一位:所有者(Owner)的权限 4
  • 第二位:所属组(Group)的权限 2
  • 第三位:其他用户(Others)的权限 1
权限(八进制)权限描述权限组合
777所有用户具有读、写、执行权限rwxrwxrwx
755所有者具有读、写、执行权限,组和其他用户有读、执行权限rwxr-xr-x
644所有者具有读、写权限,组和其他用户只有读取权限rw-r--r--
600所有者具有读、写权限,组和其他用户没有权限rw-------

write

  • 功能:将数据写入文件。
  • 语法ssize_t write(int fd, const void *buf, size_t count);
  • 参数
    • fd:文件描述符。
    • buf:缓冲区,包含要写入的数据。
    • count:要写入的字节数。
  • 返回值:返回实际写入的字节数,或返回 -1 表示错误。

close()

  • 功能:关闭打开的文件。
  • 语法int close(int fd);
  • 参数
    • fd:文件描述符。
  • 返回值:成功返回 0,失败返回 -1

示例:

#include <stdio.h>        // 引入标准输入输出库,用于printf和perror等
#include <string.h>       // 引入字符串处理库,用于处理字符串(如strlen)
#include <unistd.h>       // 引入POSIX标准库,用于访问系统级函数(如write和close)
#include <sys/types.h>    // 引入定义文件操作需要的类型
#include <sys/stat.h>     // 引入文件状态相关定义
#include <fcntl.h>        // 引入文件控制函数,如openint main()    
{    // 使用 O_CREAT、O_TRUNC 和 O_WRONLY 打开文件 'log.txt'// O_CREAT:如果文件不存在,创建文件// O_TRUNC:如果文件已经存在,清空文件内容// O_WRONLY:只写模式打开文件// 0666:设置文件权限,所有用户都有读写权限int fd = open("log.txt", O_CREAT | O_TRUNC | O_WRONLY, 0666);    // 检查文件是否成功打开,如果返回值小于0,表示打开失败if (fd < 0)    {    perror("open");  // 如果打开文件失败,输出错误信息return -1;       // 返回错误代码,程序退出}    // 定义要写入文件的字符串const char* str = "hello linux\n";                   // 定义要写入文件的次数int cnt = 5;    // 循环写入文件5次while (cnt--)    {    // 每次调用 write() 写入字符串到文件write(fd, str, strlen(str));    }    // 关闭文件close(fd);    return 0;  // 正常退出程序
}

代码解释:

  1. open() 函数:使用 O_CREATO_TRUNCO_WRONLY 标志打开文件 log.txt。如果文件不存在,将会创建文件;如果文件已存在,使用 O_TRUNC 将文件内容清空。文件权限设置为 0666,即所有用户都可以读写该文件。
  2. 错误处理:如果 open() 调用失败,会返回负值,使用 perror("open") 输出错误信息并退出程序。
  3. write() 函数:将字符串 "hello linux\n" 写入文件。写入操作会在文件 fd 中进行,strlen(str) 确保写入的字符数是字符串的实际长度。
  4. 文件操作:代码通过 write() 循环写入文件 5 次,每次写入字符串 "hello linux\n"
  5. close() 函数:在完成写入后,通过 close(fd) 关闭文件。

这份代码类似于fopen 、fwrite 、fclose,进行文件写入,这里面运用系统调用,但是 f 系类运用的是C语言函数库中函数,是对上述系统调用的再次封装。

文件描述符

文件描述符(File Descriptor) 是操作系统用来表示打开文件的一个整数标识符。它是一个指向内核中文件对象的引用,用于标识进程对文件的访问。每个进程都有一个文件描述符表,记录着当前进程打开的文件及其相关信息。

标准输入、标准输出、标准错误

  • 每个进程启动时,操作系统会为它创建三个标准的文件描述符:
    • 0:标准输入(stdin),通常用于接收输入。
    • 1:标准输出(stdout),通常用于显示输出。
    • 2:标准错误(stderr),用于输出错误信息。

文件描述符的分配: 当进程通过 open() 打开文件时,操作系统会分配一个文件描述符,该文件描述符对应于该文件的内核级数据结构。文件描述符是进程与操作系统文件系统之间的桥梁,进程通过它进行文件操作(如读取、写入)。

在这里插入图片描述

任务结构 task_struct

  • task_struct 是 Linux 内核中表示进程的数据结构,每个正在运行的进程都有一个对应的 task_struct
  • 图中提到的 struct files_struct *files 表示每个进程都有一个 files_struct,它包含了该进程打开的所有文件描述符。

文件描述符数组 fd_array[]

  • fd_array[] 是一个数组,里面保存了指向文件结构(struct file)的指针。每个 struct file 代表着一个已打开的文件。
  • 例如,fd_array[0] 代表标准输入(stdin),fd_array[1] 代表标准输出(stdout),fd_array[2] 代表标准错误输出(stderr)。
  • 数组中的其他位置保存着该进程打开的其他文件。

struct file 结构:

  • 每个 struct file 结构体代表一个具体的文件,它包含了文件的状态信息,比如文件的当前偏移量、文件访问模式、权限等。
  • 这些文件结构会通过 next 指针形成一个链表,允许操作系统管理多个文件。

所以0 1 2 对应标准输入、输出、错误流 正好与之对应

0号文件

#include <iostream>  // 引入输入输出流库,用于标准输入输出操作
#include <string.h>  // 引入字符串操作库(如 strlen、memcpy 等)
#include <unistd.h>  // 引入 UNIX 标准头文件,包含对系统调用(如 read、write 等)的定义
#include <sys/types.h>  // 引入定义系统数据类型的库,如 pid_t、off_t 等
#include <sys/stat.h>  // 引入文件状态信息定义的库(如文件权限、文件类型等)
#include <fcntl.h>  // 引入文件控制操作库,包含文件操作的标志,如 O_CREAT、O_WRONLY 等using namespace std;  // 使用标准命名空间,避免每次使用标准库的元素时都要加上 "std::"int main ()  // 主函数
{// 读取标准输入的数据,最多读取 100 个字节,存入缓冲区 buf 中char buf[1024];  // 定义一个字符数组来存储读取的数据ssize_t size = read(0, buf, 100);  // 0 代表标准输入(stdin)buf[size] = '\0';  // 添加字符串结束符,确保读取的数据是一个有效的 C 风格字符串close(fd);  // 关闭打开的文件描述符,释放资源cout << buf << endl;  // 输出读取到的内容到标准输出(屏幕)return 0;  // 程序正常结束,返回 0
}

代码功能概述:

  1. 读取标准输入的数据:
    • 从标准输入(如键盘)读取最多 100 个字节的数据,存储到缓冲区 buf 中。
  2. 输出读取的数据:
    • 输出读取到的数据内容。

在这里插入图片描述

1号文件

#include <stdio.h>        
#include <string.h>     
#include <unistd.h>       
#include <sys/types.h>   
#include <sys/stat.h>     
#include <fcntl.h>
int main()  // 主函数
{// 定义要写入文件的字符串const char* str = "hello linux\n";  // 这个字符串将被写入输出(标准输出)// 定义要写入的次数int cnt = 5;  // 设置写入的次数为 5// 循环写入文件 5 次while (cnt--)  // 当 cnt 不为 0 时,循环执行写入操作{    // 每次调用 write() 将字符串写入标准输出(文件描述符 1 对应标准输出)write(1, str, strlen(str));  // write() 的参数:1 表示标准输出,str 表示要写入的内容,strlen(str) 表示字符串长度} return 0;  // 程序执行完毕,返回 0 表示成功
}

代码功能概述:

  1. 定义字符串 str
  • 程序定义了一个常量字符串 "hello linux\n",它将被重复写入标准输出(通常是屏幕)。
  1. 定义写入次数 cnt
  • cnt 初始化为 5,表示要将字符串写入标准输出 5 次。
  1. 循环写入标准输出
  • 使用 write() 系统调用将 str 内容输出到标准输出(文件描述符 1)。
  • write(1, str, strlen(str)) 通过 write 系统调用将字符串的每次输出写入到标准输出。
  • 每次循环调用 write(),直到 cnt 达到 0。

output:

在这里插入图片描述


http://www.ppmy.cn/embedded/160545.html

相关文章

如何利用行为驱动开发(BDD)提升自动化测试的效率和准确性?

在上一篇文章中向大家介绍了行为驱动开发&#xff08;BDD&#xff09;如何提高自动化测试效率&#xff0c;在本文中我们将继续探讨一下如何利用行为驱动开发&#xff08;BDD&#xff09;提升自动化测试的效率和准确性&#xff1f; 行为驱动开发&#xff08;BDD&#xff09;通过…

了解AI绘图,Stable Diffusion的使用

AI绘图对GPU算力要求较高。 个人电脑配置可参考&#xff1a; CPU&#xff1a;14600kf 盒装 显卡&#xff1a;RTX 4080金属大师 OC&#xff0c;16G显存 主板&#xff1a;z790吹雪d4 内存&#xff1a;芝奇皇家戟4000c18,162G 硬盘&#xff1a;宏基gm7000 1T 散热&#xff1a;追风…

大数据相关职位介绍之三(数据挖掘,数据安全 ,数据合规师,首席数据官,数据科学家 )

大数据相关职位介绍之三&#xff08;数据挖掘&#xff0c;数据安全 &#xff0c;数据合规师&#xff0c;首席数据官&#xff0c;数据科学家 &#xff09; 文章目录 大数据相关职位介绍之三&#xff08;数据挖掘&#xff0c;数据安全 &#xff0c;数据合规师&#xff0c;首席数据…

HTML01-知云接力

HTML01-知云接力 后端人员做前端样式的调试&#xff0c;真的兴趣不了一点&#xff01;在此记录一些前端样式的个人小demo 知云接力&#xff08;云上风暴&#xff09;-起初我想做一个小游戏类型的项目&#xff0c;云朵上展示我每天学习的内容&#xff0c;这个知识点每天都会掉落…

OpenEuler学习笔记(二十):搭建私有AI服务

在OpenEuler上搭建私有AI服务涉及多个步骤&#xff0c;包括环境准备、依赖安装、模型部署等。 1. 环境准备 首先&#xff0c;确保你的OpenEuler系统已经安装并更新到最新版本。 sudo dnf update -y2. 安装必要的软件包 安装一些基础工具和依赖项&#xff1a; sudo dnf ins…

Linux Windows macOS如何安装Ollama

安装Ollama 安装Ollama的步骤相对简单&#xff0c;以下是基本的安装指南&#xff1a; 访问官方网站&#xff1a;打开浏览器&#xff0c;访问Ollama的官方网站。 下载安装包&#xff1a;根据你的操作系统&#xff0c;选择相应的安装包进行下载。 运行安装程序&#xff1a;下载完…

【25考研】南开大学计算机复试攻略及注意事项

一、复试内容 复试为差额复试&#xff0c;各专业分别按录取成绩由高到低进行录取。复试成绩低于60分(不含60分)&#xff0c;确定为复试不合格&#xff0c;复试不合格的考生不予录取&#xff0c;不再进行录取成绩的加权计算。 复试分为C/C编程能力测试、专业综合基础测试、面试…

Maven 插件与目标(Goals)

Maven 插件是 Maven 生态的重要组成部分&#xff0c;它们提供了扩展功能&#xff0c;使得 Maven 不仅能进行构建管理&#xff0c;还能执行编译、打包、测试、文档生成、部署等多种任务。 在 Maven 中&#xff0c;每个插件都由多个 目标&#xff08;Goal&#xff09; 组成。目标…