APUE学习笔记 第三章 文件I/O
1、函数open和openat
调用open或openat函数可以打开或创建一个文件
#include<fcntl.h>
Int open(const char *path,into flag,…/*mode_t made*/);
Int openat(int fd,const char* path,into flag,…/*mode_t mode*/);
两个函数返回值:若成功,返回文件描述符(一定是最小的未用描述符数值);若出错,返回-1;
path参数是要打开或创建文件的名字;oflag参数可用来说明此函数的多个选项,用下列一个或多个常量进行“或”运算构成oflag参数
O_RDONLY 以只读方式打开文件
O_WRONLY 以只写方式打开文件
O_RDWR 以可读写方式打开文件.
上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.大多数实现将O_RDONLY定义为0,O_WRONLY定义为1,O_RDWR定义为2;
O_EXEC 只执行打开
O_SEARCH 只搜索打开
O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
O_CREAT 若欲打开的文件不存在则自动建立该文件.
O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误.
此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
O_NDELAY 同O_NONBLOCK.
O_SYNC 以同步的方式打开文件.
O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。
fd参数把open和openat函数区分开来,共有三种可能性:
a、path参数指定的是绝对路径名,此情况下,fd参数被忽略,openat函数就相当于open函数;
b、path参数指定的是相对路径名,fd参数指定了相对路径名在文件系统中的开始地址;
c、 path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在此情况下,路径名在当前工作目录中获取,openat函数在操作上与open函数类似。
示例:
open('test', O_RDONLY); // 只读方式打开
open('test', O_WRONLY | O_APPEND); // 追加的方式打开
open('test', O_RDWR); // 读写的方式打开
open('test', O_WRONLY | O_CREAT, 0666); // 创建文件,只写,权限是 0666
open('test', O_WRONLY | O_TRUNC); // 只写打开,同时把文件长度截断成 0.
2、函数creat
调用creat函数创建一个新文件
#include<fcntl.h>
Int creat(const char* path,mode_t mode);
返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1.
此函数等效于open(path,O_WRONLY | O_CREAT |O_TRUNC,mode);
creat函数的不足之处是它以只写方式打开所创建的文件。
示例:
creat("foo",0666);
3、函数close
可调用close函数关闭一个打开的文件
#include<unistd.h>
Int close(int fd);
返回值:若成功,返回0;若失败,返回-1;
关闭一个文件时还会释放该进程加在该文件上的所有记录锁。
4、函数lseek
可调用lseek显式地为一个打开文件设置偏移量。
#include<unistd.h>
off_t lseek(int fd,off_t offset,int whence);
//返回值:若成功,返回新的文件偏移量;若出错,返回为-1;
对参数offest的解释与参数whence的指有关
a、若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节;
b、若whence是SEEK_CUR,则将该文件的偏移量设置为当前值加offset,offset可正可负;
c、 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负。(可能产生空洞)
如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek返回-,并将errno设置为ESPIPE。
示例:
lseek(fd, 6, SEEK_SET);
lseek(fd,0,SEEK_END);
5、函数read
调用read函数从打开的文件中读数据。
#include<unistd.h>
ssize_t read(int fd,void *buf,size_t nbytes);
//返回值:若成功,返回读到的字节数,若已到达文件尾,返回0;若出错,返回-1.
//示例:read(srcfd, buffer, BUFFERSIZE);
6、函数write
调用write函数向打开文件写数据。
#include<unistd.h>
ssize_t write(int fd,const void* buf,size_t nbytes);
//返回值:若成功,返回已写的字节数;若出错,返回-1.
其返回值通常与参数nbytes的值相同,否则表示出错,出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。
示例:write(dstfd, buffer, len);
7、原子操作
原子操作指的是不可再分的指令操作,即在执行原子操作时不可能被打断,要么原子操作没有执行,要么已经执行完毕。
原子操作的实现必须需要硬件的支持,操作系统仅仅是在硬件指令的基础之上进行一次封装。对于没有实现原子操作的硬件,则需要操作系统从软件算法层面进行支持。
8、函数pread和pwrite
#include<unistd.h>
ssize_t pread(int fd,void *buf,size_t nbytes,off_t offset);
//返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1;
ssize_t pwrite(int fd,const void* buf,size_t nbytes,off_t offset);
//返回值:若成功,返回已写的字节数,若出错,返回-1;
调用pread相当于调用lseek后调用read;调用pwrite相当于调用lseek后调用write。
9、函数dup和dup2
下面两个函数都可以用来复制一个现有的文件描述符。
#include<unistd.h>
int dup(int fd);
int dup2(int fd,int fd2); //用fd2指定新的文件描述符
//两函数的返回值:若成功,返回新的文件描述符;若出错,返回-1.
这些函数返回的新文件描述符与参数fd共享同一个文件表项。
示例:
newfd = dup(fd);
10、函数sync、fsync和fdatesync
通常,当内核需要重用缓冲区来存放其他数据块数据时,它会把所有延迟写数据块写入磁盘。
#include<unistd.h>
Int fsync(int fd);
Int fdatesync(int fd);
返回值:若成功,返回0;若出错,返回-1;
void sync(void);
sync只将所有修改过的快缓冲区排入写队列,然后就返回,不等待实际写磁盘操作结束;
fsync函数只对文件描述符fd指定的一个文件起作用,并等待写磁盘操作结束才返回;
fdatesync函数类似于fsync,但它只影响文件的数据部分,除数据外,fsync还会同时更新文件的属性。
11、函数fcntl
函数fcntl可以改变已经打开文件的属性。
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd ,struct flock* lock);
返回值:若成功,则依赖于cmd;若出错,返回-1.
fantl函数有以下5种功能:
a、复制一个已有的描述符(cmd=F_DUPFD或F_DUPFD_CLOEXEC);
b、获取/设置文件描述符标志(cmd=F_GETFD或F_SETFD);
c、 获取/设置文件状态标识(cmd=F_GETFL或F_SETFL);
d、获取/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN);
e、获取/设置记录锁(cmd=F_GETLK、F_SETLK或F_SETLKW)。
https://blog.youkuaiyun.com/pbymw8iwm/article/details/7974789