lienhua34
2014-08-25

1 文件描述符

对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读或写一个文件时,使用 open 或 creat 返回的文件描述符标识该文件,将其作为参数传送给 read 或 write。

按照惯例,UNIX 系统 shell 使用文件描述符 0 与进程的标准输入相关联,文件描述符 1 与进程的标准输出相关联,文件描述符 2 与进程的标准出错输出相关联。

2 不带缓冲的 I/O 函数

术语不带缓冲指的是每个 read 或 write 都调用内核中的一个系统调用。这些不带缓冲的 I/O 函数不是 ISO C 的组成部分,但是,它们是POSIX.1 和 Single UNIX Specification 的组成部分。下面分别来说明一下5 个基本的函数:open、read、write、lseek 以及 close。

2.1 open 函数

调用 open 函数可以打开或者创建一个文件。

#include <fcntl.h>
int open(const char *pathname, int oflag, ... /* mode_t mode */);
返回值:若成功则返回文件描述符,若出错则返回-1.

其中第三个参数... 表明余下的参数的数量及其类型根据具体的调用会有所不同。对于 open 函数而言,仅当创建新文件时才使用第三个参数。pathname 是要打开或创建文件的名字。oflag 参数用于说明此函数的多个选项。用下列一个或多个常量进行“或”运算构成 oflag 参数(这些常量定义在 <fcntl.h> 头文件中)。

  O_RDONLY  只读打开

  O_WRONLY  只写打开

  O_RDWR   读、写打开

这三个常量中必须指定一个且只能指定一个。下列常量则是可选的:

  O_APPEND  每次写时都追加到文件的尾端。

  O_CREAT  若文件不存在,则创建它。使用此选项时,需要第三个参数 mode,用于指定该新文件的访问权限位。

  O_EXCL  如果同时指定了O_CREAT,而文件已经存在,则会出错。用此可以测试一个文件是否存在,如果不存在,则创建此文件。

  O_TRUNC  如果此文件存在,而且为只写或者读写成功打开,则将其长度截短为 0.

  O_NOCTTY  如果 pathname 指的是终端设备,则不将该设备分配为此进程的控制终端。

  O_NONBLOCK  如果指的是一个 FIFO、一个块特殊文件或一个字符特殊文件,则此选项为文件的本地打开操作和后续的 I / O 操作设置非阻塞模式。

例1:

下面程序以只读的方式打开文件 foo.txt,并设置 open 函数的O_CREAT和O_EXCL 选项。

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
int
main(void)
{
int fd;
if ((fd = open("foo.txt", O_RDONLY | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR | S_IRGRP)) == -) {
printf("open error\n");
} else {
printf("open success\n");
}
exit();
}

编译该程序,生成open_demo,然后执行它,其结果如下:

lienhua34:demo$ gcc -o open_demo open_demo.c
lienhua34:demo$ ls
open_demo
open_demo.c
lienhua34:demo$ ./open_demo
open success
lienhua34:demo$ ls -l foo.txt
-rw-r----- lienhua34 lienhua34
8月 : foo.txt
lienhua34:demo$ ./open_demo
open error

当指定了O_CREAT 选项时,第三个参数取下面列表中的这些常量的“或”运算结果用于指定新文件的访问权限(这些常量定义在 <sys/stat.h>中)。

表. 文件访问权限
S_IRUSR 用户 -读
S_IWUSR 用户 -写
S_IXUSR 用户 -执行
S_IRGRP 组 -读
S_IWGRP 组 -写
S_IXGRP 组 -执行
S_IROTH 其他 -读
S_IWOTH 其他 -写
S_IXOTH 其他 -执行

由 open 返回的文件描述符一定是最小的未用描述符数值。这一点可用于在标准输入、标准输出或标准出错输出上打开新的文件。

open 函数的 oflag 参数还支持三个可选常量,他们是 Single UNIXSpecification 中同步输入和输出选项的一部分。

  O_DSYNC  使每次 write 等待物理 I/O 操作完成,但是,如果写操作并不影响读取刚写入的数据,则不等待文件属性被更新。

  O_RSYNC  使每一个以文件描述符作为参数的 read 操作等待,直至任何对文件同一部分进行的未决写操作都完成。

  O_SYNC  使每次 write 都等待物理 I/O 操作完成,包括由 write 操作引起的文件属性更新所需的I/O。

当文件用O_DSYNC 标志打开时,在重写其现有的部分内容时,文件时间属性不会同步更新。与此相反,如果文件是用O_SYNC 标志打开时,那么对该文件的每一次 write 操作都将在 write 返回之前更新文件时间。

2.2 creat 函数

可调用 creat 函数创建一个新文件。

#include <fcntl.h>

int creat(const char *pathname, mode_t mode);

