在Linux系统下有句名言是,Linux中一切皆文件
C语言中的文件接口
在C语言中有三个数据流,分别是stdin、stdout、stderr,这三个标准文件是交给程序员来输入输出的
可以从键盘读入,显示器输出,也可以输出到文件中,那么其实在操作系统内部,他是把显示器、键盘等外设也看作是文件,向显示器输出和向磁盘文件中输出写入是没有本质区别的
C语言打开文件的函数是fopen,读取是fread、fscanf、fgets,写入是fwrite、fprintf、fputs,和基础的scanf、gets使用都是一致的,只需要在末尾加上写入的文件指针即可
fopen的返回值和三个数据流都是文件指针类型的,是FILE*
操作文件的系统调用
每一种语言都有各自不同的文件操作函数,但不管语言如何改变,想要对文件进行读写都需要让操作系统帮忙,使用系统调用才能对文件进行操作
操作系统有四个系统调用
open、close、write、read
open
我们主要看第一个定义,他的返回值是文件描述符fd,第一个参数是打开文件的路径,flags是你想要打开文件的方式
因为文件的打开方式比较繁多,不能每一个都设置一个参数,于是就想到使用位图来表示,通过判断某一位是否为1来查看是否有这个选项
具体有这么些
- O_RDONLY:只读
- O_WRONLY:只写
- O_RDWR:读写打开
上面这三个必有并且只有一个
- O_CREAT:若文件不存在则创建
- O_APPEND:追加写
具体用法如下
int fd = open("/home/leaf/test", O_WRONLY|OCREAT)
能这么用实际上就是因为这些选项是用宏定义来的,直接按位或就能确保选上了
close
close没什么好说的,如果成功关闭则返回0
write
write是写入数据,写入的内容是buf,写入的字节数是count,调用成功就返回文件中的字节数
char* buffer = "写入数据";
int fd = open("/home/leaf/test",O_WRONLY);
write(fd, buffer, sizeof(buffer));
read
read就是从文件中读取数据,读取到buff中,大小是count,调用成功则返回读取到的字节数
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’
文件描述符详解
为什么文件描述符的类型是整数呢,整数的内容是否有规律
我们可以试试看
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;
}
这里其实是从3、4、5依次向后的
那么缺少的0、1、2其实就对应着前面的三个文件描述符stdin、stdout、stderr,之后我们创建的文件描述符也就依次创建
文件描述符的本质
文件描述符其实是对应着数组下标
操作系统要管理就需要先描述后组织,管理文件就需要管理这些结构体,也就是文件的内容、属性等,再使用链表将这些结构体链接起来
文件描述符对应的是数组下标,什么数组呢,其实就是文件结构体的指针数组,我们称之为文件描述符表
每个进程都需要知道自己打开了哪些文件,所以进程PCB中就会保存一张这个表,这样再对其中的内容做增删查改就容易许多了
Linux中一切皆文件
对于这句话不好理解的其实就是为什么硬件也是文件
磁盘、显卡、磁盘等其实对应了不同的方法,但他们的核心功能也是读写,也就是IO
操作系统会为每一个底层硬件创建file结构体,这个结构体一定包含两个函数指针,分别指向硬件的读写方法,这样文件和硬件的访问就变成了和结构体中指针的访问了
因此当进程想要使用硬件资源时,只需要访问对应文件结构体的函数指针即可