UNIX环境C语言编程(2)-文件IO
2015-01-27 15:59
399 查看
1、系统限制与功能选项
•分为两类:编译时、运行时
编译时限制定义于/usr/include/sys/limits.h
如:CHAR_BIT //char类型包含几个bit
编译时选项定义于/usr/include/unistd.h
如:_POSIX_JOB_CONTROL //是否支持任务控制
•运行时限制与选项的获取,相关调用:
long sysconf(int name) //形如_SC_OPEN_MAX
long pathconf(const char *pathname,int
name)
long fpathconf(int
fildes,int name) //形如_PC_PATH_MAX
2、函数open() --打开或创建文件
•int open(const char *pathname, int oflag, ... /* mode_t mode */ );
•oflag可以由多个选项“或”组合,必须且只能指定3个选项中的一个:
O_RDONLY
//只读
O_WRONLY
//只写
O_RDWR //读写
其他常用选项
O_APPEND
//追加
O_CREAT //文件不存在时创建
O_EXCL //如果同时指定O_CREAT且文件已存在,出错
O_TRUNC //截断,文件内容清空
3、函数creat() --创建文件
•int creat(const char *pathname, mode_t mode);
•等价于
open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
•不足
通过creat()创建的文件是“只写”的
建议使用上面等价的open语句创建文件
4、函数close() --关闭文件
•int close(int
fildes);
•进程终止时自动关闭所有打开的文件描述符
5、函数lseek() --偏移定位
•off_t lseek(int
fildes,off_t offset,int whence);
•offset的解析依赖于whence的取值
SEEK_SET
//相对于文件开头,设置
SEEK_CUR
//相对于当前偏移,offset可正可负
SEEK_END
//相对于文件结尾,offset可正可负
•lseek(fd, 0, SEEK_CUR); //获取当前偏移
•对于普通文件,偏移必须是非负值;某些设备允许负值的偏移
•例子:创建一个包含空洞的文件
6、函数read() --读取数据
•ssize_t
read(int
fildes, void *buf,size_t
nbytes);
•以下情形中,实际读取的字节数少于指定读取的字节数:
读取普通文件时,且在读取指定字节数之前已到达文件结尾;
读取终端设备时,通常每次只能读取一行;
从网络读取时,缓冲机制的作用;
读取管道或FIFO时,且管道中数据少于指定读取的字节数;
读取面向记录的设备时,例如磁带,每次只能读取一个记录大小;
读取部分数据后被信号中断;
7、函数write() --写入数据
•ssize_t
write(int
fildes,const void *buf,size_t
nbytes);
•返回值通常与参数nbytes的值相同,否则表示出错
常见原因是:磁盘已满,或者超过当前进程的文件长度限制
•如果打开文件时指定了O_APPEND选项,则每次写入之前,将文件偏移设置在文件的当前结尾
•成功写入之后,文件偏移增加实际写入的字节数
8、I/O效率
•回顾一下概述(1)中引用的一个例子
•问题:如何选择合适的缓冲区大小?
使用文件系统的块大小(通常为4096或8192)
具体数字在后面章节描述
测试数据略
9、文件共享
内核中关于打开文件的数据结构
•每次写入之后,文件表中当前偏移增加相应的字节
如果当前偏移大于文件大小,V-节点表中文件大小设置为当前偏移
•如果使用O_APPEND选项打开文件
文件表的文件状态标志中设置相应标志
写入之前,文件表中当前偏移首先被设置为V-节点表中当前文件大小
•如果使用lseek定位至文件结尾
仅仅将文件表中当前偏移设置为V-节点表中当前文件大小
注意:与O_APPEND选项打开文件不同
•lseek并不产生实际的I/O操作
10、原子操作
•追加写入文件
lseek(fd, 0L, SEEK_END); //position to EOF
write(fd,buf, 100); //and
write
存在问题:并非原子操作
•替代方案:pread()与pwrite()函数
ssize_t pread(intfildes, void
*buf,size_t
nbytes,off_t offset);
原子操作,并不移动当前文件偏移
•如果文件不存在,创建文件
if( open(…) < 0 )creat(…); //存在问题:并非原子操作
正确做法示例:open (…, O_WRONLY | O_CREAT | O_EXCL, mode);
11、函数dup()与dup2() --复制描述符
•int dup(int
fildes);
•int dup2(int
fildes,int fildes2);
•说明:
dup返回当前可用的最小描述符;
dup2可以指定目标描述符fildes2,如果fildes2已经打开,它将首先被关闭(fildes与fildes2相等时除外)
调用返回后,存在两个等价的文件描述符
dup与dup2对于实现输入/输出重定向非常有用
12、函数sync()、fsync()、fdatasync() --数据同步
•int fsync(int
fildes);
•int fdatasync(int
fildes);
•void sync(void);
•说明:
sync仅对修改过的内核缓冲区排队,并不等待数据写入磁盘
fsync只作用于单个文件,等待数据确实写入磁盘后返回
fdatasync与fsync类似,但是只影响文件的数据部分,并不维护文件属性
13、函数fcntl() --修改文件特性
•int fcntl(int
fildes,int
cmd, ... /*int
arg */ );
•功能概述:
复制描述符,cmd = F_DUPFD,与dup()类似
获取/设置描述符标志,F_GETFD / F_SETFD
获取/设置状态标志,F_GETFL / F_SETFL
获取/设置异步I/O属主,F_GETOWN / F_SETOWN
获取/设置记录锁,F_GETLK / F_SETLK / F_SETLKW
•例子:获取文件状态标志
14、函数ioctl() --包罗万象的IO操作
•int ioctl(int
fildes,int request, ...);
•ioctl函数是I/O操作的杂物箱
不能用本章中其他函数表示的I/O操作通常都能用ioctl表示
终端I/O是ioctl的最大用途
•后续章节详述
15、/dev/fd目录
•一些系统提供/dev/fd目录,其目录项是名为0、1、2等的文件
•打开文件/dev/fd/n等效于复制描述符n(假定描述符n是打开的)
fd =open("/dev/fd/0",
mode);
等价于
fd =dup(0);
•/dev/fd主要由shell程序使用,可以使用路径名参数来处理标准输入和标准输出
cat file1 /dev/fd/0 >file.out
•AIX平台并不支持
•分为两类:编译时、运行时
编译时限制定义于/usr/include/sys/limits.h
如:CHAR_BIT //char类型包含几个bit
编译时选项定义于/usr/include/unistd.h
如:_POSIX_JOB_CONTROL //是否支持任务控制
•运行时限制与选项的获取,相关调用:
long sysconf(int name) //形如_SC_OPEN_MAX
long pathconf(const char *pathname,int
name)
long fpathconf(int
fildes,int name) //形如_PC_PATH_MAX
2、函数open() --打开或创建文件
•int open(const char *pathname, int oflag, ... /* mode_t mode */ );
•oflag可以由多个选项“或”组合,必须且只能指定3个选项中的一个:
O_RDONLY
//只读
O_WRONLY
//只写
O_RDWR //读写
其他常用选项
O_APPEND
//追加
O_CREAT //文件不存在时创建
O_EXCL //如果同时指定O_CREAT且文件已存在,出错
O_TRUNC //截断,文件内容清空
3、函数creat() --创建文件
•int creat(const char *pathname, mode_t mode);
•等价于
open (pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
•不足
通过creat()创建的文件是“只写”的
建议使用上面等价的open语句创建文件
4、函数close() --关闭文件
•int close(int
fildes);
•进程终止时自动关闭所有打开的文件描述符
5、函数lseek() --偏移定位
•off_t lseek(int
fildes,off_t offset,int whence);
•offset的解析依赖于whence的取值
SEEK_SET
//相对于文件开头,设置
SEEK_CUR
//相对于当前偏移,offset可正可负
SEEK_END
//相对于文件结尾,offset可正可负
•lseek(fd, 0, SEEK_CUR); //获取当前偏移
•对于普通文件,偏移必须是非负值;某些设备允许负值的偏移
•例子:创建一个包含空洞的文件
#include <stdio.h> #include <fcntl.h> char buf1[] = "abcdefghij"; char buf2[] = "ABCDEFGHIJ"; int main(void) { int fd; /* 创建文件 */ if( (fd = creat("file.hole", 0777)) < 0 ) { perror("creat"); exit(1); } /* 写入10个字节 */ if( write(fd, buf1, 10) != 10 ) { perror("buf1 write"); exit(1); } /* offset now = 10 */ /* 设置文件偏移,越过文件结尾 * 因为位移量可能是负值,所以在比较 lseek的返回值时应当谨慎, * 不要测试它是否小于0,而要测试它是否等于-1 */ if( lseek(fd, 16384, SEEK_SET) == -1 ) { perror("lseek"); exit(1); } /* offset now = 16384 */ /* 再次写入10个字节 */ if( write(fd, buf2, 10) != 10 ) { perror("buf2 write"); exit(1); } /* offset now = 16394 */ exit(0); }
6、函数read() --读取数据
•ssize_t
read(int
fildes, void *buf,size_t
nbytes);
•以下情形中,实际读取的字节数少于指定读取的字节数:
读取普通文件时,且在读取指定字节数之前已到达文件结尾;
读取终端设备时,通常每次只能读取一行;
从网络读取时,缓冲机制的作用;
读取管道或FIFO时,且管道中数据少于指定读取的字节数;
读取面向记录的设备时,例如磁带,每次只能读取一个记录大小;
读取部分数据后被信号中断;
7、函数write() --写入数据
•ssize_t
write(int
fildes,const void *buf,size_t
nbytes);
•返回值通常与参数nbytes的值相同,否则表示出错
常见原因是:磁盘已满,或者超过当前进程的文件长度限制
•如果打开文件时指定了O_APPEND选项,则每次写入之前,将文件偏移设置在文件的当前结尾
•成功写入之后,文件偏移增加实际写入的字节数
8、I/O效率
•回顾一下概述(1)中引用的一个例子
#include <stdio.h> #include <unistd.h> int main(void) { int n; char buf[BUFSIZ]; /* 读取标准输入 */ while( (n = read(STDIN_FILENO, buf, BUFSIZ)) > 0 ) { /* 写入标准输出 */ if( write(STDOUT_FILENO, buf, n) != n ) { perror("write"); exit(-1); } } if( n < 0 ) { perror("read"); } exit(0); }
•问题:如何选择合适的缓冲区大小?
使用文件系统的块大小(通常为4096或8192)
具体数字在后面章节描述
测试数据略
9、文件共享
内核中关于打开文件的数据结构
•每次写入之后,文件表中当前偏移增加相应的字节
如果当前偏移大于文件大小,V-节点表中文件大小设置为当前偏移
•如果使用O_APPEND选项打开文件
文件表的文件状态标志中设置相应标志
写入之前,文件表中当前偏移首先被设置为V-节点表中当前文件大小
•如果使用lseek定位至文件结尾
仅仅将文件表中当前偏移设置为V-节点表中当前文件大小
注意:与O_APPEND选项打开文件不同
•lseek并不产生实际的I/O操作
10、原子操作
•追加写入文件
lseek(fd, 0L, SEEK_END); //position to EOF
write(fd,buf, 100); //and
write
存在问题:并非原子操作
•替代方案:pread()与pwrite()函数
ssize_t pread(intfildes, void
*buf,size_t
nbytes,off_t offset);
原子操作,并不移动当前文件偏移
•如果文件不存在,创建文件
if( open(…) < 0 )creat(…); //存在问题:并非原子操作
正确做法示例:open (…, O_WRONLY | O_CREAT | O_EXCL, mode);
11、函数dup()与dup2() --复制描述符
•int dup(int
fildes);
•int dup2(int
fildes,int fildes2);
•说明:
dup返回当前可用的最小描述符;
dup2可以指定目标描述符fildes2,如果fildes2已经打开,它将首先被关闭(fildes与fildes2相等时除外)
调用返回后,存在两个等价的文件描述符
dup与dup2对于实现输入/输出重定向非常有用
12、函数sync()、fsync()、fdatasync() --数据同步
•int fsync(int
fildes);
•int fdatasync(int
fildes);
•void sync(void);
•说明:
sync仅对修改过的内核缓冲区排队,并不等待数据写入磁盘
fsync只作用于单个文件,等待数据确实写入磁盘后返回
fdatasync与fsync类似,但是只影响文件的数据部分,并不维护文件属性
13、函数fcntl() --修改文件特性
•int fcntl(int
fildes,int
cmd, ... /*int
arg */ );
•功能概述:
复制描述符,cmd = F_DUPFD,与dup()类似
获取/设置描述符标志,F_GETFD / F_SETFD
获取/设置状态标志,F_GETFL / F_SETFL
获取/设置异步I/O属主,F_GETOWN / F_SETOWN
获取/设置记录锁,F_GETLK / F_SETLK / F_SETLKW
•例子:获取文件状态标志
#include <stdio.h> #include <fcntl.h> int main(int argc, char *argv[]) { int val; if( argc != 2 ) { printf("usage: %s <descriptor#>\n", argv[1]); exit(0); } /* 获取文件状态标志 */ if( (val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0 ) { printf("fcntl error for fd %d\n", atoi(argv[1])); exit(0); } /* 检查访问模式 */ switch( val & O_ACCMODE ) { case O_RDONLY: printf("read only"); break; case O_WRONLY: printf("write only"); break; case O_RDWR: printf("read write"); break; default: printf("unknown access mode"); break; } /* 检查其他标志 */ if( val & O_APPEND ) printf(", append"); if( val & O_NONBLOCK ) printf(", nonblocking"); #if defined(O_SYNC) if( val & O_SYNC ) printf(", synchronous writes"); #endif #if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) if( val & O_FSYNC ) printf(", synchronous writes"); #endif putchar('\n'); exit(0); } /* 执行示例: $ ./a.out 0 < /dev/tty read only $ ./a.out 1 > temp.foo $ cat temp.foo write only $ ./a.out 2 2>>temp.foo write only, append $ ./a.out 5 5<>temp.foo read write */
14、函数ioctl() --包罗万象的IO操作
•int ioctl(int
fildes,int request, ...);
•ioctl函数是I/O操作的杂物箱
不能用本章中其他函数表示的I/O操作通常都能用ioctl表示
终端I/O是ioctl的最大用途
•后续章节详述
15、/dev/fd目录
•一些系统提供/dev/fd目录,其目录项是名为0、1、2等的文件
•打开文件/dev/fd/n等效于复制描述符n(假定描述符n是打开的)
fd =open("/dev/fd/0",
mode);
等价于
fd =dup(0);
•/dev/fd主要由shell程序使用,可以使用路径名参数来处理标准输入和标准输出
cat file1 /dev/fd/0 >file.out
•AIX平台并不支持
相关文章推荐
- Unix环境高级编程:文件 IO 原子性 与 状态 共享
- (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO
- unix 环境高级编程4 - 文件IO_1
- unix环境高级编程-文件IO
- 文件io(二)--unix环境高级编程笔记
- 【UNIX环境高级编程】文件 IO 操作 一 ( open | close | creat | lseek | write | read )
- UNIX环境高级编程学习之第三章文件IO-文件读偏移操作
- UNIX环境编程基本函数——文件IO
- UNIX环境高级编程---------文件IO
- UNIX环境高级编程(三)文件IO
- UNIX环境C语言编程(15)-终端IO
- UNIX环境C语言编程(3)-文件与目录
- UNIX环境C语言编程(11)-高级IO
- 3.UNIX 环境高级编程--文件 IO
- UNIX环境高级编程学习之第三章文件IO-文件读操作
- unix环境高级编程-文件IO
- Unix环境编程之文件IO
- UNIX环境高级编程学习之第三章文件IO-文件写操作
- UNIX环境编程——文件IO
- UNIX环境C语言编程(5)-系统数据文件