基础IO(总)

news2025/1/18 18:51:58

接口介绍

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:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行"或"运算( '|' )

参数:

  • O_RDONLY:只读打开
  • O_WRONLY:只写打开
  • O_RDWR:读,写打开。这三个参数必定指定一个且只能指定一个
  • O_CREAT:若文件不存在,则创建它,需要使用mode选项,来指名新文件的访问权限
  • O_APPEND:追加写

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

使用:open函数具体使用哪个分场景而定,如果目标文件不存在,需要open创建,就要使用三个参数,并且设置新创建文件的权限。

int open(pathname,O_CREAT,0666);//文件不存在,创建它并设置权限为0666

OPEN函数的返回值 

在学习返回值之前,先了解一下系统调用和库函数。

  • 像fopen,fclose,fread,fwrite这些都是C标准库中的函数 ,称之为库函数
  • 而open,close,read,write,lseek,这些都是OS提供的系统调用

 

 所以可以认为f#系列的函数,都是队系统调用函数进行了封装,方便进行二次开发

 


 

 文件描述符fd

通过上面的open函数就可以知道,文件描述符fd就是一个小整数

0&1&2:

在Linux下,默认会有三个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2

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

上面代码就是利用系统调用接口write像显示器输出buf中的内容,用read从键盘上读取内容保存到数组buf中. 

 

 总结:文件描述符就是从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"

#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"。

可以得出结论:文件描述符的分配规则:在file_struct数组中,找到当前没有被使用的最小的下标,作为新的文件描述符


重定向

#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, 0644);
    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);

dup系统调用分配的文件描述符是由系统分配的,遵循文件描述符的分配原则,并不能指定一个文件描述符,这是dup的一个缺陷,而dup2就很好的解决了这个问题

oldfd:需要被复制的文件描述符

newfd:指定一个文件描述符(需要指定一个当前进程没有使用到的文件描述符)

返回值:成功时返回一个新的文件描述符,也就是newfd;失败就返回-1 

示例:

#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;
}

 跟上面的代码一个效果。刚开始创建文件的时候,根据文件描述符分配原则,获得的fd应该为3,

这个时候调用dup2(fd,1)将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

但是如果对文件进行输出重定向的话。./test > file 则文件file中会有

hello write
hello printf
hello fwrite
hello printf
hello fwrite

可以发现printf和fwrite都输出了两次,这两个都是库函数,而write只输出了一次,write为系统调用

  •  一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲的
  • printf fwrite库函数会自带缓冲区,当发生重定向到普通文件时候,数据的缓冲方式由行缓冲变成了全缓冲
  • 而我们放在缓冲区中的数据,不会被立即刷新,甚至fork之后
  • 但是进程退出之后,会统一刷新,写入文件当中
  • 但是fork时候,父子数据会发生写时拷贝,所以当父进程准备刷新的时候,子进程就有了同样的一份数据,随机就会产生两份同样的数据
  • write没有变化说明,write没有所谓的缓冲

综上:printf fwite库函数会自带缓冲区,而write系统调用没有带缓冲区,另外,我们这里的缓冲区都是用户级的缓冲区,作用就是提升整机性能,OS也会提供相关内核级缓冲区。

那这个用户级缓冲区由谁提供?

显然是C标准库提供,上述中printf和fwrite都是库函数,write是系统调用,库函数在系统调用的上层,是对系统调用的封装,但是write没有缓冲区,而printf和fwrite有缓冲区,说明缓冲区是在封装的时候加上的,也就是由C标准库提供的。 


理解文件系统 

在linux下使用指令ls -l

 [root@localhost linux]# ls -l
-rwxr-xr-x. 1 root root 7438 "1月 1 14:56" a.out
-rw-r--r--. 1 root root 654 "1月 1 14:56" test.c

每行包含7列:

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

inode:

为了解释清楚inode,先简单了解一下文件系统

磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,不可以更改。下图中的启动快(Boot Block)的大小是可以确定的

 

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

将属性和数据分开存放的想法看起来很简单,看下图。

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

 

 创建一个新文件主要有以下四个操作

  1. 存储属性内核先找到一个空闲的i节点(这里是261234)。内核把文件信息记录到其中
  2. 存储数据该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推.
  3. 记录分配情况文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
  4. 添加文件名到目录

