Linux 文件操作

这篇具有很好参考价值的文章主要介绍了Linux 文件操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文件是在磁盘上创建出来的,当我们想进行文件操作时,根据冯诺依曼体系结构,CPU 只和内存交互,为了可以进行文件操作,应将磁盘中的文件先加载到内存中,加载什么呢?至少要加载文件的某些属性

一个用户可能同时会操作多个文件,并且存在多个用户,因此在某一时间内会存在大量的内存文件,为了管理这些内存文件,需要进行先描述,在组织,操作系统为这些内存文件创建了结构体 file,file 中的属性便从磁盘中加载而来,结构体 file 中还可以存在 struct file* 属性,于是便可以将这些 file 结构体链接起来组成某种数据结构,这样对内存文件的管理,也就转换成了对存储数据类型为结构体 file 的某种数据结构的增删查改

进行文件操作时,用户不是自己操作,而是通过进程对文件操作,因此文件操作的本质其实就是进程的 task_struct 对 文件结构体 file 的操作

一、task_struct 和 file 的关系

Linux 文件操作

task_struct 和 file 的关系中采用了 files_struct 作为中间结构,file_struct 中包含了一个指针数组 fd_array,数组元素存储的是进程加载到内存文件的 file 地址

进程在加载一个文件时,首先操作系统会创建文件结构体 file(如果 file 还未加载到内存),然后会在进程指向的 file_struct 中的 fd_array 中 找到一个下标最小的且没有被使用的位置填充该 file 的地址,进程加载文件完成后,操作系统为了能让进程还可以找到该 file,操作系统给进程提供是 file 地址在 fd_array 中的下标,这个下标我们称之为文件描述符,fd_array 也就称作文件描述符表

二、文件操作的系统调用

  • 打开文件:在进程的文件描述符表中填充对应的 file 地址

系统调用 open,头文件 sys/types.h、sys/stat.h、fcntl.h

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

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

参数:

  • pathname:文件路径 + 文件名 + 文件后缀
  • flags:位图,传递多个标志位时,标志位之间用 | 连接
  • mode:新建文件的权限,受进程的 umask 影响

常用的标志位:

  • O_RDONLY:以读的方式打开文件,从文件开始处读取
  • O_WRONLY:以写的方式打开文件,从文件开始处写入,不会清空文件
  • O_APPEND:需要传递以写的方式打开文件,从文件末尾处写入
  • O_TRUNC:需要传递以写的方式打开文件,将文件清空
  • O_CREAT:如果文件不存在,则会创建文件,如果传递这个标志,最好也传递 mode 参数

系统调用 umask,头文件 sys/typse.h、sys/stat.h,设置当前进程的 umask 为 mask,该系统调用总是成功,返回设置之前的 umask

// mode_t 类型就是 unsigned int 的 typedef
mode_t umask(mode_t mask);

系统调用 close/ read / write,头文件 unistd.h

// 关闭文件:在进程的文件描述符表中删除对应的 file 地址
// 成功返回 0,失败返回 -1
int close(int fd);

// 向文件描述符 fd 对应的文件最多读取 count 个字节到 buf 中,文件偏移量会增加文件读取到的字节数
// 成功返回读取到的字节数,0 表示未读取到任何内容,表示已经读取到文件结尾,错误返回 -1
ssize_t read(int fd, void *buf, size_t count);

