文件基础IO

1.先回忆一下c语言的文件接口:

fopen,fwrite:

对应的代码如下:

注意fopen和fwrite的用法:

代码结果:

对应的strlen不需要+1吗,不需要\0对应的是c语言的存储方式,和操作系统无关。

可以看到已经在对应的文件中写入了.

可以看到在fopen中只写了文件名没写路径。对应在当前文件路径下创建

如果改变路径呢?

对应的结果:

可以看到直接写进/home/msb里面了。

  • 对应的"w"是在写入之前,对文件进行清空,再写入:

  • 对应的"a"也是写入,对应直接在文件后面追加写入:

2.认识文件系统调用:

认识:

文件其实是在磁盘上的,磁盘是外部设备,所以访问磁盘文件其实就是访问硬件。

对应系统的文件调用函数:

open,write:

open对应的使用方法:

open的返回值是int,对应的flags接口:

旗标的解释如下:

  • O_RDONLY 以只读方式打开文件

  • O_WRONLY 以只写方式打开文件

  • O_RDWR 以可读写方式打开文件.

  • // 上述三种旗标是互斥的, 也就是不可同时使用,

  • // 但可与下列的旗标利用 OR(|)运算符组合.

  • O_CREAT 若欲打开的文件不存在则自动建立该文件.

  • O_EXCL 如果 O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否

  • O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.

  • O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为 0, 而原来存于该文件的资源

  • O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面

  • O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中

  • O_NDELAY 同 O_NONBLOCK.

  • O_SYNC 以同步的方式打开文件.

  • O_NOFOLLOW 如果参数 pathname 所指的文件为一符号连接, 则会令打开文件失败.

write对应的使用方法:

不难想到fd对应的应该就是open的返回值。

写代码加深了解:

对应的flags是

O_WRONLY:对应打开的文件的方式为只写方式

O_CREAT:对应如果该文件不存在,则创建一个该文件

O_TRUNC:如果文件存在并以可写的方式打开的,那么该旗标会直接将文件中原来右的内容清0

结果如下:

我们试一下 O_TRUNC的效果:

在此文件存在并写入的情况下,写入此代码:

对应的结果如下:

再试一下O_APPEND旗标:

在log,tx为123的情况下追加:

代码如下:

结果:

可以看到与预期结果一样的。

那么对应的fd(文件描述符)到底是什么意思,有什么用?

3.对应访问文件的本质:

先对应多写几个文件打印出来看看:

发现这个fd都是从3开始的,那么为这么是3呢?

还记得c语言中的头文件<stdio.h>

对应里面有几个文件吗?

  • stdin

  • stdout

  • stderror

所以这三个文件其实不是c语言的,本质上是操作系统的,c语言只是使用者。

我们可以在系统中验证:

结果如下:

所以更加说明了:

不管是哪个语言,只要有对应的文件的操作函数,输入输出流,其底层一定会调用系统的对应接口,

所以任何语言在文件操作和输入输出的底层都是保持一致的,都对应操作系统堆其实现。

每个语言文件操作函数必须封装有文件操作符:

所以对应fopen都是通过open封装的。

结合上述只是再来学习一下read系统调用接口:

代码:

s返回的是到最后的数组下标。

对应的输出:

对应在键盘上输出就会打印。

再来深度讨论一下访问文件的本质:

对应文件存在磁盘中,通过双向列表的形式在存储

而在test_struct中也存在一个struct_file_struct用来存储文件信息:

 

只是大概,细节会在后面再说。

文件重定向:

先看下面一段代码:

对应的运行结果:

至于fd为什么等于3,和为什么写在文件里,已经讲得很清楚了。

在这之前先说明一下文件描述符对应的分配规则:

从0下标开始,寻找最小的没有使用的数组位置(上面讲到的文件本质),它的下标就是新文件的描述符。

那么如果我们在上述代码中把对应的stdout1号文件关了,并在显示器上写入呢:

对应的结果如下:

对应的hello Linux还是打印到了文件中,这其实是与预期相符的,因为此时的1号文件对应的就是指向log.txt的文件

而上述的一系列操作其实就实现了文件重定向。

难道在系统中的文件重定向也是这么实现的吗,当然不是,有对应的接口函数:

dup:

这里主要看dup2:

这个接口的命名比较绕,需要更好的理解,对应的含义是让newfd成为oldfd的一份拷贝,最终意味着olded是最终保存在newfd的,所以如果我们要实现上述的操作,

对应的函数的书写应该是:

dup2(fd,1);

close(fd);

这才是正确的,代码验证:

对应的结果:

可以看到,就实现了文件的重定向。

那么其他的printf,fprintf这些标准输出是在哪里输出的呢:

对应的结果:

可以看到是直接将log.txt的文件指针替换到标准输出的文件指针去了。

所以说,标准输入应该也是相同的:

结果:

会直接输出文件中的内容。

如果在自制的shell中写入重定向,进程替换会不会影响文件访问?

不会,对应的进程具有独立性。

补充重定向的一些其他玩法:

先写出如下代码:

这是对应之前的玩法,将输出stdout重定向到notmal.log里面。

其中error正常输出在显示器上。

对应两个输出重定向。

那么如果我就想把这两种输出放在一个文件中呢?

对应将2号文件重定向到一号文件地址了。

如何理解一切皆文件?

外设是如何被操作系统识别的?

只要是外设,虽然肯定会有接口种类的不同,但一定都存在读接口和写的接口

而对应操作系统中的文件虚拟系统与这些外设的读写接口构成多态,从而使

操作系统通过进程访问文件从而访问到这些输入输出设备。

文件虚拟系统中存在具有函数指针的结构体,从而可以很好的接收输入输出设备的

读写函数接口。

所以对于操作系统而言,无论访问什么,都可以通过对应的进程用对应的open,write,read,来实现,

所以,对于操作系统而言,一切皆文件:

用户缓冲区:

先来看一段代码:

对应的输出:

对应的输出没问题。

再来看下一段代码:

只添加了一个fork创建子进程。

对应的结果:

说明一定是fork的作用,再看一段代码:

对应的结果:

对应在显示器上没有打印。

接下来我们来说明缓冲区的使用规则:

对应我们在调用c语言接口的时候使用的一定是c语言对应的缓冲区。

同理调用系统接口的时候一定是内核的缓冲区。

缓冲区的刷新方式:

  • 无缓冲 —— 直接刷新

  • 行缓冲 —— 不刷新,直到碰到\n才刷新

  • 全缓冲 —— 缓冲区满了,才刷新

对应的显示器文件的刷新方案是行刷新,

用户刷新的本质,就是将数据通过1 +  write写入到内核中

如果重定向到文件中,会变为全缓冲,等缓冲区满了才会输出,对应如下:

结果:

对应只有write打印出来,说明其他都还在缓冲区中。

补充问题:

1.缓冲区在进程退出的时候也会刷新。

就是说如果上面的代码不写close是没有这种效果的,对应还是会全部打印出来,因为在退出的时候也会清理缓冲区。

2.为这么会有缓冲区?

  • 提高效率

  • 配合格式化

3.缓冲区在哪里?

对应在FILE的结构体中。

也就是在c语言中,对应malloc(FILE)的时候对应都会创建出来一个缓冲区。

4.我们目前认为:

只要将数据刷新到对应的内核中,数据就可以直接被输出了。

接下来解决这个问题:

对应结果:

为什么会是这样呢?

现在应该是可以理解了:

对应将输出重定向到了文件中,对应的缓冲方式由行缓冲转变为了全缓冲。

所以c接口的函数先不会打印,而是存在缓冲区中,

而对应到了系统接口的时候直接打印。

最或在函数的返回的时候才把缓冲区冲刷了。

打印两次的原因是创建子进程发生了写时拷贝。

为了有更好的理解,模拟实现一下C语言标准库:

innode和软硬链接:

认识磁盘:

磁盘的构造:

磁盘的存储构成:

将磁盘的物理结构逻辑化:

对应成线性的:

以及在第几面,第几个磁道,第几个扇区:

文件系统:

 

软硬链接:

datablocks:存文件的内容,以块的形式呈现。常见的是4KB大小。

innodetable:对应单个文件的所有属性,128字节,一个文件对应一个inode:

对应的inode结构体:

上面所有信息都存在inode结构体中。

inodebitmap:

比特位的位置和inode编号映射起来,用来设置对应inode的有效性。

blockbitmap:

用来将比特位的位置和对应的块号映射起来,比特位中的1,0来表示文件内容,块有没有被使用,在删除文件的时候,直接将对应的比特位置0即可。

groupdescribetable:

组描述表,用来描述组中文件内容和对应inode的使用情况。

superblock:

对应文件系统的基本信息:

  • 一共有多少个组

  • 每个组的大小

  • 每个组的block数量

  • 每个组的起始inode

  • 文件的类型与名称等

但是在linu文件属性中,并不包含文件的名称,而在其中表示的都是对应的inode。

那么我们怎么知道一个文件的inode呢?

对目录的加深理解:

目录也是文件,也有自己的inode。目录也有自己的属性。

目录中也有内容,也有对应的数据块。

在数据块中存放的是文件的文件名和对应文件的inode映射关系!

对应的一些常用的inode在linux下有对应的缓存,所以找起来能快一些。

接下看对应的软硬链接:

这就是对应的软硬链接:

从inode中就可以看出:

软链接是一个独立的文件,具有独立的inode,该如何理解软链接?

软链接也有独立的数据块,其数据块里保存的是指向文件的路径:

其实相当于Windows中的快捷方式。

软链接的应用场景:

对应在有些情况下,我们要执行的运行文件或着安装的软件对应的绝对路径很长,这时候建立一个软链接就可以很好的解决这个问题。

如何理解硬链接:

没有独立的inode,没有独立的文件:

  • 所谓的建立硬链接,本质就是在特定目录的数据块中新增文件名

和执行文件的incode的映射关系。

  • 每一个inode内部,都有一个叫做引用计数器

应用场景:

目录中的. ..对应都是目录的硬链接。

但硬链接是必不可少的,应为在操作系统中,返回上级和当前目录对应的. .. 是不能改变的。

问题linux为什么不允许对目录建立硬链接?

因为会产生无限递归的情况。

因为每个目录都会有是上层目录的地址和自己的地址,

如果建立硬链接之后,对应要在该目录中找对应的文件,

当我们好不容易从根目录找找到了该目录,可是发现对应有

两个inode完全相同的两个目录,那么就会进入到一个环路中

一直无限循环下去,所以操作系统直接禁止了这一操作。

动态库,静态库:

静态库:

目的是不想让用户知道源码,将.c文件对应转换为.o二进制文件,然后再将这些二进制文件打包

最后形成静态库:

例子:

对应的.h.c文件:

对应生成二进制文件以及生成静态库的方法:

下面的output是对应静态库放在一个目录里面从而方便其他用户使用。

对应创建了一个新目录,把对应的lib目录拷贝过去;

在test底下写一个main.c:

注意运行代码是只能这么运行:

-I指定头文件路径,-L指定.c库路径,-l指定的是对应的静态库名

本来的静态库是:libmymath.a

对应真正的库名是去头去尾,最后就是mymath

所以对应文件执行起来了:

所以静态库就是,将对应的.h文件和被打包成库的源代码形成库。

动态库:

与静态库作用相同,也是不让看原码:

例子:

对应的代码以及Makefile文件:

现在已经转到vscode里面了:

 

Makefile文件:

框柱的是形成动态库目录。

shared:表示生成动态库格式。

fPIC:产生位置无关码

对应后面解释。

在终端的命令行与静态库的一样:

可以看到我们已经生成了可执行文件但是运行不了,显示不能打开对应的文件,

再来看看库的链接情况:

可以看到我们想要链接的动态库是找不到的。

对应的解决办法有四种,但第一种最常用:

1.将动态库拷贝到系统默认的库路径:user/lib64/

可以看到动态库直接链接上了,而且可执行文件也跑起来了。

2.在默认库路径下建立软连接:

也可以看到动态库链接上了。

3.将自己的库的所在路径添加到系统的环境变量中LD_LIBRARY_PATH:

对应也产生了链接。

4./etc/ld.so.conf.d建立自己的动态库路径配置文件,然后重新ldconfig即可:

对应也能找到。

ncurses ---  基于终端的图形界面库

总结:

动态库在进程运行的时候,是要被加载的(静态库没有,直接放在可执行文件中)

常见的动态库被左右的可执行程序,都要使用,动态库  ———— 共享库

所以动态库在系统中加载之后,会被所有进程共享。

3.动态库是怎么加载的?

