文件的原理和应用

常识:

1 文件包括属性和内容

2 文件有打开和未打开文件,

3 本文先讨论谁打开的文件,以及如何管理已经打开的文件

一 回忆c接口

1 fopen

        我们在test.c里面用一下fopen函数,不存在打开的文件会默认创建,那为什么默认新建在当前目录下是因为cwd,而不是PWD,我们知道PWD是环境变量,如果我们去到其它工作目录,PWD会变,但是CWD不变,此时再运行一下test.c,此时文件还是会创建在CWD存的路径下,而不随着环境变量改变而变化。

2 fwrite

        fwrite函数可以往文件写数据,值得一提的是fwrite的size参数,这个是要写入的字节数,我们大部分时候都是往文件写个字符串,例如"hello linux",有时候size传strlen("hello linux"),有时候传sizeof(hello linux),我们会发现sizeof多写入的\0被文件识别为乱码,这说明一个问题,字符串结尾有\0这个字符是c语言的规定,文件是不认这个\0是字符的。

二 文件操作和系统调用

        fwrite库函数一定封装了系统调用因为文件是在磁盘上的,fwirte要往硬件写数据,那不就相当于访问硬件的资源,由于操作系统不相信用户,所以fwrite一定不是直接把数据弄给硬件,而是通过操作系统的接口将数据传给硬件。接下里就来认识认识几个系统调用。

        1 认识open

        从fopen的名字上看,我们也知道fopen封装的是open,接下来就看看open的参数和返回值。man 2 open就可从手册调出open的信息。

显然,函数1是函数2的的子集合,我们只要说清楚了函数2,函数1的使用也就明白了。

参数1 文件名,不带路径应该是默认在当前路径下找。

参数2 flags是什么呢?

        

        我们要给flags传的是上面图片中的宏,这些宏都表示一个一个的整数,接下来就介绍介绍这些宏的意义,以及如何使用O_RDONLY表示open以只读方式打开,O_WRONLY表示open以写方式打开,  而O_RDWR则表示以读写方式打开,这三个宏最好只出现一个,至于其它的宏O_APPEND,这个是表示向文件写时以追加的方式去写。

使用:

| :按位或?这个使用又是啥意思呢? 

        举个例子,O_RDONLY可能是用0001来表示,O_WRONLY则是0010O_RDWR则是0100来表示,同理得,O_APPEND就要用1000来表示,只用了一个比特位就能唯一表示一个宏,这样的设计非常巧妙,首先我们可能会传多个宏,设计者没有用可模板参数来接收,而是只用一个整型,因为我们可以对传的参数进行按位或,这样只要对按位或的结果一分析,就知道你打开文件是要读还是写了,所以flag的类型就只是个朴素的int,可其实里面门道也不少。

参数3 权限初始化,因为我们open发现打开文件不存在,要创建文件,此时文件的权限是要指定的,不然会给一个初始值,但这个初始值如下图。

       2 认识write和read

write和read就简单多了。

write:往fd这个文件描述符对应的文件写入buf数组中的元素,字节数为count。

read:从fd这个文件描述符对应的文件读取count个字节的数据,写入buf数组中。

        现在我们就能解释:现在我们就可以解释为什么fwrite就传一个"w"可以实现清空写,以及创建文件,"w+"为什么能实现追加写,就是因为在底层封装了这些宏,然后操作系统识别到了,在调用系统调用的时候传了给flag传了不同的宏,至于返回值会在下面访问文件的本质中提及。

三 访问文件的本质

        open的返回值-文件描述符,这个文件描述符怎么是int类型呢,我fopen用的可是FILE*,这两者有什么关系吗?

        先来看看操作系统如何管理文件,首先操作系统打开的文件有很多,这些必然会被操作系统管理,操作系统管理文件,就像管理进程一样,只要用一个file结构体描述文件即可,根本就不需要管文件内容,这样在系统内核处,就又增加了一个数据结构将所有的file结构体管理起来诶,不对啊,文件不是进程打开的吗,那不是应该进程管理吗,如果仅仅是被进程管理,那如果进程出异常了,被kill了,这些文件不就丢失了?所以系统必须也要管理。如下图:

        好吧,既然上面是系统管理文件的方式,那进程呢怎么管理呢?

        所以会有一个files_struct(这个和FILE*可不相同)来管理,这个结构体内部会有一个数组,数组每元素就是一个文件指针,而文件描述符就是数组下标,所以说一个文件描述符一定对应一个文件,当然多个文件描述符可以对应同一个文件(file结构体内部肯定是会有引用计数记录的)也就是说底层进程是通过下标来找文件指针,从而找到文件的,所以FILE*内部一定封装了文件描述符,不然系统调用找不到文件。

        我们发现所有语言写的代码运行起来都要默认打开三个文件,stdin,stdout,stderror,因为系统就要这样做,系统设计这就认为开机后天然需要键盘,显示器文件,所以就要打开,而所有语言写的代码不管写了啥,形成进程后,就会把已经打开的文件填到files_struct内,所以程序一运行,该数组内就有了三个元素。

        既然stderror和stdout都是指向显示器文件,它们的区别是什么,我想也就是其内部的封装的文件描述符不同,当我们close(1),printf就用不了了,但是perror还可以向显示器打印。

