【Linux】低级IO

news2024/11/13 20:33:19


目录

一、文件的概念

二、C语言文件操作回顾

三、使用系统调用进行文件I/O

1、系统调用open

1.1open接口介绍

1.2open形参中标记位flags的意义

1.3open的使用

2、系统调用write

2.1write接口介绍

2.2write的使用

3、系统调用read

3.1read接口介绍

3.2read的使用

四、文件描述符fd

1、文件描述符

1.1进程如何找到自己打开的文件

1.2文件描述符fd为什么从3开始

1.3文件描述符fd和FILE的区别

2、文件描述符的分配规则

五、重定向

1、重定向的本质

2、重定向接口dup2

2.1输出重定向

2.2追加重定向

2.3输入重定

六、Linux中一切皆文件


一、文件的概念

1、空文件,在磁盘中也会占据空间,因为文件=内容+属性;

2、文件路径+文件名具有唯一性,如果没有指明文件路径,默认在当前路径下进行文件访问;

3、文件操作的本质是进程和被打开文件的关系。

二、C语言文件操作回顾

#include <stdio.h>
#include <string.h>
#define FILE_NAME "log.txt"
int main()
{
    //FILE* fp=fopen(FILE_NAME,"w");//r(只读,出错)w(只写,新建)r+(读写,出错)w+(读写,新建)a(追加)a+(读写追加)
    FILE* fp=fopen(FILE_NAME,"r");//r(只读,出错)w(只写,新建)r+(读写,出错)w+(读写,新建)a(追加)a+(读写追加)
    if(fp==NULL)
    {
        perror("fopen");
        return 1;
    }
    //int cnt=5;
    //while(cnt--)
    //{
    //    fprintf(fp,"%d%s\n",1,"jly");//向文件中写入数据
    //}
    char buffer[60];
    while(fgets(buffer,sizeof(buffer),fp)!=NULL)//遇到\n或num-1或文件末尾为一轮读取。
    {
        buffer[strlen(buffer)-1]='\0';//因为后面的puts会补个'\n',所以要先去除一个'\n'
        puts(buffer);//将str指向的C字符串写入标准输出(stdout)并附加换行符('\n')
    }
    fclose(fp);
    return 0;
}

C语言的文件操作可以看博主的这一篇文章:【C语言】文件相关函数详解

三、使用系统调用进行文件I/O

1、系统调用open

1.1open接口介绍

NAME
       open, creat - open and possibly create a file or device

SYNOPSIS
       #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);//用于处理文件比存在的情况

       int creat(const char *pathname, mode_t mode);
RETURN VALUE
       open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set  appro‐
       priately).//open ()和 create ()返回新的文件描述符fd,如果出现错误,返回 -1(错误码被设置)
NOTES
	   O_RDONLY(只读), O_WRONLY(只写), and O_RDWR(读写)
       O_CREAT(创建文件),O_TRUNC(清空文件),O_APPEND(追加)

1.2open形参中标记位flags的意义

参数flags是标记位的意思:标记位可以利用每个比特位只有01的特点,比特位之间通过按位或传递选项。(有点像单片机操纵寄存器的感觉)

//利用不同比特位打印对应结果
#include <stdio.h>
//只有一个比特位是1,其他位都是0
#define ONE (1<<0)
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)
#define FILE_NAME "log.txt"
void show(int flags)
{
    if(flag&ONE) printf("one ");
    if(flag&TWO) printf("TWO ");
    if(flag&THREE) printf("three ");
    if(flag&FOUR) printf("four ");
    printf("\n");
}
int main()
{
    show(ONE|TWO);//输出one two
    show(ONE|TWO|THREE|FOUR);//输出one two three four
    return 0;
}

标记位flags使用三个宏进行传参,他们分别是O_RDONLY(只读), O_WRONLY(只写), 和O_RDWR(读写),结合上方代码,这三个宏为了表示不同的文件读取方式,内部二进制1的位置绝对不相同。

