apue阅读笔记(2)、文件I/O

文件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可以说明函数具体的行为

image.png

image.png

image.png

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的解释

image.png

image.png
来测试书中的例子

  #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);
  }
复制代码

image.png
编译测试一下

$ ./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);
}
复制代码

image.png
测试运行一下

$ 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
复制代码

image.png

函数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节点)

image.png

原子操作

#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);
}
复制代码

image.png
编译运行一下

$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");
}
复制代码

image.png
就是先获取文件描述符的状态,再和要设置的标志做或运算,再设置回原来的文件描述符

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享