Linux操作系统——第三章 基础IO

news2024/11/27 13:45:12

 

 

目录

接口介绍

open 

文件描述符fd

0 & 1 & 2

文件描述符的分配规则

重定向

FILE

理解文件系统

inode

​编辑 

理解硬链接

软链接

动态库和静态库

静态库与动态库 

生成静态库

库搜索路径

生成动态库

使用动态库

运行动态库

使用外部库




接口介绍




open 


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写

返回值:
成功:新打开的文件描述符
失败:-1

open 函数具体使用哪个,和具体应用场景相关。如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open


 

 fopen fclose fread fwrite 都是C标准库当中的函数,我们称之为库函数(libc)。
而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口。

f#系列的函数,都是对系统调用的封装,方便二次开发



文件描述符fd




0 & 1 & 2



Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
0,1,2对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式: 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
        char buf[1024];
        ssize_t s = read(0, buf, sizeof(buf));
        if(s > 0){
                buf[s] = 0;
        write(1, buf, strlen(buf));
        write(2, buf, strlen(buf));
        }
return 0;
}

文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件 



文件描述符的分配规则



#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
        int fd = open("myfile", O_RDONLY);
        if(fd < 0){
                perror("open");
return 1;
}
        printf("fd: %d\n", fd);
        close(fd);
return 0;

 输出发现是 fd: 3


关闭0或者2:


#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
        close(0);
        //close(2);

        int fd = open("myfile", O_RDONLY);
                if(fd < 0){
        perror("open");
return 1;
}
        printf("fd: %d\n", fd);
        close(fd);
return 0;
}

发现是结果是: fd: 0 或者 fd 2

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



重定向



那如果关闭1呢?看代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
        close(1);
        int fd = open("myfile", O_WRONLY|O_CREAT, 00644);
        if(fd < 0){
        perror("open");
return 1;
}
        printf("fd: %d\n", fd);
        fflush(stdout);
        close(fd);
exit(0);

 本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出
重定向。常见的重定向有:>, >>, <



FILE



 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
所以C库当中的FILE结构体内部,必定封装了fd。

#include <stdio.h>
#include <string.h>
int main()
{
        const char *msg0="hello printf\n";
        const char *msg1="hello fwrite\n";
        const char *msg2="hello write\n";
        printf("%s", msg0);
        fwrite(msg1, strlen(msg0), 1, stdout);
        write(1, msg2, strlen(msg2));
        fork();
return 0;

}

hello printf
hello fwrite
hello write

但如果对进程实现输出重定向呢? ./hello > file , 我们发现结果变成了
hello write
hello printf
hello fwrite
hello printf
hello fwrite 

发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!
一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。
printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据
的缓冲方式由行缓冲变成了全缓冲。
而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后
但是进程退出之后,会统一刷新,写入文件当中。
但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的
一份数据,随即产生两份数据。
write 没有变化,说明没有所谓的缓冲。


综上:

printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,
都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。
那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统
调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。 



理解文件系统



ls -l的时候看到的除了看到文件名,还看到了文件元数据

ls -l


总用量 12
-rwxr-xr-x. 1 root root 7438 "9月 13 14:56" a.out
-rw-r--r--. 1 root root 654 "9月 13 14:56" test.c 

每行包含7列:

  • 模式
  • 硬链接数
  • 文件所有者
  • 大小
  • 最后修改时间 
  • 文件名

ls -l读取存储在磁盘上的文件信息,然后显示出来

 


inode


 

上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被
划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的

Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。
超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了
GDT,Group Descriptor Table:块组描述符,描述块组属性信息
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
i节点表: 存放文件属性 如 文件大小,所有者,最近修改时间等
数据区:存放文件内容

创建一个新文件(比如文件名abc)主要有一下4个操作:

[root@localhost linux]#touch abc
[root@localhost linux]# ls -i abc
263466 abc

  • 1. 存储属性:内核先找到一个空闲的i节点(比如是263466)。内核把文件信息记录到其中。
  • 2. 存储数据:该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。
  • 3. 记录分配情况:文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  • 4. 添加文件名到目录:新的文件名abc。linux如何在当前的目录中记录这个文件?内核将入口(263466,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。 

理解硬链接



我们看到,真正找到磁盘上文件的并不是文件名,而是inode。 其实在linux中可以让多个文件名对应于同一个inode。

[root@localhost linux]# touch abc

[root@localhost linux]# ln abc def

[root@localhost linux]# ls -1i
abc def 263466 abc 263466 def
abc和def的链接状态完全相同,他们被称为指向文件的硬链接。内核记录了这个连接数,inode
263466 的硬连接数为2。
我们在删除文件时干了两件事情:1.在目录中将对应的记录删除,2.将硬连接数-1,如果为0,则将对应的磁盘释放 


软链接



硬链接是通过inode引用另外一个文件,软链接是通过名字引用另外一个文件,在shell中的做法

263563 -rw-r--r--. 2 root root 0 9月 15 17:45 abc
261678 lrwxrwxrwx. 1 root root 3 9月 15 17:53 abc.s -> abc
263563 -rw-r--r--. 2 root root 0 9月 15 17:45 def

下面解释一下文件的三个时间:
Access 最后访问时间
Modify 文件内容最后修改时间
Change 属性最后修改时间



动态库和静态库




静态库与动态库 


静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间


生成静态库


[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o


生成静态库
[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)


查看静态库中的目录列表
[root@localhost linux]# ar -tv libmymath.a
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出静态库中的文件
v:verbose 详细信息


[root@localhost linux]# gcc main.c -L. -lmymath
-L 指定库路径
-l 指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行


库搜索路径



从左到右搜索-L指定的目录。
由环境变量指定的目录 (LIBRARY_PATH)
由系统指定的目录
/usr/lib
/usr/local/lib 


生成动态库



shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so


示例:

[root@localhost linux]# gcc -fPIC -c sub.c add.c

[root@localhost linux]# gcc -shared -o libmymath.so* .o

[root@localhost linux]# ls add.c

add.h add.o libmymath.so main.c sub.c sub.h sub.o



使用动态库



编译选项
l:链接动态库,只要库名即可(去掉lib以及版本号)
L:链接库所在的路径.
示例: gcc main.o -o main –L. -lhello



运行动态库



1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib
2、更改 LD_LIBRARY_PATH

[root@localhost linux]# export LD_LIBRARY_PATH=.
[root@localhost linux]# gcc main.c -lmymath
[root@localhost linux]# ./a.out
add(10, 20)=30
sub(100, 20)=80

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

[root@localhost linux]# cat /etc/ld.so.conf.d/bit.conf
/root/tools/linux
[root@localhost linux]# ldconfig


使用外部库



系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成 

                   

 

 

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

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

相关文章

(顶刊复现)配电网两阶段鲁棒故障恢复(matlab实现)

参考文献&#xff1a; X. Chen, W. Wu and B. Zhang, "Robust Restoration Method for Active Distribution Networks," in IEEE Transactions on Power Systems, vol. 31, no. 5, pp. 4005-4015, Sept. 2016, doi: 10.1109/TPWRS.2015.2503426. 1.研究背景 1.1摘…

2023 Navicat for Redis 与 Navicat Premium 16.2 现已正式发布 | 释放 Redis 全部潜能

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

【C++】 Lambda表达式详解

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 什么是Lambda表达式Lambda 表达式的各个部分 2️⃣ 优缺点优点缺点 3️⃣ 使用场景在线C工具STL算法库STL容器中需要传递比较函数&#xff08;示例失败了&#xff09;多线程示例 4️⃣ Lambda表达式与函数指针的比较5️⃣ 捕获列表…

KISS复盘法

KISS复盘法 KISS复盘法是一种科学的项目复盘方法&#xff0c;能够把过往经验转化为实践能力&#xff0c;以促进下一次活动更好地展开&#xff0c;从而不断提升个人和团队的能力&#xff01; 模型介绍 【复盘】原是围棋术语&#xff0c;本意是对弈者在下完一盘棋之后&#xff0…

距离保护原理

距离保护是反映故障点至保护安装处的距离&#xff0c;并根据距离的远近确定动作时间的一种保护。故障点距保护安装处越近&#xff0c;保护的动作时间就越短&#xff0c;反之就越长&#xff0c;从而保证动作的选择性。测量故障点至保护安装处的距离&#xff0c;实际上就是用阻抗…

Spring Boot banner详解

Spring Boot 3.x系列文章 Spring Boot 2.7.8 中文参考指南(一)Spring Boot 2.7.8 中文参考指南(二)-WebSpring Boot 源码阅读初始化环境搭建Spring Boot 框架整体启动流程详解Spring Boot 系统初始化器详解Spring Boot 监听器详解Spring Boot banner详解 自定义banner Spring …

快速排序算法的编码和优化

快速排序的基本思路是&#xff1a; 先通过第一趟排序&#xff0c;将数组原地划分为两部分&#xff0c;其中一部分的所有数据都小于另一部分的所有数据。原数组被划分为2份通过递归的处理&#xff0c; 再对原数组分割的两部分分别划分为两部分&#xff0c;同样是使得其中一部分…

springboot+java高校教材征订管理系统

教材管理系统从功能、数据流程、可行性、运行环境等方面进行需求分析。对教材管理系统的数据库、功能进行了详细设计。分析了主要界面设计和相关组件设计&#xff0c;对教材管理系统的具体实现进行了介绍。 采用Java技术&#xff0c;从数据库中获取数据、向数据库中写入数据&am…

Linux 4.10 将带来深远影响的三项小改变

Linux的演进永不停歇。Linus Torvalds一直在努力工作&#xff0c;希望能够在新的内核版本当中(4.11)融入更多变化。不过在目前的Linux 4.10中&#xff0c;我们同样发现了三组能够有效提升性能并实现多种前所未有功能集的变更。 Linux的演进永不停歇。Linus Torvalds一直在努力…

如何快速写出一个完整的测试用例

一、前言 测试工作中最为基础核心的内容就是设计测试用例&#xff0c;我们一般会认为数量越少、发现缺陷越多的用例就是好的用例。那么&#xff0c;怎样才能设计出好的测试用例呢&#xff1f;本次专题就向大家介绍如何编写一个完整且靠谱的测试用例。 二、测试用例的重要性 …

使用IPSW文件将iOS系统从Beta恢复到稳定正式版教程

起因 作为一名iOS开发者&#xff0c;为了拥抱新系统&#xff08;手贱&#xff09;&#xff0c;将开发机升级到了最新的iOS 17 Beta版本&#xff0c;从而导致使用现有的Xcode无法成功配对该版本系统。故准备想方设法回滚到原先的iOS 16.5稳定版 回滚方式 若要将iOS设备回退至…

【MySQL高级篇笔记-数据库其它调优策略(中) 】

此笔记为尚硅谷MySQL高级篇部分内容 目录 一、数据库调优的措施 1、调优的目标 2、如何定位调优问题 3、调优的维度和步骤 二、优化MySQL服务器 1、优化服务器硬件 2、优化MySQL的参数 三、优化数据库结构 1、拆分表&#xff1a;冷热数据分离 2、增加中间表 3、增加…

CKA 01_docker部署Kubernetes 部署docker 使用kubeadm引导集群 安装Pod网络

文章目录 1. 虚拟机步骤2. Docker 部署 Kubernetes2.1 部署 docker2.1.1 环境要求2.1.2 安装 docker 引擎2.1.3 worker 节点对 master 节点免密2.1.4 设定 docker 开机自启2.1.5 打开桥接&#xff0c;查看桥接流量2.1.6 设定 systemd 方式管理 cgroup2.1.7 docker部署完成2.1.8…

光伏储能直流系统MATLAB仿真(PV光伏阵列+Boost DCDC变换器+负载+双向DCDC变换器+锂离子电池系统)

PV光伏阵列Boost DCDC变换器负载双向DCDC变换器锂离子电池系统 资源地址&#xff1a; 光伏储能直流系统MATLAB仿真&#xff08;PV光伏阵列BoostDCDC变换器负载双向DCDC变换器锂离子电池系统&#xff09;-Matlab文档类资源-CSDN文库 主要模块&#xff1a; PV光伏阵列、Boost…

这 3个Python 函数你知道吗?

动动发财的小手&#xff0c;点个赞吧&#xff01; 作为21世纪最流行的语言之一&#xff0c;Python当然有很多有趣的功能值得深入探索和研究。今天将介绍其中的三个&#xff0c;每个都从理论上和通过实际示例进行介绍。 我想要介绍这些函数的主要原因是它们可以帮助您避免编写循…

CKA 09_Kubernetes工作负载与调度 资源调度 三类QoS request 资源需求 limit 资源限额

文章目录 1. 资源调度1.1 准备工作1.2 为什么需要 request 和 limit1.3 内存限制1.3.1 Brustable1.3.2 Guaranteed1.3.3 BestEffort1.3.4 当容器申请的资源超出 limit 和 request 1.4 CPU限制 1. 资源调度 1.1 准备工作 Kubernetes 采用 request 和 limit 两种限制类型来对资源…

中国电子学会2023年05月份青少年软件编程Python等级考试试卷一级真题(含答案)

2023-05 Python一级真题 分数&#xff1a;100 题数&#xff1a;37 测试时长&#xff1a;60min 一、单选题(共25题&#xff0c;共50分) 1. 可以对Python代码进行多行注释的是&#xff1f;&#xff08;C &#xff09; A.# B." " C. D. 2. 下列可以作为Py…

ChatGPT有哪些神奇的使用方式? ChatGPT十大功能

原文&#xff1a;ChatGPT有哪些神奇的使用方式? ChatGPT十大功能_其他工具_软件教程_脚本之家 随着微软、百度等巨头加码&#xff0c;AIGC&#xff08;人工智能自动生成内容&#xff09;领域或将成为2023年最值得关注的、全球最热门赛道。AI大模型相当于是通过积累大量知识&a…

MT6705B 同步整流器

MT6705B 是用于反激式变换器的高性能45V 同步整流器。它兼容各种反激转换器类型。支持 DCM、CCM 和准谐振模式。MT6705B集成了一个40V功率MOSFET&#xff0c;可以取代肖特基二极管&#xff0c;提高效率。V SW <V TH-ON 时&#xff0c;内部 MOSFET 导通。V SW >V TH-OFF 时…

前端响应式布局--更新中

前端响应式布局原理与方案&#xff08;详细版&#xff09; 文章目录 媒体查询Grid布局Flex布局 响应式布局&#xff1a;在不同屏幕尺寸下&#xff0c; 同一页面有不同的布局。 传统的开发模式&#xff1a;PC端搞一套布局&#xff0c;移动端搞一套布局。 使用了响应式布局&#…