- #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 */ );
- //返回文件描述符
- STDIN_FILENO //标准输入
- STDOUT_FILENO //标准输出
- #include<fcntl.h>
- int creat(const char *path, mode_t moe);
- //返回文件描述符,负数表示错误
- #include <unistd.h>
- int close(int fd);
- //关闭一个文件还会释放该进程在该文件上的所有锁记录
- #include <unistd.h>
- 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 <unistd.h>
- 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 <unistd.h>
- 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 <unistd.h>
- int dup(int fd);
- int dup2(int fd, int fd2);
- //同步刷新
- #include <unistd.h>
- int fsync(int fd);
- int fdatasync(int fd);
- void sync(void);
- //sync只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写
- //磁盘操作结束
- //一般系统守护进程30秒调用sync函数
- //fsync函数只对文件描述符fd指定的一个文件起作用,并且等待写磁盘操作结束
- //fdatasync函数类似fsync,但是只影响文件的数据部分,fsync还会同步更新
- //文件的属性
- //更改已经打开文件的属性
- #include <fcntl.h>
- 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 <unistd.h>
- #include <sys/ioctl.h> //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 <fcntl.h>
- #include <stdio.h>
- #include <unistd.h>
- 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 <fcntl.h>
- #include <stdio.h>
- #include <unistd.h>
- #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选项,所以程序被编写成下列形式
- if( lseek(fd,0L2) < 0 ) {
- err_sys("lseek error");
- }
- if( write(fd, buf, 100) != 100 ) {
- err_sys("write error");
- }
创建一个文件
- 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命令对其采用了一种特殊处理
- filter file2 | cat file1 - file3 | ipr
- //首先cat读file1,然后是标准输入,然后是file3
- //如果cat支持/dev/fd 也可以改成
- filter file2 | cat file1 /dev/fd/0 file3 | ipr