Linux: 基础IO

news2024/11/25 4:18:41

学习目标

1.C接口与系统调用接口的差别

2.文件描述符, 重定向, 一切皆文件, 缓冲区

3.fd与FILE, 系统调用和库函数的关系

4.系统中的inode

5.软硬链接

6.动静态库

预备知识

1.文件 = 内容 + 属性

2.文件的所有操作: a. 对内容的操作 b.对属性的操作

3.文件在磁盘(硬件)上,  我们访问文件的过程: 代码 -> 编译->exe->运行

  访问文件本质上是谁访问的?  进程

  如何访问? 通过调用系统接口

  原因: 要向硬件写入内容, 只有OS有权力

访问文件需要调用系统接口

4.之前为什么没听说过

--1.语言上对这些接口做了封装, 让这些接口更好的使用

--2.跨平台性:  如果语言不提供对文件系统接口的封装, 一旦使用系统接口, 编译所谓的文件代码 , 就无法在其它平台上运行

原因: 语言不同, 所写出的代码也不同

解决: 把所有平台的代码都实现一遍, 使用条件编译,  动态裁剪

1.文件描述符

1.1 C语言接口与系统调用接口

C语言接口

1.头文件: #include<stdio.h>

2.相关接口:

FILE * fopen ( const char * filename, const char * mode );

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

int fputs (const char* str, FILE *stream);

int fclose ( FILE * stream );

系统调用接口

1.头文件:#include<sys/types.h>   #include<sys/stat.h>    #include<fcntl.h> 

2.相关接口:

int  open(const char *pathname, int flags);

int  open(const char *pathname, int flags, mode_t mode);

ssize_t  write(int fd, const void *buf, size_t count);

ssize_t  read(int fd, const void *buf, size_t count);

int  close(int fd);

flags: O_APPEND  O_CREATE  O_TRUNC等  flags:选项标记位

ssize_t : 表示实际读/写的个数

代码演示

1.2 fd,重定向,一切皆文件,缓冲区

fd

fd: 文件描述符(file descriptor)

本质: 一个数组下标

原因: 系统中会存在大量被打开的文件, 系统要管理这些文件, 就将这些文件的内容和属性抽象出来, 构建struct file{} ,创建一个struct file{}对象来充当被打开的文件,  若文件很多,则用双链表链接起来,  方便找到这些文件, 创建一个数组,用来存放这些对象的地址 , 这个数组的下标就叫做文件描述符, 这个数组就叫文件描述符表,  通过文件描述符(下标),我们能在文件描述符表(数组)找到这个文件对象

fd的分配原则:  最小的, 没有被占用的文件描述符

重定向

1.现象描述: 重定向可以理解为, 本该显示在显示其上的东西, 却显示在了另一个文件当中

2.原理: 文件描述符表中, 文件描述符对应存的地址不再是原来文件的地址, 变为另一个文件的地址了 

比如这张图中, 1原来存的是stdout的地址, 变为log.txt的地址,  就会造成,此时执行ls命令, 本该显示再显示其中的东西, 显示再log.txt中了  

我们打开一个文件, 向一个文件内里读/写,  是通过文件描述符来找到这个文件的

一切皆文件

Linux 设计哲学 --> 体现操作系统的软件设计层面的!

--多态:访问同一种类型的对象, 最后可以表现出不同的行为

Linux C语言写的! 如何使用C语言实现面向对象, 甚至运行时多态?

--构造一个结构体对象,  里面存放文件的共有属性, 对于不同的硬件, 需要采用不同的读写方法, 那么就在结构体中定义函数指针, 指向不同硬件的读写方法.

缓冲区

1.缓冲区是什么? --就是一段内存空间

a.这个空间谁提供? 库

b.缓冲区刷新策略:

--1.立即刷新

--2.行刷新 (\n) ---显示器

--3.满刷新(全缓冲) ---磁盘文件 (效率考量)  可以尽量减少

     特殊情况: 1.用户强制刷新(fflush) 2.进程退出

所有的设备永远都倾向于全缓冲的! ---缓冲区满了 , 才刷新 -->需要更少的IO操作 --> 更少次的外设访问

2.为什么要有缓冲区?

--缓冲区可以用来提高性能

        在许多情况下,读写数据时直接访问主存或磁盘可能会很慢,因为这些操作涉及到物理硬件的访问。

        通过将数据暂时存储在缓冲区中,可以减少对慢速设备的访问次数,从而提高程序的运行速度。