四 重定向

        周边小知识:write写的时候如果不close,一直写,那就会一直往后写,而不是覆盖,open以追加方式写,指的是第一次write写的时候从哪开始写。

1 文件描述符的分配规则

        自数组开头遍历,在数组中最先遇到的空格位置的下标,就是被分配的文件描述符。先前已经说了系统会打开两个文件(说打开三个是方便理解),这两个文件是键盘,显示器,而显示器被打开了两次,在files_struct内的数组就会有三个文件描述符,其中0存的是键盘文件指针,1,2存的都是显示器文件指针,我前面说多个文件描述符可以对应同一个文件,这就是实实在在的例子。

2 手动实现重定向

        输出重定向就是printf本来是要写给显示器的,但是由于close(1),然后又打开了myfile.txt后占用了一号位(这就验证了文件描述符的分配规则)。

    1 #include<stdio.h>2 #include<stdlib.h>3 #include<unistd.h>4 #include<string.h>5 #include <sys/types.h>6 #include <sys/stat.h>7 #include <fcntl.h>8 int main()9 {10     printf("我的id:%d\n",getpid());11     close(1);
W> 12     int fd = open("myfile.txt", O_CREAT|O_WRONLY,0666);13     printf("我的id:%d\n",getpid());                                                14     return 0;15 }~

所以两句printf就只有一句输出,因为第二句printf就变成往myfile.txt输出了。

        这也侧面说明在操作系统看来,往显示器写和普通的文件没有区别,而printf内部用的stdout一定是封装了1号文件描述符,这是编码定死的,printf也不管这个标识符对应的文件变了没,拿到就写,就有了输出重定向这种乌龙。

3 系统调用dup2

        虽然可以先close,再打开文件实现重定向,但这样的代码还是不如直接调用系统调用那么优雅,直接上代码,看看使用。结果一致。

问题1 oldfd和newfd谁覆盖谁显然从先前的例子来看,是oldfd上的内容覆盖到newfd的内容,本质是数组对应下标上元素,也就是文件指针的拷贝。

读者可以尝试进行输入重定向

同理也就是对0号位置下标做手脚

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

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

相关文章

【Kettle实战】字符串处理及网络请求JSON格式处理

经过大量的kettle操作实践&#xff0c;我们会渐渐掌握一些技巧&#xff0c;大大减轻清洗的工作量。比如在哪里 处理字符串更方便&#xff0c;在哪儿处理更合理都是一个取舍问题。 字符串拼接 MySQL中使用concat(字段1,字段2)&#xff0c;但是如果“字段2”为NULL&#xff0c;结…

如何在windows使用别名远程执行命令

需求背景 在开发中,需要在服务器执行脚本,需要如下几步操作: 1.打开xshell 2.登录服务器 3.进入命令脚本的路径 4.执行脚本 但是,作为懒人来说,操作太繁琐了,真麻烦,能不能一键就解决那么多操作?所以,开始研究windows有没有这个东西,而且不需要额外的软件就可以实现的.结…

ABeam Recruiting | ABeam旗下德硕管理咨询(上海)最新招聘岗位

诚聘英才 市场部经理 招聘背景 作为起源于亚洲的全球化咨询公司&#xff0c; ABeam立志成为中国、乃至全球的杰出品牌 本次招聘岗位 以加快ABeam中国本土各法人 在中国市场的品牌推广、提高企业知名度 强化Marketing部门的体制为目标 工作职责 在现有的PR业务基础上&#…

C++多线程学习(二):多线程通信和锁

参考引用 C11 14 17 20 多线程从原理到线程池实战代码运行环境&#xff1a;Visual Studio 2019 1. 多线程状态 1.1 线程状态说明 初始化 (lnit)&#xff1a;该线程正在被创建就绪 (Ready)&#xff1a;该线程在就绪列表中&#xff0c;等待 CPU 调度运行 (Running)&#xff1a;…

xss-labs靶场6-10关

文章目录 前言一、靶场6-10关1、关卡62、关卡73、关卡84、关卡95、关卡10 总结 前言 此文章只用于学习和反思巩固xss攻击知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随意去尚未授权的网站做渗透测试&#xff01;&#xff01;&#xff01; …

在win10上安装pytorch-gpu版本2

安装anaconda即下载了python&#xff0c;还可以创建虚拟环境。 目录 1.1 anaconda安装 1.2 pytorch-gpu安装 1.1 Anaconda安装 anaconda的安装请看我之前发的tensoflow-gpu安装&#xff0c;里面有详细的安装过程&#xff0c;这里不做重复描述&#xff0c;传送门 1.2 pyt…

羊大师提示,羊奶都有哪些惊人功效?

