【Linux】Linux之基础IO

news2024/11/25 0:41:48

 我们先回顾一下C文件接口,

#include <stdio.h>
#include <string.h>

int main()
{
    FILE *fp = fopen("myfile", "w");
    if (!fp)
    {
        printf("fopen error!\n");
    }
    const char *msg = "hello world!\n";
    int count = 5;
    while (count--)
    {
        fwrite(msg, strlen(msg), 1, fp);
    }
    fclose(fp);
    return 0;
}

stdin & stdout & stderr

C 默认会打开三个输入输出流,分别是 stdin, stdout, stderr
仔细观察发现,这三个流的类型都是 FILE*, fopen 返回值类型,文件指针
总结:
r Open text file for reading.
  The stream is positioned at the beginning of the file.
r+ Open for reading and writing.
     The stream is positioned at the beginning of the file.
w Truncate( 缩短 ) file to zero length or create text file for writing.
    The stream is positioned at the beginning of the file.
w+ Open for reading and writing.
      The file is created if it does not exist, otherwise it is truncated.
      The stream is positioned at the beginning of the file.
a Open for appending (writing at end of file).
   The file is created if it does not exist.
   The stream is positioned at the end of the file.
a+ Open for reading and appending (writing at end of file).
     The file is created if it does not exist. The initial file position
     for reading is at the beginning of the file,
     but output is always appended to the end of the file.

 如上,是我们之前学的文件相关操作。还有 fseek ftell rewind 的函数。

系统文件I/O

操作文件,除了上述 C 接口(当然, C++ 也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问,先来直接以代码的形式,实现和上面一模一样的代码:
写文件:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()
{
    umask(0);
    int fd = open("myfile", O_WRONLY | O_CREAT, 0644);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    int count = 5;
    const char *msg = "hello bit!\n";
    int len = strlen(msg);
    while (count--)
    {
        write(fd, msg, len); // fd: 后面讲, msg:缓冲区首地址, len: 本次读取,期望写入多少个字节的数
        //据。 返回值:实际写了多少字节数据
    }
    close(fd);
    return 0;
}

接口介绍

open         man 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

write,read,close,lseek 类比C文件相关接口

open函数返回值

在认识返回值之前,先来认识一下两个概念:系统调用和库函数。

上面的 fopen fclose fread fwrite 都是 C 标准库当中的函数,我们称之为库函数( libc )。
而, open close read write lseek 都属于系统提供的接口,称之为系统调用接口
回忆一下我们讲操作系统概念时,画的一张图:
系统调用接口和库函数的关系一目了然,所以,可以认为,f#系列的函数,都是对系统调用的封装,方便二次开发。

文件描述符fd

通过对open函数的学习,我们知道了文件描述符就是一个小整数。

0 & 1 & 2

Linux进程默认情况下会有三个缺省打开的文件描述符,分别是标准输入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 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了fifile 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*fifiles, 指向一张表 fifiles_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数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

那么如果关闭fd1呢???

#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.这种现象叫做输出重定向。常见的重定向有:>,>>,<

重定向的本质是什么呢?

使用dup2系统调用

函数原型如下:

#include <unistd.h>

int dup2(int oldfd, int newfd);

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    int fd = open("./log", O_CREAT | O_RDWR);
    if (fd < 0)
    {
        perror("open");
        return 1;
    }
    close(1);
    dup2(fd, 1);
    for (;;)
    {
        char buf[1024] = {0};
        ssize_t read_size = read(0, buf, sizeof(buf) - 1);
        if (read_size < 0)
        {
            perror("read");
            break;
        }
        printf("%s", buf);
        fflush(stdout);
    }
    return 0;
}

printf C 库当中的 IO 函数,一般往 stdout 中输出,但是 stdout 底层访问文件的时候,找的还是 fd:1, 但此时, fd:1下标所表示内容,已经变成了myfifile 的地址,不再是显示器文件的地址,所以,输出的任何消息都会往文件中写入,进而完成输出重定向。

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 > file , 我们发现结果变成了,
我们发现 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 的时候看到的除了看到文件名,还看到了文件元数据。
每行包含7列:
  • 模式
  • 硬链接数
  • 文件所有者
  • 大小
  • 最后修改时间
  • 文件名

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

这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息,

上面的执行结果有几个信息需要解释清楚,

inode

为了能解释清楚inode我们先简单了解一下文件系统。