看下面这段代码:

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

//myfile helloworld
//int main(int argc, char *argv[])
int main()
{
    // 往显示器上打印
    // C语言提供的
    printf("hello printf\n");
    fprintf(stdout, "hello fprintf\n");
    const char *s = "hello fputs\n";
    fputs(s, stdout);

    // OS提供的
    const char *ss = "hello write\n";
    write(1, ss, strlen(ss));

    fork(); //创建子进程
    return 0;
}

运行结果

我们发现同一段程序,  向显示器打印,输出4行文本, 向普通文件(磁盘)打印的时候,变成了7行

--1.C IO接口,是打印了2次  --2.系统接口, 只打印一次和显示器打印一样

原因: 往显示器打印时行刷新, 往普通文件打印, 缓冲区刷新规则转换为全刷新

--往显示器打印, fork之前,缓冲区内的数据已经刷新完, 不发生写时拷贝

--往普通文件打印,fork之前,缓冲区的内数据不刷新, 在进程退出的时候,发生写实拷贝, 代码会拷贝从而打印2次

--系统接口只打印1次, 调用系统接口直接将数据刷新到内核中

stdout与stderr

1和2对应的都是显示器, 但是它们两个是不同的, (同一个显示器被打开了2次)

一般而言:

如果程序有问题, 建议使用stderr或者cerr打印

如果是常规的文本内容, 我们建议进行cout,stdout打印

三种重定向写法:

1 ./myfile > ok.txt 2>err.txt

将正确信息打印到ok.txt中,将错误信息打印到err.txt

2 ./myfile > log.txt 2>&1

把1,2的信息都打印到log.txt中

3. cat < log.txt > back.txt

把log.txt的数据给cat, 然后把结果重定向到back.txt

1.3 fd与FILE,系统调用与库函数的关系

在进程中 , OS会默认打开stdin, stdout, stderr  对应0  1   2

这里可以看出: 库函数接口是封装后的系统接口

2.文件系统与inode

预备知识

磁盘文件 - 了解磁盘

内存 -- 掉电易失存储介质

磁盘 -- 永久性存储介质 -- SSD, U盘 flash卡 , 光盘 , 磁带

磁盘是一个外设, 还是我们计算机中唯一的一个机械设备 ----慢!!!

磁盘的结构

1.磁盘的物理结构

磁盘盘片. 磁头, 伺服系统, 音圈马达...

2.磁盘的存储结构

在物理上, 如何把数据写入到知道的扇区里?

如何找到一个扇区? CHS寻址

a.在哪一个盘面上?(对应的就是哪一个磁头)Head

b.在哪一个磁道上?(柱面)Cylindar

c.在那一个扇区(512字节)上?Sector

3.磁盘的抽象结构(虚拟,逻辑)

LBA -> CHS

磁盘管理 --> 线性结构管理

分区:

文件系统

块组的基本信息:

这5个信息 , 能够让一个文件的信息可追溯 , 可管理.

我们将块组分割成为上面的内容 , 并且写入相关的管理数据 -> 每一个快组都这么干 -> 整个分区就被写入了文件系统信息 (格式化)

若文件特别大怎么办?

--一个文件对应一个inode节点,inode编号, 但不一定只有一个block

--data block中 , 不是所有的 data block, 只能存文件数据 , 也 可能存其它块的块号

目录

目录也是文件, 有自己的inode,data block

它的data block放的是文件名和inode的映射关系

文件名不会在inode的属性里存在, 放在了目录的内容里

所以我们拿到文件名就可以将其作为key值找到inode, 反之同理 所以创建一个文件需要W权限, 显示文件属性要R权限

inode 与文件名

inode与文件名互为key值

找到文件 : inode编号 -> 分区他的的bg -> inode -> 属性 -> 内容

如何知道inode编号? 依托于目录结构

1.创建文件, 系统做了什么?

2.删除文件, 系统做了什么?

3.查看文件, 系统做了什么?

1.创建文件, 系统做了什么?

--根据文件系统, 在分区当中, 找到目录所在的分区, 块组,

a.在inodebitmap中, 找到第一个为0的比特位, 将其0置为1, 同时我们也拿到了一个inode号 ,

b.在inode表里面, 把新建文件的属性写进去 , (拥有者,所属组...)

c.data blocks : 在后面写的时候,再在block bitmap里面找块, 把数据写道块中, 在建立inode和块的映射关系

