cha5.深入探究文件IO
练习1
请使用标准文件IO系统调用(open()和lseek())和off_t数据类型修改程序清单5-3中的程序。将宏_FILE_OFFSET_BITS的值设置为64进行编译,并测试该程序是否能够成功创建一个大文件。
将xxx64改为xxx,off64_t改为off_t,可以创建大文件(使用 -m32编译)
1 | // #define _LARGEFILE64_SOURCE |
练习2
5-2.编写一个程序,使用O_APPEND标志并以写方式打开一个已存在的文件,且将文件偏移量置于文件起始处,再写入数据。数据会显示在文件中的哪个位置?为什么?
文件末尾,在write时,lseek失效了
练习3
本习题的设计目的在于展示为何以O_APPEND标志打开文件来保障操作的原子性是必要的。请编写一程序,可接收多达3个命令行参数:
$ atomic_append filename num-bytes [x]
该程序应打开所指定的文件(如有必要,则创建之),然后以每次调用write()写入一个字节的方式,向文件尾部追加num-bytes个字节。缺省情况下,程序使用O_APPEND标志打开文件,但若存在第三个命令行参数(x),那么打开文件时将不再使用O_APPEND标志,代之以在每次调用write(前调用lseek(fd,0,SEEK_END))。同时运行该程序的两个实例,不带x参数,将100万个字节写入同一文件:
1 | $ atomic_append f1 10000oo & atomic_append f1 1000000 |
重复上述操作,将数据写入另一文件,但运行时加入x参数:
1 | $ atomic_append f2 1000000 x & atomic_append f2 1000000 x |
使用ls-1命令检查文件f1和f2的大小,并解释两文件大小不同的原因,
代码
1 |
|
运行结果
1 | $ ./practice5.3 f5.3 "Meow " 1048576 & ./practice5.3 f5.3 "Woof " 1048576 |
解释原因
不加x的10M,加x的5.9M
通过计算,如果所有数据正常写入,那么文件大小应该刚好是10M
加X的指针移动和写入不具有原子性,可能存在两个进程同时向同一偏移量处写入数据,相互覆盖,导致文件大小偏小。
不加X的具有原子性,指针移动和写入不会打断,不会在同一位置写入
练习4
使用fcntl()和 close()(若有必要)来实现 dupO和 dup2()。(对于某些错误,dup2()和fentl()返回的errno值并不相同,此处可不予考虑。)务必牢记dup2()需要处理的一种特殊情况,即 oldfd与 newfd相等。这时,应检查oldfd是否有效,测试fcntl(oldfd,F_GETFL)是否成功就能达到这一目的。若oldfd无效,则dup2()将返回-1,并将errno置为EBADF。
1 |
|
练习5
编写一程序,验证文件描述符及其副本是否共享了文件偏移量和打开文件的状态标志。
1 |
|
练习6
说明下列代码中每次执行 write()后,输出文件的内容是什么,为什么。
1 | fd1 = open(file, O_RDNR | O_CREAT | O_TRUNC,S_IRUSR | S_IMUSR); |
练习7
使用read()、write()以及 malloc函数包(见7.1.2节)中的必要函数以实现readv()和 writev()功能。
1 |
|
学习的read
,write
,readv
,writev
,preadv
,pwritev
,其输入输出的内容不必都是字符串,由iovec
的定义以及他们的函数原型可见,写入读取数据的类型为void *
或const void *
,是不限制输入输出的数据类型的。
此外,pread*
和pwrite*
具有原子性,可以代替lseek + write/read
的组合,防止多个进程相互读写同一文件时,出现读写位置出错的情况,如果常出现lseek + write/read
的组合,使用pread*
和pwrite*
可以大量减少系统调用的使用,提高性能