【Linux系统编程】文件系统

进程与文件

        当我们对文件进行操作时,文件必须要被加载到内存中,然后CUP从内存中拿到此文件进行操作,没有打开的文件放在磁盘中存储。 

        文件的打开其实也是设计到内部某个进程。无论是系统调用,还是专有库中的函数,都是启动进程来进行打开。进程会自动记录目前启动时的当前路径,平常所说的相对路径就是指相对于当前进程路径下的路径。当我们没有特意说明文件路径在此进程中对文件操作时,默认会在此进程的路径下进行。比如我们使用C语言新建文件使用绝对路径,默认就会在此进程的路径下进行,若是此进程的路径发生改变,新建的文件会在改变后的路径下进行,这就是相对路径的原理。

[zhu@VM-16-10-centos day3]$ ll ..
.........
[zhu@VM-16-10-centos day3]$ cat myfile.cpp
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
using namespace std;

int main()
{
    chdir("..");
    FILE* fp = fopen("test.txt", "w");
    if (!fp)
    {
        perror("fopen");
        exit(1);
    }
    char* str = "file system";
    int n = fwrite(str, strlen(str), 1, fp);
    cout << "write block: " << n << "  " << "pid = " << getpid() << endl; 
    fclose(fp);
    return 0;
}

 

[zhu@VM-16-10-centos day3]$ ./myfile
write block: 1  pid = 12291
[zhu@VM-16-10-centos day3]$ ll ..
...........
-rw-rw-r-- 1 zhu zhu   11 Mar 21 10:52 test.txt    //创建的文件

        总的来说是进程在打开文件。我们对文件的操作都是由系统下的某个进程进行的。

Linux下的文件系统

        在Linux系统下,我们可以把一切都看成文件(包括硬件)。Linux系统有一个重要特性,即“一切皆文件”的原则。无论是普通的磁盘文件、目录,还是网络套接字、硬件设备,在Linux中都被抽象为文件。

        我们平常使用高级语言进行底层文件的调用,本质上是封装了系统调用。因为用户不能直接调用系统硬件,本质上是操作系统进行调用的。我们通常使用语言进行调用实际就是操作系统提供了相应的接口供用户使用。比如以C语言为例,C语言的库函数接口fopen、fclose、fread、fwrite 在某种意义上来讲调用的是系统接口open、close、read、write(这些函数运用跟C中的文件操作相似,不懂的可用man指令查看文档,这里不再过多解释),只不过对系统调用进行了封装。系统调用接口和库函数的关系如下:

        下面我们来认识一下文件流操作。程序在启动时,默认会打开三个文件流:stdin、stdout、stderr。这三种流的类型都是文件指针FILE*。

        stdin:标准输入——默认是键盘设备。计算机系统从此文件流中获取数据信息,即从此文件中读取数据。

        stdout:标准输出——默认是显示器设备。将数据输出到此文件流中,即从此文件中输出数据。

        stderr:标准错误——默认是显示器设备。用于输出程序或命令的错误信息,与stdout原理相似。

        正因有了标准输入输出流操作(I/O设备操作),才能使得程序能够与用户和其他程序进行有效的交互。

        那么问题来了,系统下的所有都是文件,程序系统又是如何找到对应的文件?其实每个文件都有一个对应的文件描述符进行标志。文件描述符是一个非负整数,与文件名形成了一种索引关系,使得程序可以通过这个整数来访问和操作对应的文件。

        文件描述符的范围是0到N,其中0、1、2是特殊文件的文件描述符:0代表标准输入(stdin),1代表标准输出(stdout),2代表标准错误输出(stderr)。一般情况下,文件描述符从3开始数往后分配。因为内部的文件描述符其实就是存放管理文件结构体(struct file)的指针数组 fd_array 的下标,此指针数组每个元素都是一个指向打开文件的结构体指针,而task_struct内部指向存放此指针数组的结构体(struct files_struct)。总的来说文件描述符就是数组的下标,当使用一个文件时就必须找到此文件的文件描述符,通过文件描述符来找到对应的文件。

        C标准库中的FILE其实就是自己封装的一个结构体,里面封装了 stdin、stdout、stderr 的文件描述符0,1,2。之所以系统不直接封装而让语言单独封装是为了保证可移植性。若是系统直接封装,一旦换了平台系统可能就会出问题。导致不可移植。其实不仅仅是流操作,很多有关系统接口也一样,为了保证可移植性,都是在不同语言内部封装不同系统调用的接口和相关的文件接口。

文件描述符及重定向

        从上可看出,每个文件都有唯一的文件描述符。每个文件的文件描述符的分配规则是最小的没有被使用的数组下标会分配给最新打开的文件。我们先来看以下代码。   

[zhu@VM-16-10-centos day3]$ cat open.cpp

