欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Linux理解文件fd

Linux理解文件fd

2025/5/8 13:06:30 来源:https://blog.csdn.net/sqm_C/article/details/145839196  浏览:    关键词:Linux理解文件fd

先来段代码回顾C文件接口

myfile.c写文件

#include <stdio.h>int main()
{FILE *fp = fopen("log.txt","a");if(NULL == fp){perror("fopen");return 1;}fprintf(fp,"helloWorld,%d,%s,%lf\n",10,"lsf",3.14);fclose(fp);return 0;
}

输出信息到显示器,你有哪些方法

#include <stdio.h>
#include <string.h>int main()
{const char *msg = "hello fwrite\n";fwrite(msg, strlen(msg), 1, stdout);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;
}

stdin & stdout & stderr

  • C默认会打开三个输入输出流,分别是stdin, stdout, stderr
  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

总结

打开文件的方式

r Open text file for reading. The stream is positioned at the beginning of the file.r+ Open for reading and writing.The stream is positioned at the beginning of the file.w Truncate(缩短) file to zero length or create text file for writing.The stream is positioned at the beginning of the file.w+ Open for reading and writing.
The file is created if it does not exist, otherwise it is truncated.The stream is positioned at the beginning of the file.a Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file.a+ Open for reading and appending (writing at end of file).The file is created if it does not exist. The initial file positionfor reading is at the beginning of the file, but output is always appended to the end of the file.

如上,是我们之前博客学的文件相关操作。

系统文件I/O

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问, 先来直接以代码的形式,实现和上面一模一样的代码:

myfile.c文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{umask(0);int fd = open("myfile", O_WRONLY|O_CREAT, 0644);if(fd < 0){perror("open");return 1;}int count = 5;const char *msg = "hello linux!\n";int len = strlen(msg);while(count--){write(fd, msg, len);//fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数
据。 返回值:实际写了多少字节数据}close(fd);return 0;
}

myfile.c读文件

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main()
{int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}const char *msg = "hello linux!\n";char buf[1024];while(1){ssize_t s = read(fd, buf, strlen(msg));//类比writeif(s > 0){printf("%s", buf);}else{break;}}close(fd);return 0;
}

接口介绍

man 2 open

我将它们整理一下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读,写打开这三个常量,必须指定一个且只能指定一个O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限O_APPEND: 追加写O_TRUNC: 清空文件内容
返回值:成功:新打开的文件描述符失败:-1

mode_t理解:直接 man 手册,比什么都清楚。

open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

// system callumask(0);int fd  = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);//int fd  = open("log.txt",O_WRONLY | O_CREAT | O_APPEND,0666);if(fd < 0){perror("open");return 1;}const char* message = "abcdefg\n";write(fd,message,strlen(message));close(fd);return 0;

如图所示,我们的0666就是我们的文件权限。

write read close lseek ,类比C文件相关接口。

open函数返回值

在认识返回值之前,先来认识一下两个概念: 系统调用库函数

  • 上面的 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
  • 而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口
  • 回忆一下我们讲操作系统概念时,画的一张图

系统调用接口和库函数的关系,一目了然。

所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。

文件描述符fd

printf("stdin->fd: %d\n",stdin->_fileno);  printf("stdout->fd: %d\n",stdout->_fileno);  printf("stderr->fd: %d\n",stderr->_fileno);  FILE* fp = fopen("log.txt","w");if(fp ==NULL) return 1;printf("fd: %d\n",fp->_fileno);fwrite("hello",5,1,fp);fclose(fp);const char* message = "hello Linux file!\n";write(1,message,strlen(message));

通过对打印文件fd,我们知道了文件描述符就是一个小整数

0 & 1 & 2

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器 所以输入输出还可以采用如下方式:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>int main()
{char buf[1024];ssize_t s = read(0, buf, sizeof(buf));if(s > 0){buf[s] = 0;write(1, buf, strlen(buf));write(2, buf, strlen(buf));}return 0;
}

而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。

文件系统从软件到硬件

我们可以大致来分析一下文件系统从软件到硬件是一个什么样的流程(C语言到硬件):

首先,我们的C语言调用C标准库当中的文件操作函数,这个函数比如fopen在打开一个文件的时候FILE结构体里面必定包含有打开的这个文件fd,因为操作系统只认fd,我们的fopen里面实际上调用的也是系统接口的open函数,我们的open就会拿到fd,将fd传给当前进程(task_struct),我们的task_struct会拿着这个fd去它的file* fd_array[]数组里面去对照下标找到对应映射关系的文件结构体地址,我们找到我们的文件之后,操作系统会根据程序调用对应的方法列表里面的函数指针来进行对外设(这里是磁盘)的读写操作(这种在结构体里面使用函数指针的方法我们叫他为C语言版的类【类由我们的属性加方法,我们的结构体本身就有属性,这是我们使用函数指针就相当于在结构体里面加入了方法】),我们的写入/读取数据的操作都要通过文件内核级缓存区来进行(从磁盘上加载数据到缓存,再交给文件结构体,完成读操作,写操作也是类似的)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com