// 向文件描述符 fd 对应的文件最多写入 count 个 buf 中的字符
// 成功返回写入的字节数,0 表示未写入任何内容,错误返回 -1
ssize_t write(int fd, const void *buf, size_t count);
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    // O_WRONLY 从文件开始处写入,不会清空文件
    // O_APPEND 从文件末尾处写入 
    // O_RDWR   从文件开始处读取
    // int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
    // int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
    int fd = open("log.txt", O_RDONLY);
    if (fd == -1)
    {
        printf("open error : %s\n", strerror(errno));
        exit(1);    
    }

    // R
    char buf[128];
    while (1)
    {
        // int count = read(fd, buf, sizeof(buf) - 1);
        // if (count <= 0) break;

        // buf[count] = '\0';
        // printf("%s", buf);
        
        // 按行读取
        char ch = 1;
        int i = 0;
        for (; read(fd, &ch, 1) != 0 && ch != '\n'; ++i)
            buf[i] = ch; 

        if (i == 0) break;

        buf[i] = '\0';
        printf("%s\n", buf);
        sleep(1);
    }
    
    // W
    // char buf[128];
    // int cnt = 10;
    // while (cnt)
    // {
    //     snprintf(buf, sizeof(buf), "%s, %d\n", "hello world", cnt);
    //     ssize_t ret = write(fd, buf, strlen(buf));
    //     assert(ret != (ssize_t)-1);
    //     (void)ret;
    //     cnt--;
    // }

	// 关闭文件
    close(fd);

    return 0;
}

三、进程默认打开的三个文件

Linux 文件操作

进程默认打开的三个文件:

  • 0:标准输入,对应于键盘文件
  • 1:标准输出,对应于显示器文件
  • 2:标准错误,对应于显示器文件
#include <stdio.h>
#include <unistd.h>

int main()
{
    // 从键盘文件中读取
    while (1)
    {
        char buf[128];
        char ch;
        int i = 0;
        for (; read(0, &ch, 1) != 0 && ch != '\n'; ++i)
            buf[i] = ch;

        if (i == 0) break;

        buf[i] = '\0';
        printf("从键盘文件中按行读取 : %s\n", buf);
    }

    return 0;
}

Linux 文件操作

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	// 输出到显示器文件
    int cnt = 5;
    while (cnt)
    {
        char buf[128];
        snprintf(buf, sizeof(buf), "写入到 fd 1 : %s, %d\n", "hello world", cnt);
        write(1, buf, strlen(buf));
        
        snprintf(buf, sizeof(buf), "写入到 fd 2 : %s, %d\n", "hello world", cnt);
        write(2, buf, strlen(buf));

        cnt--;

        printf("\n");
    }
}

Linux 文件操作

在进程中打开的第一个文件,返回的文件描述符是 3

#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    // 打开文件
    int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
    assert(fd != -1);

    printf("fd : %d\n", fd);

    // 关闭文件
    close(fd);

    return 0;
}

Linux 文件操作

四、文件重定向

进程打开新文件时,返回的文件描述符为:文件描述符表中最小并且没有被使用的下标

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd1 = open("file.txt", O_WRONLY | O_CREAT, 0666);
    int fd2 = open("file.txt", O_WRONLY | O_CREAT, 0666);
    int fd3 = open("file.txt", O_WRONLY | O_CREAT, 0666);
    int fd4 = open("file.txt", O_WRONLY | O_CREAT, 0666);
    int fd5 = open("file.txt", O_WRONLY | O_CREAT, 0666);

    printf("%d %d %d %d %d\n", fd1, fd2, fd3, fd4, fd5);

    close(fd3);

    int newFd = open("file.txt", O_WRONLY | O_CREAT, 0666);

    printf("%d\n", newFd);

    close(fd1);
    close(fd2);
    close(fd4);
    close(fd5);
    close(newFd);

	return 0;
}

Linux 文件操作

重定向的原理:更改进程的文件描述符表中特定下标指向的文件

Linux 文件操作

实现输出重定向:正常信息输出到 log.normal,异常信息输出到 log.error

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    // 关闭标准输出,打开 log.normal 文件, 此时 log.normal 的文件描述符 为 1
    close(1);
    int normalfd = open("log.normal", O_WRONLY | O_CREAT, 0666);

    // 关闭标准错误,打开 log.error 文件,此时 log.error 的文件描述符为 2 
    close(2);
    int errorfd = open("log.error", O_WRONLY | O_CREAT, 0666);
    
    // 写入到标准输出,即写入到 1 号文件描述符,即写入到 log.normal 文件
    printf("log.normal fd : %d\n", normalfd);
    printf("log.error  fd : %d\n", errorfd);

    // 写入到标准错误,即写入到 2 号文件描述符,即写入到 log.error 文件
    // 以读方式打开一个不存在的文件,模拟错误信息
    int fd = open("abc.txt", O_RDONLY);
    if (fd == -1) perror("open");

	return 0;
}

