`

不带缓冲的文件I/O之其它函数

阅读更多
        上一节介绍了不带缓冲文件I/O中的 open 函数,这一节继续介绍其它重要的常用函数。
        首先是可用来创建一个新文件的 creat() 函数:
#include <fcntl.h>

int creat(const char *path, mode_t mode);
    /* 返回值:若成功,返回为只写打开的文件描述符;否则,返回 -1 */

        此函数等价于: open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
        接着是读写打开文件的 read 和 write 函数:
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t nbytes);
            /* 返回值:读到的实际字节数,若已到文件尾,返回 0;若出错,返回 -1 */

ssize_t write(int fd, const void *buf, size_t nbytes);
            /* 返回值:若成功,返回已写的字节数 nbytes ;若出错,返回 -1 */

        在读取文件时,有多种情况可使实际读到的字节数少于要求读的字节数:
        * 读普通文件时,在读到要求字节数之前已到达了文件尾端。
        * 当从终端设备读时,通常一次最多读一行(可自行设置)。
        * 当从网络读时,网络中的缓冲机制可能造成返回值小于所要求的字节数。
        * 当从管道或 FIFO 读时,管道包含的字节少于所需的数量。
        * 当从某些面向记录的设备(如磁带)读时,一次最多返回一个记录。
        * 当一信号造成中断,而已经读了部分数据量时。
        write 函数出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。对于普通文件,读/写操作都从文件的当前偏移量处开始,如果在打开该文件时,指定了 O_APEND 选项,则在每次写操作时,都会将文件偏移量设置在文件的当前结尾处,在写成功后,该文件偏移量增加实际写的字节数。
        另外,Single UNIX Specification包括了XSI扩展,该扩展允许原子性地定位并执行I/O,其中就含有 pread 和 pwrite 函数:
#include <unistd.h>

ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
            /* 返回值:读到的实际字节数,若已到文件尾,返回 0;若出错,返回 -1 */

ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
            /* 返回值:若成功,返回已写的字节数 nbytes ;若出错,返回 -1 */

        调用 pread 相当于调用 lseek 后调用 read,但是又与这种顺序调用有区别:
        * 调用 pread 时,无法中断其定位和读操作。
        * 不更新当前文件偏移量。
        同理,调用 pwrite 类似。
        然后是关闭一个打开文件的 close() 函数:
#include <unistd.h>        // 注意头文件

int close(int fd);       /* 返回值:若成功,返回 0 ;否则,返回 -1 */

        注意,关闭一个文件时还会释放该进程加在该文件上的所有记录锁。当一个进程终止时,内核会自动关闭它所有的打开文件,所以有时可以不用显示地调用 close() 函数关闭打开文件。
        每个打开文件都有一个与其相关联的“当前文件偏移量”,它通常是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都从当前偏移量处开始,并使偏移量增加所读写的字节数。按系统默认的情况,当打开一个文件时,除非指定 O_APPEND 选项,否则该偏移量都被设置为 0。
        可以调用 lseek() 函数显示地为一个打开文件设置偏移量:
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);
    /* 返回值:若成功,返回新的文件偏移量;否则,返回 -1 */

        对参数 offset 的解释与参数 whence 的值有关:
    1、若 whence 是 SEEK_SET/0,则将该文件的偏移量设置为距文件开始处offset个字节。
    2、若 whence 是 SEEK_CUR / 1,则将该文件的偏移量设置为其当前值加 offset,offset 可为正或负。
    3、若 whence 是 SEEK_END / 2,则将该文件的偏移量设置为文件长度加 offset,offset 可为正或负。
        利用该函数的返回值可确定所涉及的文件是否可以设置偏移量:如果文件描述符指向的是一个管道、FIFO 或网络套接字,则 lseek 返回 -1,并将 errno 设置为 ESPIPE 。
        通常,文件的当前偏移量应当是一个非负整数,但是,某些设备也可能允许负的偏移量(例如在 Intel X86处理器上运行的 FreeBSD 的设备 /dev/kmem)。所以在比较 lseek 的返回值时,不要测试它是否小于0,而要测试它是否等于 -1。
        另外,lseek 仅将当前的文件偏移量记录在内核中,它并不引起任何 I/O 操作。然后该偏移量用于下一个读或写操作。文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,位于文件中但没有写过的字节都被读为 0。文件中的空洞并不要求在磁盘上占用存储区。具体的处理方式与文件系统的实现有关。当定位到超出文件尾端之后写时,对于新写的数据需要分配磁盘块,但是对于原文件尾端和新开始写位置之间的部分则不需要分配磁盘块。下面这段代码将用来创建一个带有空洞的文件:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

/* Default file access permissions for new files */
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

int main(void){
	char buf1[] = "abcdefghij";
	char buf2[] = "ABCDEFGHIJ";
	int fd;

	if((fd=creat("file.hole", FILE_MODE)) < 0){
		printf("creat() error.");	
		exit(2);
	}

	if(write(fd, buf1, 10) != 10){
		printf("buf1 write error");	
		exit(2);
	}		// offset now = 10

	if(lseek(fd, 16384, SEEK_SET) == -1){
		printf("lseek() error");
		exit(2);
	}

	if(write(fd, buf2, 10) != 10){
		printf("buf2 write error");	
		exit(2);
	}		// offset now = 16394

	exit(0);
}

        运行程序得到file.hole文件,并使用 od 命令查看文件的实际内容:
$ ./hole.out 
$ ls -l file.hole
-rw-r--r--. 1 lei root 16394 5月  31 22:09 file.hole

$ od -c file.hole
0000000   a   b   c   d   e   f   g   h   i   j  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0040000   A   B   C   D   E   F   G   H   I   J
0040012

        为了证明该文件中确实含有一个空洞,将其与具有同样长度但无空洞的文件比较:
$ ls -ls file.hole file.nohole
 8 -rw-r--r--. 1 lei root 16394 5月  31 22:09 file.hole
20 -rw-r--r--. 1 lei root 16395 5月  31 22:14 file.nohole

        虽然两个文件的长度一样,但具有空洞的文件只占用 8 个磁盘块,而无空洞的文件占用了 20 个。
分享到:
评论

相关推荐

    嵌入式Linux下文件I/O

    文件I/O函数如`open`、`close`、`read`、`write`等直接操作内核级的文件描述符,不带缓冲,适合资源受限的嵌入式系统。"文件IO.rar"中可能包含了这些函数的实例,展示了如何在嵌入式Linux下进行低级文件操作。 4. ...

    linux系统文件I/O编程

    掌握Linux中系统调用的基本概念 掌握Linux中用户编程接口(API)及系统...掌握Linux下文件相关的不带缓存I/O函数的使用 掌握Linux下设备文件读写方法 掌握Linux中对串口的操作 熟悉Linux中标准文件I/O函数的使用

    APUE(UNIX环境高级编程)——文件I/O篇

    这些函数构成了不带缓存的I/O(unbuffered I/O),即每次读写操作都会直接调用内核的系统调用,确保数据的实时传输。不带缓存的I/O不是ANSI C标准的一部分,但属于POSIX.1和XPG3标准,因此在UNIX和类UNIX系统中广泛...

    文件io编程

    另外,Linux下还有带缓存的I/O函数和不带缓存的I/O函数。带缓存的I/O函数如fprintf、fscanf等,它们会使用内存缓冲区来暂存数据,可以提高I/O性能;不带缓存的I/O函数如open、read、write等,直接与文件系统交互,...

    无缓存IO操作和标准IO文件操作区别

    无缓存I/O操作,如Linux中的`read()`和`write()`函数,虽然是被称为“无缓存”,但并不意味着数据直接写入磁盘。事实上,即使在用户层没有可见的缓存,内核仍然会进行内部缓存。这意味着当调用`write()`时,数据首先...

    文件基础学习资料

    标准I/O操作会由C运行时库管理缓冲区,从而减少系统调用的次数,提高效率,而底层的文件I/O则是不带缓冲的,直接与内核交互。 UNIX系统中,程序是指存放在磁盘文件中的可执行文件,而进程则是程序的执行实例。通过...

    《Linux高级系统编程》教学教案—02输入输出.pdf

    文件I/O则是直接通过系统调用,如`read`和`write`,来与文件交互,它通常不带缓冲或者具有更直接的控制。 系统调用是操作系统提供给用户程序的一个低级别接口,允许程序请求操作系统执行特定服务。在Linux系统中,...

    嵌入式Linux应用程序开发详解 pdf 第六章

    Linux提供了丰富的文件I/O函数,这些函数可以分为两大类:**不带缓存的I/O函数**和**带缓存的I/O函数**。不带缓存的I/O函数更接近于系统调用层面,它们直接与内核交互,没有额外的缓存机制。这类函数包括`read()`、`...

    深究标准IO的缓存

    程序员在使用标准I/O函数时,通常是通过FILE*类型的指针操作具体的文件。FILE结构体在标准I/O库内部被正确地初始化和管理,程序员无需直接关心FILE的内部结构,但在深入理解标准I/O工作原理和进行底层调试时,了解...

    Unix环境高级编程3

    在文件I/O中,"不带缓存的I/O"(unbuffered I/O)是指每次read和write操作都会直接调用内核的系统调用,这意味着数据的传输是实时且无缓冲的。这种方式对于需要精确控制数据流的应用来说尤其重要,但效率可能较低。...

    Linux 系统IO

    不带缓冲的I/O意味着每次函数调用都会直接导致一个系统调用,这种方式效率较低,但能提供更精确的控制。 在Linux中,文件被视为一组相关联元素的有序序列,这一概念被称为“一切皆文件”。这意味着无论是文本、二...

    实验5Linux文件操作之带缓存和非缓冲文件的读写.doc

    实验内容涉及了`open()`, `read()`, `write()`, 和 `fcntl()`等关键函数的使用,这些都是Linux文件I/O的基础。 1. **文件操作基础** - `open()`: 这个函数用于打开一个已存在的文件或创建一个新文件。在实验中,`O...

    UNIX环境高级编程003

    它们不涉及缓冲机制,因此被称为不带缓冲的I/O。 2. **文件描述符**:文件描述符是内核用来标识打开文件的非负整数值。当进程打开或创建文件时,内核返回一个文件描述符。标准的文件描述符有0(标准输入)、1(标准...

    嵌入式 LINUX 应用程序开发标准教程

    - **定义**: 不带缓存的I/O函数直接与文件系统交互,不会使用缓冲区。 - **常用函数**: - `open()`: 打开或创建文件。 - `close()`: 关闭文件。 - `read()`: 从文件读取数据。 - `write()`: 向文件写入数据。 -...

    UNIX环境高级编程03

    不带缓存的I/O意味着每次read和write操作都会直接调用内核级别的系统调用,这虽然效率较低,但提供了更直接的控制。这些函数虽非ANSI C标准,但属于POSIX.1和XPG3标准,因此在跨平台的UNIX开发中至关重要。 在多...

    缓冲区数据输入与输出c语言源程序库.rar_数据缓冲_缓冲_缓冲区

    C语言的stdio库提供了带缓冲的I/O操作。例如,`printf`和`scanf`等函数就使用了内部的缓冲区。默认情况下,标准输出(如屏幕)是行缓冲的,意味着只有在遇到换行符或者缓冲区满时,数据才会被实际输出;而标准错误和...

    进程之间的调用基于arm开发

    Unbuffered I/O 函数是一组系统调用函数,提供了基本的输入/输出操作,不带缓冲区。这些函数包括 `open`、`read`、`write`、`close` 等。Unbuffered I/O 函数的优点是可以提供更好的性能和更低的系统调用延迟,但是...

    ANSI C文件管理.ppt

    全缓冲区通常用于磁盘文件,行缓冲区适用于终端输入输出,不带缓冲区则直接进行系统调用,不进行任何缓存。程序员可以通过 `setvbuf()` 函数指定文件流的缓冲区类型和大小。 文件流的操作还包括读写字符、行和块。...

Global site tag (gtag.js) - Google Analytics