新的文件名字abc。内核将入口(261234,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来(建立映射关系)。


软硬链接  

硬链接 :

 特点:

  • 具有相同inode节点号的多个文件互为硬链接 文件
  • 删除硬链接 文件或者删除源文件任意之一,文件实体并未被删除
  • 只有删除了源文件和所有对应的硬连接文件,文件实体才会被删除
  • 硬链接 ​​​​​​文件是文件的另一个入口
  • 可以用过给文件设置硬连接文件来防止重要文件被误删
  • 可以通过ls -i看到index;
  • 硬链接文件是普通文件,可以用rm删除
  • 对于静态文件(没有进程正在调用),当硬链接数为0时文件就被删除。注意:如果有进程正在调用,则无法删除或者及时文件名被删除空间也不会释放

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

[root@localhost linux]# ln abc def

[root@localhost linux]# ls -1i
abc def 261234 abc 261234 def

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

 

软链接 :

特点:

  • 软链接 类似windows系统的快捷方式
  • 软链接里面存放点的是源文件的路径
  • 删除源文件,软链接依然存在,但无法访问源文件内容
  • 软链接失效时一般是白字红底闪烁
  • 创建软链接命令ln -s 源文件 软链接文件;
  • 软链接文件和源文件是不同的文件,稳健类型也不同,inode号也不同
  • 软链接的文件类型是"I",可以用rm删除

硬链接是通过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

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

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

相关文章

两数之和 C++实现(力扣题目1)

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出和为目标值 target 的那两个整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案…

SQL案例-高校信息管理系统实现要求

SQL案例-高校信息管理系统实现要求 (1) 建表 stuInfo(学生信息表) 字段名称数据类型说明stuName字符学生姓名&#xff0c;该列必填&#xff0c;要考虑姓氏可能是两个字的&#xff0c;如欧阳俊雄stuNo字符学号&#xff0c;该列必填&#xff0c;学号不能重复&#xff0c;且必须…

AC规则-4-规则和冲突解决

3.3 Introduction to Access Control Rule Conflict Resolution 3.3 访问控制规则冲突解决简介 本节从高层次讨论访问控制规则冲突解决。 本文档稍后会提供更多详细信息。 规则的优先级不是基于它在其他规则中的阅读顺序。 管理冲突规则的策略基于三个基本原则&#xff08;…

linux中 list_entry 设计背景及原理解析

Linux 2.4.22 在这一版本中的 list_entry的宏定义实现如下&#xff1a; #define list_entry(ptr, type, member) \((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))乍一看&#xff0c;会觉得特别复杂&#xff0c;其实分析之后&#xff0c;会发现清晰…

LeetCode高频算法刷题记录8

文章目录 1. 零钱兑换【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 最小栈【最小栈】2.1 题目描述2.2 解题思路2.3 代码实现 3. 最长有效括号【困难】3.1 题目描述3.2 解题思路3.3 代码实现 4. 从前序与中序遍历序列构造二叉树【中等】4.1 题目描述4.2 解题思路4.3 代码实…

day8 域名解析与http服务器实现原理

域名解析gethostbyname函数 主机结构在 <netdb.h> 中定义如下 结构的成员包括&#xff1a; h_name &#xff1a;主机的正式名称 h_aliases&#xff1a;主机的备用名称数组&#xff0c;以 NULL 结尾指针 h_addrtype&#xff1a;地址类型;&#xff08;AF_INET或AF_INET…

基础知识6

知乎上的面试题&#xff1a;https://zhuanlan.zhihu.com/p/546032003 一、Topk问题以及变种&#xff0c;各种解法 微博的热门排行就属于 TopK 问题 TopK 一般是要求在 N 个数的集合中找到最小或者最大的 K 个值&#xff0c;通常 N 都非常得大。 算法的优点是不用在内存中读入全…

经典组件知识(kafka,ngix)

消息队列的作用 解耦、削峰、 异步&#xff08;非必要逻辑异步运行&#xff0c;加快响应速度&#xff09; kafka 首先有个topic的概念&#xff0c;类似于表。 Partition 分区&#xff1a;一个topic下面有多个分区&#xff0c;这些分区会存储到不同的服务器上面&#xff0c;或者…

如何使用 Django 进行测试驱动开发,我来告诉你

所谓测试驱动开发(TDD)&#xff0c;就是先编写测试用例&#xff0c;然后编写代码来满足测试用例&#xff0c;具体包含以下步骤&#xff1a; 编写测试用例。编写代码满足测试用例中的需求。运行测试用例。如果通过&#xff0c;说明代码满足了测试用例所定义的需求。如果未通过&…

armday2

.text .global _start _start: bl LED1_INITbl LED1_ONbl delay_1sbl LED1_OFFbl LED2_INITbl LED2_ONbl delay_1sbl LED2_OFFbl LED3_INITbl LED3_ONbl delay_1sbl LED3_OFFbl _start/**********LED1点灯PE10**************/ LED1_INIT:1.通过RCC_HB4_ENSETR寄存器设置GPIOE组…

PoseiSwap:为何青睐 Layer3?又为何选择 Celestia 作为技术伙伴?

自 PoseiSwap 在 Nautilus Chain 上线后&#xff0c;该 DEX 的整体市场进展十分顺利。我们看到&#xff0c;其不仅在 4 月&#xff0c;以 1000万美元的估值&#xff0c; 获得了来自于 Zebec Labs 的首轮 150 万美元的融资。而在本月&#xff0c;在 Zebec 生态的帮助下&#xff…

利用prusa 3d打印地形模型 如何合理切片并提高打印速度

如上图&#xff0c;我们选择打印一个宽10cm 的地形模型&#xff0c;通过prusa切片&#xff0c;显示时间为22小时31分钟。这个速度大概一天&#xff0c;确实不是我们要的速度&#xff0c;如何提高速度又不失去打印细节且不增加失败风险呢。 1.减少填充 10%的填充对于创想三维en…

RK3568平台开发系列讲解(驱动基础篇)RK平台UART的使用

🚀返回专栏总目录 文章目录 一、引脚定义1.1 UART91.2 RS232和RS485二、设备树配置三、调试3.1 连接硬件3.2 打开主机的串口终端3.3 板子发送数据3.4 板子接收数据3.5 主机退出kermit串口连接四、UART2普通串口配置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇…

C++——模板初阶

文章目录 一.泛型编程二.函数模板1.函数模板的概念2.函数模板的格式3.函数模板的原理4.函数模板的实例化&#xff08;1&#xff09;隐式实例化&#xff08;2&#xff09;显式实例化 5.模板参数的匹配原 三.类模板1.类模板的定义格式2.类模板的实例化 前言&#xff1a; 本章我们…

Flutter问题记录 - Text组件设置不限行数无效

文章目录 前言开发环境问题描述问题分析解决方案最后 前言 梳理Flutter项目的过程中发现还有一些遗留的TODO没处理&#xff0c;其中有一个和Text组件相关。 开发环境 Flutter: 3.7.12Dart: 2.19.6 问题描述 Text组件设置maxLines: null不限制行数&#xff1a; Text(The […

gradio入门示例

随着chat-gpt等机器人对话框架的流行&#xff0c;让一个名为gradio的框架也火热起来&#xff0c;这个框架可以开启一个http服务&#xff0c;并且带输入输出界面&#xff0c;可以让对话类的人工智能项目快速运行。 gradio号称可以快速部署ai可视化项目。 下面通过两个示例来感受…

C++学习记录——이십사 位图、布隆过滤器、哈希切割

文章目录 1、位图位图应用优缺点 2、布隆过滤器1、哈希函数2、删除 3、哈希切割应用 本篇gitee 1、位图 先看一个题目&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个树是否在这40亿个数中&#xff1f; 这里可能…

《面试1v1》线程池

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你好&#xff0c;很高兴见到你。请问你对线程池有什么了解&#xff1f; 候选人&#xff1a; 你好&#xff0c;我对线程池非常了解。线程池是一种…

前后端联调统一校验规则

文章目录 统一校验实现1.什么是统一校验2.统一校验的实现&#xff08;1&#xff09;引入依赖&#xff08;2&#xff09;基于注解&#xff08;3&#xff09;使用案例【1】定义校验规则【2】开启校验【3】统一异常处理器捕获校验产生的异常 3.分组校验(1)定义公共的校验分组(2)定…

完全自主研发,聚芯微发布3D dToF图像传感器芯片!

日前&#xff0c;由中国半导体行业协会IC设计分会&#xff08;ICCAD&#xff09;、芯原股份、松山湖管委会主办的主题为“AR/VR/XR元宇宙”的“2023松山湖中国IC创新高峰论坛”正式在广东东莞松山湖召开。武汉市聚芯微电子有限责任公司发布了完全自主知识产权的3D dToF图像传感…