以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
前言
在命令行中使用“man 2 open”可以获知open这个文件IO API的使用方法。
open函数的模型有两种,根据需要选择其中一种即可。
int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
两种函数模型中都有flags这个标志,它表示对文件的权限设置以及其他的一些设置,使用方法都像下面这样。
file_fd=open("a.txt", O_WRONLY | O_TRUNC | …… )
1、O_RDONLY、O_WRONLY、O_RDWR
Linux中的文件有读写权限,使用open函数打开文件时,也可以附带一定的权限说明。
比如O_RDONLY就表示以只读方式打开,O_WRONLY表示以只写方式打开,O_RDWR表示以可读可写方式打开。
当附带权限说明后,打开的文件就只能按照这种权限来操作。
2、O_APPEND、O_TRUNC
当使用open函数打开一个内部有内容的文件时,有如下情况:
(1)如果使用O_TRUNC标志,则原来的内容会被丢弃。
(2)如果使用O_APPEND标志,则新写入的内容会接续到原来内容的后面。
(3)默认不使用O_APPEND 和 O_TRUNC标志时,则原来文件中的内容保持不变。
(4)如果O_APPEND和O_TRUNC同时出现,则O_TRUNC将O_APPEND屏蔽掉。
3、O_CREAT、O_EXCL
当使用open函数打开一个文件,如果这个文件不存在,则会打开文件错误。
使用O_CREAT标志后,如果想要打开的文件不存在,则去创建该文件并打开它;如果想要打开的文件存在,则重新创建这个文件,原来的内容会消除。使用这个属性时,最好明确地知道要打开的文件是不存在的,因为如果文件存在,新创建的文件会覆盖已经存在的文件,导致误删问题。
使用O_CREAT和O_EXCL标志后,如果想打开的文件不存在时,则去创建这个文件;如果想打开的文件存在,则报错。
open函数在使用O_CREAT标志去创建文件时,可以使用第三个参数mode来指定要创建的文件的权限。mode使用4个数字来指定权限的,其中后面三个很重要,对应我们要创建的这个文件的权限标志,比如创建一个可读可写不可执行的文件就用0666。
4、O_NONBLOCK
打开文件默认是阻塞式的,如果你希望以非阻塞的方式打开文件,则要添加O_NONBLOCK标志。另外,这个标志只用于设备文件,而不用于普通文件。
补充说明
(1)阻塞与非阻塞的定义
如果一个函数是阻塞式的,调用这个函数时,函数有可能被卡住,即这个函数内部要完成的事情条件不具备,当前没法做,要等待条件成熟,函数被阻塞住了就不能立刻返回。如果一个函数是非阻塞式的,调用这个函数后会立即返回,但是函数有没有完成任务则不确定。
(2)阻塞与和非阻塞的对比
它们是两种不同的设计思路,并没有好坏之分。总的来说,阻塞式的结果有保障但是时间没保障,非阻塞式的时间有保障但是结果没保障。
操作系统提供的API和由API封装而成的库函数,有很多被设计为阻塞式或者非阻塞式的,应用程度调用这些函数时,需要特别注意。
5、O_SYNC
注意这个标志是写在open函数中的,然后影响write函数的。
open函数没有O_SYNC标志时,write函数将内容写入底层缓冲区即可返回。(然后OS会在合适的时机,将buf中的内容一次性的同步到硬盘中。这种设计是为了提升硬件操作的性能,提升硬件寿命。)
open函数有O_SYNC标志时,write函数会阻塞等待底层完成硬盘写入才返回。这意味着把内容立即写入硬盘中,不需要等待合适的时机。
6、再谈O_APPEND
1)情形1
如果在同一程序中使用两次open函数打开同一个文件,然后分别读取,结果会怎样?
结果可能有两种:一种是fd1和fd2分别读,第二种是接续读。
经过实验验证,证明了结果是fd1和fd2分别读。
结果分析说明:fd1和fd2所对应的文件指针是不同的2个独立的指针。文件指针是包含在动态文件的文件管理表中的,所以可以看出linux系统的进程中不同fd对应的是不同的独立的文件管理表。
2)情形2
如果在同一程序中使用两次open函数打开同一个文件,然后分别写入,结果会怎样?
结果可能有两种:一种是fd1和fd2分别写,第二种是接续读。
经过实验验证,证明了结果是fd1和fd2分别写。(会导致覆盖问题。)
结果分析说明:原因和情形1)一样。另外,正常情况下我们有时候需要分别写,有时候又需要接续写,所以这两种本身是没有好坏之分的,关键看用户需求。
3)如何实现接续写(解决覆盖问题)?
在open时加O_APPEND标志,即可实现接续写,不再出现覆盖问题。
4)O_APPEND的实现原理、原子操作性说明
O_APPEND为什么能够将分别写改为接续写?关键在于文件指针。分别写的内部原理就是2个fd拥有不同的文件指针,并且彼此只考虑自己的位移。但是O_APPEND标志可以让write和read函数内部多做一件事情,就是移动自己文件指针的同时,把别人的文件指针同时移动。虽然加了O_APPEND,fd1和fd2还是各自拥有一个独立的文件指针,但是这两个文件指针关联起来了,一个动了会通知另一个跟着动。
O_APPEND对文件指针的影响,对文件的读写是原子的。整个操作一旦开始就不会被打断,必须等到操作结束后其他代码才能得以调度运行,这就叫原子操作。每种操作系统中都有一些机制来实现原子操作,以保证那些需要原子操作的任务可以运行。