Linux 文件操作

系统调用 dup2,头文件 unistd.h

// 将 oldfd 拷贝给 newfd,必要时先关闭 newfd
// 即 fd_arrray[newfd] = fd_array[oldfd]
// 成功返回新的文件描述符,失败返回 -1
int dup2(int oldfd, int newfd);

Linux 文件操作

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);

    // 重定向 fd_array[1] = fd_array[fd]
    int ret_fd = dup2(fd, 1);

    char buf[128];
    snprintf(buf, sizeof(buf), "dup2 返回值 : %d\n", ret_fd);
    write(1, buf, strlen(buf));
    write(fd, buf, strlen(buf));

    close(fd);
    
    return 0;
}

Linux 文件操作

五、Linux 下一切皆文件

通过 file 结构体用户可以像文件一样看待 Linux 中的硬件
Linux 文件操作

六、缓冲区

在调用向文件写入数据的库函数时,数据并不是立刻写入到文件,而是先写到语言提供的缓冲区中

#include <stdio.h>
#include <unistd.h>

int main()
{
    // 写入到显示器文件
    for (int i = 0; i < 3; ++i)
    {
        printf("you can see me? ");
        sleep(1);
    }
    printf("\n");

    return 0;
}

程序并不会一秒一秒的显示 you can see me?,而是 3 秒后全部显示
Linux 文件操作

缓冲区的作用是为了防止频繁的调用系统调用而花费太多时间,最好可以尽量少的调用系统调用就可以将所有数据写入到 file 的缓冲区中
Linux 文件操作

对于不同的文件,语言的缓冲区采用的刷新策略有三种:

  • 无缓冲:不提供缓冲区,直接调用系统调用写到 file 的缓冲区
  • 行缓冲:遇到 \n 时,调用系统调用写到 file 的缓冲区
  • 全缓冲:缓冲区写满时,调用系统调用写到 file 的缓冲区

语言中对于显示器文件采用的是行缓冲,普通文件采用的是全缓冲,而 file 的缓冲区的刷新策略是由操作系统自主决定的

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
    // C语言库函数
    fprintf(stdout, "hello fprintf\n");
    
    // 系统调用
    const char* msg = "hello write\n";
    write(1, msg, strlen(msg));

    // 创建子进程
    fork();

    return 0;
}

重定向前后输出的数据不一样
Linux 文件操作

  1. 输出重定向前,fprintf 写入到显示器文件中,此时采用的是行缓冲,因此在 fork 之前,缓冲区的数据已经被刷新到 file 的缓冲区了

  2. 输出重定向后,fprintf 写入到普通文件中,此时采用的是全缓冲,因此在 fork 之前,缓冲区的数据并没有刷新,于是 fork 之后,父子进程结束时都会刷新缓冲区也就造成了这种现象

  3. write 直接写入到 file 的缓冲区中,因此 fork 和 输出重定向 对 write 不受影响文章来源地址https://www.toymoban.com/news/detail-506245.html