返回值:若成功则返回以只写打开的文件描述符,若出错则返回-1.

此函数等效于:

open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);

creat 函数的一个不足之处是它以只写方式打开所创建的文件。之所以存在creat 函数,是因为早期的 UNIX 系统中,open 函数的第二个参数只能是0、1 或 2。没有办法打开一个尚未存在的文件,于是需要 creat 函数来创建新文件。现在 open 函数支持O_CREAT 选项之后,creat 函数就没有存在的必要了。

2.3 close 函数

调用 close 函数关闭一个打开的文件。

#include <unistd.h>

int close(int filedes);

返回值:若成功则返回0,若出错则返回-1.

当一个进程终止时,内核自动关闭它所有打开的文件。

2.4 read 函数

调用 read 函数从打开的文件中读数据。

#include <unistd.h>

ssize_t read(int filedes, void *buf, size_t nbytes);

返回值:若成功则返回读到的字节数,若已到文件结尾则返回0,若出错则返回-1.

读操作从文件的当前偏移量处开始,在成功返回之前,该偏移量将增加实际读到的字节数。

2.5 write 函数

调用 write 函数想打开的文件写数据。

#include <unistd.h>

ssize_t write(int filedes, const void *buf, size_t nbytes);

返回值:若成功则返回已写的字节数,若出错则返回-1.

对于普通文件,写操作从文件的当前偏移量处开始。如果在打开该文件时,制定了O_APPEND 选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件的偏移量增加实际写的字节数。

2.6 lseek 函数

调用 lseek 显示地为一个打开的文件设置其偏移量。

#include <unistd.h>

off_t lseek(int filedes, off_t offset, int whence);

返回值:若成功则返回新的文件偏移量,若出错则返回-1.

每个打开的文件都有一个与其关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。按系统默认的情况,当打开一个文件时,除非指定O_APPEND 选项,否则该偏移量被设置成 0.

对 lseek 函数的 offset 参数的解释与参数 whence 的值有关。

• 若 whence 是SEEK_SET,则将该文件的偏移量设置为距离文件开始处offset 个字节。

• 若 whence 是SEEK_CUR,则将该文件的偏移量设置为其当前值加 offset,offset 可为正或负。

• 若 whence 是SEEK_END,则将该文件的偏移量设置为文件长度家 offset,offset 可为正或负。

管道、FIFO 和网络套接字都不支持设置偏移量,如果一个文件描述符引用的是这三者之一,则 lseek 函数返回 -1,并将 errno 设置为 ESPIPE。对于普通文件,其偏移量只能是非负值。但是,某些设置可能支持负的偏移量。所以,在比较 lseek 函数的返回值时应当谨慎,不要测试它是否小于 0,而要测试它是否等于 -1.

文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞。位于文件中但没有写过的字节都被读为 0.

实例:

下面程序创建一个新文件 file.hole,先在文件开始处写入“abcd”,然后将文件偏移量移动到距离开始处的 10 字节处,在该处写入“ABCD”,则在文件 file.hole 从第 4 个字节处到第 9 个字节之间就形成了一个空洞。

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
char buf1[] = "abcd";
char buf2[] = "ABCD";
int
main(void)
{
int fd;
if ((fd = open("file.hole", O_WRONLY | O_CREAT | O_TRUNC,S_IRUSR | S_IWUSR | S_IRGRP)) < ) {
printf("open error");
}
if (write(fd, buf1, ) != ) {
printf("write buf1 error");
}
if (lseek(fd, , SEEK_SET) == -) {
printf("lseek error");
}
if (write(fd, buf2, ) != ) {
printf("write buf2 error");
}
exit();
}

编译该程序,生成文件 writedemo,然后执行 writedemo 文件,

lienhua34:demo$ gcc -o writedemo write_demo.c
lienhua34:demo$ ./writedemo
lienhua34:demo$ ls -l file.hole
-rw-r----- lienhua34 lienhua34 8月 : file.hole
lienhua34:demo$ od -c file.hole
a b c d \ \ \ \ \ \ A B C D