1.3open的使用

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME "log.txt"
int main()
{
    //umask(0);//将子进程的umask置0,后续创建的文件权限即为666
    //当文件不存在时,open的可写并不会创建log.txt,需要按位或上O_CREAT,加上创建文件功能,最后一个参数是文件创建时的权限
    int fd=open(FILE_NAME,O_WRONLY | O_CREAT,0666);//最终权限=666&~umask
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    close(fd);//使用系统调用close关闭文件指针
    return 0;
}

1、当文件不存在时,使用int open(const char *pathname, int flags, mode_t mode);

如果目标文件不存在,open并不会像fopen那样帮助使用者创建对应文件,需要按位或O_CREAT,加上创建文件功能。第三个参数是创建文件的默认权限,如果不写,系统创建的文件会是乱码。

2、当文件存在时,使用int open(const char *pathname, int flags);

2、系统调用write

2.1write接口介绍

SYNOPSIS
       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
 返回值:写入个数  目标写入文件  缓冲区数据位置   个数

2.2write的使用

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME "log.txt"
int main()
{
    //"w"==O_WRONLY | O_CREAT | O_TRUNC,0666清空式写入
    int fd=open(FILE_NAME,O_WRONLY | O_CREAT | O_TRUNC,0666);

    //"a"==O_WRONLY | O_CREAT | O_APPEND,0666追加
    //int fd=open(FILE_NAME,O_WRONLY | O_CREAT | O_APPEND,0666);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    char outBuff[60];
    int cnt=5;
    while(cnt)
    {
        sprintf(outBuff,"%s:%d\n","jly",cnt--);
        write(fd,outBuff,strlen(outBuff));//将字符写入文件不用让strlen+1写入\n,除非就像在文件中写入\n
    }
    close(fd);
    return 0;
}

将字符写入文件中可以不用写入\n。

write再次对有文本内容的文件进行写入时,并不会和C语言文件操作中的"w"选项一样,先清空文本内容,而是直接覆盖,使用时可以加上O_TRUNC对文本先进行清空操作。

使用O_APPEND增加追加操作。

3、系统调用read

3.1read接口介绍

SYNOPSIS
       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
返回值:读取成功字节数       读取至缓冲区   要求读取字节

3.2read的使用

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME "log.txt"
int main()
{
    int fd=open(FILE_NAME,O_RDONLY | O_CREAT,0666);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    char buffer[100];
    ssize_t num=read(fd,buffer,sizeof(buffer)-1);//留一个位置放\0
    if(num>0)
    {
        buffer[num]='\0';//把\0补回去
    }
    printf("%s\n",buffer);
    close(fd);
    return 0;
}

四、文件描述符fd

1、文件描述符

1.1进程如何找到自己打开的文件

一个进程可以打开多个文件,操作系统为了管理这些打开的文件,需要为每个被打开的文件创建对应的内核数据结构标识文件(struct file{}),这个结构体中包含文件的大部分属性。

那么操作系统中被打开的文件这么多,进程是如何区分哪些文件是属于我的,哪些文件时非我的?那是因为进程的task_struct结构体中包含struct files_struct* files的结构体指针,这个指针指向struct files_struct这个结构体,这个结构体中存在一个叫做struct file* fd_array[]的结构体指针数组,数组中每个结构体指针指向对应被打开文件的struct file{}。

1.2文件描述符fd为什么从3开始

当我们在一个进程中用open打开多个文件时,通过打印fd的值,发现文件描述符fd从3开始分配。那0、1、2这三个文件描述符分配给谁了呢?

printf("stdin->fd:%d\n",stdin->_fileno);
printf("stdout->fd:%d\n",stdout->_fileno);
printf("stderr->fd:%d\n",stderr->_fileno);

0、1、2默认被这三个流占用,所以我们打开的文件的文件描述符fd从3开始。

1.3文件描述符fd和FILE的区别

FILE* fp=fopen(FILE_NAME,"w");
int fd=open(FILE_NAME,O_WRONLY | O_CREAT | O_TRUNC,0666);

系统调用访问文件必须使用文件描述符。而C语言中的FILE是一个结构体,这个结构体中包含了文件描述符。