到了这里,关于Linux 文件操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • lvm + raid(逻辑磁盘+阵列)创建删除恢复 for linux

    本教程适用于linux lvm为逻辑磁盘,raid为阵列,两种技术可以单独使用也可以搭配使用 2023.9.3更新 前三节是操作命令和基础知识,后面是实操。 硬盘分区相关操作在后面用的到,可以先略过,有需要了再回来查 1.查看所有存储硬件详细信息 2.查看文件系统 3.查看分区对应硬盘

    2024年02月09日
    浏览(45)
  • 【玩转Linux操作】详细讲解 Linux分区&&磁盘 操作以及相关的命令

    🎊专栏【玩转Linux操作】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题🥰 在Linux中,分区是将硬盘或其他存储设备划分为逻辑部分的过程。每个分区都被视为一个独立的存储空间,可以用于存储文件系统、

    2024年02月16日
    浏览(49)
  • 【Shell 命令集合 磁盘管理 】Linux 创建目录 mkdir 命令使用指南

    Shell 命令专栏:Linux Shell 命令全解析 mkdir命令是Linux系统中的一个用于创建目录的命令。它的作用是在指定的路径下创建一个新的目录。 使用mkdir命令可以方便地创建一个空的目录,该目录可以用于存储文件或其他目录。通过指定路径参数,可以在当前工作目录或其他指定目

    2024年02月08日
    浏览(61)
  • Linux进阶篇:磁盘管理(二):LVM的创建、格式化和使用

    Linux磁盘管理(二):LVM的创建、格式化和使用 LVM的工作原理进行一个总结: (1)物理磁盘被格式化为PV,空间被划分为一个个的PE (2)不同的PV加入到同一个VG中,不同PV的PE全部进入到了VG的PE池内 (3)LV基于PE创建,大小为PE的整数倍,组成LV的PE可能来自不同的物理磁盘 (4)LV现在

    2024年04月09日
    浏览(50)
  • Ubuntu磁盘和目录和文件的相关操作

    目录 0、常见操作 1、目录的切换 2、查看目录及文件 3、目录的常见操作 4、文件的常见操作 0、常见操作 关机命令 重启命令 清除终端屏幕上的内容 显示最近执行的命令历史记录 Ctrl + C :中断当前运行的命令 Ctrl + D :退出当前终端会话 Ctrl + Alt + T :打开新终端窗口。 Ctrl

    2024年02月04日
    浏览(33)
  • Linux磁盘操作:分区、格式化、挂载

    fdisk分区 (1)fdisk命令只支持msdos,分区的时候只支持小容量硬盘(=2T),但是如果不需要分区的话,那么整块sdb硬盘,类型为msdos,那么他的大小是可以大于2T的。 (2)fdisk命令不支持gpt,所以当使用fdisk命令给gpt类型硬盘分区是会出现告警 首先先把设备关机,添加一块新的

    2024年02月11日
    浏览(48)
  • 【Linux操作系统】深入理解Linux磁盘分区和挂载

    Linux磁盘分区和挂载是系统管理中非常重要的一部分,它们可以帮助我们更好地管理存储空间和文件系统。本文将详细介绍Linux磁盘分区和挂载的概念、原理以及实践操作,并提供相应的例子、代码和指令,帮助读者全面了解和掌握这两个关键概念。 磁盘分区是将物理硬盘划

    2024年02月14日
    浏览(45)
  • linux空磁盘挂载到指定目录操作步骤

    fdisk -l  或  lsblk 如图所示:/dev/sdb磁盘还未分区 执行fdisk /dev/sdb 按照下面步骤依次输入指令 mkfs.xfs /dev/sdb1 blkid mount 磁盘分区 目标目录 mount /dev/sdb1 /ynat/ 注:此挂载方式为临时挂载,重启服务器后,硬盘挂载消失; 首先通过blkid命令将分区的uuid查询出来并复制uuid(往/etc/fs

    2024年03月09日
    浏览(94)
  • linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘

    linux-centos7操作系统查看系统未挂载的磁盘,挂载磁盘 查看当前磁盘空间 根目录 / 下也只有44G,其他目录只有10几G,正式环境肯定不够用 查看硬盘数量和分区情况 查看到/dev/vdb 有500多G了 将/dev/vdb在分出一个区使用 第一步:编辑分区。执行命令fdisk /dev/vdb:该命令意思是为

    2024年02月14日
    浏览(48)
  • Linux文件系列:磁盘,文件系统,软硬链接

    我们之前所学的都是被进程打开了的文件,接下来我们要学习没有被进程所打开的文件,它们是存储在磁盘当中的 要学习这些文件,首先我们要先学习一下磁盘 1.LBA地址 我们知道磁带在展开之后呈现一种带状结构,磁带中的数据就是以这种线性的方式进行存储的 那么我们可不可以

    2024年03月27日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包