一、Linux文件IO的说明

(1)、文件的概念(Linux下一切皆文件

        狭义:指普通的文本文件,或二进制文件,包括日常所见的txt文档、源代码、word文档压缩包、图片、视频、音频文件等。

       广义:除了狭义上的文件外,几乎所有可操作的设备或结构都可视为文件。包括键盘、鼠标、硬盘、串口、显示屏、触摸屏等,也包括网络通讯端口(多机通信要用到的文件)、进程间通讯管道(单机通信)等抽象概念。

(2)、Linux中文件的分类

普通文件(符号:-)(popular):      存在于外部存储器中,用于存储普通数据               --- 标准IO、文件属性与目录操作(*****)
目录文件(符号:d)(directory):    用于存放目录项,是文件系统管理的重要文件类型
管道文件(符号:p)(pipeline):     一种用于进程间通信的特殊文件,也被称为命名管道FIFO  --- 系统编程
套接字文件(符号:s)(socket):     一种用于网络间通信的特殊文件                       --- 网络编程
链接文件(符号:l)(link):         用于间接访问另外一个目标文件,相当于windows系统快捷方式
字符设备文件(符号:c)(character):字符设备在应用层的访问接口(以字符为单位,跟系统进行数据交换的设备,比如:键盘、鼠标、触摸屏等)   --- 系统IO、设备操作(*****)
块设备文件(符号:b)(block):      块设备在应用层的访问接口  (以块为单位(256、512、1024字节为一       块)),跟系统进行数据交换的设备,比如:U盘、内存、硬盘等

(3)、系统IO与标准IO说明

        说明:对文件的操作,基本上就是输入输出,因此也一般称为IO接口

        在操作系统层面上:这一组专门针对文件的IO接口就被称为系统IO --- 偏向于底层(设备文件)

        在标准库的层面上:这一组专门针对文件的IO接口就被称为标准IO --- 偏向于上层(软件程序文件)

二、系统IO的基本API

(1)、文件的打开

1)、关键点:

        1、open函数有两个版本,一个有两个参数,一个有三个参数

        2、当打开一个已存在的文件时,指定两个参数即可(使用两个参数的那个open即可)

        3、当创建一个新文件,需要用第三个参数指定新文件的权限,否则新文件的权限是随机值(系统会帮你配置)

        4、模式flags,可以使用位或的方式,来同时指定多个模式

        5、模式flags,O_NOCTTY主要用在后台精灵进程,阻止这些精灵进程拥有控制终端。 --- 系统编程的时候才讲的。

2)、linux创建的文件权限:

注意:umask值和open函数设置得文件权限值

        第一个数:表示八进制的意思

        第二个数:文件所有者的权限

        第三个数:文件所属组的权限

        第四个数:其它用户的权限

3)、示例代码:

#include
#include
#include
int main(int argc, char const *argv[])
{
int fd = 0;     // 文件描述符
// 一、以下三种打开方式,都要求文件已存在,否则失败返回 (知道文件存在)
fd = open("1.txt", O_RDWR);      // 以可读可写方式打开文件
fd = open("1.txt", O_RDONLY);    // 以只读方式打开文件
fd = open("1.txt", O_WRONLY);    // 以只写方式打开文件
// 二、以下三种打开方式,如果文件不存在,则创建文件,并设置其权限为0644。 存在则返回错误信息 (你想要确保文件存在)
fd = open("1.txt", O_RDWR|O_CREAT|O_EXCL,   0644);    // 以可读可写方式打开文件, 若文件不存在,就创建该文件,若文件存在则返回错误信息
fd = open("1.txt", O_RDONLY|O_CREAT|O_EXCL, 0644);    // 以只读方式打开文件, 若文件不存在,就创建该文件,若文件存在则返回错误信息
fd = open("1.txt", O_WRONLY|O_CREAT|O_EXCL, 0644);    // 以只写方式打开文件, 若文件不存在,就创建该文件,若文件存在则返回错误信息
// 三、以下三种打开方式,如果文件不存在,则创建文件,并清空里面的信息,并设置其权限为0644   (你想要确保文件存在,并且里面是没有东西的)
fd = open("1.txt", O_RDWR|O_CREAT|O_TRUNC,   0644);    // 以可读可写方式打开文件, 若文件不存在,就创建该文件,并清空里面的信息
fd = open("1.txt", O_RDONLY|O_CREAT|O_TRUNC, 0644);    // 以只读方式打开文件, 若文件不存在,就创建该文件,并清空里面的信息
fd = open("1.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644);    // 以只写方式打开文件, 若文件不存在,就创建该文件,并清空里面的信息
// 四、以下两种打开方式,都要求文件已存在,否则失败返回,并追加文件内容   (知道文件存在,并且想要追加数据到里面去)
fd = open("1.txt", O_RDWR|O_APPEND);                   // 以可读可写方式打开文件, 并追加文件内容
fd = open("1.txt", O_WRONLY|O_APPEND);                 // 以只写方式打开文件,并追加文件内容
return 0;
}

(2)、文件的读取、写入

1)、关键点:

        1、参数count是读写字节数的愿望值,实际读写成功的字节数由返回值决定。

        2、读取普通文件时,如果当读到了文件末尾,read()会返回0。

        3、读取管道文件时,如果管道中没有数据,read()会默认阻塞(相当于scanf) --- 系统编程的时候才学。

2)、示例代码1:不断地读取文件里面的数据,并打印出来

