文件I/O
文件I/O操作大概就是打开文件,读文件,写文件,关闭文件,大部分操作只会用到open, read, write, lseek, close。
open函数
#include <fcntl.h>
int open(const char*path, int oflag, ...);
int openat(int fd, const char *path, int oflag, ...);
//成功返回文件描述符,失败返回-1
复制代码
path指定文件名字。oflag可以说明函数具体的行为
creat函数
creat函数可以创建一个新文件
#include <fcntl.h>
int create(const char *path, mode_t mode);
// 成功返回文件描述符,失败返回-1
// 等价于open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
复制代码
create的不足是它以只写方式创建一个文件
close函数
关闭一个打开的文件
#include <fcntl.h>
int close(int fd);
// 成功返回0,失败返回-1
复制代码
关闭一个文件时会释放该进程加在该文件上的所有记录锁
当一个进程终止时,内核会自动关闭它打开的所有文件
lseek函数
每个打开的文件都有一个与之关联的”当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。按系统默认的情况,当打开一个文件时,除非指定O_APPEND
选项,否则该偏移量被设置成0。lseek可以显式地为一个打开文件设置偏移量。
off_t lseek(int fd, off_t offset, int whence);
//成功返回新的文件偏移量,失败返回-1
复制代码
whence参数影响offset的解释
来测试书中的例子
#include "apue.h"
int main(void)
{
if (lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
printf("cannot seek\n");
else
printf("seek OK\n");
exit(0);
}
复制代码
编译测试一下
$ ./a.out < /etc/passwd
seek OK
$ ./a.out < /etc/passwd| ./a.out
cannot seek
复制代码
注意某些偏移量可能是负值,所以比较lseek的返回值时不要测试是否小于0,而是测试是否等于-1
文件偏移量可以超出文件的长度,中间会产生文件空洞
测试书中的一个例子
#include "apue.h"
#include <fcntl.h>
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int
main(void)
{
int fd;
if ((fd = creat("file.hole", FILE_MODE)) < 0)
err_sys("creat error");
if (write(fd, buf1, 10) != 10)
err_sys("buf1 write error");
/* offset now = 10 */
if (lseek(fd, 16384, SEEK_SET) == -1)
err_sys("lseek error");
/* offset now = 16384 */
if (write(fd, buf2, 10) != 10)
err_sys("buf2 write error");
/* offset now = 16394 */
exit(0);
}
复制代码
测试运行一下
$ gcc hole.c -lapue
$ ./a.out
$ ls -l file.hole
-rw-r--r-- 1 yuanzhihong yuanzhihong 16394 Jun 6 22:03 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
复制代码
函数read和write
#include<unistd.h>
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, void *buf, size_t nbytes);
// 返回读或者写的字节数。出错返回-1
复制代码
有时候会读不到那么多的字节数。比如网络中的缓冲,已经到达了文件末尾。
进程共享文件表项(i节点)
原子操作
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
// 返回读/写的字节数,出错返回-1
复制代码
dup和dup2
用来复制一个文件描述符
#include<unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);
// 返回新的文件描述符。出错返回-1
复制代码
dup相当于复制一个原来的文件描述符,dup2相当于指定fd2为复制fd,如果fd2已经打开,就先关闭fd2。
函数sync、fsync、fdatasync
这三个函数都是用来清空缓冲区的
#include<unistd.h>
int fsync(int fd);
int fdatasync(int fd);
//返回0表示成功,返回-1表示出错
void sync(void);
复制代码
fcntl函数
#include<fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg */);
// 出错返回-1
复制代码
fcntl函数有5种功能
- 1、复制一个已有的描述符,cmd=
F_DUPFD
或cmd=F_DUPFD_CLOEXEC
- 2、获取/设置文件描述符,cmd=
F_GETFD
或者cmd=F_SETFD
- 3、获取/设置文件描述符状态,cmd=
F_GETFL
或者cmd=F_SETFL
- 4、获取/设置异步I/O所有权,cmd=
F_GETOWN
或者cmd=F_SETOWN
- 5、获取/设置记录锁,cmd=
F_GETLK
或者cmd=F_SETLK
或者cmd=F_SETLKW
来看书中的一个例子,第一个参数指定文件描述符,并对于该描述符打印其所选择的文件标志说明
#include "apue.h"
#include <fcntl.h>
int
main(int argc, char *argv[])
{
int val;
if (argc != 2)
err_quit("usage: a.out <descriptor#>");
if ((val = fcntl(atoi(argv[1]), F_GETFL, 0)) < 0)
err_sys("fcntl error for fd %d", atoi(argv[1]));
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:
err_dump("unknown access mode");
}
if (val & O_APPEND)
printf(", append");
if (val & O_NONBLOCK)
printf(", nonblocking");
if (val & O_SYNC)
printf(", synchronous writes");
#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)
if (val & O_FSYNC)
printf(", synchronous writes");
#endif
putchar('\n');
exit(0);
}
复制代码
编译运行一下
$gcc fileflags.c -lapue
$./a.out 0 < /dev/tty #0表示标准输入
read only
$ ./a.out 1 > temp.foo #1表示标准输出
$ cat temp.foo
write only
$ ./a.out 2 2>>temp.foo
write only, append
$ ./a.out 5 5<>temp.foo # 5<>表示在文件描述符5上打开文件temp.foo以供读写
read write
复制代码
再看看书上的另外一个函数。对于一个文件描述符设置一个或者多个文件状态标志的函数
#include "apue.h"
#include <fcntl.h>
void
set_fl(int fd, int flags) /* flags are file status flags to turn on */
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
err_sys("fcntl F_GETFL error");
val |= flags; /* turn on flags */
if (fcntl(fd, F_SETFL, val) < 0)
err_sys("fcntl F_SETFL error");
}
复制代码
就是先获取文件描述符的状态,再和要设置的标志做或运算,再设置回原来的文件描述符
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END