首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-15 23:02
  • 3128
  • 12842
blog.csdn.net

个人主页:chian-ocean

文章专栏-Linux

前言:

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

在这里插入图片描述

C语言文件操作

C语言文件

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

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

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

#include        
#include     
          
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,表示程序成功执行
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

理解当前工作路径:

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

在这里插入图片描述

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

就会更改当前的工作路径,如图查看到的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);
  • 1

为什么会用 |呢

#include 

#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 2
    show(ONE | THREE);      // 传递 ONE | THREE,即 flags = 0001 | 0100 = 0101 -> func 1 和 func 3
    show(ONE | TWO | THREE); // 传递 ONE | TWO | THREE,即 flags = 0001 | 0010 | 0100 = 0111 -> func 1, func 2 和 func 3
    return 0;   // 程序正常结束
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
代码解释:
  1. 宏定义:使用 #define 来定义常量 ONE、TWO、THREE 和 FOUR,它们分别表示不同的二进制位:
    • 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):将 ONE 和 TWO 进行按位“或”运算,得到 0011,所以会显示 "func 1" 和 "func 2"。
    • show(ONE | THREE):将 ONE 和 THREE 进行按位“或”运算,得到 0101,所以会显示 "func 1" 和 "func 3"。
    • show(ONE | TWO | THREE):将 ONE、TWO 和 THREE 进行按位“或”运算,得到 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         // 引入标准输入输出库,用于printf和perror等
#include        // 引入字符串处理库,用于处理字符串(如strlen)
#include        // 引入POSIX标准库,用于访问系统级函数(如write和close)
#include     // 引入定义文件操作需要的类型
#include      // 引入文件状态相关定义
#include         // 引入文件控制函数,如open

int 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

代码解释:

  1. open() 函数:使用 O_CREAT、O_TRUNC 和 O_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   // 引入输入输出流库,用于标准输入输出操作
#include   // 引入字符串操作库(如 strlen、memcpy 等)
#include   // 引入 UNIX 标准头文件,包含对系统调用(如 read、write 等)的定义
#include   // 引入定义系统数据类型的库,如 pid_t、off_t 等
#include   // 引入文件状态信息定义的库(如文件权限、文件类型等)
#include   // 引入文件控制操作库,包含文件操作的标志,如 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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

代码功能概述:

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

在这里插入图片描述

1号文件

#include         
#include      
#include        
#include    
#include      
#include 
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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

代码功能概述:

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

output:

在这里插入图片描述

注:本文转载自blog.csdn.net的chian-ocean的文章"https://blog.csdn.net/Cayyyy/article/details/145501038"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

120
运维
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top