羊奶不仅是一种美味的健康饮品&#xff0c;在近年来备受瞩目的的健康圈子里&#xff0c;羊奶还被赋予了更多的功效&#xff0c;成为一种备受推崇的保健品。羊奶不但富含营养&#xff0c;而且还有着非常多的益处&#xff0c;它能够用来美容、保健&#xff0c;甚至还可以治疗某些…

【Java】多线程-单例模式/volatile-指令重排序

单例模式即代码中只有一个实例的模式 适用场景&#xff1a;有些场景下&#xff0c;有的类只能有一个对象&#xff0c;不能有多个 要注意&#xff1a;在单例模式下&#xff0c;要保证不能产生多个实例 1、饿汉模式 class Singleton{private static Singleton instance new …

Mybatis plus 简介

简介 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 官网:https://baomidou.com/pages/24112f/ 特性 无侵入&…

《QT从基础到进阶·三十八》QWidget实现炫酷log日志打印界面

QWidget实现了log日志的打印功能&#xff0c;不仅可以在界面显示&#xff0c;还可以生成打印日志。先来看下效果&#xff0c;源码放在文章末尾&#xff1a; LogPlugin插件类管理log所有功能&#xff0c;它可以获取Log界面并能打印正常信息&#xff0c;警告信息和错误信息&…

runnergo全栈测试平台

一、全栈测试平台runnergo使用 官网 官方使用文档 二、单接口测试 三、性能测试 1.性能测试 2.性能测试报告 四、自动化测试&#xff08;暂时不支持UI自动化&#xff0c;或许会上&#xff09;

Jmeter 压测实战保姆级入门教程

1、Jmeter本地安装 1.1、下载安装 软件下载地址&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/apache/jmeter/binaries/ 选择一个压缩包下载即可 然后解压缩后进入bin目录直接执行命令jmeter即可启动 1.2 修改语言 默认是英文的&#xff0c;修改中文&#xff0c;点击…

使用Java Servlet生成动态二维码

文章目录 引入ZXing库创建QRCodeServlet部署到Servlet容器拓展功能1. 动态生成二维码内容2. 调整二维码尺寸3. 错误修正级别4. 日志输出 结语 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&…

【追求卓越04】数据结构--栈与队列

引导 今天我们开始学习栈与队列的内容&#xff0c;我觉得栈并不难&#xff0c;所以篇幅也就不会那么多了。在虚拟空间中&#xff0c;栈是用户空间中的一种数据结构&#xff0c;它主要用于保存局部变量。那么问题来了&#xff0c;为什么用栈来保存局部变量&#xff0c;不用别的数…

Go 语言函数、参数和返回值详解

函数是一组语句&#xff0c;可以在程序中重复使用。函数不会在页面加载时自动执行。函数将通过调用函数来执行。 创建函数 要创建&#xff08;通常称为声明&#xff09;一个函数&#xff0c;请执行以下操作&#xff1a; 使用 func 关键字。指定函数的名称&#xff0c;后跟括…

10年开发工程师总结,8大主流程序员兼职平台,月入30k不是梦!

今年互联网行业陆续裁员减薪&#xff0c;许多人怨声载道的同时也开始另谋出路。而对于程序员更是应该提早做好准备&#xff0c;活跃在兼职接单的最前沿。 我们程序员是一门技术工种&#xff0c;与互联网其他行业相比薪水会相对高一点&#xff0c;不过钱也不是那么好赚的&#…

2023年【广东省安全员B证第四批(项目负责人)】报名考试及广东省安全员B证第四批(项目负责人)复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 广东省安全员B证第四批&#xff08;项目负责人&#xff09;报名考试是安全生产模拟考试一点通总题库中生成的一套广东省安全员B证第四批&#xff08;项目负责人&#xff09;复审考试&#xff0c;安全生产模拟考试一点…

java:springboot单元测试spring-boot-starter-test

背景 Java的单元测试可以使用多个框架&#xff0c;其中比较流行的包括&#xff1a; JUnit&#xff1a;JUnit是Java单元测试最常用的框架&#xff0c;它提供了一套丰富的API&#xff0c;可以方便地编写测试用例和测试套件。JUnit 5是JUnit的最新版本&#xff0c;引入了许多新功…

各大电商平台双十一“狂飙”,如何选择商城系统?

今年是“双十一”的第十五年。作为各大平台和品牌的全年最重要的营销节点&#xff0c;品牌们可谓是来势汹汹&#xff0c;各种促销活动和优惠力度让人眼花缭乱。 淘天数据显示&#xff0c;天猫促销活动开售当晚&#xff0c;155个品牌开卖成交额突破1亿元&#xff1b;首小时内7.1…

str转wstr的三种方法和从网站获取json数据到数据随机提取,返回拼接字符串和动态数组

库的设置 hv库 外部包含目录&#xff1a;…\include\libhv_new\hv; 库目录&#xff1a;…\include\libhv_new\lib\x86\Release; 附加依赖项&#xff1a;hv.lib; //Get请求 获取json数据&#xff0c;然后提取符合 条件的&#xff0c;time值大于自定义变量的值&#xff0c;然后取…