Linux ext2文件系统,上图位磁盘系统图,磁盘是典型的块设备,磁盘分区被划分为一个个block,一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024,2048或者4096字节。

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 节点表 : 存放文件属性 如 文件大小,所有者,最近修改时间等
数据区:存放文件内容
创建一个新文件主要有以下4个操作:
1. 存储属性
内核先找到一个空闲的 i 节点(这里是 263466 )。内核把文件信息记录到其中。
2. 存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块: 300,500 800 。将内核缓冲区的第一块数据复制到300 ,下一块复制到 500 ,以此类推。
3. 记录分配情况
文件内容按顺序 300,500,800 存放。内核在 inode 上的磁盘分布区记录了上述块列表。
4. 添加文件名到目录

新的文件名 abc linux 如何在当前的目录中记录这个文件?内核将入口( 263466 abc )添加到目录文件。文件名和inode 之间的对应关系将文件名和文件的内容及属性连接起来。

 理解硬链接

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

软链接

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

ACM

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
由系统指定的目录

生成动态库

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 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

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

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

相关文章

DORA 机器人中间件学习教程(5)——3D激光雷达数据可视化

DORA中3D激光雷达数据可视化 1 总体思路2 DORA数据接收节点3 编写yml文件4 启动节点参考资料 截止目前 DORA还没有类似于ROS2中RVIZ这样的可视化工具&#xff0c;而在调试算法时我们又需要将数据进行可视化以验证代码的正确性。目前的解决方法是将DORA中的数据发送到ROS2中&…

SpringCloud Gateway保姆级入门教程

什么是微服务网关 SpringCloud Gateway是Spring全家桶中一个比较新的项目&#xff0c;Spring社区是这么介绍它的&#xff1a; 该项目借助Spring WebFlux的能力&#xff0c;打造了一个API网关。旨在提供一种简单而有效的方法来作为API服务的路由&#xff0c;并为它们提供各种增强…

深度学习:开启人工智能的新纪元

深度学习&#xff1a;开启人工智能的新纪元 深度学习是机器学习的一个子领域&#xff0c;它基于人工神经网络的学习算法&#xff0c;特别是那些具有多个非线性变换的层&#xff08;即“深度”&#xff09;。这些算法模仿人脑处理信息的方式&#xff0c;通过学习数据的多层次表…

Python Appium自动化操作抖音

1、功能介绍 使用Python和Appium给手机抖音上的同城模块自动评论&#xff0c;主要是通过模拟用户在抖音同城模块的操作&#xff0c;实现自动发送评论的功能。具体步骤如下&#xff1a; - 安装并配置好Python环境&#xff1b; - 安装Appium库&#xff0c;用于自动化操作手机应…

AI与测试行业调研

业务方向及应用场景 方向 技术 应用 大语言模型 私有化大模型&#xff1a; llama2 privateGPT 业务分析 测试数据生成 机器学习、深度学习应用 视觉自动化&#xff1a; FastbotApplitools 视觉自动化 缺陷预测与挖掘 知识图谱 neo4j 测试用例生成 精准测试 大语言模…

从A到Z,一文通览Python所有内置函数:编程效率提升100%

Python中的内置函数&#xff0c;这些函数覆盖了从基本数据处理到高级编程功能的各种用途。下面&#xff0c;将逐一介绍每个函数的用途和提供相应的代码示例。 A abs()&#xff1a;返回数字的绝对值。 print(abs(-5)) # 输出: 5 aiter()&#xff1a;返回异步迭代器。 async…

【从零开始的LeetCode-算法】910. 最小差值 II

给你一个整数数组 nums&#xff0c;和一个整数 k 。 对于每个下标 i&#xff08;0 < i < nums.length&#xff09;&#xff0c;将 nums[i] 变成 nums[i] k 或 nums[i] - k 。 nums 的 分数 是 nums 中最大元素和最小元素的差值。 在更改每个下标对应的值之后&#xf…

jmeter 从多个固定字符串中随机取一个值的方法

1、先新增用户参数&#xff0c;将固定值设置为不同的变量 2、使用下面的函数&#xff0c;调用这写变量 ${__RandomFromMultipleVars(noticeType1|noticeType2|noticeType3|noticeType4|noticeType5)} 3、每次请求就是随机取的值了

驾驶员异常行为检测数据集(猫脸码客 第223期)

驾驶员异常行为检测数据集 随着智能交通系统的快速发展&#xff0c;驾驶员危险驾驶行为检测已成为确保道路安全、减少交通事故的重要手段。通过先进的图像处理和机器学习技术&#xff0c;可以实现对驾驶员行为的实时监测和预警&#xff0c;从而有效遏制危险驾驶行为的发生。本…