UNIX环境编程学习笔记(2)——文件I/O之不带缓冲的 I/O的更多相关文章

  1. UNIX环境编程学习笔记(6)——文件I/O之判断文件类型

    lienhua342014-09-01 1 文件类型 我们平时最常接触的文件类型有普通文件(regular file)和目录(di-rectory file),但是 UNIX 系统提供了多种文件类型: ...

  2. UNIX环境编程学习笔记(13)——文件I/O之标准I/O流

    lienhua342014-09-29 1 标准 I/O 流 之前学习的都是不带缓冲的 I/O 操作函数,直接针对文件描述符的,每调用一次函数可能都会触发一次系统调用,单次调用可能比较快捷.但是,对于 ...

  3. UNIX环境编程学习笔记(12)——文件I/O之目录操作

    lienhua342014-09-18 1 引言 在 UNIX 系统中,目录是一种特殊的文件类型.我们可以使用 open 函数来打开目录,获取文件描述符,然后调用 stat 函数来获取目录的属性信息, ...

  4. UNIX环境编程学习笔记(10)——文件I/O之硬链接和符号链接

    lienhua342014-09-15 1 文件系统数据结构 UNIX 文件系统通过 i 节点来存储文件的信息.如图 1 所示为一个磁盘柱面上的 i 节点和数据块示意图.其中 i 节点是一个固定长度的 ...

  5. UNIX环境编程学习笔记(9)——文件I/O之文件访问权限的屏蔽和更改

    lienhua342014-09-10 1 文件访问权限 在文件访问权限和进程访问控制中,我们已经讲述过文件访问权限位,为了方便,我们重新列在下面, 表 1: 文件的 9 个访问权限位  st_mod ...

  6. UNIX环境编程学习笔记(7)——文件I/O之文件访问权限与进程访问控制

    lienhua342014-09-02 1 文件的设置用户 ID位 和设置组 ID位 与进程相关联的 ID 如下表所示, 表 1: 与进程相关联的用户 ID 和组 ID 实际用户 ID 我们实际上是谁 ...

  7. UNIX环境编程学习笔记(4)——文件I/O之dup复制文件描述符

    lienhua342014-08-23 UNIX 提供了两个函数 dup 和 dup2 用于复制一个现存的文件描述符. #include <unistd.h> int dup(int fi ...

  8. UNIX环境编程学习笔记(3)——文件I/O之内核 I/O 数据结构

    lienhua342014-08-27 内核使用三种数据结构表示打开的文件,分别是文件描述符表.文件表和 V 节点表. (1) 每个进程在进程表中都有一个记录项,记录项中包含有一张打开文件描述符表,每 ...

  9. UNIX环境编程学习笔记(14)——文件I/O之临时文件

    lienhua342014-10-01 ISO C 标准 I/O 库提供了个两个函数 tmpnam 和 tmpfile 以帮助创建临时文件, #include <stdio.h> char ...

随机推荐

  1. java好用的邮件发送

    1.action代码 // 发送邮件 String strEMAIL = Tools.readTxtFile(Const.EMAIL); //读取邮件配置 String strEM[] = strEM ...

  2. Android NFC近场通信2——NFC标签调度

    上面一篇文章简单介绍了NFC的背景和技术应用,今天主要是讲解一下NFC如何发起通信和标签通信(主要是翻译android官网的资料,中间加入个人心得). NFC总是在一个发起者和一个被动目标之间发生.发 ...

  3. [转]ssh中如何实现定时任务(spring对quartz的支持)

    原文地址:http://blog.csdn.net/qq_18675693/article/details/50413889 实现定时任务:quartz spring中对quartz进行了封装,使得我 ...

  4. Thinkphp CURD中的where方法

    今天来给大家讲下查询最常用但也是最复杂的where方法,where方法也属于模型类的连贯操作方法之一,主要用于查询和操作条件的设置.where方法的用法是ThinkPHP查询语言的精髓,也是Think ...

  5. Postgres快速创建大量测试数据

    DROP TABLE IF EXISTS color; CREATE TABLE color( cname ), remark ) ); ),) ,); 插入10000000数据之后,用时51秒 对于 ...

  6. Badboy+Jmeter进行性能测试

    摸索了两天,终于搞定! 目的:使用Badboy和Jmeter添加5000个系统用户 一开始用Badboy进行批量添加,想先在excel中准备好5000个用户名,再导入到Badboy中.但是一直不成功, ...

  7. Eclipse调试Java的10个技巧【转】

    clipse调试Java的10个技巧 先提三点 不要使用System.out.println作为调试工具 启用所有组件的详细的日志记录级别 使用一个日志分析器来阅读日志 1.条件断点 想象一下我们平时 ...

  8. 代码备忘, TODO宏实现

    代码备忘, TODO宏实现 我们平时在开发过程中, 往往并非憋足气一股脑敲完所有代码.每一个模块, 每一个函数的实现总有个先后顺序. 又或者哪个部分须要做调整, 改动- 所以, 我们须要有一个东西, ...

  9. HTML5里的input标签的required属性的提示

    <input type="text" name="usr_name" required='required' oninvalid="setCus ...

  10. PCL点云曲面重建(1)

    在测量较小的数据时会产生一些误差,这些误差所造成的不规则数据如果直接拿来曲面重建的话,会使得重建的曲面不光滑或者有漏洞,可以采用对数据重采样来解决这样问题,通过对周围的数据点进行高阶多项式插值来重建表 ...