通过每个进程的PCBtesk_struct中的进程地址空间中执行代码,要调用动态库的时候将要建立链接的共享区中的虚拟地址取出通过页表在映射到物理地址中的共享库中。

再加载到可执行程序中的。

结论:

  • 从此以后,我们执行的任何代码,都是我们在进程地址空间中执行的。

  • 系统在运行时,一定会存在多个动态库,对应要用操作系统管理起来,管理方式:先描述,再组织。

4.再谈地址空间:

什么是虚拟地址,什么是物理地址?

程序在编译好之后,内部也有地址的概念。

在学习c++的时候一定看过对应一些代码的反汇编,其实反汇编后面的函数,还是指令对应都已经转化成了地址,而且是虚拟地址,对应方便我们在进程地址空间中找到它,并映射到真正的物理地址,并执行操作。

那么如果是执行第一条指令呢?

对应的页表就会发生缺页中断从而在物理地址中取寻找。

所以CPU接受到的其实全部都是虚拟地址:

5.动态库的地址:

关键问题:共享库大了,具体映射到哪里呢?

动态库加载到固定的地址空间位置是不可能的。

解决方法:逻辑地址+偏移量的方法:

让自己库中的内部函数不要再用绝对编址,值表示在库中的偏移量即可

 所以来解释fPIC的含义:

产生位置无关码,其实就是产生对应的偏移量的:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用3D Touch,让你左右逢源,操作更自然

本文介绍了如何在苹果设备上使用3D Touch&#xff0c;以及哪些应用程序支持该工具。3D Touch在Apple Watch上也称为Force Touch&#xff0c;在iPhone XR上也称为Haptic Touch。 如何改变3D触摸的灵敏度 按照以下步骤调整3D Touch的灵敏度&#xff1a; 1、打开“设置”应用程…

HTML5+CSS3+Vue小实例:输入框打字放大特效

实例:输入框打字放大特效 技术栈:HTML+CSSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&…

使用Nodejs搭建简单的Web网页并实现公网访问

目录 前言 1. 安装Node.js环境 2. 创建Node.js应用 3. 安装Cpolar内网穿透实现公网访问Nodejs服务 3.1 注册cpolar账号 3.2 下载cpolar客户端 3.3 创建隧道映射本地端口 4. 固定公网远程地址 前言 Node.js是建立在谷歌Chrome的JavaScript引擎(V8引擎)的Web应用程序框架…

翻译环境(编译和链接)(简单讲解,理解图就行)

前言 这是我们学习代码的最重要的一个知识点之一&#xff0c;因为我们要去运行一个代码并不是简单的去直接出结果&#xff0c;而是经过了很多我们看不到的步骤&#xff0c;我们在这里以C语言为例子在Linux的环境下讲解&#xff0c;大家没有学过Linux的不用担心&#xff0c;最后…

Maya 2024 for Mac(3D建模软件)

Maya 2024是一款三维计算机图形软件&#xff0c;具有强大的建模、动画、渲染、特效等功能&#xff0c;广泛应用于影视、游戏、广告等行业。以下是Maya 2024软件的主要功能介绍&#xff1a; 建模&#xff1a;Maya 2024具有强大的建模工具&#xff0c;包括多边形建模、曲面建模、…

功能强大的制作电子杂志网站,小白也可轻松上手

现在&#xff0c;越来越多的人开始关注电子杂志的制作&#xff0c;因为它不仅时尚&#xff0c;而且方便快捷。如果你是一个新手&#xff0c;想要制作一本属于自己的电子杂志&#xff0c;那么今天这个网站一定不能错过。它不仅功能强大&#xff0c;而且操作简单&#xff0c;小白…

Windows系统安装Redis、配置环境变量

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…

【C++基础 】类和对象(上)

C基础 类和对象&#xff08;上&#xff09; 1.面向过程和面向对象初步认识2.类的引入3.类的定义4.类的访问限定符及封装4.1 访问限定符4.2 封装 5.类的作用域6.类的实例化7.类对象模型7.1 如何计算类对象的大小7.2 类对象的存储方式猜测7.3 结构体内存对齐规则 8.this指针8.1 t…

非遗文化展示预约小程序的效果如何

