Linux基础IO

news2024/11/15 19:48:38

基础IO

    • 🏞️1. 系统文件I/O
      • 📖1.1 接口介绍
      • 📖1.2 creat系统调用
      • 📖1.3 理解fd文件描述符
      • 📖1.4 不按顺序的读取和写入
      • 📖1.5 文件重命名
    • 🌁2. 理解Linux下一切皆文件
    • 🌠3. 理解重定向
      • 📖3.1 重定向原理
      • 📖3.2 dup2系统调用
    • 🌿4. 理解缓冲区
      • 📖4.1 什么是缓冲区
      • 📖4.2 为什么要有缓冲区?
      • 📖4.3 缓冲区存在哪里?
    • 🍁5. 硬链接和软链接

🏞️1. 系统文件I/O

📖1.1 接口介绍

操作文件,除了语言级别的接口,我们还可以采用系统接口来进行文件访问.

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:追加写

image-20221119000130029

返回值:

成功:新打开的文件描述符

失败:-1

介绍了这个接口之后,让我们先来简单使用一下它:

int fd = open("test.txt", O_CREAT | O_WRONLY | O_TRUNC);

函数open()接受一些不同的标志. 在本例中,程序创建文件(O_CREAT),只能写入该文件,因为以(O_WRONLY)这种方式打开,并且如果该文件已经存在,则首先将其截断为0字节大小,删除所有现有内容(O_TRUNC).

write()系统调用:

ssize_t write(int fd, const void *buf, size_t count);
int main()
{
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC);

    ssize_t s = write(fd, "aaa", 3);

    close(fd);

    return 0;
}

read()的第一个参数是文件描述符,从而告诉文件系统读取哪个文件,一个进程当然可以同时打开多个文件,因此文件描述符fd使操作系统能够知道某个特定的读取引用了哪个文件.

第二个参数指向一个用于放置read()结果的缓冲区.

第三个参数是缓冲区的大小.

📖1.2 creat系统调用

补充:creat()系统调用

创建文件的旧方法是调用creat(),如下所示:

int fd = creat("foo");

你可以认为creat()open()加上以下标志:O_CREAT | O_WRONLY | O_TRUNC,因为open()可以创建一个文件,所以creat()的用法有些失宠(实际上,它可能就是实现对open()的一个库调用),然而,它确实在UNIX知识中占有一席之地.

📖1.3 理解fd文件描述符

open()的一个重要方面是它的返回值:文件描述符.

文件描述符只是一个整数,是每个进程私有的,在Linux系统中用于访问文件,因此,一旦文件被打开,你就可以使用文件描述符来读取或写入文件,假如你有权这样做.

那么,我们所,open()系统调用的返回值是文件描述符,不妨我们将它打印出来看看:

image-20221119011839501

观察结果为:3

接下来,不妨我们多创建几个文件看看:

int main()
{
    int fd1 = open("test1.txt", O_WRONLY | O_CREAT | O_TRUNC);
    int fd2 = open("test2.txt", O_WRONLY | O_CREAT | O_TRUNC);
    int fd3 = open("test3.txt", O_WRONLY | O_CREAT | O_TRUNC);
    int fd4 = open("test4.txt", O_WRONLY | O_CREAT | O_TRUNC);
    int fd5 = open("test5.txt", O_WRONLY | O_CREAT | O_TRUNC);

    printf("fa1: %d\n", fd1);
    printf("fa2: %d\n", fd1);
    printf("fa3: %d\n", fd1);
    printf("fa4: %d\n", fd1);
    printf("fa5: %d\n", fd1);


    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);

    return 0;
}

image-20221119012243839

image-20221119013219987

image-20221119014518131

image-20221119015434904

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

📖1.4 不按顺序的读取和写入

到目前为止,我们已经讨论过了如何读取和写入文件,但所有访问都是顺序的,也就是说,我们从头到尾读取一个文件,或者从头到尾写一个文件,但是,有时能够读取或写入文件中的特定偏移量是有用的,例如:如果在文本文件上构建了索引并利用它来查找单词,最终可能会从文件中的某些随机偏移量中读取数据,为此,我们将使用lseek()系统调用,下面是函数原型:

