目录
(三)文件拓展与截断:truncate 和 ftruncate 函数
一、Linux 文件 IO 函数初相识
在 Linux 系统的世界里,文件操作是日常使用和系统管理的基础,而文件 IO 函数则是与内核交互进行文件操作的关键工具。无论是开发应用程序,还是进行系统脚本编写,熟练掌握 Linux 文件 IO 函数都能让你事半功倍。从简单的文件读取和写入,到复杂的文件权限管理和文件描述符操作,这些函数涵盖了文件处理的方方面面,是深入理解 Linux 系统运作的重要一环。
二、文件描述符:文件的数字身份证
在深入探讨文件 IO 函数之前,不得不先提及文件描述符,它是理解 Linux 文件操作的重要基石。
(一)什么是文件描述符
文件描述符是一个非负整数,就像是文件在系统中的 “数字身份证” ,是内核用于标识打开文件的方式。当一个进程打开一个现有文件或者创建一个新文件时,内核会返回一个文件描述符给进程。之后,进程在对文件进行各种操作,如读取、写入、关闭等时,都需要使用这个文件描述符来指定对应的文件。例如,你打开了一个文本文件,系统就会给这个打开的文件分配一个文件描述符,可能是 3、4 或者其他数字,后续你要读取这个文件的内容,就需要通过这个文件描述符来告诉系统你要操作的是哪个文件。在 Linux 系统中,一切皆文件,不仅普通文件,像管道、套接字等也都通过文件描述符来进行管理和操作,它是进程与文件、设备之间进行交互的关键接口。
(二)文件描述符的分配规则
在进程中,文件描述符的分配是有规律的。当进程打开一个文件时,文件描述符会从最小的未使用值开始分配 。一般情况下,进程启动时会默认打开三个文件描述符:标准输入(0)、标准输出(1)和标准错误(2),所以新打开的文件描述符通常从 3 开始。例如,当你在一个 C 程序中依次打开多个文件时,第一个新打开的文件得到的文件描述符大概率是 3,第二个是 4,以此类推。每个进程都有一个文件描述符表,用于记录进程打开的文件描述符。在这个表中,从 0 开始依次寻找未被占用的位置,将其作为新文件描述符。
在 Linux 系统中,每个进程默认可以打开的文件描述符数量是有限制的,通常这个默认值是 1024 。但这个限制并不是固定不变的,你可以通过一些方法来查看和修改。比如使用ulimit -n命令可以查看当前进程可打开的最大文件描述符数量;若要临时修改,可以使用ulimit -n [新的数量]命令,不过这种修改只在当前会话有效。如果想要永久修改,可以通过编辑/etc/security/limits.conf文件来实现,在文件中添加或修改相应的配置项,如* soft nofile 65536和* hard nofile 65536,分别设置软限制和硬限制(软限制是当前系统生效的设置值,硬限制是系统中所能设定的最大值),修改完成后重新登录或使用source /etc/security/limits.conf使设置生效。
(三)特殊文件描述符
在文件描述符中,有三个特殊的存在,它们是标准输入(0)、标准输出(1)和标准错误(2) 。标准输入(0)通常与键盘关联,当程序需要从外部获取数据时,默认从标准输入读取,比如在 C 语言中使用scanf函数读取用户输入的数据,就是从标准输入获取;标准输出(1)一般对应显示器,程序执行结果的输出默认通过标准输出展示在屏幕上,printf函数就是将内容输出到标准输出;标准错误(2)同样也是输出到显示器,主要用于输出程序运行过程中的错误信息,perror函数会将错误信息输出到标准错误。这三个特殊文件描述符在进程中起着至关重要的作用,它们是进程与用户交互以及获取程序状态信息的重要途径。在进行文件操作时,理解它们的作用和特性,能够帮助我们更好地编写程序,处理各种输入输出情况。
三、文件的打开与创建:open 和 creat 函数
在 Linux 文件操作中,打开现有文件和创建新文件是基础操作,而open和creat函数就是完成这些任务的关键工具 。它们不仅能帮助我们实现文件的访问和创建,还能通过各种参数设置,满足不同的文件操作需求。
(一)open 函数详解
open函数在 Linux 文件 IO 中扮演着至关重要的角色,它就像是打开文件世界大门的钥匙 ,通过它可以以各种方式打开或创建文件,获取文件描述符,进而进行后续的文件操作。
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是一个指向以 null 结尾的字符串指针,用于指定要打开或创建的文件路径名 ,可以是绝对路径(如/home/user/file.txt),也可以是相对路径(如file.txt,相对当前工作目录)。flags参数是一个整数值,用于指定打开文件的方式和行为,它是一个位掩码,可以使用逻辑或(|)操作符组合多个选项,常用的打开标志有:
-
O_RDONLY:以只读方式打开文件,只能读取文件内容,不能写入,如open("file.txt", O_RDONLY)。
-
O_WRONLY:以只写方式打开文件,只能向文件写入内容,不能读取,如open("file.txt", O_WRONLY)。
-
O_RDWR:以可读可写方式打开文件,既能读取文件内容,也能写入,如open("file.txt", O_RDWR)。
这三个标志是互斥的,只能选择其中一个 。除此之外,还有一些可选标志,它们可以与上述三个基本标志组合使用:
-
O_CREAT:如果指定文件不存在,则创建这个文件 。使用此标志时,需要同时指定mode参数,用于设置新文件的权限,如open("new_file.txt", O_RDWR | O_CREAT, 0644)。
-
O_EXCL:与O_CREAT一起使用,如果文件已经存在,则返回错误(-1) ,这可以用于检查文件是否存在并避免覆盖已有文件,如open("new_file.txt", O_RDWR | O_CREAT | O_EXCL, 0644),若new_file.txt已存在,该操作会失败。
-
O_APPEND:每次写操作都将数据追加到文件的末尾 ,而不是覆盖原有内容,如open("log.txt", O_WRONLY | O_APPEND),后续写入的数据会接着文件原有内容之后。
-
O_TRUNC:如果文件存在,并且以只写(O_WRONLY)或读写(O_RDWR)方式打开,则将文件内容清空 ,文件长度变为 0,如open("temp.txt", O_WRONLY | O_TRUNC),会清空temp.txt的内容。
-
O_NOCTTY:如果路径名指向终端设备,不要把这个设备用作控制终端 ,这在一些特定场景下,避免程序意外将终端设备设为控制终端。
-
O_NONBLOCK:如果路径名指向 FIFO、块文件或字符文件,则把文件的打开和后继 I/O 设置为非阻塞模式 ,在非阻塞模式下,I/O 操作不会等待数据准备好,而是立即返回,提高程序的执行效率和响应性,常用于网络编程等场景。
mode参数是一个八进制数,用于在创建新文件时指定文件的权限 ,它由三个部分组成:所有者权限、组权限和其他用户权限,每个部分都可以有读(r,值为 4)、写(w,值为 2)和执行(x,值为 1)权限,例如,0644表示所有者具有读和写权限,组用户和其他用户具有读权限;0755表示所有者具有读、写和执行权限,组用户和其他用户具有读和执行权限 。实际创建文件时的权限还会受到umask值的影响,最终文件权限是mode与umask按位取反后的结果。例如,umask值为0022,mode为0666,则实际创建文件的权限为0644(0666 & ~0022) 。
2. 实际使用示例
下面通过一些实际代码示例来展示open函数的使用:
#include <std