漫漫历史长河&#xff0c;我国积累的各种非遗文化广而多&#xff0c;也有相应的机构整理展示和收录&#xff0c;区域限制下&#xff0c;传统非遗文化内容传播度并不高&#xff0c;实际线下查看了解的人也并不是很多&#xff0c;在实际展示方面也面临着一些难题&#xff1a; 线…

【Java面向对象编程(中)】- 探索封装的秘密

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏:Java学习系列专栏&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 回顾 封装​编辑 为什么进行封装 ​​编辑​ 如何调用私有的变量 ​​编辑​ 1.get set方法(当形参和成员变量不同名时)​…

游戏平台采集数据

首先&#xff0c;你需要在你的项目中添加Kotlin的网络库&#xff0c;例如OkHttp。你可以在你的build.gradle文件中添加以下依赖&#xff1a; dependencies {implementation com.squareup.okhttp3:okhttp:4.9.0 }然后&#xff0c;你可以使用以下代码来创建一个基本的网络爬虫&a…

基于python+django的美食餐厅点餐订餐网站

运行环境 开发语言&#xff1a;Python python框架&#xff1a;django 软件版本&#xff1a;python3.7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;PyCharm/vscode 前端框架:vue.js 项目介绍 本论文主要论述了如何使用python语言开发…

原码补码相关运算

求补码步骤 原补转换 -127为负数&#xff0c;其补码为原码01111111&#xff0c;取反10000000&#xff0c;加一&#xff0c;10000001。 例如&#xff1a; 【-1】原码 10000001 反码bai11111110 补码duzhi 11111111 【3】原码 00000011 反码 00000011 补码 00000011 【-127】…

在Ubuntu下安装Redis

文章目录 前言一、配置JAVA运行环境二、Ubuntu下安装Redis1.安装c语言编译环境2.下载解压Redis3.make编译4.启动Redis4.运行Redis 三、性能测试总结 前言 版本 jdk版本&#xff1a;jdk-17_linux-x64_bin 地址&#xff1a;https://www.oracle.com/cn/java/technologies/downloa…

Ubuntu 创建并发布 Django 项目

Ubuntu 创建并发布 Django 项目 升级操作系统和软件 sudo apt updatesudo apt -y dist-upgrade 安装 python3-pip sudo apt -y install python3-pip安装 django pip install -i https://pypi.tuna.tsinghua.edu.cn/simple djangosudo apt -y install python3-django创建 dj…

Django基础介绍及HTTP请求

文章目录 Django框架的介绍Django的安装 Django框架开发创建项目的指令Django项目的目录结构URL 介绍视图函数(view)Django 中的路由配置带有分组的路由和视图函数带有命名分组的路由和视图函数 HTTP协议的请求和响应HTTP 请求HTTP 响应GET方式传参POST传递参数form 表单的name…

数字媒体技术基础之:分辨率

分辨率 Resolution&#xff0c;中国大陆译为“分辨率”&#xff0c;中国香港地区、中国台湾地区分别译为“解像度”和“解析度”&#xff0c;泛指测量设备对细节的分辨能力。 ◆ ◆ ◆ 图像尺寸 在数字图像处理中&#xff0c;像素 Pixel是一个无具体物理尺寸的抽象单位。 一张…

第18章 类集框架

通过本章需要掌握Java设置类集的主要目的与实现原理&#xff0c;掌握Collection接口的作用及小狐妖操作方法&#xff0c;掌握Collection子接口List、Set的区别及常用子类的使用与核心实现原理&#xff0c;掌握Map接口的作用及与Collection接口的区别&#xff0c;理解Map接口设计…

主题模型LDA教程:主题数选取 困惑度perplexing

文章目录 LDA主题数困惑度1.概率分布的困惑度2.概率模型的困惑度3.每个分词的困惑度 LDA主题数 LDA作为一种无监督学习方法&#xff0c;类似于k-means聚类算法&#xff0c;需要给定超参数主题数K&#xff0c;但如何评价主题数的优劣并无定论&#xff0c;一般采取人为干预、主题…

postgresql实现job的六种方法

简介 在postgresql数据库中并没有想oracle那样的job功能&#xff0c;要想实现job调度&#xff0c;就需要借助于第三方。本人更为推荐kettle&#xff0c;pgagent这样的图形化界面&#xff0c;对于开发更为友好 优势劣势Linux 定时任务&#xff08;crontab&#xff09; 简单易用…