Linux--基础IO(上)

目录

[1. 文件的边角知识](#1. 文件的边角知识)

[1.1 文件是什么?](#1.1 文件是什么?)

[1.2 文件是怎么打开的?](#1.2 文件是怎么打开的?)

[1.3 进程与文件](#1.3 进程与文件)

进程与文件的关系

[2. 重温c语言文件接口](#2. 重温c语言文件接口)

[2.1 打开文件的方式](#2.1 打开文件的方式)

[2.2 读写文件接口的重温](#2.2 读写文件接口的重温)

[2.2.1 写文件](#2.2.1 写文件)

[2.2.2 读文件](#2.2.2 读文件)

[3. 系统文件I/O](#3. 系统文件I/O)

[3.1 系统接口](#3.1 系统接口)

[3.2 系统接口的参数详解](#3.2 系统接口的参数详解)

[3.3 文件描述符](#3.3 文件描述符)

[3.4 fd的分配规则](#3.4 fd的分配规则)

[3.5 重定向](#3.5 重定向)

[3.6 dup2](#3.6 dup2)


1. 文件的边角知识

1.1 文件是什么?

文件=内容+属性。常说的对文件进行操作,不仅是对文件内容的读写,也有对文件属性的修改。比如修改文件名称,更新文件最新写入时间等。

1.2 文件是怎么打开的?

在linux的命令行界面中,我们打开文件是通过指令打开;在图形化界面中,我们只需要点击文件就可以打开。指令本质是程序,图形化界面点击同样也是一种程序,只不过被封装成了点击的行为罢了,在底层其实没有区别。

访问文件之前需要先打开文件,从上面的叙述中我们明白,文件就是被程序打开的,也即文件的打开工作是进程做的。

为什么呢?为什么不直接让操作系统打开?

文件不能直接被操作系统打开,而是由应用程序通过系统调用来请求操作系统打开文件。

冯诺依曼体系结构中明确规定,cpu不能与硬件接触。而对文件的处理要靠cpu来运算,但文件存储在磁盘中,因此我们需要将文件加载到内存中才能进行操作,当对文件的修改完成后,将新的文件在磁盘中更新。

1.3 进程与文件

文件是被进程打开的,但进程能够打开的文件并不只限于一个,即进程与文件是一对多的关系。

已打开的文件被加载到内存中,那么没有打开的文件呢?在磁盘里。

进程与文件的关系

我们知道,进程打开后会有一个进程控制块pcb来管理进程。Linux中的pcb被称为task_struct,即管理进程的结构体。文件当然也需要类似的结构体来管理,内核中必然有一个描述被打开文件的结构体,并用其定义对象,我们姑且称他为file_struct。

当进程打开文件后,进程与文件的关系就如下图。

在Linux内核源码中,我们看到在task_struct中包含files_struct结构体,注释为打开文件的信息。

2. 重温c语言文件接口

2.1 打开文件的方式

有没有发现一件奇妙的事情,w与输出重定向>功能十分类似,而a则与追加重定向>>类似,r与输入重定向<类似。

2.2 读写文件接口的重温

2.2.1 写文件

2.2.2 读文件

c语言会默认打开三个输入输出流:stdin,stdout,stderr。

我们可以将stdin视作键盘,将stdout视作显示器,stderr视作显示器(输出错误信息)。

我们来看看:

3. 系统文件I/O

3.1 系统接口

我们前面说,文件只能被程序通过系统调用来打开,也就是说c语言的文件接口中必定封装着文件操作的诸多系统调用。

我们之前说,c语言会默认打开三个流。注意:这里的FILE*是c语言自己封装的结构体。我们来看看他们是如何封装文件的系统调用 。

下图是我们常用的几个系统调用接口。我们发现,open会返回一个int值fd,其他的接口都会使用这个fd。有没有可能,这个fd就类似于进程的pid,是文件的标识符呢?答案是肯定的。

也就是说,FILE*中封装着文件的标识符fd。

验证:

因此我们可以在系统调用中使用0/1/2来使用键盘/显示器/显示器。

3.2 系统接口的参数详解

我们以open接口为例:

下图介绍了flag与mode的常用选项。

这里的flag传参涉及了位运算传参 ,我们写一段代码来看看。

cpp 复制代码
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
int main()
{
   size_t fd = open("./log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);//以只写权限打开,如果文件不存在就创建,存在就清空
   const char* str="hahaha dashagua!";
   write(fd,str,strlen(str));
   close(fd);
    return 0;
}

3.3 文件描述符

文件描述符即3.1 中的fd,那么files_struct是如何管理被打开的文件呢?

请看下图,下面的structural flie* fd array[]就是一个指针数组,里面存放着每一个文件结构体的地址,而文件描述符就是数组下标,进程通过文件下标访问文件。

3.4 fd的分配规则

当我们关闭fd为1的stdout时,此时再打开一个文件log.txt,我们会发现此时我们对1的写入,会写进 log.txt中。这是因为此时打开的文件其fd为1。

如果我们关闭0或2,输出就会是0/2。

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

3.5 重定向

我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出

重定向。常见的重定向有:>, >>, <

那重定向的本质是什么呢?

重定向的本质就是改变了文件的输入输出流。

针对这一现象,我们有一个系统接口dup2,可以实现重定向的功能。

3.6 dup2

接口解释:

代码实验:

cpp 复制代码
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>

int main()
{
    size_t fd = open("./log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
    if(fd<0)
    {
        perror("open error!");
        return 1;
    }
    dup2(fd,1);//重定向,让fd覆盖fd为1的显示器
    write(fd,"hahahah\n",8);//向fd写入,验证并非替换,且fd并未被关闭
    printf("我原本是要输入到显示器的,你看看我现在在哪?\n");//此时fd,1都指向log.txt
    close(fd);
    printf("fd关了,你看看我现在在哪?\n");



    //printf("hello hello!\n");
    return 0;
}

结果展示:

原理剖析图:

相关推荐
zym大哥大8 分钟前
Linux的权限
linux·服务器
Stark-C10 分钟前
功能齐全,支持协作 | Docker部署一款支持多人共享的私密浏览器『n.eko』
运维·docker·容器
嘟嘟Listing31 分钟前
设置jenkins时区记录
运维·jenkins
嘟嘟Listing32 分钟前
jenkins docker记录
java·运维·jenkins
伴野星辰32 分钟前
小乌龟TortoiseGit 安装和语言包选择
linux·运维·服务器
枫叶丹439 分钟前
【在Linux世界中追寻伟大的One Piece】多线程(一)
java·linux·运维
残念ing41 分钟前
【Linux】—简单实现一个shell(myshell)
linux·运维·服务器
明月心9521 小时前
linux mount nfs开机自动挂载远程目录
linux·运维·服务器
Ray55051 小时前
bridge-multicast-igmpsnooping
linux·服务器·网络
库库的里昂1 小时前
Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程
linux·运维·docker·开源