[Linux]文件/文件描述符fd

2024-05-26 19:04
文章标签 linux fd 描述符

本文主要是介绍[Linux]文件/文件描述符fd,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、关于文件

文件=内容+属性

  • 那么所有对文件的操作,就是对内容/属性操作。
  • 内容是数据,属性也是数据,那么存储文件,就必须既存储内容数据,又存储属性数据。默认就是在磁盘中的文件。
  • 当进程访问一个文件时,都需要先把文件打开。对于普通的磁盘文件,“打开”就是将文件加载到内存。
  • 一个进程可以通过操作系统(操作系统提供系统调用接口)打开多个文件,多个进程可以通过操作系统打开多个文件。加载到内存中被打开的文件可能会存在多个。(进程:打开的文件 = 1 : n
  • 加载磁盘上的文件,一定会涉及访问磁盘设备,是由操作系统来做的。

操作系统在运行时,可能会打开很多文件,那么,操作系统该如何对文件进行管理呢?

一个文件要被打开,一定要在内核中先形成被打开的文件对象,例如:

struct file
{//文件的属性struct file* next;...
}

文件的属性与文件内容一样,在磁盘中同样有保存。

我们使用链表对这些对象进行管理。那么,对于文件的管理,就转换成了对链表的增删查改。

被打开的文件在内存中,未被打开的文件在磁盘中。 所有的文件操作,都是在内存中操作的。

因此,研究文件操作的本质就是研究进程和被打开文件的关系。

二、常见文件接口(C语言)

1、fopen

我们可以在Linux中使用man来查看fopen的手册(man fopen):

打开的模式:

以“w”方式打开为例:

当文件 以“w”方式打开时,若该文件不存在,则自动创建文件。

打开文件时,会先清空文件内容。

#include<stdio.h>//以"w"打开,若文件不存在则自动创建该文件
int main()
{FILE* fp = fopen("log.txt","w");if(NULL == fp){perror("fopen");return 1;}const char* s = "hello world\n";int i = 0;for(i = 0; i < 10; i++){fputs(s,fp);}fclose(fp);return 0;
}

在执行该程序之前,当前目录下只有两个文件。 

make会产生一个myfile.c的可执行文件myfile。

执行该程序后,当前目录下出现了一个新文件:log.txt。

我们可以看到此时该log.txt文件中,就写入了我们程序中设定的内容。

我们对该C程序进行修改:

#include<stdio.h>//以"w"打开,若文件不存在则自动创建该文件
int main()
{FILE* fp = fopen("log.txt","w");if(NULL == fp){perror("fopen");return 1;}fputs("aaa\n",fp);fclose(fp);return 0;
}

重复执行上述内容(注意:我们的log.txt文件并未删除,仍旧存在),我们再次查看log.txt中的内容:

可以看出,之前写入的内容已经被清空。

注:我们使用“> log.txt”也可以实现对该文件进行清空。“>”是重定向,向文件写入。要向文件写入,就一定要打开文件。此时文件内容就会被清空。

 

以"a"方式打开为例:

append:追加。

“a”方式也是写入,只不过是从文件结尾进行写入。

我们将myfile.c文件进行修改:

#include<stdio.h>int main()
{FILE* fp = fopen("log.txt","a");if(NULL == fp){perror("fopen");return 1;}fputs("bbbbbbbbb\n",fp);fclose(fp);return 0;
}

运行该程序,我们可以看出,原先log.txt文件中的内容并未变化,我们是在此基础上追加了内容。

我们可以不断执行,不断追加。

 注:我们使用“echo “cc” >> log.txt”也可以实现追加。

2、

三、系统调用接口

由于打开文件的操作只能由操作系统来完成,所以我们C语言中打开文件的接口,底层一定是封装了系统调用接口的。

1、open

第一个参数是文件名称(不带路径默认当前路径下)。

第二个参数是flags:

第三个参数表示我们要以什么权限来创建新文件。

返回值:

 file descriptor文件描述符:fd

模拟fopen("filename","w"):

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main()
{//umask(0);//权限掩码改为0 //O_TRUNC:将打开的文件的长度清零(清空)int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd < 0){perror("open");return 1;}const char* s = "abc\n";write(fd, s, strlen(s));close(fd);return 0;
}

模拟fopen("filename","a")  :

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main()
{//O_APPEND:在文件末尾进行写入(追加)int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);if(fd < 0){perror("open");return 1;}const char* s = "abc\n";write(fd, s, strlen(s));close(fd);return 0;
}

四、fd:文件描述符

 返回值类型为int,那么fd究竟是什么?

我们可以输出fd观察:

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main()
{int fd1 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);int fd2 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);int fd3 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);int fd4 = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);printf("fd1:%d\nfd2:%d\nfd3:%d\nfd4:%d\n",fd1,fd2,fd3,fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
}

可以看到,fd是连续的整数。 

当进程打开文件时,操作系统会给每一个文件创建文件对象(被打开文件的描述结构体对象),并对它们使用链表进行维护。

操作系统需要对进程(task_struct)和打开的文件(struct file)之间的对应关系进行维护,就产生了struct files_struct。而task_struct中包含:struct files_struct* files,该指针就指向struct files_struct

struct files_struct这个结构体中,包含了一个数组:struct file* fd_array[]。该数组中,存放struct file的地址。

因此,当进程打开文件时,操作系统会创建struct file对象,并在该数组:struct file* fd_array[]中查询可使用的下标,将struct file对象的地址填入到该下标的位置。然后将该数组的下标返回,我们将该数字称为文件描述符。我们将struct file* fd_array[]称为:进程文件描述符表。

因此,文件描述符fd本质就是数组的下标。

那么,为什么我们显示打印的fd是从3开始的呢?

这是因为进程在运行的时候,默认打开:

标准输入:键盘                stdin                   0

标准输出:显示器             stdout                1

标准错误:显示器             stderr                 2

 stdin/stdout/stderr都是FILE*类型。FILE是一个结构体,其中封装了文件描述符。

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>int main()
{printf("stdin->fd:%d\n",stdin->_fileno);printf("stdout->fd:%d\n",stdout->_fileno);printf("stderr->fd:%d\n",stderr->_fileno);return 0;
}

 那么,我们为什么要把stdin/stdout/stderr打开呢?

这是为了能够让我们默认进行输入输出代码编写。

这篇关于[Linux]文件/文件描述符fd的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1005320

相关文章

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服