Linux笔记 --- IO编程之文件操作

目录

文件操作

系统IO

打开、关闭

读写

调整读写位置

复制文件描述符


文件操作

对一个文件来说有两种不同的操作方式,既可以使用由操作系统提供的编程接口(API),即系统调用,也可以使用标准C库提供的标准IO函数

在几百个Linux 系统调用中,有一组函数是专门针对文件操作的,比如打开文件、关闭文件、读写文件等,这些系统调用接口就被称为"系统IO",相应地,在几千个标准C库函数中,有一组函数也是专门针对文件操作的,被称为"标准IO",他们是工作在不同层次,但都是为应用程序服务的函数接口。

下面我们来逐一对系统IO函数和标准IO函数中最重要最常用的接口进行详细剖析,理解他们的异同,以便于在程序中恰当地使用他们。

系统IO

我们首先使用几个简单的函数进行举例

打开、关闭

此表确实一个flags参数,O_CREAT,创建文件,如果使用了这个参数则要指定mode的值

使用系统调用open()需要注意的问题有:

1,flags的各种取值可以用位或的方式叠加起来,比如创建文件的时候需要满足这样的选项:读写方式打开,不存在要新建,如果存在了则清空他。那么此时指定的flags的取值应该是:O_RDWR|O_CREAT|O_TRUNC

2,mode是八进制权限,比如0644,或者0755等,具体值有表参考。

3,它可以用来打开普通文件、块设备文件、字符设备文件、链接文件和管道文件,但只能用来创建普通文件,每一种特殊文件的创建都有其特定的其他函数。

4,其返回值就是一个代表这个文件的描述符,是一个非负整数。这个整数将作为以后任何系统IO函数对其操作的句柄,或称入口。

close函数只需要提供一个文件描述即可使用关闭biaozhun件

cpp 复制代码
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main(int argc, char const *argv[])
{
    int fd = open("open.txt",O_CREAT|O_TRUNC|O_WRONLY,0644);
    printf("%d\n",fd);

    close(fd);
    return 0;
}

此处结果打印为3,代表第四个文件,012分别代表标准输入、标准输出、标准出错,通常用STDIN_FILNO、STDOUT_FILENO、STDERR_FILENO进行代替

那么,这个所谓的文件描述符究竟是什么玩意儿呢?其实他是一个数组的下标值,在内核中打开的文件是用 file 结构体来表示的,每一个结构体都会有一个指针来指向他们,这些指针被统一存放在一个叫做 fd_array 的数组当中,而这个数组被存放在一个叫做 files_struct 的结构体中,该结构体是进程控制块 task_struct 的重要组成部分。他们的关系如图所示。

上图中task_struct 被称为进程控制块(Process Control Block),是程序运行时在 内核中的实现形式。它里面包含了一个进程在运行时的所有信息,当然就包括了进程在运行过程中所打开文件的信息,这些信息被一个files指针加以统一的管理,files 指针所指向的结构体files_struct{}里面的数组fd_array[]是一个指针数组, 用户空间每一次调用open()都会使得内核实例化一个file{}结构体并将一个指向该结构体的指针依次存放在fd_array[]中,并且该指针所占据的数组下标将作为所谓的"文件描述符(file descriptor)"返回给用户空间的调用者,这就是为什么文件描述符是非负整数的原因。

读写

这两个函数都非常容易理解,需要特别注意的是:

1,实际的读写字节数要通过返回值来判断,参数count只是一个"愿望值"。

2,当实际的读写字节数小于count时,有以下几种情形:

A)读操作时,文件剩余可读字节数不足count

B)读写操作期间,进程收到异步信号。

3,读写操作同时对f_pos起作用。也就是说,不管是读还是写,文件的位置偏移量(即内核中的f_pos)都会加上实际读写的字节数,不断地往后偏移。因此连续的对同一个目标文件进行写操作和读操作会无法读取数据

调整读写位置

针对上面的第三点问题,我们可以使用下面这个函数对普通文件的读写位置进行手动调整

从效果来看,我们不仅可以通过lseek()来调整当前文件偏移量,甚至还可以可以将位置偏移量调整到文件之外,形成一个空洞,这种特性其实是非常重要的,它提供了可以在不同地方同时写一个文件的可能,对于一个较大的文件而言,我们可以通过在文件中定位到一个指定的地方,让多个进程同时在不同的偏移量处写入文件数据。

复制文件描述符

对于dup来说复制的目标作为返回值返回,dup2则可以通过newfd指定复制后的存放描述符,如果这个指定的描述符已经存在则覆盖

复制原有的文件描述符相当于对文件描述符进行一次重定向

多功能函数

ioctl函数中存放了大量的除了读和写以外的功能,但是因为没有统一规范因此被称为"垃圾桶", 而fcntl函数则是对其进行规范化后的函数,因此不到万不得已不要轻易使用ioctl函数

这两个都是变参函数,先来看下 ioctl(),其 request是一个由底层驱动提供的命令字,一些通用的命令字被放置在**头文件/usr/include/asm-generi/ioctls.h(不同的系统存放 位置也许不同)**中,后面的变参也由前面的request命令字决定。比如调整文件为异步工作模式:

cpp 复制代码
int on =1;

ioctl(fd, FIOASYNC, &on);

上述代码将fd对应的文件的工作模式设置为所谓的异步方式,FIOASUNC就是其中的一个通用的命令字,而后续的变量on则是其所需要的对应的值。这个操作也可以用fcntl()来达到:

cpp 复制代码
fcntl(fd, F_SETFL, O_ASYNC);

对于fcntl()而言,其第二个参数命令字cmd有很多,具体查看手册及教程,《LINUX环境编程图文指南》P357

内存映射

该函数在进程的虚拟内存空间中映射出一块内存区域,用于对应指定的一个文件,该内存上的数据跟指定文件的数据一一对应,并在一开始使用文件的内容对这块内存进行初始化

相关推荐
新手小新11 小时前
通信工程师学习笔记3-电信网间互联管理规定和网络安全法
网络·笔记·学习
creator_Li11 小时前
Kafka 全面技术笔记
笔记·学习·kafka
智者知已应修善业12 小时前
【CD4022八进制计数器脉冲分配器】2023-5-31
驱动开发·经验分享·笔记·硬件架构·硬件工程
青葱味奶糖12 小时前
管理学之深度管理21法则--笔记2
笔记·深度管理·陈浩老师
农村小镇哥13 小时前
Html的字体+字符编码+图片标签
chrome·笔记·html
守护安静星空1 天前
esp32开发笔记-工程搭建
笔记·单片机·嵌入式硬件·物联网·visual studio code
ljt27249606611 天前
Compose笔记(七十七)--视频录制
笔记·android jetpack
周周不一样1 天前
Andorid基础笔记2-jar&反射
笔记·pycharm·jar
智者知已应修善业1 天前
【51单片机单按键切换广告屏】2023-5-17
c++·经验分享·笔记·算法·51单片机
凉、介1 天前
别再把 PCIe 的 inbound/outbound、iATU 和 eDMA 混为一谈
linux·笔记·学习·嵌入式·pcie