#include <iostream>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
int main()
{
    close(1);
    int fd = open("myfile", O_WRONLY | O_CREAT | O_TRUNC, 0666);  //打开文件,成功后返回文件的文件描述符,失败返回-1
    cout << "fd = " << fd << endl;
    close(fd);
    return 0;
}
[zhu@VM-16-10-centos day3]$ ./open  //发现在标准输出流中什么也没有输出
[zhu@VM-16-10-centos day3]$ ll
total 32
-rw-rw-r-- 1 zhu zhu    7 Mar 21 20:31 myfile
............
[zhu@VM-16-10-centos day3]$ cat myfile   //发现输出到文件log.txt中
fd = 1

         注意,文件描述符会按照最小下标分配,以上程序中关掉了标准输出流的文件描述符1,当我们创建文件log.txt时会给此文件分配描述符1。我们平常输出的数据都是往标准输出流stdout中输出的,也就是系统会自动往文件描述符为1的文件中输出。这里log.txt的文件描述符为1,所以就会出现以上输出重定向。输入重定向同理,将文件描述符为0的进行覆盖。

        重定向运用的就是以上的原理——系统将有关文件的文件描述符进行修改为指定的文件描述符或指针数组元素的重新指向。以下是输入重定向的原理示例。

[zhu@VM-16-10-centos day3]$ cat log.txt
123456
[zhu@VM-16-10-centos day3]$ cat myfile.cpp
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    close(0);   //将标准输入重定向(键盘)去除
    open("log.txt", O_RDONLY);   //此时文件log.txt的文件描述符为0,即成为了输入流
    int a = 0;
    cin >> a;  //从输入流中读取数据
    cout << a << endl;  
    return 0;
}
[zhu@VM-16-10-centos day3]$ ./myfile
123456

        不难发现,以上类似的程序完成重定向功能比较麻烦。说白了,重定向功能就是分配到指定的文件描述符,而文件描述符对应指定文件的功能,这里我们可直接让指定文件的文件描述符指向对应功能的文件描述符所指向文件的功能即可。比如将文件描述符为3的指向文件描述符为1所对应的系统文件,即指针数组元素之间的浅拷贝,fd_array[1]=fd_array[3]。

        系统提供了相应接口函数dup来完成这种浅拷贝。

头文件

#include <unistd.h>

接口形式

int dup2(int oldfd, int newfd); 

将所对应的oldfd拷贝到newfd,即fd_array[newfd]=fd_array[oldfd],此时文件描述符newfd所对应的文件结构将被覆盖。

dup2用法

[zhu@VM-16-10-centos day3]$ cat myfile.cpp
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;

int main()
{
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd, 1);
    cout << "fd = " << fd << endl;
    return 0;
}

[zhu@VM-16-10-centos day3]$ ./myfile
[zhu@VM-16-10-centos day3]$ cat log.txt
fd = 3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/479926.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软考 网络工程师 每日学习打卡 2024/3/22

学习内容 第9章 网络操作系统与应用服务器 本章主要讲解&#xff1a;了Windows和Linux操作系统的基础知识&#xff0c;并详细讲述了常用的各种服务器的 配置方法。这一章的内容主要是在具体操作方面&#xff0c;网络工程师要能够熟练地配置各种网络服务 器&#xff0c;排除网络…

Linux内核编译与安装

Linux内核介绍 Linux内核是一个用C语言写成的&#xff0c;符合POSIX标准的类Unix操作系统。内核是操作系统中最基本的一部分&#xff0c;提供了众多应用程序访问计算机硬件的机制。Linux内核的一大特点就是采用了整体式结构&#xff0c;有很多过程组成&#xff0c;每个过程都可…

hadoop namenode 查看日志里面报错8485无法连接

一、通过日志排查问题&#xff1a; 1、首先我通过jpsall命令查看我的进程&#xff0c;发现namenode都没有开启 2、找到问题后首先进入我的日志目录里查看namenode.log [rootnode01 ~]# /opt/yjx/hadoop-3.3.4/logs/ [rootnode01 ~]# ll [rootnode01 ~]# cat hadoop-root-nam…

手拉手Java爬虫HttpClient

JAVA爬虫 HttpClient HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新的版本和建议。 使用 HttpClient 的 6 个步骤 1. 创建 HttpClient 的实例 2…

环信新版单群聊UIKit集成指南——Android篇

前言 环信新版UIKit已重磅发布&#xff01;目前包含单群聊UIKit、聊天室ChatroomUIKit&#xff0c;本文详细讲解Android端单群聊UIKit的集成教程。 环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开发的一款即时通讯 UI 组件库&#xff0c;提供各种组件实现会话列表、聊天界…

【国家计算机二级C语言】高分笔记

二叉树 参考 http://t.csdnimg.cn/ozVwT 数据库 SQL程序语言有四种类型&#xff0c;对数据库的基本操作都属于这四类&#xff0c;它们分别为&#xff1b;数据定义语言(DDL)、数据查询语言&#xff08;DQL&#xff09;、数据操纵语言&#xff08;DML&#xff09;、数据控制语言…

