Linux基础IO——文件的系统调用与文件描述符的本质

1.1k words

在Linux系统下有句名言是,Linux中一切皆文件

C语言中的文件接口

在C语言中有三个数据流,分别是stdin、stdout、stderr,这三个标准文件是交给程序员来输入输出的

可以从键盘读入,显示器输出,也可以输出到文件中,那么其实在操作系统内部,他是把显示器、键盘等外设也看作是文件,向显示器输出和向磁盘文件中输出写入是没有本质区别的

C语言打开文件的函数是fopen,读取是fread、fscanf、fgets,写入是fwrite、fprintf、fputs,和基础的scanf、gets使用都是一致的,只需要在末尾加上写入的文件指针即可

fopen的返回值和三个数据流都是文件指针类型的,是FILE*

操作文件的系统调用

每一种语言都有各自不同的文件操作函数,但不管语言如何改变,想要对文件进行读写都需要让操作系统帮忙,使用系统调用才能对文件进行操作

操作系统有四个系统调用

open、close、write、read

open

image.png

我们主要看第一个定义,他的返回值是文件描述符fd,第一个参数是打开文件的路径,flags是你想要打开文件的方式

因为文件的打开方式比较繁多,不能每一个都设置一个参数,于是就想到使用位图来表示,通过判断某一位是否为1来查看是否有这个选项

image.png

具体有这么些

  • O_RDONLY:只读
  • O_WRONLY:只写
  • O_RDWR:读写打开

上面这三个必有并且只有一个

  • O_CREAT:若文件不存在则创建
  • O_APPEND:追加写

具体用法如下

1
int fd = open("/home/leaf/test", O_WRONLY|OCREAT)

能这么用实际上就是因为这些选项是用宏定义来的,直接按位或就能确保选上了

close

image.png

close没什么好说的,如果成功关闭则返回0

write

image.png

write是写入数据,写入的内容是buf,写入的字节数是count,调用成功就返回文件中的字节数

1
2
3
char* buffer = "写入数据";
int fd = open("/home/leaf/test",O_WRONLY);
write(fd, buffer, sizeof(buffer));

read

image.png

read就是从文件中读取数据,读取到buff中,大小是count,调用成功则返回读取到的字节数

1
2
3
4
5
char buffer[1000];
int fd = open("/home/leaf/test",O_WRONLY);
ssize_t n = read(fd, buffer, sizeof(buffer));
if(n>0)
buffer[n] = '\0';

需要注意的是,文件是什么就会读到什么,我们需要手动添加’\0’

文件描述符详解

为什么文件描述符的类型是整数呢,整数的内容是否有规律

我们可以试试看

1
2
3
4
5
6
7
8
9
int main()
{
int fd1 = open("/home/leaf/code/24_8_5/text1.txt",O_WRONLY|O_CREAT);
int fd2 = open("/home/leaf/code/24_8_5/text2.txt",O_WRONLY|O_CREAT);
int fd3 = open("/home/leaf/code/24_8_5/text3.txt",O_WRONLY|O_CREAT);
printf("fd1 = %d; fd2 = %d; fd3 = %d\n",fd1, fd2, fd3);
return 0;
}

image.png

这里其实是从3、4、5依次向后的

那么缺少的0、1、2其实就对应着前面的三个文件描述符stdin、stdout、stderr,之后我们创建的文件描述符也就依次创建

文件描述符的本质

文件描述符其实是对应着数组下标

操作系统要管理就需要先描述后组织,管理文件就需要管理这些结构体,也就是文件的内容、属性等,再使用链表将这些结构体链接起来

文件描述符对应的是数组下标,什么数组呢,其实就是文件结构体的指针数组,我们称之为文件描述符表

每个进程都需要知道自己打开了哪些文件,所以进程PCB中就会保存一张这个表,这样再对其中的内容做增删查改就容易许多了

Linux中一切皆文件

对于这句话不好理解的其实就是为什么硬件也是文件

磁盘、显卡、磁盘等其实对应了不同的方法,但他们的核心功能也是读写,也就是IO

操作系统会为每一个底层硬件创建file结构体,这个结构体一定包含两个函数指针,分别指向硬件的读写方法,这样文件和硬件的访问就变成了和结构体中指针的访问了

因此当进程想要使用硬件资源时,只需要访问对应文件结构体的函数指针即可

Comments