unix环境高级编程 第三版 第三章 文件I/O
3.1 引言
理解常用的五个函数:open,read,write,lseek,close,另外了解所谓不带缓冲的I/O,即每个read和write都调用内核中的一个系统调用。还要通过open函数来讨论原子操作的概念,然后说明dup,fcntl,sync,fsync,ioctl几个函数即可!
3.2 文件描述符
1.对于内核而言,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,当打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
2.一般而言,unix的shell将文件描述符0,1,2分别与标准输入,标准输出,标准错误相关联,即STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。这些常量定义在<unistd.h>中,文件描述符的范围是0~OPEN_MAX-1。
3.3 函数open和openat
1.调用这两个函数可以打开或则创建一个文件。
#include<fcntl.h>
int open(const char *path, int oflag, .../*mode_t mode*/); //仅当创建新文件时才使用最后这个参数
int openat(int fd, const char *path, int oflag, ... /*mode_t mode);
下面有各参数含义:
path:打开或者创建文件的名字
oflag参数可以用来说明此参数是多个选项,可用 | 构成该参数,
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_EXE 只执行打开
O_SEARCH 只搜索打开
很显然,上面这5个参数有且只能有一个参与,然而下面这几个参数可以不只一个参与,即没有唯一性
O_APPEND 每次写时都追加到文件尾
O_CLOEXEC 把FD_CLOEXEC常量设置为文件描述符
O_CREAT 若此文件不存在则创建它
O_DIRECTORY 如果path引用的不是目录,则出错
O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错
O_NOCTTY 如果path引用的是终端设备,则不将该设备分配作为此进程的控制终端
O_NOFOLLOW 如果path引用的是一个符号链接,则出错
O_NOBLOCK 如果path引用的是一个FIFO,一个块特殊文件或一个字符特殊文件,则出错
O_SYNC 使每次write等待物理I/O操作完成,包括该write操作引起的文件属性更新所需I/O
O_TRUNC 如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0
O_TTY_INIT 如果打开一个还未打开的终端设备,设置非标准termious参数值,使其符合独立的UNIX特性。
2. fd参数把open和openat函数区分开,共有三种可能性
(1)path参数指定的是绝对路径名,在这种情况下fd参数被忽略,openat函数就相当于open函数
(2)path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址,fd参数是通过打开相对路径名所在的目录来获取
(3)path指定的是相对路径名,fd参数具有特殊值AT_FDCWD,在这种情况下路径名在当前工作目录中获取,open函数在操作上与openat函数相似
3.4 函数create
#include<fcntl.h>
int creat(const char *path,mode_t mode); //若成功,返回为只写打开的文件描述符,若出错,返回-1
注意,此函数等效为open(path,O_WRONLY|O_CREAT|O_TRUNC,mode);这个函数的缺点是只能创建以只写的方式打开的文件
3.5 函数close
可以调用close函数关闭一个打开文件
#include<unistd.h>
int close(int fd);//成功返回0,出错则返回-1
当一个进程终止时,内核会自动关闭它所打开的文件。
3.6 函数lseek
每个打开文件都有一个与其相关联的“当前文件偏移量”,其通常是一个非负整数,用以度量从文件开始出的计算的字节数。可以用lseek显示地为一个文件设置偏移量。
#include<unistd.h>
off_t lseek(int fd,off_t offset ,int whence); //若成功,返回新的文件偏移量,若出错,返回-1
对参数offset的解释与whence的值相关
1.若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始出offset个字节
2.若whence是SEEK_CUR,则将该文件偏移量设置为当前值加offset,offset可正可负
3.若whence的值是SEEK_END,则将该文件偏移量设置为文件长度加offset,offset可正可负
3.7 read函数
- /*
- *函数功能:读取已打开文件的数据;
- *返回值:若成功返回读取的字节数,若到文件尾则返回0,若出错则返回-1;
- */
- /*
- *函数原型:
- */
- #include <unistd.h>
- ssize_t read(int filedes, void *buf, size_tnbyte);
- /*
- *参数解释:
- *filedes是指所要读取文件的文件描述符;
- *buf存储所读取数据;
- *nbyte表示最多读取的字节数;
- */
3.8 write函数
- /*
- *函数功能:向已打开文件写入数据;
- *返回值:若成功返回已写的字节数,若出错则返回-1;
- */
- /*
- *函数原型:
- */
- #include <unistd.h>
- ssize_t write(int filedes, const void *buf,size_t nbyte);
- /*
- *参数解释:
- *filedes是指所要写入数据文件的文件描述符;
- *buf存储所要存储的数据;
- *nbyte表示最多写入的字节数;
- */
3.10 文件共享
UNIX支持不同进程共享打开的文件。内核使用三种数据结构表示打开的文件:
进程在进程表中都有一个记录项,记录项中包含一张打开文件的描述符表。每个描述符占一项:描述符标志fd;指向一个文件表项的指针。
内核维持一张所有打开文件的文件表,每个文件表项包含文件状态标志、当前文件偏移量、指向文件v节点表项的指针。
每个打开文件有一个v节点表,每个v节点包含文件类型、操作函数指针和文件的i节点等。
Linux将v节点和i节点实现为独立于文件系统的i节点和依赖文件系统的i节点。
不同进程共享文件时,每个进程都有一个该文件的文件表项,指向同一个v节点表。多个文件描述符也可能指向同一个文件表项,如使用 dup 函数和 fork 后的父子进程。以下是不同进程共享同一个文件的示意图:
3.11 原子操作
原子操作指由多步组成的操作,执行时要么全部执行,要么一步也不执行。
多个进程共享同一个文件,可能造成进程对文件的连续的操作被打乱,这就需要使操作成为原子操作。如 O_APPEND 将到尾端和写入数据组成原子操作,还有 O_CREAT 和 O_EXCL 将检查文件是否存在和创建文件组成原子操作。例如:pread 和 pwrite 将偏移量和读/写组成原子操作。
pread 和 pwrite 函数
- #include <unistd.h>
- /* 函数功能:定位并读取数据
- * 返回值:成功返回读到的字节数,已到文件尾则返回0,出错返回-1 ;
- */
- ssize_t pread(int fd, void *buf, size_tcount, off_t offset);
- /* 函数功能:定位并写入数据
- * 返回值:成功返回写入的字节数,出错返回-1;
- */
- ssize_t pwrite(int fd, const void *buf,size_t count, off_t offset);
3.12 函数dup 和dup2
这两个函数用于复制一个文件描述符
#include<unistd.h>
int dup(int fd);
int dup2(int fd ,int fd2); //这两个函数用于复制一个文件描述符,dup返回一个新的文件描述符,且这个文件描述符一定是当前可用文件描述符中的最小数。
对于dup2,可以用fd2来描述新的文件描述符,若fd2已经打开,则先将其关闭。若fd=fd2,则dup2返回fd2,而且不关闭它。否则fd2的FD_CLOEXEC文件描述标志就被清除,这样fd2在进程调用exec时是打开状态。
3.13 刷新缓冲区函数
- unix系统在写数据时是采用文件延迟写,但是我们可以自己刷新缓冲区,将数据写入磁盘。以下是刷新缓冲区,将数据写入磁盘的函数sync,fsync,fdatasync;
- #include <unistd.h>
- /* 将所有修改过的块缓冲区排入写队列,然后返回,不等待写磁盘结束 */
- void sync(void);
- /* 对指定文件刷新块缓冲区,等待写磁盘结束,更新文件属性
- * 返回值:成功返回0,出错返回-1 */
- int fsync(int fd);
- /* 对指定文件刷新块缓冲区,等待写磁盘结束,不更新文件属性
- * 返回值 成功返回0,出错返回-1 */
- int fdatasync(int fd);
3.14 fcntl函数
该函数可以改变已经打开文件的属性
#include<unistd.h>
int fcntl(int fd ,int cmd , .../*int arg*/);
该函数有以下五个功能
(1)复制一个已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC)
(2)获取/设置文件描述符(cmd=F_GETFD或F_SETFD)
(3)获取/设置文件状态标志(cmd=F_GETFL或F_SETFL)
(4)获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
(5)获取/设置记录锁(cmd=F_GETLK,F_SETLK,F_SETLKW)
(3)ioctl函数
头文件 |
#include <unistd.h> /*System V*/ #include <sys/ioctl.h> /*BSD and Linux*/ #include <stropts.h> /*XSI STREAM*/ |
函数原型 |
int ioctl(int filedes, int request, …) |
参数 |
filedes:文件描述符 request:请求 |
返回 |
若出错则返回-1,若成功则返回其他值 |
功能 |
ioctl函数是I/O操作的杂物箱,不能用本章中其他函数表示的I/O操作通常都能用ioctl表示。 |