#includeint open(const char* path, int oflag, .../mode_t mode/); //成功返回文件描述符,失败返回-1
path参数为要打开或创建文件的名字,oflag用下面一个或多个常量“或”运算(只列出常用):
- O_RDONLY:只读打开
- O_WRONLY:只写打开
- O_RDWR:读写打开
- O_EXEC:只执行打开
- O_SEARCH:只搜索打开(应用与目录)
以上五个必须指定一个,且只能指定一个。以下常量是可选的 - O_APPEND:追加写
- O_CLOEXEC:把FD_CLOEXEC常量设置成文件描述符标志
- O_CREAT:若此文件不存在则创建。使用此选项时,同时需要说明第三个参数mode,用该mode指定该文件的访问权限位
- O_NONBLOCK:非阻塞
- O_TRUNC:若此文件存在,且为写或读写成功打开,则将其长度截断为0
- O_SYNC:使每次write等待物理I/O操作完成,包括由该write操作引起的文件属性更新所需的I/O
- O_DSYNC:使每次write等待物理I/O操作完成,但若该写操作并不影响读取刚写入的数据,则无需等待文件属性被更新
- O_RSYNC:使每一个以文件描述符作为参数进行的read操作等待,直至所有对文件同一部分挂起的写操作都完成
由open函数返回的描述符一定是最小的未用描述符值。这一点被某些应用程序用来在标准输入、标准输出或标准错误上打开新的文件。
2. creat创建新文件:
#inlcudeint creat(const char* path, mode_t mode); //返回为(只写打开的)文件描述符,若出错返回-1
等同于以下open
open(path, O_WRonLY | O_CREAT | O_TRUNC, mode)3. close
#includeint close(int fd); //成功返回0,失败返回-1
关闭一个文件时会释放该进程加在该文件上的所有记录锁。
当进程终止时,内核自动关闭它所有的打开文件。
每个打开文件都有一个与其相关联的“当前文件偏移量”(current file offset)。通常是非负整数,记录文件从开始处计算的字节数。当打开一个文件时,除非指定O_APPEND选项,否则该偏移量设置为0。
可调用lseek显示地为一个打开文件设置偏移量:
#includeoff_t lseek(int fd, off_t offset, int whence); //成功时返回新的文件偏移量,失败返回-1
对参数offset的解释与参数whence的值有关。
- 若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节
- 若whence是SEEK_CUR,则将该文件的偏移量设置为当前值加offset,offset可正可负
- 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负
例如可用以下方式确定打开文件的当前偏移量:
off_t curpos; curpos = lseek(fd, 0, SEEK_CUR);
这种方法也可用来测试所涉及的文件是否可以设置偏移量。若文件描述符指向的是一个管道、FIFO或socket,则lseek返回-1,并将errno设为ESPIPE。
注:对于普通文件,当前的偏移量必须是一个非负整数;但是某些设备也可能允许负的偏移量。因为偏移量可能是负值,所以在比较lseek的返回值时应当谨慎,不要测试它是否小于0,而是是否等于-1。
#includessize_t read(int fd, void* buf, size_t nbytes);
返回读到的字节数,若已到文件尾,返回0;若出错,返回-1。
6. write#includessize_t write(int fd, const void* buf, size_t nbytes);
返回值通常与参数nbytes相同
二、I/O的效率 三、文件共享内核使用3种数据结构表述打开文件,它们之间的关系决定了在文件共享方面,一个进程对另一个进程可能产生的影响。
(1)每个进程在进程表中都有一个记录项,其中包含一张打开文件描述符表,每个描述符占用一项。每一项包括:
- 文件描述符标志
- 指向一个文件表项的指针
(2)内核为所有打开文件维护一张文件表。表项包括:
- 文件状态标志
- 当前文件偏移量
- 指向该文件v/i节点表项的指针
(3)每个打开文件都有一个v-node结构。对于大多数文件,v节点还包含了该文件的i节点(i-node, 索引节点)。这些信息都是在打开文件时从磁盘读入内存的。Linux没有使用v节点,而是使用了i-node。但两者概念是一样的,都指向文件系统特有的i-node结构。
注:关于文件系统的i-node,见另一篇博客https://blog.csdn.net/sunximei/article/details/120593787
多个进程读取同一个文件能正确工作。因为每个进程都有它自己的文件表项
,其中也有它自己的当前偏移量。但是当多个进程写同一文件时,则可能出错。
一般而言,原子操作(atomic operation)指的是由多步组成的一个操作。即要么执行完全部步骤,要么一步也不执行。
四、函数dup和dup2复制一个现有的描述符:
#inlcudeint dup(int fd); int dup2(int fd, int fd2);
成功返回新的文件描述符,失败返回-1。
- dup:返回的新文件描述符一定是当前当用描述符的最小值。
- dup2:可以用fd2参数指定新描述符的值。如果fd2已经打开,则先将其关闭;若fd等于fd2,则dup2直接返回fd2,否则fd2的FD_CLOEXEC文件描述符标志就被清除,这样fd2在进程调用exec时是打开状态。
关于FD_CLOEXEC的解释:https://blog.csdn.net/bemf168/article/details/80025365
传统的UNIX系统在内核中设有缓冲区高速缓存或页高速缓存,大多数磁盘I/O都通过缓冲区进行。当我们向文件写数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘(也叫做delayed write)。
有时需要立马冲洗缓冲区,以下三个函数
#includevoid sync(void); int fsync(int fd); int fdatasync(int fd);
成功返回0,失败返回-1(除了sync)
- sync:只是将所有修改过的块缓冲区排入写队列,然后就返回,并不实际等待写磁盘操作结束。通常,update系统守护进程周期性地(一般30s一次)调用sync函数。保证定期冲洗(flush)内核缓冲区。
- fsync:只对fd指定的一个文件起作用,并且等待写磁盘操作结束才返回。
- fdatasync:类似于fsync,但只影响文件数据部分;而fsync还会同步更新文件的属性
改变已经打开文件的属性:
#includeint fcntl(int fd, int cmd, ...);
在本节中,第三个参数只是整数。
失败返回-1,成功时,返回值依赖cmd
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)