#include
// open函数
#include
#include
#include
// read函数、close
#include
// 全局错误码声明所在的文件
#include
// strerror函数
#include
// 主函数
int main(int argc, char const *argv[])
{
// 0、判断命令行参数
if (argc != 2)
printf("命令行参数错误,格式为(./可执行文件 要打开文件)!\n");
// 1、打开文件(以只读的方式打开)
int fd = open(argv[1], O_RDONLY);
if ( -1 == fd )
{
printf("错误信息:打开%s文件失败,  错误原因:%s,  错误位置:%d行\n", argv[1], strerror(errno), __LINE__);
return -1;
}
// 2、读取文件里面的信息,并打印出来
char buf[128] = {0};
int  ret = 0;
while (1)
{
// 清空buf里面的数据,防止对下一次的读取数据造成干扰
bzero(buf, sizeof(buf));
// 每次最多读取128个字节
ret = read(fd, buf, sizeof(buf));
// 如果返回0,则证明文件已被读取完毕
if (ret == 0)
{
printf("啊,我读完了!\n");
break;
}
// 打印读取的数据
printf("%s", buf);
}
// 3、关闭文件
close(fd);
return 0;
}

(3)、文件描述符的本质

 1)、说明:

        函数open()的返回值,是一个整型int数据,这个整型数据,实际上是内核中的一个称为fd_array的数组的下标

 2)、图解:

 3)、解释:

        打开文件时,内核产生一个指向file{}的指针,并将该指针放入一个位于file_struct{}的数组fd_array[]中,而该指针所在的数组的下标,就被open()函数返回给用户,用户把这个数组下标称为文件描述符(fd)

4)、结论:

        1、文件描述符从0开始,每打开一个文件,就产生一个新的文件描述符。

        2、可以重复打开同一个文件,每次打开文件都会使用内核产生系列结构体,并得到不同的文件描述符。

        3、由于系统在每一个进程开始运行时,都默认打开了一次键盘、两次屏幕,因此0、1、2描述符分别代表标准输入、标准输出和标准出错。

(4)、文件的读写位置的设置

1)、关键点:

        1、lseek函数可以将文件位置调整到任意的位置,可以是已有数据的地方,也可以是未有数据的地方,假设调整到文件末尾之后的某个地方,那么文件将会形成所谓的"空洞" 。

        2、lseek函数只能对普通文件调整文件位置,不能对管道文件调整 。

        3、lseek函数的返回值是调整后的文件位置距离文件开头的偏移量,单位是字节。

2)、示例代码:形成空洞文件

#include
// open函数、lseek函数
#include
#include
#include
// lseek函数
#include
// 全局错误码声明所在的文件
#include
// strerror函数
#include
// 主函数
int main(int argc, char const *argv[])
{
// 0、判断命令行参数
if (argc != 2)
printf("命令行参数错误,格式为(./可执行文件 要打开文件)!\n");
// 1、打开文件(以可读可写的形式打开,如果没有这个文件就创建,并清空文件)
int fd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0644);
if (-1 == fd)
{
printf("错误信息:打开%s文件失败,  错误原因:%s,  错误位置:%d行\n", argv[1], strerror(errno), __LINE__);
return -1;
}
// 2、在空洞前,写入数据(头信息数据)
char w_buf1[128] = "文件的大小+文件的名字+文件的格式";
write(fd, w_buf1, strlen(w_buf1));
// 3、利用lseek形成一个空洞文件
lseek(fd, 10*1024*1024, SEEK_CUR);
// 4、在空洞后,写入数据(尾信息数据)
char w_buf2[128] = "文件的末尾";
write(fd, w_buf2, strlen(w_buf2));
// 5、关闭文件
close(fd);
return 0;
}

示例代码2:获取文件大小

#include
// open函数、lseek函数
#include
#include
#include
// lseek函数
#include
// 全局错误码声明所在的文件
#include
// strerror函数
#include
/**
* @brief: 获取文件的大小
* @note:  None
* @param: pathname:要计算大小的文件路径
* @retval: 成功:返回计算的文件的大小
*          失败:返回-1
*/
off_t GetFileSize(const char *pathname)
{
// 1、打开文件(以读写的权限打开文件)
int fd = open(pathname, O_RDWR);
if (-1 == fd)
{
printf("错误信息:打开%s文件失败,  错误原因:%s,  错误位置:%d行\n", pathname, strerror(errno), __LINE__);
return -1;
}
// 2、获取文件的大小
off_t ret_len = lseek(fd, 0, SEEK_END); // 将文件位置偏移到文件末尾,并只移动0字节
// 3、关闭文件
close(fd);
// 4、返回文件的大小
return ret_len;
}
// 主函数
int main(int argc, char const *argv[])
{
// 0、判断命令行参数
if (argc != 2)
printf("命令行参数错误,格式为(./可执行文件 要打开文件)!\n");
// 1、打印文件的大小
printf("%s文件的大小为: %ld字节\n", argv[1], GetFileSize(argv[1]));
return 0;
}

(5)、文件的关闭

1)、关键点:

        1、当不再使用一个文件时,应当关闭文件,防止系统资源浪费。

        2、对同一文件重复执行关闭操作(或其它操作)会失败返回,不会由其它副作用。

(6)、内存映射函数

1)、说明:

        该函数全称是memory map,意为内存映射,即将某个文件与某块内存关联起来,达到通过操作这块内存来进阶操作其所对应的文件的效果

2)、关键点:

       1、 mmap函数的flag是参数是有很多的,上表只罗列了最简单的几个,详细信息请使用man手册进行查询。

        2、mmap函数理论上可以对任意文件进行映射的,但通常用来映射一些比较特殊的设备文件,比如液晶屏LCD。

posted on 2025-09-14 11:44  ycfenxi  阅读(9)  评论(0)    收藏  举报