用户提供文件名, 文件系统,将文件在内部创建好后, 提供inode

把用户输入的文件名 和 inode 建立映射关系 , 写到目录的内容当中

2.删除文件, 系统做了什么?

-a.找到该目录对于的data block

-b. 以文件名作为索引 ,找到文件对应的inode, 在特定的快组内,根据编号

-c.把对应的inode bitmap由1置为0

-d. 把对应的data block bitmap由1置为0

-e. 把文件名和inode的映射关系去掉

3.查看文件, 系统做了什么?

找到目录 , 找到inode , 找到data block ,将文件名对应的内容显示

软硬链接

1.命令: 软连接 ln -s 链接的文件 起的文件名 (硬链接不 + s)

2.软硬连接有什么本质区别 : 有没有独立的inode

    --软连接有独立的inode -> 软连接是一个独立的文件

    --硬连接没有独立的inode -> 硬连接不是一个独立的文件

    硬链接本质就是对inode内部的一种引用计数, 当这个计数减为0的时候这个文件才会被删除

3.动静态库

  1. 我如果想写一个库 ? (编写库的人的角度)
  2. 如果我把库给别人, 别人是怎么用的呢? (使用库的人的角度)
  3. 为什么要有库? 1.简单 2. 代码安全

     自己所写的库是第三方库,  

静态库:  .a后缀

动态库:  .so后缀

生成动静态库

1.生成静态库

Archive files 归档文件  r : replace c : create

命令:

ar  -rc  lib库名.a    mymath.o  myprint.o  
//例如:
ar  -rc  libhello.a   mymath.o  myprint.o

Makefile:

libhello.a:mymath.o myprint.o
	ar -rc libhello.a mymath.o myprint.o  
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:output
output:
	mkdir -p output/include
	mkdir -p output/lib
	cp -rf *.h output/include
	cp -rf *.a output/lib

.PHONY:clean
clean:
	rm -rf *.a output *.o

效果:

2.生成动态库

生成.o文件

gcc   -fPIC  -c  mymath.c  -0  mymath.o

生成动态库

gcc  -shared  myprint.o  mymath.o   libhello.so

Makefile

libhello.so:mymath_d.o myprint_d.o
	gcc -shared mymath_d.o myprint_d.o -o libhello.so 
mymath_d.o:mymath.c 
	gcc -c -fPIC  mymath.c -o mymath_d.o 
myprint_d.o:myprint.c 
	gcc -c -fPIC  myprint.c -o myprint_d.o 

.PHONY:output
output:
	mkdir -p output/include
	mkdir -p output/lib
	cp -rf *.h output/include
	cp -rf *.so output/lib

.PHONY:clean
clean:
	rm -rf  output *.o *.out *.so

效果:

3.同时生成动静态库

构造一个伪目标all

.PHONY:all 
all:libhello.so libhello.a 

libhello.so:mymath_d.o myprint_d.o
	gcc -shared mymath_d.o myprint_d.o -o libhello.so 
mymath_d.o:mymath.c 
	gcc -c -fPIC  mymath.c -o mymath_d.o 
myprint_d.o:myprint.c 
	gcc -c -fPIC  myprint.c -o myprint_d.o 

libhello.a:mymath.o myprint.o
	ar -rc libhello.a mymath.o myprint.o  
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:output
output:
	mkdir -p output/include
	mkdir -p output/lib
	cp -rf *.h output/include
	cp -rf *.so output/lib
	cp -rf *.a output/lib

.PHONY:clean
clean:
	rm -rf *.a output *.o *.out

效果:

使用动静态库

  1. 头文件gcc的默认搜索路径是 : /usr/include
  2. 库文件的默认搜索路径是: /lib64 or /usr/lib64

使用动静态库规则:  当动静态库同时存在时, 默认优先使用动态库

1.使用静态库

方法一: 告诉gcc头文件和库的路径在那  

命令:

gcc main.c -I ./output/include/ -L ./output/lib/ -lhello

-I: include (头文件)     -L: lib(库)    -lhello    -l(link),去掉前缀lib,后缀.a

效果:

方法二:拷贝库到系统下

命令:

sudo cp  output/include/* /usr/include //拷贝头文件
sudo cp  output/lib/libhello.a /lib64  //拷贝静态库

效果:

但不建议这么做: 避免自己写的文件, 污染系统库

上面将库拷贝到系统库就叫做库的安装

2.使用动态库

问题引入

这里执行的命令和运行静态库的命令一样, 但是运行的时候报错了 说没有这个文件

说明当动静态库同时存在的时候, 默认优先使用动态库

原因: 我们是在编译的时候, 告诉gcc库的路径在哪, 运行加载的时候并没有告诉系统在哪

解决方法: 1.使用静态库 2. 配置环境变量

方法一: 加static

static的意义: 摒弃优先使用动态库的原则, 使用静态库

命令:

gcc main.c -I ./output/include/ -L ./output/lib/ -lhello -static

 效果:

方法二: 配置环境变量 LD_LIBRARY_PATH

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:绝对路径(不用带库名)

效果;

缺点:重新登陆后失效 

原因: 这是内存级的环境变量  重新登陆后LD_LIBRARY_PATH, 会在系统配置文件里再去拿

,会把之前的环境变量清掉

方法三:修改配置文件

ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新

sudo touch /etc/ld.so.conf.d/文件名.conf //创建文件
sudo vim /etc/ld.so.conf.d/文件名.conf   //然后把库的路径放进去就行

1.创建文件,并将路径写到文件中

2.sudo ldconfig更新一下

效果:

删除:

sudo rm /etc/ld.so.conf.d/文件名.conf
sudo ldconfig    

方法四:软链接添加到系统默认路径(不推荐)

sudo ln -s 动态库路径(绝对,带上库) /lib64/libhello.so(库名)

删除:

sudo unlink /lib64/库名

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

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

相关文章

通过IP地址管理提升企业网络安全防御

在今天的数字时代&#xff0c;企业面临着越来越多的网络安全威胁。这些威胁可能来自各种来源&#xff0c;包括恶意软件、网络攻击和数据泄露。为了提高网络安全防御&#xff0c;企业需要采取一系列措施&#xff0c;其中IP地址管理是一个重要的方面 1. IP地址的基础知识 首先&a…

04训练——基于YOLO V8的自定义数据集训练——训练结果说明

YOLOv8的训练执行情况指标说明 运行YOLO V8的训练代码将会看到以下执行的情况。 在上图中,我们可以看到每一轮训练的指标情况,YOLOv8训练过程中的输出指标具体介绍如下: • Epoch: 当前的训练轮数,一轮表示所有的训练数据都被模型处理一次。例如,1/100表示第一轮训练,总…

高效截屏方法,你值得拥有!在Windows10中截屏的3种方法

本文介绍如何在Windows 10中捕获屏幕截图&#xff0c;包括使用键盘组合、使用Snipping Tool、Snipp&Sketch Tool或Windows游戏栏。 使用WindowsPrtSc组合键截图 在Windows 10中捕获屏幕截图的最简单方法是按下键盘上的PrtScWindows键盘组合。你将看到屏幕短暂闪烁&#x…

微信小程序发布流程

前言 上周写了如何写一个小程序&#xff0c;然后经过查资料&#xff0c;改bug&#xff0c;找chatgpt美化页面&#xff0c;我写了一个计算代谢率的小工具&#xff0c;写完了之后该怎么办呢&#xff0c;当然是发布上架&#xff0c;然后我就开始了发布的折腾 提交代码 这一步很…

线性表相关知识

1.简述 线性表&#xff0c;全名为线性存储结构。使用线性表存储数据的方式可以这样理解&#xff0c;即“把所有数据按照顺序&#xff08;线性&#xff09;的存储结构方式&#xff0c;存储在物理空间”。 按照空间分类&#xff1a; 顺序存储结构&#xff1a;数据依次存储在连续…

可爱的回调函数

目录 一、作者声明&#xff1a; 二、什么回调函数&#xff1f; 三、库函数qsort为例&#xff0c;讲解回调函数 一、作者声明&#xff1a; 标题中的可爱纯纯是用来凑字数&#xff0c;没有特殊含义&#xff0c;因为可爱的平台不让用四个字作为标题&#xff01; 如果平台允许我…

上海未来产业创投联盟启动成立,和鲸Heywhale成为首批发起单位

第三届“海聚英才”全球创新创业峰会于近期举办&#xff0c;会上正式启动成立“上海未来产业创投联盟”&#xff0c;上海和今信息科技有限公司等 31 家单位成为首批发起单位。 为进一步坚定产业投资信心&#xff0c;营造最优人才生态&#xff0c;9月20日下午&#xff0c;第三届…

mstp vrrp bfd 实验

LSW1配置 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sys lsw1 [lsw1]vlan batch 10 20 30 [lsw1]int g0/0/1 [lsw1-GigabitEthernet0/0/1]port link-type access [lsw1-GigabitEthernet0/0/1]port default vlan 10 [lsw1-GigabitEthernet0…

ELK集群 日志中心集群

ES&#xff1a;用来日志存储 Logstash:用来日志的搜集&#xff0c;进行日志格式转换并且传送给别人&#xff08;转发&#xff09; Kibana:主要用于日志的展示和分析 kafka Filebeat:搜集文件数据 es-1 本地解析 vi /etc/hosts scp /etc/hosts es-2:/etc/hosts scp /etc…

铁路防护网RFID锁控,实现铁路防护网智能防盗防破坏

一、铁路防护网的挑战与需求 铁路防护网作为铁路运输系统中的重要组成部分&#xff0c;面临着安全性和防盗防破坏的挑战&#xff0c;传统的锁控系统存在以下问题&#xff1a; 1、安全隐患难以发现&#xff1a;传统锁控系统无法及时发现锁被剪断或破坏的情况&#xff0c;容易造…

【SpringCloud】Eureka原理分析、搭建Eureka服务、服务注册、服务发现

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 eureka 一、Eureka原理分析1.1 服务调用出现…

【项目开发 | C语言项目 | 贪吃蛇】

项目简单介绍 本项目是一个命令行版的贪吃蛇游戏。用户需要控制一个蛇在屏幕上移动&#xff0c;吃食物来增长&#xff0c;同时避免撞到边界和自己的身体。 一&#xff0c;开发环境需求 操作系统 &#xff1a;Windows 开发环境工具 &#xff1a;Qt, VSCode, Visual Studio 技…

springboot基于Web的社区医院管理服务系统springboot025

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

10.8队列安排,最少找字典次数,表达式转换与计算模拟(栈、队列)

队列安排1160 灵活的插入与删除 用队列实现的话&#xff0c;就是双端队列&#xff0c; 第一阶段是要找到对应编号的同学&#xff0c;然后根据p的取值决定是怎么插入 第二阶段也是要找到对应编号同学&#xff0c;之后就删除&#xff0c;如果找不到就返回 思路是这个思路&…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(六)

思维导图 一、正则表达式 1.1正则表达式介绍 1.2 语法 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewpor…

JavaWeb-Vue

JavaScript-Vue 什么是Vue Vue Vue是一套前端框架&#xff0c;免除原生JavaScript中的DOM操作&#xff0c;简化书写。基于MVVM&#xff08;Model-View-ViewModel&#xff09;思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上。官网&#xff1a;http…

若依项目新建子模块

官方资料:后台手册 | RuoYi 建设完成后测试详情 在新建的业务模块添加com.ruoyi.ding包&#xff0c;新建TestService.java&#xff1b; 在里面写测试代码返回hello 在ruoyi-admin新建测试类&#xff0c;调用helloTest&#xff0c;成功返回hello代表成功。

基于springboot实现汽车租赁管理系统项目演示【项目源码+论文说明】分享

基于springboot实现汽车租赁管理系统项目演示 摘要 随着社会的发展&#xff0c;计算机的优势和普及使得汽车租赁系统的开发成为必需。汽车租赁系统主要是借助计算机&#xff0c;通过对汽车租赁信息等信息进行管理。减少管理员的工作&#xff0c;同时也方便广大用户对个人所需汽…

(Vue3)defineOptions、defineModels Pinia及持久化

Vue3.3新特性defineOptions v-model和defineModel 开启特性vite.config.js中加配置 重启架子&#xff08;试验性质&#xff09;npm run dev Pinia Vue最新的状态管理工具&#xff0c;代替Vuex Pinia配置创建项目时自动添加 安装 npm install pinia 创建一个 pinia 实例 (根 s…

Unity简单操作:Unity接sdk写的java代码放在Plugins/Android/libs目录中即可被打进apk中,无需提前编译成jar

Unity项目&#xff0c;接入第三方sdk的时候&#xff0c;难免需要写一下java代码&#xff0c;之前的做法是把自己写的java先编译成一个game.jar&#xff0c;然后把game.jar放到Plugins/Android/libs目录中。 事实上&#xff0c;直接把java代码放在Plugins/Android/libs目录中即…