Day17:LeedCode 110.平衡二叉树 257.二叉树的所有路径 404.左叶子之和

110. 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 平衡二叉树:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 思路: 二叉树节点的深度&#xff1a;指从根节点到该节点的最长简单路径边的条数。二叉树节点的高度&#xff1a;指从该节点到叶…

深度探索:在 Postman 中实现自动化测试的全面指南!

在当今的软件开发过程中&#xff0c;API&#xff08;应用程序编程接口&#xff09;的使用变得越来越普遍&#xff0c;API 允许不同系统之间进行通信和数据交换&#xff0c;从而实现复杂的功能和服务集成&#xff0c;为了确保 API 的可靠性和稳定性&#xff0c;自动化测试至关重…

如何利用RunnerGo简化性能测试流程

在软件开发过程中&#xff0c;测试是一个重要的环节&#xff0c;需要投入大量时间和精力来确保应用程序或网站的质量和稳定性。但是&#xff0c;随着应用程序变得更加复杂和庞大&#xff0c;传统的测试工具在面对比较繁琐的项目时非常费时费力。这时&#xff0c;一些自动化测试…

量子计算+运营优化!IonQ 和 德国DESY 合作提升机场登机口调度效率

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨 沛贤 深度好文&#xff1a;1200字丨8分钟阅读 3月14日&#xff0c;量子计算公司IonQ宣布了与德国电子同步加速器&#xff08;DESY&#xff0c;德国的大型粒子物理学研…

出现nginx error 问题

报错&#xff1a; Something has triggered an error on your website. This is the default error page for nginx that is distributed with Fedora. It is located /usr/share/nginx/html/50x.html You should customize this error page for your own site or edit the er…

PLC网关在工业自动化领域的作用及如何选择-天拓四方

一、PLC网关在工业自动化领域的重要性和作用 PLC网关在工业自动化领域的重要性和作用不言而喻。作为工业自动化系统的重要组成部分&#xff0c;PLC网关起到了关键的桥梁作用&#xff0c;实现了PLC与其他设备、系统之间的数据传输和通信。 首先&#xff0c;PLC网关的重要性体现…

nodeJs 学习

常用快捷键 二、fs模块 回调函数为空&#xff0c;则表示写入成功&#xff01; 练习 const fs require(fs); fs.readFile(../files/成绩.txt, utf-8, (err, dataStr) > {if (err) {console.log(读取失败);return err;}console.log(读取成功);const arr dataStr.split( )co…

SpringBoot整合WebService

WebService是一个SOA&#xff08;面向服务的编程&#xff09;的架构&#xff0c;它是不依赖于语言&#xff0c;不依赖于平台&#xff0c;可以实现不同的语言间的相互调用&#xff0c;通过Internet进行基于Http协议的网络应用间的交互。 其实WebService并不是什么神秘的东西&…

MISC:常见编码

一、字符编码 1.ASCII码 使用指定7位或8位二进制数组合表示128-256种可能。 常⻅考点&#xff1a;解题过程中给出十进制或十六进制的连续数值。 进制转换工具&#xff1a; ASCII text,Hex,Binary,Decimal,Base64 converter (rapidtables.com) 2.Base64编码 ASCII编码以8个比特…

鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)

Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量…

马斯克的 Grok-1 开源,3140亿参数目前最大开源模型,最佳实践教程来啦

近几天开源社区最大的热点&#xff0c;莫过于埃隆马斯克信守承诺的最大开源模型Grok-1。 Grok-1 是一款 314B 大型专家混合 (Mixture of Expert&#xff0c;MoE) Transformer&#xff0c;作为基础模型&#xff0c;基于大量文本数据进行训练&#xff0c;没有针对任何具体任务进…

计算机二级Python题目3

题目来源&#xff1a;计算机二级Python半个月抱佛脚大法&#xff08;内呈上真题版&#xff09; - 知乎 目录 1. 基础题 1.1 基础题1 1.2 基础题2 1.3 基础题3 2. turtle绘图题 3. 大题 3.1 大题1 3.2 大题2 1. 基础题 1.1 基础题1 a,b,ceval(input()) ls[] for i in …

Base系列

1.计数系统 base,这个词在数学中表示基数&#xff0c;即计数系统中用于表示数字的不同符号的数量。 例&#xff1a; 二进制计数系统中只有两个符号表示数字&#xff0c;即0和1&#xff0c;故二进制系统可以用Base2表示。 十进制计数系统中仅使用十个符号表示数字&#xff0c;即…

二、阅读器的开发(初始)-- 2、阅读器开发

1、epubjs核心工作原理 1.1 epubjs的核心工作原理解析 epub电子书&#xff0c;会通过epubjs去实例化一个Book对象&#xff0c;Book对象会对电子书进行解析。Book对象可以通过renderTo方法去生成一个Rendition对象&#xff0c;Rendition主要负责电子书的渲染&#xff0c;通过R…