2、文件描述符的分配规则

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    close(1);//关闭1
    int fd=open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);//新文件fd为1
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    printf("%d\n",fd);//printf->stdout
	fprintf(stdout,"%d\n",fd);//printf->stdout
	fflush(stdout);//刷新缓冲区内容至文件log.txt中
    close(fd);
    return 0;
}

1、如果手动把文件描述符数组下标为1的fd关闭,那么后续用open打开一个新的文件,将会占用这个下标为1的位置。文件描述符的分配规则为:从小到大,按照顺序寻找最小的且没有被占用的fd位置。

2、fd为1的位置代表标准输出流,1被关闭,新文件被打开将会占用1。如果后续想要执行打印操作,文件内容将会被打印至这个新文件中。这是一种输出重定向

五、重定向

1、重定向的本质

>输出重定向
>>追加
<输入重定向

重定向的本质:oldfd不变,在内核中更改newfd下标对应的struct file*的指针。

文件描述符的本质是数组下标,当系统运行时,默认已加载3个文件描述符,分别是标准输入(键盘)、标准输出(显示器)、标准错误(显示器),他们占据了数组0,1,2三个位置。

可以用close(1)手动关闭标准输出,再重新打开一个文件,这个新文件会占据数组为1的位置(标准输出)。如果后续使用printf或fprintf函数进行输出,数据将会输出重定向至这个新文件(因为这个新文件占据了标准输出)。

不过一般不会用手动关闭这三个文件描述符来达到重定向的效果,可以使用int dup2(int oldfd, int newfd);将文件拷贝至前三个文件描述符中,达到重定向的目的。

2、重定向接口dup2

SYNOPSIS
       #include <unistd.h>

       int dup2(int oldfd, int newfd);//将oldfd拷贝到newfd中
                  fd             1
RETURN VALUE
        成功返回fd,失败返回-1

如果要输出重定向到fd对应的文件中,那么oldfd就是fd,newfd就是1。

函数功能为将oldfd描述符重定向到newfd描述符,相当于重定向完毕后都是操作oldfd所操作的文件 但是在过程中如果newfd本身已经有对应打开的文件信息,则会先关闭文件后再重定向(否则会资源泄露)

2.1输出重定向

使用dup2对上方输出重定向代码写法进行更改。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd=open("log.txt",O_WRONLY | O_CREAT | O_TRUNC,0666);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    dup2(fd,1);//输出重定向到fd对应的文件
    printf("%d\n",fd);//printf->stdout,在fd对应的文件中打印
	fprintf(stdout,"%d\n",fd);//printf->stdout,在fd对应的文件中打印
    close(fd);
    return 0;
}

在fd对应的文件中进行printf和fprintf打印。

2.2追加重定向

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
    int fd=open("log.txt",O_WRONLY | O_CREAT | O_APPEND,0666);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    dup2(fd,1);//输出重定向到显示器
    printf("%d\n",fd);//printf->stdout
	fprintf(stdout,"%d\n",fd);//printf->stdout
    const char* str="jly";
    write(1,str,strlen(str));//将更多内容写入1中
    close(fd);
    return 0;
}

在fd对应的文件中进行printf和fprintf打印并进行write追加写入。

2.3输入重定

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
    int fd=open("log.txt",O_RDONLY);
    if(fd<0)
    {
        perror("open");
        return 1;
    }
    dup2(fd,0);//输入重定向,将fd的内容拷贝至0
    char line[60];
    while(1)
    {
        if(fgets(line,sizeof(line),stdin)==NULL)//因为上方输入重定向了,所以这里stdin中会获取log.txt的内容
        {
            break;
        }
        printf("%s",line);
    }
    close(fd);
    return 0;
}

如果一个进程打开了文件并创建了子进程,子进程将会拷贝父进程的PCB及文件描述符表等大部分信息,包括曾经父进程打开的文件信息,但是文件是不会被拷贝的;只要一个进程退出,它的PCB、文件描述符表要被释放,指向文件的指针就变少。

六、Linux中一切皆文件

打开的struct file中会有一个函数指针,会作对应的初始化,指向对应驱动层的读写方法,通过读写方法,完成对应设备的读写。

