理论教育 实践:Linux操作系统文件操作

实践:Linux操作系统文件操作

更新时间:2025-09-11 理论教育 版权反馈
【摘要】:系统调用提供的文件操作类系统调用有open、 close、 read、 write、 ioctl 等, 常用功能函数原型描述如下。如果打开文件成功, 将返回一个可被read、 write 等系统调用使用的文件描述符, 该描述符帮助进程唯一地定位到该文件。

系统调用提供的文件操作类系统调用有open、 close、 read、 write、 ioctl 等, 常用功能函数原型描述如下。

1. 打开文件

注意对文件读写操作前, 一定要先执行打开文件操作, 打开操作的目的是找到文件位置并登记到系统, 以免每次读写操作时都要反复查找文件在哪里。 如果打开文件成功, 将返回一个可被read、 write 等系统调用使用的文件描述符, 该描述符帮助进程唯一地定位到该文件。 open 或creat 函数都可实现打开文件, 其头文件及函数原型说明如下。

常用参数说明如下。

(1)pathname: 要打开的文件名, 可以是相对路径也可以是绝对路径。

(2)flags: 标志位, 指定打开文件的操作方式及打开时的行为, 该参数有以下几个基本取值。

①O_RDONLY、 O_WRONLY、 O_RDWR: 必须项, 指定打开文件的操作方式(只读、只写、 读写), 这些标志位不能同时用, 可与后面标志用按位或的方式组合起来使用。

②O_CREAT: 如果被打开的文件不存在, 则自动创建新文件, 一般要给出mode 参数(mode 模式值可用按位或的方式组合使用), 新文件的所属用户和所属组是创建它的进程的所属用户和所属组。

③O_EXCL: 如果O_CREAT 标志已经使用, 则当pathname 参数指定的文件已经存在时open 函数返回失败。 如果pathname 给出的是一个符号链接, 无论它指向的文件是否存在, 对open 函数的调用都会返回失败。

④O_TRUNC: 如果文件存在并以可写方式打开, 则设置文件长度为0, 清空文件。

⑤O_APPEND: 写入的内容追加在文件末尾, 即打开后文件的读写位置被置于文件尾。

⑥O_NONBLOCK: 被打开的文件将以非阻塞的方式进行操作, 这关系到多个进程同时操作文件时的同步问题。

⑦O_NDELAY: 同O_NONBLOCK。

⑧O_SYNC: 被打开的文件将以同步I/O 的方式进行操作, 即任何写操作都会先被同步到硬件设备上。 同步完成后, 对写函数的调用才返回。

⑨O_NOFOLLOW: 如果pathname 是一个符号链接, 则对open 函数的调用将返回失败。

⑩O_DIRECTORY: 如果pathname 不是目录, 则对open 函数的调用将返回失败。

(3)mode: 用于指定新文件的权限标志位。 参数意义如下。

如果函数调用操作成功返回文件描述符, 否则返回-1, 并设置标量errno 的值标志错误情况。 文件描述符是一个取值从0 开始的整数。 内核默认一个进程同时打开的文件数有一个上限, 文件描述符取值的上限一般是1024。 每个进程在启动后就默认有三个打开的文件描述符, 如果启动程序时没有进行重定向, 则文件描述符0 关联到标准输入, 1 关联到标准输出, 2 关联到标准错误输出。 每新打开一个文件, 所获得的fd 为当前最大fd 加1。

鉴于在调用open 函数时, O_WRONLY、 O_CREAT、 O_TRUNC 三个标志位经常组合使用, 因此由一个专门的函数creat 来实现等价功能, 如:

2. 读文件

文件打开后就可以进行读写操作了。 读操作的接口头文件及函数原型如下:

参数说明如下。

(1)fd: 要读取的文件描述符。

(2)buf: 指向读取到的数据要放入的缓冲区。 (buf 指向的内存空间必须事先分配好。)

(3)count: 要读取缓冲区的字节数。

函数将从fd 代表的文件的当前读写位置读取不超过count 个字节到buf 指向的内存中,调用成功将返回读取到的字节数, 失败则返回-1, 并设置标量errno 的值。 如果文件本身提供的字节数就比count 小或者该调用被信号打断, read 函数的返回值可能会小于count 指定的值。