基于SpringBoot设计模式之结构型设计模式·桥接模式

文章目录 介绍开始架构图定义类的功能定义类的实现 测试样例 总结 介绍 将抽象部分与它的实现部分分离&#xff0c;使他们都可以独立地发生变化。 Bridge的意思是桥梁。就像在现实世界中&#xff0c;桥梁的功能是将河流的两侧连接起来一样, Bridge模式的作用也是将两样东西连接…

FPGA图像处理之中值滤波

文章目录 一、什么是中值滤波&#xff1f;二、均值滤波和中值滤波对比三、FPGA实现3.1 Verilog代码3.2 仿真验证 一、什么是中值滤波&#xff1f; 在前一篇《FPGA图像处理之均值滤波》中&#xff0c;我们了解到了图像处理中常遇到的一些噪声类型以及均值滤波的原理以及实现。我…

【Linux】实现倒计时、进度条、gdb

文章目录 缓冲区1.概念2.作用3.刷新策略4.缓冲区位置 实现倒计时实现进度条Linux调试器----gdb 缓冲区 1.概念 缓冲区是计算机内存的一部分&#xff0c;用于暂时存储数据。它在数据传输过程中起到一个缓冲桥梁的作用&#xff0c;帮助协调数据传输的速度差异。缓冲区可以是磁盘…

魔百和 HG680-MC免拆 2+8免拆机卡刷固件

HG680MC免拆完美bin卡刷24年10月21日版本【修复语音、ADB端口9999、开启ADB的Root权限】 更新特点&#xff1a; 1.更新开机桌面为SMART动画&#xff1b; 2.安装app自动安装&#xff0c;无需点击确认、下一步等提示按钮内容; 3.ADB端口号为9999&#xff1b; 4.ADB权限为完美RO…

wireshark 解密浏览器https数据包

一、导出浏览器证书有两种方法 1、在浏览器快捷方式追加启动参数&#xff1a; --ssl-key-log-file"d:\log\2.log" C:\Users\Administrator\AppData\Local\Google\Chrome\Application\chrome.exe --ssl-key-log-file"d:\log\2.log" 2、环境变量中新建用…

SpringbootSpringcloud面试题(含代码及展示图)

1 简单说一下springboot? Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程。 -使用springboot以后,搭建一个spring应用和开发&#xff0c;部署变得很简单. 2 怎么简单实现一个springboot应用 1&#xff09;新建ma…

SpringBoot获取bean的几种方式

目录 一、BeanFactory与ApplicationContext的区别 二、通过BeanFactory获取 三、通过BeanFactoryAware获取 四、启动获取ApplicationContext 五、通过继承ApplicationObjectSupport 六、通过继承WebApplicationObjectSupport 七、通过WebApplicationContextUtils 八、通…

Chrome DevTools 三: Performance 性能面板扩展—— 性能优化

Performance 性能 &#xff08;一&#xff09;性能指标 首次内容绘制 (First Contentful Paint&#xff0c;FCP)&#xff1a; 任意内容在页面上完成渲染的时间 最大内容绘制 (Largest Contentful Paint&#xff0c;LCP)&#xff1a; 最大内容在页面上完成渲染的时间 第一字节…

创客项目秀|基于XIAO ESP32C3的本地个人助理Mr.M

作者&#xff1a;Matthew Yu 来自&#xff1a;Fab academy 在数字化时代的浪潮中&#xff0c;柴火创客空间作为创新与实践的摇篮&#xff0c;不仅为Fab Academy 2024的学员们提供了一个充满活力的学习和创作环境&#xff0c;更是将科技的力量与人文关怀深度融合。今天&#x…

Redis——事务

文章目录 Redis 事务Redis 的事务和 MySQL 事务的区别:事务操作MULTIEXECDISCARDWATCHUNWATCHwatch的实现原理 总结 Redis 事务 什么是事务 Redis 的事务和 MySQL 的事务 概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执行 Redis 的事务和 MySQL 事务的区别:…

【Next.js 项目实战系列】07-分配 Issue 给用户

原文链接 CSDN 的排版/样式可能有问题&#xff0c;去我的博客查看原文系列吧&#xff0c;觉得有用的话&#xff0c;给我的库点个star&#xff0c;关注一下吧 上一篇【Next.js 项目实战系列】06-身份验证 分配 Issue 给用户 本节代码链接 Select Button​ # /app/issues/[i…