linux文件与重定向

news2024/11/20 2:33:12

目录

一、共识原理

二、回顾C语言文件函数

1.fopen

2.fwrite

 3.fclose

三、文件系统调用

1.open

2.write 

3.访问文件的本质

4.stdin&&stdout&&stderror

5.文件的引用计数 

 四、重定向

1.文件描述符的分配规则

2. 输出重定向

3.重定向系统调用

4.追加重定向

5.输入重定向 

6.1号VS2号


一、共识原理

1.文件=内容 + 属性

我们关注文件,不仅要关注它的内容,也要关心它的属性,一个文件即使没有内容,它的大小也不是空的,因为该文件的属性也是会占用空间的。

2.文件分为打开的文件和没打开的文件

3.打开的文件

谁在打开文件?我们在代码中写一个fopen,fwrite,最终都会变成进程,因此是进程在打开文件。

研究打开的文件---本质是研究进程和文件的关系!文件被打开,必须被加载到内存!内容和属性都被加载到内存。

进程:打开的文件 = 1 : n。一个进程是可以打开多个文件的,因此进程和打开的文件关系是1:n的

操作系统内部,一定存在大量的被打开的文件!OS要不要管理这些打开的文件呢?-怎么管理???-先描述在组织,在内核中,一个被打开的文件都必须有自己的文件打开对象,包含文件的很多属性。

4.没打开的文件:在哪里放着呢?在磁盘上。我们最关注什么问题?没有被打开的文件非常多,我怎么找到我要打开的文件?因此文件必须被分门别类的归置好---方便我们快速的进行增删查改---快速找到文件。

二、回顾C语言文件函数

1.fopen

fopen的第一个参数是文件名,如果不带路径,默认就在当前路径下打开,如果带了绝对路径,就在绝对路径下打开,第二个参数是以什么方式打开,是读呢?还是写呢?还是追加写?下面我们打开了"log.txt"这个文件,在当前路径下是没有log.txt这个文件的,fopen如果打开不存在的文件会新建在打开,是以读方式打开。果然在我们的当前路径下创建了一个log.txt文件。

下面的问题是,当前路径,什么是当前路径呢?当前路径就是进程的当前路径,如果我们把进程的当前路径修改了,是不是就可以把文件新建到其它目录下呢?那怎么修改进程的当前路径呢?chdir。

可以看到,当把 进程的当前工作目录修改了之后,创建的文件log.txt的路径也随之发生了变化。

2.fwrite

fwrite的第一个参数是写入内容的起始地址,第二个参数是写多少个,第三个是当做一个几部分写入,第四个是要写入那个文件里。

 

这里我们直接把message当做一个整体写入到fp里,也就是log.txt文件里,这里有一个问题就是strlen(message)需要+1吗?strlen求字符串长度,求到'\0'就结束了,也就是说如果+1就是把'\0'也写入到文件里,这里需要吗?答案是不需要,因为'\0'是C语言的要求,C语言不知道字符串从哪里结束才要'\0',但是和我文件有啥关系? 

运行一下,在查看log.txt里的内容,发现果然hello world被写入到文件中了。

下面我们给log.txt里面多加几个2字符,然后在运行一下。

我们发现原来的内容全部没有了,这说明"w"方式,在写入之前,都会对文件进行清空处理!然后再从头开始写。

这个>重定向,是不是就是把log.txt打开,然后以w的方式把"hello world" 写进去呀?因此,我们>前面不加任何东西就是以w方式打开log.txt文件但是不写任何内容,此时就把log.txt清空了。

那如果我们就想要追加写呢?可以以"a"的方式打开,a在文件结尾,追加写

 3.fclose

如果我们把一个文件使用完毕了,就需要使用fclose关闭一下这个文件。它的使用非常简单,把打开的文件指针传进去即可。

