open() 系统调用很有可能是Linux开发者用到的第一个系统调用,它打开一个指定目录上的文件或设备,必要的时候会创建一个。
函数原型:int open(const char *pathname, int flags, ...);
其中的可选参数是在创建文件的时候指定新文件的模式。flags参数应该至少包含一个访问模式:O_RDONLY,O_WRONLY或者O_RDWR,分别表示文件是只读的,只写的,或可读可写的。
该函数如果执行成功,它会返回系统中可用的最小的文件描述符整数来标识操作的文件。例如,新运行的程序会默认打开标准输入,标准输出和标准错误输出三个文件描述符,分别占用0,1,2三个整数。所以,程序中第一次成功调用open之后获得的文件描述符通常是从3开始一次编号的。如果首先关闭了标准输出,再调用open,那么得到的文件描述符就会是1。
常见的用法:
打开一个文件用于读取内容:int fd = open("/tmp/readfile.txt", O_RDONLY);
打开一个文件写入新的内容,int fd = open("/tmp/writefile.txt", O_WRONLY);
打开一个文件向里面追加内容:int fd = open("/tmp/appendfile.txt", O_WRONLY | O_APPEND);
可选的控制选项的详细解释如下:
O_APPEND:以该模式打开文件之后,文件的当前偏移指针会移动到文件的末尾。需要注意的是,如果有多个进程同时以这种模式向NFS文件系统中的文件写入数据,可能会造成文件的损坏,因为NFS文件系统是不支持文件内容追加的,所以内核不得不用另外的方式模拟这种行为,但是这是会有一种无法避免的竞态条件。
O_ASYNC:设置此标志之后,打开的文件描述符准备好输入或输出时,会产生SIGIO信号。这个标志只能给终端,伪终端,套接字,管道或者命名管道使用。但是目前在open中设置这个标志好像是没有作用,需要打开文件之后用fcntl设置该标志。
O_CLOEXEC:这个标志是从Linux 2.6.23版本的内核之后才有的。设置这个标志之后,在当前进程执行execve系列系统调用执行一个新程序的时候,这个文件会被关闭。这个标志的意义在于,当execve()成功执行的时候,原来程序中已经打开的文件都应该被关闭,但是execve()也是有可能会失败的,当由于某种原因execve()执行失败的时候,打开的文件描述符应该保持在打开状态,而且文件偏移位置也应该保持原样。让应用程序来维护这些状态太过于复杂,甚至基本上是不可能的。所以,需要用这个标志来让内核帮助应用做这个事情。最早的时候,这个标志只能在fcntl()系统调用中设置给一个已经打开的文件描述符。但是这样分两步的操作在多线程程序中会有一种无法避免的竞态问题,比如,当有一个线程打开文件,但是还没设置O_CLOEXEC标志之前,另外一个线程就执行了fork()+execve()。这种情况下,这个新打开的文件就没有机会关闭了。所以,就把对这个标志的设置放在了open中,保证这一连串操作是原子的,就不会有这种棘手的竞态条件出现了。
O_CREAT:这个标志表示如果文件不存在,就创建一个新文件。当指定这个标志打开文件的时候,还需要设置新文件的访问模式,可用的访问模式是一组用户,组和其他组用户的读写执行权限标志的组合,包括:S_IRWXU,S_IRUSR,S_IWUSR,S_IXUSR,S_IRWXG,S_IRGRP,S_IWGRP,S_IXGRP,S_IRWXO,S_IROTH,S_IWOTH,S_IXOTH。各标志的意义不言自明,就不用多说了。
O_DIRECT: 设置了这个标志的文件的读写在内核中不会有读写缓冲区,而是直接同步地从用户控件buffer刷新到磁盘或相反。这个标志是在Linux 2.4.10开始加入的,这个标志会引起读写性能的下降,所以在默认情况下是关闭的。这个标志不在POSIX标准中,要使用它需要定义_GNU_SOURCE宏。
这个标志的作者认为它对一些自己管理数据缓存的应用是有用的,例如一些数据库管理程序。但是口无遮拦的Linus先生却并不这么认为,他说:“O_DIRECT有个一直困扰我的问题是它整个接口都很愚蠢,它可能是某个被严重精神药物控制的疯狂的猴子设计的。”(The thing that has always disturbed me about O_DIRECT is that the whole interface is just stupid, and was probably designed by a deranged monkey on some serious mind controlling substances.)他这么说也并不是没有道理,使用这个标志的时候有诸多需要注意的事项,否则可能会产生并不希望看到的结果。详情请参考open的帮助手册。
O_DIRECTORY:指定要打开一个目录,如果不是目录会打开失败。
O_EXCL:这个标志与O_CREATE一起使用的时候,如果文件已经存在,open()调用会失败。如果要在在NFS文件系统上使用这个标志,需要至少2.6或之后的内核,以及NFSv3及之后的文件系统版本。
O_LARGEFILE:允许在32位系统上打开一个超过2G的文件。在内核数据结构中,标记文件大小的字段是个32位有符号整形,所以只能表示不超过2G的大小,考虑到文件和目录在内核中使用相同的数据结构,其中还有个目录使用的32位整形数,普通的文件肯定不会用到这个字段,所以,当设置了大文件标记之后,内核就用这个目录使用的32位整形与文件大小字段的32位整形合并在一起标记文件的大小。
O_NOATIME:打开文件是不更新文件的访问时间信息。这个标志对一些后台归档备份程序特别有用。因为访问时间等文件元信息通常会存放在与文件内容不同的磁盘页面上,所以,设置了这个标志之后,可以减少一次寻道操作,从而为索引或备份程序带来更好的性能。这个标志是在Linux 2.6.8版本引入的。但是这个标志对NFS上的文件是无效的,因为在NFS上,是由文件服务器维护访问时间的。
O_NOCTTY:如果打开了一个终端设备,不让这个终端成为当前进程的控制终端。
O_NOFOLLOW:如果目录名指向的是一个符号链接文件,那么不解引用去打开被链接的文件,打开会失败。从内核2.1.126开始支持这个标志。
O_NONBLOCK 或 O_NDELAY:以非阻塞的方式打开文件。
O_SYNC:以同步方式打开文件,在设置了该标志的文件上的write操作会一直阻塞进程,知道数据已经真正写入到物理存储设备上才返回。在一些数据可靠性非常重要的应用中会使用这个标志。
O_TRUNC:如果文件已经存在,并且是个普通文件,并且以可写的方式打开,那么这个标志会把文件内容清空。如果打开的文件是命名管道或者是一个设备文件,这个标志会被忽略。
如果open()系统调用执行失败,会返回-1,同时会把errno设置为错误原因,errno的含义请参考errno的参考手册。
另外需要注意的是,访问模式标志:O_RDONLY, O_WRONLY,O_RDWR虽然可以与其他的标志按位或,但是他们三个之间不能这样做,因为它们三个分别被定义成了整数值0,1,2,而不是每个标志占用一个位,所以O_RDONLY | O_WRONLY并不等于O_RDWR。
==================== 以下是广告 ====================
更多系统详尽的Linux系统编程内容,欢迎订阅GitChat专栏 《攻克Linux系统编程》