相关api列表
#include
int open(const char *path, int oflag, /* mode_t mode */ );
int openat(int fd, const char *path, int oflag, /* mode_t mode */ );
//返回文件描述符
STDIN_FILENO //标准输入
STDOUT_FILENO //标准输出
#include
int creat(const char *path, mode_t moe);
//返回文件描述符,负数表示错误
#include
int close(int fd);
//关闭一个文件还会释放该进程在该文件上的所有锁记录
#include
off_t lseek(int fd, off_t offset, int whence);
//1.若whence是 SEEK_SET,则将该文件的偏移量设置为距离文件开始出offset个
//字节
//2.若whence是 SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,
//offset可为正或负
//3.若whence是 SEEK_END,则将该文件的偏移量设置为文件长度加offset,
//offset可正可负
#include
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);
//read成功,返回读到的字节数,如果已达到文件尾端则返回0
//读普通文件时,读要求的字节数之前已经达到文件尾端则返回0
/从终端设备读一次最多读一行
//从网络读,网络中的缓冲机制可能造成返回值小于所要求读的字节数
//当从管道或FIFO读时,若管道包含的字节少于所需的数量,那么read将返回实际
//可用的字节数
//当从某些面向记录的设备(如磁带)读时,一次最多返回一个记录
//当一信号造成中断,而已经读了部分数据时,在成功返回之前,该偏移量将增加
//实际读到的字节数
//write成功,返回实际写入的字节数
//原子性的执行读写操作
#include
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int fd, void *buf, size_t nbytes, off_t offset);
//复制文件描述符
#include
int dup(int fd);
int dup2(int fd, int fd2);
//同步刷新
#include
int fsync(int fd);
int fdatasync(int fd);
void sync(void);
//sync只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写
//磁盘操作结束
//一般系统守护进程30秒调用sync函数
//fsync函数只对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束
//fdatasync函数类似fsync,但是只影响文件的数据部分,fsync还会同步更新
//文件的属性
//更改已经打开文件的属性
#include
int fcntl(int fd, int cmd, /* int arg */);
//fcntl函数有5种功能
//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)
//杂物箱函数
#include
#include //BSD and linux
int ioctl(int fd, int request);
open函数的oflag参数
参数 | 含义 |
O_RDONLY | 只读打开 |
O_WRONLY | 只写打开 |
O_RDWR | 读,写打开 |
O_EXEC | 只执行打开 |
O_SEARCH | 只搜索打开 |
O_APPEND | 每次写时都追加到文件尾端 |
O_CLOEXEC | 把FD_CLOEXEC常量设置为文件描述符 |
O_CREAT | 若文件不存在则创建它,同时需要第三个参数指明 新文件的访问权限 |
O_DIRECTORY | 如果path不是目录则出错 |
O_EXCL | 如果同时指定了O_CREAT,而文件已存在则出错 次功能可测试一个文件是否存在若不存在则创建 |
O_NOCTTY | 如果path引用的是终端设备,则不将该设备分配作为 进程的控制终端 |
O_NOFOLLOW | 如果path引用的是一个符号链接则出错 |
O_NONBLOCK | 如果path引用的是一个FIFO,一个块特文件或一个字符特殊文件,则 此选项为文件的本次打开操作和后续的I/O操作设置为非阻塞方式 |
O_SYNC | 每次write等待物理I/O操作完成,包括由该write操作引起的文件属性更新 所需的I/O |
O_TRUNC | 如果此文件存在,而且为只写或读-写成功打开,则将其长度截断为0 |
O_TTY_INIT | 如果打开一个还未打开的终端设备,设置非标准termios参数值,使其符合Single UNIX Specificationi |
O_DSYNC | 每次write要等待物理I/O操作完成,但如果该写操作并不影响读取写入的数据, 则不需要等待文件属性被更新 |
O_RSYNC | 使每一个以文件描述符作为参数进行的read操作等待,直至所有对文件同一部分挂起 的写操作都完成 |
写空洞的例子
#include
#include
#include
char buf1[] = "abcdefghij";
char buf2[] = "ABCDEFGHIJ";
int main(int argc, char *argv[]) {
if(argc < 2) {
printf("input create file path!\r\n");
return 1;
}
int fd;
if( (fd=creat(argv[1],0755)) < 0 ) {
printf("create error");
printf("\r\n");
}
if( write(fd,buf1,10) != 10 ) {
printf("buf write error");
printf("\r\n");
}
if( lseek(fd,16384, SEEK_SET) == -1) {
printf("lseek error");
printf("\r\n");
}
if( write(fd,buf2,10) != 10 ) {
printf("buf2 write error");
printf("\r\n");
}
return 0;
}
读写文件的例子,将一个文件读取,写入到另一个文件中
#include
#include
#include
#define BUFFSIZE 4096
int main(int argc, char *argv[]) {
if(argc < 3) {
printf("input read path, write path\r\n");
return 1;
}
int r_fd;
if( (r_fd=open(argv[1],O_RDONLY)) < 0 ) {
printf("open error\r\n");
}
int w_fd;
if( (w_fd=creat(argv[2],0755)) < 0 ) {
printf("create error\r\n");
}
int num;
char buf[BUFFSIZE];
while( (num=read(r_fd, buf, BUFFSIZE)) > 0) {
if( write(w_fd, buf, num) != num ) {
printf("write error\r\n");
}
}
if( num < 0 ) {
printf("read error\r\n");
}
return 0;
}
读写文件时候,buf大小不同对影响的影响,12核/12G内存
buf大小 | 真正时间 | 用户时间 | 系统时间 |
512 | 3.269s | 0.077s | 2.523s |
1024 | 1.793s | 0.039s | 1.723s |
2048 | 1.682s | 0.015s | 1.214s |
4096 | 2.410s | 0.017s | 2.184s |
8192 | 5.404s | 0.009s | 1.218s |
16384 | 5.433s | 0.013s | 1.175s |
32768 | 5.396s | 0.005s | 1.188s |
65535 | 5.383s | 0.004s | 1.205s |
8M | 0.878s | 0.000s | 0.876s |
如果没每一次,调用fsync(fd)将内存刷新到磁盘上,性能结果如下:
buf大小 | 真实时间 | 用户时间 | 系统时间 |
1024 | 1m52.317s | 0.386s | 12.616s |
2048 | 1m7.329s | 0.086s | 8.528s |
4096 | 36.584s | 0.036s | 4.183s |
8192 | 20.883s | 0.085s | 2.886s |
16384 | 13.335s | 0.026s | 2.200s |
32768 | 9.761s | 0.020s | 1.711s |
65535 | 6.901s | 0.010s | 1.462s |
8M | 5.675s | 0.001s | 1.199s |
UNIX支持在不同进程共享打开文件,内核使用三种数据结构表示打开文件
1.每个进程再进程表中都有一个记录项,记录项中包含一张打开文件描述符表,可将其视为一个矢量,每个
描述符 占用一项,与每个文件描述符相关联的是:
a)文件描述符标志(close_on_exec)
b)指向一个文件表项的指针
2.内核为所有打开文件维持一张文件表,每个文件表项包含:
a)文件状态标志(读,写,添写,同步和非阻塞等)
b)当前文件偏移量
c)指向该文件v节点表项的指针
3.每个打开文件(或设备)都有一个v节点,linux没有v节点而是用通用的 i节点结构。v节点包含了文件类型和对
此文件进行各种操作的函数指针,对于大多数文件,v节点还包含了该文件的 i节点(i-node)。这些信息是在
打开文件时从磁盘上读入内存的,所以文件的所有相关信息都是随时可用的。
打开文件的内核数据结构
两个独立进程各自打开同一个文件
原子操作
早起的unix版本不支持O_APPEND选项,所以程序被编写成下列形式
C代码
if( lseek(fd,0L2) < 0 ) {
err_sys("lseek error");
}
if( write(fd, buf, 100) != 100 ) {
err_sys("write error");
}
创建一个文件
C代码
if( (fd=open(path,O_WRONLY)) < 0 ) {
if(errno == ENOENT) {
if( (fd=creat(path,mode)) < 0 ) {
err_sys("create error");
}
} else {
err_sys("open error");
}
}
fcntl函数的cm参数有11个,先说明前8个,后3个跟锁有关
参数 | 含义 |
F_DUPFD | 复制文件描述符fd |
F_DUPFD_CLOEXEC | 复制文件描述符,设置与新描述符关联的FD_CLOEXEC文件描述符 标志的值,返回新文件描述符 |
F_GETFD | 对应于fd的文件描述符标志作为函数值的返回。当前只定义了一个文件 描述符标志FD_CLOEXEC |
F_SETFD | 对于fd设置文件描述符标志 |
F_GETFD | 对于fd设置文件描述符标志 |
F_GETFL | 对应于fd的文件状态标志作为函数值返回 |
F_SETFL | 将文件状态标志设置为第三个参数的值,可以更改的几个标志是 O_APPEND,O_NONBLOCK,O_SYNC,O_DSYNC,O_RSYNC, O_FSYNC和O_ASYNC |
F_GETOWN | 获取当前接收SIGIO和SIGURG信号的进程ID或进程组ID |
F_SETOWN | 设置接收SIGIO和SIGURG信息的进程ID或进程组ID |
对于F_GETFL的函数返回值,在open函数中有说明,返回下列内容
文件状态标志 | 说明 |
O_RDONLY | 只读打开 |
O_WRONLY | 只写打开 |
O_RDWR | 读写打开 |
O_EXEC | 只执行打开 |
O_SEARCH | 只搜索目录打开 |
O_APPEND | 追加写 |
O_NONBLOCK | 非阻塞模式 |
O_SYNC | 等待写完成(数据和属性) |
O_DSYNC | 等待写完成(仅数据) |
O_RSYNC | 同步读和写 |
O_FSYNC | 等待写完成(仅BSD和Mac) |
O_ASYNC | 异步I/O(仅BSD和Mac) |
/dev/fd
较新的系统都提供名为 /dev/fd 的目录,其目录项是名为 0,1,2,等的文件,打开文件/dev/fd/n 等效于复制
描述符n
cat命令对其采用了一种特殊处理
参考
APUE学习笔记系列
《Unix环境高级编程》学习笔记:从点到面