下面就有一个问题了,文件是在磁盘上的,磁盘是外部设备,我们上述的fwrite,fopen,包括fclose,其实实在访问硬件,那我们用户能直接访问硬件吗?不能,操作系统不相信任何人,我们要访问硬件,必须通过操作系统提供的系统调用,因此,我们上述写的库函数,一定要封装系统调用!什么printf/fscanf/fwrite/fread......这些库函数,都是封装了系统调用的。下面我们就学习一下这些文件相关的系统调用。

三、文件系统调用

1.open

给我们提供了两个打开文件的系统调用,我们只要学会下面那个参数多的即可,参数少的是参数多的一子集。

第一个参数是要打开的文件路径,如果没有带路径,默认就是进程的当前路径。第二个参数是以什么方式打开,第三个参数是文件的权限,可以设置创建文件的权限。

返回值如果失败返回<0的数,成功,返回>0的数。

我们先来以只读方式打开,只读方式打开传递O_WRONLY这个宏即可。

我们发现我们直接打开文件失败了,这是为啥呢?这是因为如果打开的文件不存在,并不会给新建,因此此时要在传递一个宏O_CREAT, 表示如果文件不存在就创建。此时我们可以看到就创建了log.txt文件。

但是这里细心的同学会发现有问题,就是,我明明文件权限设置的是666呀,文件权限应该是rw-rw-rw,但这里却是rw-rw-r--,不是666而是664,这是因为权限掩码。如果你说我就要创建权限是666的文件呢?可以,有设置掩码的函数umask,直接把掩码设成0即可。 

题外话:open的第二给参数是一个整数呀,可是我们给他传递了O_WRONLYO_CREAT两个选项,是咋做到的呢? 其实就是简单的位运算。

#include "stdio.h"
#include "string.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define ONE (1 << 0) //1
#define TWO (1 << 1) //2
#define THREE (1 << 2)//4
#define FOUR (1 << 3) // 8

void show(int flags)
{
    if(flags & ONE) printf("hello funcion1\n");
    if(flags & TWO) printf("hello funcion2\n");
    if(flags & THREE) printf("hello funcion3\n");
    if(flags & FOUR) printf("hello funcion4\n");
}


int main()
{

    show(ONE);
    printf("----------------------------\n");

    show(TWO);
    printf("----------------------------\n");

    show(ONE | TWO);
    printf("----------------------------\n");

    show(TWO | THREE);
    printf("----------------------------\n");

    show(ONE | THREE | FOUR);
    printf("----------------------------\n");

    return 0;
}

通过位运算,我们就通过给1给标记位传递不同的值,让它表示多种信息。

2.write 

第一个参数,就是对应文件的id,就是open的返回值,第二个参数是要写入的起始位置,第三个参数是写入的长度。

 下面运行一下,看下结果。我们运行了两次,打印出来的都是一样的,之后我手动往log.txt里面加了一串b,在运行,发现,那一串b并没有被清空,因此,我们可以发现写入的时候是从头覆盖写的,但是并不会对文件做清空处理。

那如果我也想做到清空呢? 我们需要在打开文件的时候在加一个宏,O_TRUNC,截断的意思,就是每次打开文件都做清空。

如果我想做到如同C语言中的a一样,追加写呢?还有一个宏,O_APPEND。如果带了O_APPEND就要把O_TRUNC去掉追加和清空是冲突的。

我们现在用的都是系统调用,fopen,就是用open封装的,"w"方式就会被转化成O_WRONLY | O_CAEAT  | O_TRUNC.

这都没问题,但是有一个东西我们一直没谈,就是open的返回值可是一个int啊,但是fopen的返回值是一个FILE类型的指针,这两个玩意八竿子都打不着,有啥关系呢?下面我们就要谈谈文件的管理了。

3.访问文件的本质