但是, 实际上文件读取数据的过程并不像写得这么简单。 由于系统中并发执行的多个进程都有可能对文件做操作, 往往由于各种情况, 从文件中读取的数据可能读不动, 可能读到的量为0, 可能小于指定量, 可能等于指定量。 实际上读不到数的情况是重点要分析的。

比如: 打开文件时如果不设置O_NONBLOCK, 默认要照顾与当前读/写文件操作配合的写/读的另一方, 如果在对方没写入数据前就读, 读操作的执行将阻塞, 也就出现读不动的情况。 而当数据写入后, 系统底层会有触发机制, 唤醒读端运作重新读。(https://www.daowen.com)

而如果设置O_NONBLOCK, 意味着无论情况如何, 读写操作都不阻塞, 文件操作不再是交给系统进行底层的同步管理, 而是调用会立即返回结果, 将主动权交给用户, 由用户根据返回情况, 决定如何写代码处理不同情况, 如没有可读的数据时进程也不会阻塞,而是返回-1, 并且errno 变量被设置。 用户可以在代码中判断errno 标记的值, 根据不同取值情况, 给不同的处理代码。

(1)erron 为EINTR 表示在读入有效字节前收到一个信号, 这种情况可以重新进行读操作。

(2)errno 为EAGAIN 说明在非阻塞方式下, 读文件时并没有可读的数据, 这种情况下, 往往用户可以将程序转向其他功能代码执行, 过后再在合适的时机, 在程序的某处再尝试执行读调用。

errno 取值的其他情况对应了不同类型的错误发生, 必须根据具体情况进行处理。 此处不再赘述。

3. 写文件

write 函数原型为:

参数说明如下。

(1)fd: 要写入的文件的描述符。

(2)buf: 指向要写入的数据所存放的缓冲区。

(3)count: 要写入的字节数。

要写文件, 必须先以可写权限用open 系统调用打开一个文件, 获得所打开文件的fd。写文件操作执行时, 从fd 所代表的文件当前读写位置开始, 把buf 指向的内存中最多count 个字节写入文件。 写入成功则返回写入的字节数, 并更新文件的读写位置。 对fd 执行写调用, 返回值为实际写入的字节数, 调用失败则返回-1, 并设置变量errno 的值。

同样, 由于系统中并发执行的多个进程都有可能对文件做操作, 往往由于各种情况,向文件中写入数据也会出现各种不同的情况。

(1)调用返回值等于count, 说明数据全部写入成功。

(2)调用返回一个大于0 小于count 的值, 说明仅部分数据写入。 这可能是因为写入过程被信号打断, 或者底层的设备暂时没有足够的空间存放写入的数据。

(3)调用阻塞, 说明暂时不能写入数据, 这种情况下如果以非阻塞方式操作文件, 那么会立即返回错误。

(4)调用返回-1, errno 被置为EINTR, 表示收到一个信号, 应用程序可以再次进行写操作。

(5)调用返回-1, errno 被置为EAGAIN, 说明是在非阻塞方式下写文件但文件暂时不能写入数据。

(6)调用返回-1, errno 被置为EBADF, 表示给定的文件描述符非法, 或者文件不是以写方式打开。

(7)调用返回-1, errno 被置为EFAULT, 表示buf 是无效的指针。

(8)调用返回-1, errno 被置为EFBIG, 表示写入的数据超过了最大文件尺寸, 或超过了允许的文件读写位置。

(9)调用返回-1, errno 被置为EPIPE, 说明写入时发生了数据通道断层的错误, 这种情况只在文件是管道或者是socket 的情况下发生。 在这种情况下, 进程还将收到一个SIGPIPE 信号, 信号的默认处理程序是使进程退出。

(10)调用返回-1, errno 被置为ENOSPC, 说明底层设备没有足够的空间。

对于设备文件、 管道或socket 通信, 读/写系统调用的同步处理很重要, 读写关系问题我们在后面进程通信部分再以管道通信为例体验。

4. 关闭文件

程序完成对文件的操作后, 要使用close 系统调用将文件关闭, 操作成功时返回0, 否则返回-1, 其接口头文件与函数原型如下:

【例5-4】 利用Write 系统调用写文件举例, testOpenW.c 源代码如下。

其编译执行结果如下。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