off_t lseek(int fd, off_t offset, int whence);
If whence is SEEK_SET, the offset is set to offset bytes.
If whence is SEEK_CUR, the offset is set to its current location plus offset bytes.
If whence is SEEK_END, the offset is set to the size of the file plus offset bytes.

📖1.5 文件重命名

有了一个文件后,有时需要给一个文件一个不同的名字,在命令行键入时,这是通过mv命令完成的,在下面的例子中,文件foo被重命名为bar.

prompt> mv foo bar

利用strace,我们可以看到mv使用了系统调用rename(char* old, char* new),它只需要两个参数:文件的原来名称和新名称.

rename()调用提供了一个有用的保证:它通常是一个原子调用,无论系统是否崩溃,如果系统在重命名期间崩溃,文件将被命名为旧名称或新名称,不会出现奇怪的中间状态.

int fd = open("foo.txt.tmp", O_WRONLY|O_CREAT|O_TRUNC);
write(fd, buffer, size); // write out new version of file
fsync(fd);
close(fd);
rename("foo.txt.tmp", "foo.txt");

这个例子中,编辑器做的事很简单:将文件的新版本写入临时名称(foot.txt.tmp),使用 fsync()将其强制写入磁盘. 然后,当应用程序确定新文件的元数据和内容在磁盘上,就将临时文件重命名为原有文件的名称. 最后一步自动将新文件交换到位,同时删除旧版本的文件,从而实现原子文件更新.

🌁2. 理解Linux下一切皆文件

在文件描述符中,0、1、2分别对应stdin, stdout, stderr,可是stdin是键盘,stdout是显示器,这也用文件来标识吗?

所以,我们应该如何用文件来标识硬件呢?又如何理解Linux下一切皆文件?

image-20221207223126573

所以,当我们要向硬盘中写入数据时,操作系统的内存文件系统为硬盘创建一个struct file结构,并将struct file结构体中的读写方法函数指针指向对应硬盘的驱动读写方法,当我们想要写入数据时,用struct file中的读写方法便可以完成我们的操作,也实现了以统一的方式看待所有的设备,即Linux下一切皆文件.

🌠3. 理解重定向

📖3.1 重定向原理

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

int main()
{
    //关闭了stdout
    close(1);
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

    if(fd < 0)
    {
        perror("open");
        return 0;
    }

    fprintf(stdout, "打开文件成功, fd: %d\n", fd);

    fflush(stdout);
    close(fd);

    return 0;
}

在这段代码中,进程将自己的stdout关闭,然后打开一个文件,文件描述符的分配规则:遍历文件描述符表,将第一个未分配的下标分配给当前打开的文件,也就是说,fd1,当我们向stdout中写入数据时,即对应fd为1的文件,本应该写到显示器上的内容就写入到了log.txt中,这不就是重定向吗?

重定向原理:

image-20221207224444432

📖3.2 dup2系统调用

#include <unistd.h>

int dup2(int oldfd, int newfd);

dup2系统调用的作用是将本应该写入newfd中的内容写到oldfd中.

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

int main()
{
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);

    if(fd < 0)
    {
        perror("open");
        return 0;
    }

    //将本该写入到文件描述符1中的内容写入到fd中
    dup2(fd, 1);
	
    fprintf(stdout, "打开文件成功, fd: %d\n", fd);

    fflush(stdout);
    close(fd);

    return 0;
}

🌿4. 理解缓冲区

📖4.1 什么是缓冲区

缓冲区的本质就是一段内存.

📖4.2 为什么要有缓冲区?

  1. 解放使用缓冲区的进程时间,可以只将写入放在缓冲区中,就可以做其他任务
  2. 缓冲区的存在可以集中处理数据刷新,减少IO次数,从而达到提高整机效率的目的.

📖4.3 缓冲区存在哪里?