操作系统里会有很多个进程,每个进程可能要打开很多个文件,这些被打开的文件要不要被被管理起来呢?要,先描述在组织。操作系统用struct file描述一个被打开的文件信息,struct file里应该包含什么呢?1.文件在磁盘的什么位置 2.文件的基本属性(权限,大小,读写位置,谁打开的...)3.文件的内核缓冲区,总之,这个结构体里包含了文件的大部分信息,类似的,struct file里还有一个strcut* next指针,每打开一个文件,内核创建一个struct file,然后用strcut* next指针链接到一起,此时操作系统要对文件进行增删查改就是对文件链表的增删查改,如果要添加一个文件,就在文件链表里插入,如果要关闭文件,就是把文件的所有属性释放掉,从链表删除,再把数据刷新到磁盘上。

一个进程可能打开多个文件,那些文件是被那个进程打开的呢?我怎么知道,所以,必然要建立进程PCB和打开文件struct file的对应关系。

那怎么建立的呢?在进程PCB里会存在一个指针,struct file_struct *f;这个指针指向struct file_struct结构体这个结构体里面会包含一个数组,数组的名字叫做 struct file* fd_arrdy[],这个数组显然是个指针数组,这个数组的下标从0开始,数组每个元素的类型是 struct file*,所以,当我们打开一个文件的时候,操作系统会创建好struct file,然后在这个数组里分配一个下标,把创建好的struct file的地址,填到这个下标上,以后,每个进程就可以根据这个文件描述符表,就能把打开的文件找到。

所以,为啥open的返回值是个整数呢?open会创建一个struct file,然后在当前进程的文件描述符表里找一个没有用过的下标,把创建的struct file的地址填进去,然后把这个数组下标返回给用户,因此,这个int本质就是一个数组的下标

所以,在我们写的时候,必须得把这个数组的下标传进去,进程通过指针找到文件描述符表,然后在通过这个数组下标,索引到文件的地址,从而往该文件写入。

文件和进程产生关联是通过数组下标关联的,这样就可以做到文件和进程的解耦。

这个文件描述符可还没见过呢,下面我们看看文件描述符是几呢?

我们可以看到是3, 下面我们多打开几个文件看看。

可以看到,是连续的整数。但是问题来了,0、1、2哪里去了呢?

4.stdin&&stdout&&stderror

C程序在默认启动的时候,会打开三个标准输入输出流(文件):stdin(键盘文件),stdout(显示器文件),stderr(显示器文件)。所以我们在打印的时候为啥要包含stdio.h呢?std就是标准的意思,io就是输入输出,是C语言会打开吗?任何语言都会打开这三个文件,这不是C语言的特性,是操作系统的特性,电脑在打开的时候,键盘和显示器文件默认就会打开,进程只需要把打开键盘,显示器文件的地址填入即可。因此0、1、2是被这3个家伙占着呢?怎么验证呢?

我们直接用write往1和2里面写入。1和2是显示器文件哦,直接打印出来了。

下面用read接口验证一下0号,键盘文件。 

为啥卡住了呢?因为0是键盘文件,在等待键盘就绪!

现在问题又来了哦,可C语言的返回值是FILE指针啊,这和int有啥关系呢?FILE是C语言的内置类型吗?不是,FILE是C库里封装的一个结构体,这个结构体里面一定包含了该文件的数组下标,下面验证一下。 

5.文件的引用计数 

关闭文件的系统调用是close,现在我们把下标为1的文件stdout关闭。然后打印了stdout和stderr的fileno,fprintf的用法和printf基本一致,只不过前面加了一个文件描述符而已。我们可以看到,printf没有打印在显示器上打印出来,这肯定和我们关闭close有关,因此,printf底层肯定访问了stdout显示器文件,然后我们把stdout关闭,因此在屏幕上就打印不出来了。但是为啥stderr文件能打印出来呢?stdout和stderr都指向显示器文件,因此显示器文件的引用计数就是2,如果再来一个指向显示器,引用计数就会继续增加。当把stdout关闭,引用计数--变成了1,因此stderr还是能打印出来,关闭文件是把该下标的地址填成NULL。

printf也是有返回值的,其实stdout已经关闭了,但printf以为自己打印成功了,因此就把打印的字符个数13返回了过来。

 四、重定向