用户站在struct file(文件的结构体对象)上层看来,所有的设备和文件,统一都是struct file,至于struct file内部是如何使用函数指针来调用对应设备文件的读写方法,用户并不了解也并不关心。所以在用户的视角下,Linux一切皆文件。

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

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

相关文章

什么是 SAP ABAP FDA - Fast Data Access 技术?

快速数据访问 (Fast Data Access&#xff0c;缩写为 FDA) 是一种可在 SAP ABAP 环境中访问的协议。 它允许针对 SAP HANA 提交 OPEN SQL SELECT 语句&#xff0c;从而使用 SAP ABAP 的数据格式。 在快速数据访问场景里&#xff0c;SAP ABAP 内表数据传输到数据库并返回。这种方…

Java+MySQL基于ssm的物理竞赛管理系统

物理竞赛是对课堂上所学的理论知识的实践运用,加深所学知识的理解,锻炼团队协作能力。学生可以根据个人爱好,个人特长选择参与,不仅能培养学生的创新意思、提高专业技能,还能锻炼学生的组织能力和协作能力。物理竞赛是在中国科协的领导下,由中国物理学会主办,各省、自治区、直辖…

【力扣周赛#324】6266. 使用质因数之和替换后可以取到的最小值+6267. 添加边使所有节点度数都为偶数+6268. 查询树中环的长度

目录 6265. 统计相似字符串对的数目 - ac 6266. 使用质因数之和替换后可以取到的最小值 分解质因数 1、tle代码 2、优化ac代码 6267. 添加边使所有节点度数都为偶数 - 建图分类讨论 关于建图 6268. 查询树中环的长度 - LCA最近公共祖先 6265. 统计相似字符串对的数目…

[附源码]Nodejs计算机毕业设计基于微信平台的车险投保系统设计与实现Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Vue实现后台管理系统

目录 前言 登录页面 后台界面 前言 今天用Vue实现一个简易的后台&#xff0c;不借助接口和后端代码&#xff0c;仅通过前端实现&#xff0c;本案例涉及Vue路由相关知识&#xff0c;不熟悉Vue路由可以先看一下右边的文章再接着看下面的项目案例&#xff1a;Vue路由 这篇文章详…

[CISCN2019 华北赛区 Day1 Web2]

目录 前言 涉及考点 部分记录 前言 第一次看到这题时没有思路&#xff0c;通过搜索相关WP并简单了解考点所涉及的知识后&#xff0c;通过解题来扩展自己的知识面 涉及考点 python爬虫&#xff0c;支付逻辑漏洞&#xff0c;cookie篡改&#xff0c;重放攻击&#xff0c;JWT…

C++11后的STL算法

文章目录一、函数对象二、预定义的函数对象三、算法函数1.自己实现foreach算法2.自己实现的findif算法3.自己实现bsort算法一、函数对象 STL提供了很多处理容器的函数模板&#xff0c;它们的设计是相同的&#xff0c;有以下特点&#xff1a; 1&#xff09;用迭代器表示需要处理…

数据结构---用栈实现队列

用栈实现队列模拟入队模拟出队JAVA实现总结用栈来模拟一个队列&#xff0c;要求实现队列的两个基本操作&#xff1a;入队、出队。栈是先入后出&#xff0c;队列是先入先出 用两个栈来实现一个队列功能 让其中一个栈作为队列的入口&#xff0c;负责插入新元素&#xff1b;另一个…

ARM9和STM32什么关系?

已剪辑自: https://mp.weixin.qq.com/s/QHARY-D2SwFoQbFsJoCNlg 有小伙伴问&#xff1a;ARM9和STM32什么关系&#xff1f;如果时间倒退10年&#xff0c;ARM9、 s3c2410还算是比较流行的年代&#xff0c;但STM32这类“新时代”的ARM处理器也问世了。 ARM9是早期的ARM处理器&…

Java+MySQL基于ssm的打车管理系统的设计与实现

随着时代的发展,交通也变的越来越便利,但是很多时候人们出行的时候因为各种原因还是会进行打车,因为一些地区比较偏远或者处于特殊的时间段而用户又急需用车等情况导致没办法及时的打到车,这种情况下就急需一套基于互联网的打车系统出现了。 本打车管理系统采用JAVA语言来进行开…

Vivado2019+Modelsim仿真环境搭建

目录 一、安装准备 二、安装与配置 2.1 Vivado安装 2.2 modelsim安装 2.3 Vivado设置modelsim仿真环境 1&#xff09;仿真库生成设置 2&#xff09;modelsim添加仿真库&#xff08;逐个添加&#xff09; 3&#xff09;modelsim添加仿真库&#xff08;批量添加&#xff…

Spring 中 @Value 注解使用和源码分析

1、Value 注解使用 先配置本地 application.properties 如下&#xff1a; apple.nameabc代码如下&#xff1a; PropertySource("application.properties") public class Apple {Value("${apple.name}")public String name; }ComponentScan public class …

非零基础自学Golang 第9章 结构体 9.7 匿名结构体 9.8 小结 9.9 知识拓展

非零基础自学Golang 文章目录非零基础自学Golang第9章 结构体9.7 匿名结构体9.7.1 匿名结构体定义与初始化9.7.2 匿名结构体的应用9.8 小结9.9 知识拓展9.9.1 使用结构体解析XML文件第9章 结构体 9.7 匿名结构体 9.7.1 匿名结构体定义与初始化 匿名结构体&#xff0c;顾名思…

blender源代码编译运行

其实在blender官网上已经给出了编译步骤https://wiki.blender.org/wiki/Building_Blender/Windows&#xff0c;由于在源码编译的过程中还遇到了很多问题&#xff0c;特此记录一下。 文章目录前提准备代码下载1. blender源码下载2. lib下载编译前提准备 Visual Studio2019或者2…

Innodb存储引擎-执行流程分析和二阶段提交分析

文章目录执行流程分析基本流程WAL(Write-Ahead Log)write和fsync区别innodb_flush_logs_at_trx_commit & sync_binlog二阶段提交执行流程分析 基本流程 &#xff08;1&#xff09;连接,分析,优化,执行 客户端与MySQL Server建立连接&#xff0c;发送语句给MySQL Server&a…

2022.12.18 学习周报

文章目录摘要文献阅读1.题目2.摘要3.介绍4.RNNConventional Recurrent Neural Networks5.Deep Recurrent Neural Networks5.1 Deep Transition RNN5.2 Deep Output5.3 Stacked RNN6.实验6.1 训练6.2 结果与分析7.讨论深度学习GRU公式推导1.GRU前向传播2.GRU反向传播GRU代码实现…

关于数学中“函数(function)”的含义

目录 1. 问题 2. “function”是如何翻译成“函数”的&#xff1f; 3. “function”是谁引入数学中的&#xff0c;其意义何在&#xff1f; 3.1 “function”的词源 3.2 “function”引入数学中 3.3 “function”的含义 4. 常见的函数(Common Functions) 4.1 线性函数(L…

word页码如何设置为章节加页码,例如第一章第一页1-1、第二章章第一页2-1

由于用到word页码分章节页码的形式&#xff0c;从网上查了一下&#xff0c;质量真的很差&#xff0c;没有一篇文章讲清楚的&#xff0c;有的所答非所问&#xff0c;一怒之下&#xff0c;利用几个小时的时间解决问题并写下这篇文章&#xff0c;以供大家学习参考&#xff01;&…

【JSP】

文章目录简介Scriptlet脚本小程序JSP的指令标签include静态包含include动态包含不传参传参JSP的四大域对象四种属性范围验证属性范围的特点EL表达式操作字符串操作集合emptyJSTL条件动作标签if标签choose、when 和 otherwise 标签迭代标签foreach格式化动作标签formatNumber标签…

NProgress 进度条的使用方法

安装NProgress 进度条 npm install nprogress --save-dev 在vue项目中mian.js中或router.js或axios.js&#xff1a; import NProgress from nprogress import nprogress/nprogress.css 使用NProgress进度条 NProgress.start(); &#xff1a;进度条开始&#xff1b; NProgr…