对于我们在进行文件操作时,缓冲区其实是封装在文件FILE结构体中的,可以通过设置刷新策略,来决定什么时候将缓冲区的数据刷新,即什么时候调用write()系统调用.

刷新的本质:把缓冲区的数据write到OS内部,清空缓冲区.

缓冲区存在FILE内部,在C语言中,我们每次打开一个文件,都有一个对应的FILE* 返回,那么也就意味着,每一个文件都有一个fd和属于它自己的语言级别缓冲区.

接下来,我们可以自己试着封装一个FILE来模拟一下这个缓冲区的实现:

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

#define NUM 1024

#define NONE_FLUSH 0x0
#define LINE_FLUSH 0x1
#define FULL_FLUSH 0x2

typedef struct _MyFILE
{
    int _fileno;
    char _buffer[NUM];
    int _end;
    int _flags;  //fflush method
}MyFILE;

MyFILE* my_fopen(const char* filename, const char* method)
{
    assert(filename);
    assert(method);

    int flags = O_RDONLY;

    if(strcmp(method, "r") == 0)
    {}
    else if(strcmp(method, "r+"))
    {}
    else if(strcmp(method, "w") == 0)
    {
        flags = O_WRONLY | O_CREAT | O_TRUNC;
    }
    else if(strcmp(method, "w+") == 0)
    {}
    else if(strcmp(method, "a") == 0)
    {
        flags = O_WRONLY | O_CREAT | O_APPEND;
    }
    else if(strcmp(method, "a+") == 0)
    {}

    int fileno = open(filename, flags, 0666);
    if(fileno < 0)
    {
        return NULL;
    }

    MyFILE* fp = (MyFILE*)malloc(sizeof(MyFILE));
    if(fp == NULL)
        return fp;
    
    memset(fp, 0, sizeof(MyFILE));
    fp->_fileno = fileno;
    fp->_flags |= LINE_FLUSH;
    fp->_end = 0;

    return fp;
}

void my_fflush(MyFILE* fp)
{
    assert(fp);

    if(fp->_end > 0)
    {
        write(fp->_fileno, fp->_buffer, fp->_end);
        fp->_end > 0;
        syncfs(fp->_fileno);
    }
}

void my_fwrite(MyFILE* fp, const char* start, int len)
{
    assert(fp);
    assert(start);
    assert(len > 0);

    //将数据写入缓冲区
    strncpy(fp->_buffer + fp->_end, start, len);
    fp->_end += len;

    if(fp->_flags & NONE_FLUSH)
    {}
    else if(fp->_flags & LINE_FLUSH)
    {
        if(fp->_end > 0 && fp->_buffer[fp->_end - 1] == '\n')
        {
            //写到内核中
            write(fp->_fileno, fp->_buffer, fp->_end);
            fp->_end = 0;
            syncfs(fp->_fileno);
        }
    }
    else if(fp->_flags & FULL_FLUSH)
    {}
}

void my_fclose(MyFILE* fp)
{
    my_fflush(fp);
    close(fp->_fileno);
    free(fp);
}

🍁5. 硬链接和软链接

在谈论软硬链接之前,我们先使用一下它:

建立软链接:

image-20221208135300869

建立硬链接:

image-20221208135508917

image-20221208135628918

那么,我们明显观察到了软链接和硬链接的一个不同:

软链接的inode编号与它所链接的文件不同,而硬链接的inode编号与它所链接的文件是相同的.

其实,软链接是一个独立的文件,而硬链接就是单纯在Linux指定的目录下,给指定的文件新增文件名和inode编号的映射关系.

怎么使用软硬链接呢?

首先我们来看软链接的使用

现在,我们在距离当前目录较远的目录下建立一个可执行程序:

image-20221208140510707

如果不使用软链接,我们在执行可执行程序的时候就需要带上指定路径,但有了软链接,我们就可以在当前目录下为它建立一个软链接,从而直接执行它:

image-20221208140719874

硬链接的使用:

在使用硬链接之前,首先我们来认识一个叫硬链接数的概念:

image-20221208140942036

硬链接数本质就是该文件inode属性中的一个计数器,标识有几个文件名和我的inode建立了映射关系.

一个普通文件的默认硬链接数就是1,因为它自己的文件名就和它的inode建立了映射.

创建一个空的目录,它的默认链接数是2:

image-20221208141337659

因为除了它自己的文件名和自己的inode建立了链接,在它的目录下,还有一个.与它的inode也建立了映射.

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

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

相关文章

5G LAN赋能智慧工厂加速落地,四信5G工业路由器成最佳助攻

近年来&#xff0c;随着5G技术的蓬勃发展&#xff0c;信息技术与企业发展更加紧密结合已经成为未来企业发展的主要趋势。而作为“新基建”的代表&#xff0c;5G与工业网络的融合无疑成为未来5G网络的一个重要应用场景。 然而&#xff0c;在传统的工厂制造中&#xff0c;大多数设…

Java基础-面向对象综合练习-拼图游戏

1. 设计游戏的目的 锻炼逻辑思维能力利用Java的图形化界面&#xff0c;写一个项目&#xff0c;知道前面学习的知识点在实际开发中的应用场景 2. 游戏的最终效果呈现 Hello&#xff0c;各位大家好。今天&#xff0c;我们要写一个非常有意思的小游戏 —《拼图小游戏》 我们先来…

ADI Blackfin DSP处理器-BF533的开发详解5:PLL配置内核和系统时钟(含源代码)

又是一个熟悉的名词&#xff0c;PLL。 硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 功能介绍 PLL(Phase Locked Loop)是 ADSP-BF53x 的内核和时钟设置的机制&#xff0c;叫做锁相环。通过 P…

论文阅读笔记 | 三维目标检测——CenterPoint算法

如有错误&#xff0c;恳请指出。 文章目录0. 前言1. 背景2. 准备工作3. 网络结构3.1 RPN3.2 RCNN4. 实验结果paper&#xff1a;《Center-based 3D Object Detection and Tracking》&#xff08;2021CVPR&#xff09; 0. 前言 CenterPoint应该是与CenterNet是同一个团队的成果…

[附源码]Python计算机毕业设计Django在线项目管理

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

庄懂的TA笔记(十一)< >

庄懂的TA笔记&#xff08;十一&#xff09;&#xff1c; &#xff1e; 目录&#xff1a; 一、shader面板玩法 > 增广 : 多种 面板设计玩法&#xff1a; 二、shader参数类型 > 增广 &#xff1a;认识 多种参数类型 &#xff1a; 三、增广 &#xff1a;认识 IVO …

体验一下传说中可以干掉程序员的OpenAI ChatGPT

ChatGPT初体验 ​ OpenAI公司最近开发了一个新的聊天AI模型&#xff0c;ChatGPT。网上铺天盖地传言AI可以替代程序员。于是今天也来体验了一下。下面是一个使用的案例&#xff1a; ​ 首先看来他也不是万能的。 问&#xff1a; 今年世界杯冠军预测 ChatGPT: 对于世界杯的冠军…

【20221208】【排序专题】

一、冒泡排序&#xff08;稳定排序&#xff09; 思想&#xff1a;冒泡排序的思想就是比较当前数和后一个数的大小&#xff0c;将较大的数往后移动&#xff0c;这样可以确保一轮下来能将最大的数放在数组的最末端。然后重复此操作即可完成排序。 上面第一轮比较完&#xff0c;我…

VSCode无法远程连接VMWare(Ubuntu)其中一个可能原因(inet消失)

有时候把虚拟机挂起后&#xff0c;等自己下次再想用&#xff0c;打开vscode用之前的ssh配置&#xff0c;却始终无法连接&#xff0c;进程sshd也是开启的&#xff0c;就很纳闷。 然后么&#xff0c;咱就想查查是不是网络的问题&#xff0c;毕竟ssh是通过ipv4连的虚拟机&#xff…