1.文件描述符的分配规则

我们把2号文件描述符关闭之后,新创建的文件描述符就是2,因此我们可以得知,文件描述符的分配规则就是从0下标开始,寻找最小的没有被使用的数组位置,它的下标就是新的文件描述符

2. 输出重定向

下面我们把2号文件描述符关闭,然后创建一个文件fd,之后调用write往1号文件里写入5次。

1号文件描述符对应的显示器文件,因此我们把内容写入到了显示器上。

下面我们把1号文件描述符关闭,然后创建一个文件fd,之后调用write往1号文件里写入5次。

我们发现,本来应该向1号文件描述符也就是显示器写入的信息,居然写到了 log.txt里,这是因为我们把1号文件描述符关闭了,然后又创建了一个文件,这个文件根据分配规则,就分配到了1号描述符,然后我们往1号描述符里面写,就写到了文件里。本来应该往显示器写却写入到了文件里,这就叫输出重定向。这里我们可以画张图理解一下。

这样写不是不可以,但是要先关一次,然后在打开一个文件,当别人问你为啥这么做的时候,你就要和别人解释半天,有没有一写系统调用能帮我们做这件事呢?打开文件就行了,然后重定向调用函数就行,有这样的接口吗?是有的。

3.重定向系统调用

是有dup,dup2,dup3系统调用的,常用的就dup2,因此我们详细谈谈dup2,dup就是duplicate,复制的意思,参数是2个文件描述符,一个旧的文件描述符,一个新的文件描述符。

那么问题来了,是把旧的文件描述符内容拷贝给新的文件描述符内容,还是新的文件描述符内容拷贝给旧的文件描述符内容呢?这样说吧,dup2之后,2个文件描述符内容全都变成newfd的还是oldfd的?常理来看应该是全都变成newfd吧,但实际结果是全都变成oldfd,这里挺奇怪的是吧,也不懂老外为啥这样起名字。因此如果我们要让1号文件描述符内容是新建的文件描述符fd的内容,要怎么传参呢?就要dup2(fd, 1)这样传参。下面我们来使用一下。

我们用dup2就实现了同样的效果。 上面的代码忘记close了,要记得close文件。

4.追加重定向

上面的代码中,文件是O_APPEND方式打开,我们多运行几次,这个log.txt就会越来越大,这就叫追加重定向。

5

5.输入重定向 

read的第一个参数是文件描述符,要读哪个文件,第二个参数是读到哪,第三个是读多少个字节,返回值是实际读了多少个字节。

下面我们读取文件方式改为只读方式,然后读取0号文件,也就是键盘文件,阻塞住了,我们往键盘输入内容,然后回显出来了。

下面我们把0号重定向一下。 

我们可以看到本来应该从键盘文件标准输入变读取,变为了从指定的文件读取,这就叫输入重定向

重定向的本质,就是在内核里对文件的地址做拷贝。

6.1号VS2号

我们直接往1号和2号文件描述符里打印,运行,可以看到没问题,都打印出来了。但是当 ./myfile > normal.txt,也就是把1、2号文件的打印输出重定向到normal.txt文件的时候,为啥2号文件的内容没有重定向到nomral.txt里呢?为啥cat只能看到1号文件描述符重定向的内容呢?这是因为>是把1号显示器的内容重定向到了文件里,和我2号文件描述符有啥关系?

 

如果我想把正常消息打印到一个文件,错误消息打印到一个文件,该咋办呢?可以进行下图中的操作,这里其实非常直观,就是把1号文件的内容重定向到normal.log,2号文件的内容重定向到err.log里。默认不写的话是把1号重定向到文件。下面就多出来了err.log文件。

那如果我要把1、2的内容重定向到一个文件里咋办呢? 

默认不写就是1号文件重定向到all.log文件,指令是从左往右执行的,&1(取地址1)的意思就是把1号文件的内容写入到2号文件里,因为左边的指令已经执行完了,1号文件的地址已经是all.log的地址了,然后把1号文件内容拷贝给2,此时1和2都指向了all.log文件。最后就能都写入到all.log里了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CS DAC的Matlab建模与电路设计