【explain执行计划】MySQL性能调优

explain是MySQL性能调优过程中必须掌握的工具&#xff0c;explain结果中常见的type结果及代表的含义&#xff0c;并且通过同一个SQL语句的性能差异&#xff0c;说明建立正确的索引多么重要。 explain结果中的type字段代表什么意思&#xff1f; MySQL的官网解释非常简洁&#x…

前端埋点分析

第三方代码埋点 已经有很多成型的服务商了如友盟&#xff0c;百度统计等&#x1f32f;&#xff0c;都提供了比较成型的方案&#x1f3a8;&#xff0c;并可以在后台管理系统中查看比较详细的数据分析 自己动手 优点&#xff1a; 控制精准&#xff0c;可以非常精确地选择什么时…

Bulma的简单使用

文章目录1. 引入Bulma2. 字体和颜色3. 间距和容器1. 引入Bulma 通过 CDN 引入&#xff1a; <style>import "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.4/css/bulma.min.css";</style>官网还有其他多种引入方式。 2. 字体和颜色 在 Bulma 中&…

基于jQuery或Zepto的图片延迟加载插件

当我们网站的页面图片过多时&#xff0c;加载速度就会很慢。尤其是用手机2G/3G访问页面&#xff0c;不仅页面慢&#xff0c;而且还会用掉很多流量。我们主题之前也都采用了图片的懒加载形式&#xff0c;但都不完美&#xff0c;部分主题还采用了占位图片来控制懒加载&#xff0c…

CP2102N高度集成USB全速带电池充电检测控制芯片

目录CP2102N简介主要特点芯片特性CP2102N开发板开发板功能应用领域CP2102N简介 CP2102N是USBXpress系列中新的一款高度集成的USB转RS232的桥接芯片。不但较上一代CP210X系列更有成本效益&#xff0c;而且在功能上也有更多创新。其中&#xff0c;符合USB-BCS 1.2规范的充电器识…

【人脸识别】基于直方梯度图 HDGG 的人脸识别算法研究附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步进步&#xff0c;matlab项目目标合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信息&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算…

入门系列 - Git的管理修改与撤销修改

本篇文章&#xff0c;是基于我自用Linux系统中的自定义文件夹“test_rep”&#xff0c;当做示例演示 具体Git仓库的目录在&#xff1a;/usr/local/git/test_rep Git的管理修改与撤销修改 在讲这个之前&#xff0c;还是有必要再阐述一下“工作区和暂存区”。Git 和其它版本控制…

计算机毕业论文java毕业设计选题源代码S2SH校园BBS论坛系统

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 计算机毕业设计java毕设之S2SH校园论坛_哔哩哔哩_bilibili计算机毕业设计java毕设之S2SH校园论坛共计2条视频&#xff0c;包括&#xff1a;计算机…

机器学习与数据挖掘——分类与预测模型

如果有兴趣了解更多相关内容&#xff0c;欢迎来我的个人网站看看&#xff1a;瞳孔空间 一&#xff1a;分类与预测 分类(Classification)&#xff1a;分类是找出描述并区分数据类或概念的分类函数或分类模型(也常常称作分类器)&#xff0c;该模型能把数据库中的数据项映射到给…

设计行业文档管理怎么做?天翎群晖全新解决方案来了!

编者按&#xff1a;解决设计行业文档管理难题&#xff0c;天翎群晖全新解决方案来帮忙&#xff01;本文分析了设计行也中的文档管理难点&#xff0c;并从五个方面介绍了天翎群晖是如何解决这些难题的。 关键词&#xff1a;免安装&#xff0c;免维护&#xff0c;私有化部署&…

ant design select 搜索同时支持输入和下拉选中

这个需求看着简单&#xff0c;但是实现起来走了不少弯路。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/74008ea8204c47e5b33ada2e82c56e26.png 1. 需求 当输入关键词时&#xff0c;远程搜索内容&#xff0c;有返回则下拉展示&#xff0c;无返回也要展示当前输入的关键…