在模拟电路设计的复杂世界里&#xff0c;每一个细节都至关重要。Current Steering DAC作为模拟数字转换的核心组件&#xff0c;其设计和性能优化一直是工程师们追求的目标。 “什么是Current Steering DAC&#xff1f; CS DAC通过控制电流源的开关&#xff0c;将数字输入信号…

网络传输:网卡、IP、网关、子网掩码、MAC、ARP、路由器、NAT、交换机

目录 网卡IP网络地址主机地址子网子网掩码网关默认网关 MACARPARP抓包分析 路由器NATNAPT 交换机 网卡 网卡(Network Interface Card&#xff0c;简称NIC)&#xff0c;也称网络适配器。 OSI模型&#xff1a; 1、网卡工作在OSI模型的最后两层&#xff0c;物理层和数据链路层。物…

STM32完全学习——系统时钟设置

一、时钟框图的解读 首先我们知道STM32在上电初始化之后使用的是内部的HSI未经过分频直接通过SW供给给系统时钟&#xff0c;由于内部HSI存在较大的误差&#xff0c;因此我们在系统完成上电初始化&#xff0c;之后需要将STM32的时钟切换到外部HSE作为系统时钟&#xff0c;那么我…

基于Java Springboot滁州市特产销售系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

H.265流媒体播放器EasyPlayer.js视频流媒体播放器关于直播流播放完毕是否能监听到

EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0c;也能支持WebS…

Ubuntu22.04基于ROS2-Humble安装moveit2教程(亲测)

一、安装ROS2-Humble 1、参考&#xff1a;Ubuntu22.04安装ROS2-humble-CSDN博客 2、确保安装完成 source /opt/ros/humble/setup.bash 方法一&#xff1a;二进制安装 sudo apt install ros-humble-moveit* 方法二&#xff1a;安装源码编译 一、卸载二进制安装包 sudo a…

SpringBoot学习记录(三)之多表查询

SpringBoot学习记录&#xff08;三&#xff09;之多表查询 一、多表查询概述1、数据准备2、介绍3、分类 二、内连接三、外连接四、子查询1、标量子查询2、列子查询3、行子查询4、表子查询 三、案例1、准备环境2、需求实现3、&#xff08;附&#xff09;数据准备 一、多表查询概…

Ubuntu 的 ROS 操作系统 turtlebot3 SLAM仿真

引言 SLAM&#xff08;同步定位与地图构建&#xff09;在Gazebo仿真环境中的应用能够模拟真实机器人进行环境建图和导航。通过SLAM仿真&#xff0c;开发者可以在虚拟环境中测试算法&#xff0c;而不必依赖真实硬件&#xff0c;便于调试与优化。 Gazebo提供了多个虚拟环境&…

TCP连接秘籍:三次握手建立连接,四次挥手优雅告别

在数字通信的广阔天地中&#xff0c;TCP协议如同一座稳固的桥梁&#xff0c;连接着网络世界的每一个角落。它不仅确保了数据的可靠传输&#xff0c;还通过精细设计的连接建立与断开机制&#xff0c;展现了其无与伦比的优雅与智慧。TCP的三次握手&#xff0c;犹如初次相遇时的礼…

【惠州大亚湾】之维修戴尔服务器DELLR730XD

1&#xff1a;广东省惠州市大亚湾某游客服务中心来电报修1台DELL PowerEdge R730xd服务器无法正常开机的问题。听该负责描述这台服务器因为服务中心电力切换导致意外关机&#xff0c;来电后发现就无法正常开机了。所以找到我们希望配合维修。 2&#xff1a;该机器由于特别着急…

常见网络厂商设备默认用户名/密码大全

常见网络厂商的默认用户名/密码 01 思科 (Cisco) 设备类型&#xff1a;路由器、交换机、防火墙、无线控制器 默认用户名&#xff1a;cisco 默认密码&#xff1a;cisco 设备类型&#xff1a;网管型交换机 默认用户名&#xff1a;admin 默认密码&#xff1a;admin 02 华…

PH热榜 | 2024-11-18

DevNow 是一个精简的开源技术博客项目模版&#xff0c;支持 Vercel 一键部署&#xff0c;支持评论、搜索等功能&#xff0c;欢迎大家体验。 在线预览 1. Momen 标语&#xff1a;快速搭建你的最小可行产品&#xff0c;然后扩展它&#xff0c;无需任何编程经验。 介绍&#xff…

LoFTR: Detector-Free Local Feature Matching with Transformers—特征点匹配算法系列

LoFTR: Detector-Free Local Feature Matching with Transformers 受到&#xff1a;受到开创性作品 SuperGlue 的启发 摘要总结&#xff1a; 提出了一种局部图像特征匹配的新方法。更为突出说明的是室内场景下的特征点的匹配问题。 不是依次执行图像特征检测、描述和匹配&#…

Percona XtraBackup备份docker版本mysql 5.7

my.cnf配置文件 [client] default_character_setutf8[mysqld] # 数据存储目录&#xff08;必须手动指定&#xff09; datadir/var/lib/mysql/data# 字符集 collation_server utf8_general_ci character_set_server utf8 # 二进制日志 server-id1 log_bin/var/log/mysql/binl…

Windows安装vcpkg教程(VS2022)

内容摘要&#xff1a; 本文详细介绍如何在Windows系统上使用 Git 克隆 vcpkg 仓库来安装vcpkg工具&#xff0c;并链接Visual Studio 2022。 目录 一、关于vcpkg 二、开发环境 三、安装Git 四、使用 Git 克隆 vcpkg 仓库 一、关于vcpkg vcpkg 是一个开源的 C 包管理工具&am…

nodejs入门(1):nodejs的前后端分离

一、引言 我关注nodejs还是从前几年做了的一个电力大数据展示系统开始的&#xff0c;当然&#xff0c;我肯定是很多年的计算机基础的&#xff0c;万变不离其宗。 现在web网站都流行所谓的前后端结构&#xff0c;不知不觉我也开始受到这个影响&#xff0c;以前都是前端直接操作…

智能停车解决方案之停车场室内导航系统(二):核心技术与系统架构构建

hello~这里是维小帮&#xff0c;如有项目需求和技术交流欢迎大家私聊我们&#xff01;点击文章最下方获取智慧停车场方案~撒花&#xff01; 随着城市化进程的加速&#xff0c;停车难问题日益凸显。智能停车系统作为缓解停车压力的有效手段&#xff0c;其核心技术与架构的构建至…

Django5 2024全栈开发指南(三):数据库模型与ORM操作

目录 一、模型的定义二、数据迁移三、数据表关系四、数据表操作4.1 Shell工具4.2 数据新增4.3 数据修改4.4 数据删除4.5 数据查询4.6 多表查询4.7 执行SQL语句4.8 数据库事务 Django 对各种数据库提供了很好的支持&#xff0c;包括 PostgreSQL、MySQL、SQLite 和 Oracle&#x…

中仕公考怎么样?事业编面试不去有影响吗?

事业编考试笔试已经通过&#xff0c;但是面试不去参加会有影响吗&#xff1f; 1. 自动放弃面试资格&#xff1a;未能按时出席事业单位的面试将被视为主动放弃该岗位的竞争机会。 2. 个人信誉问题&#xff1a;面试作为招聘流程的关键步骤&#xff0c;无故缺席可能被解释为诚信…

MySql结合element-plus pagination的分页查询

实现效果如下&#xff1a; 重点&#xff1a;使用mysql查询的limit和offset 原生SQL写法&#xff1a; select c.id as deptid,c.name as department,position,a.name staffname,2024-11 as shijian ,CASE WHEN b.shijian IS NULL THEN no ELSE yes END AS submit from fa_wecom…