- open("./src_file", O_RDONLY) //单独使用某一个标志
- open("./src_file", O_RDONLY | O_NOFOLLOW) //多个标志组合
mode:此参数用于指定新建文件的访问权限,只有当flags参数中包含O_CREAT或O_TMPFILE标志时才有效(O_TMPFILE标志用于创建一个临时文件)。权限对于文件来说是一个很重要的属性,那么在Linux系统中,我们可以通过touch命令新建一个文件,此时文件会有一个默认的权限,如果需要修改文件权限,可通过chmod命令对文件权限进行修改,譬如在Linux系统下我们可以使用"ls -l"命令来查看到文件所对应的权限。
当我们调用open函数去新建一个文件时,也需要指定该文件的权限,而mode参数便用于指定此文件的权限,接下来看看我们该如何通过mode参数来表示文件的权限,首先mode参数的类型是mode_t,这是一个u32无符号整形数据,权限表示方法如下所示:
图 2.3.2 mode权限表示方法
我们从低位从上看,每3个bit位分为一组,分别表示:
O---这3个bit位用于表示其他用户的权限;
G---这3个bit位用于表示同组用户(group)的权限,即与文件所有者有相同组ID的所有用户;
U---这3个bit位用于表示文件所属用户的权限,即文件或目录的所属者;
S---这3个bit位用于表示文件的特殊权限,文件特殊权限一般用的比较少,这里就不给大家细讲了。
关于什么是文件所属用户、同组用户以及其他用户,这些都是Linux操作系统相关的基础知识,相信大家都理解这些概念;3个bit位中,按照rwx顺序来分配权限位(特殊权限除外),最高位(权值为4)表示读权限,为1时表示具有读权限,为0时没有读权限;中间位(权值为2)表示写权限,为1时表示具有写权限,为0时没有写权限;最低位(权值为1)表示执行权限,为1时表示具有可执行权限,为0时没有执行权限。接下来我们举几个例子(特殊权限这里暂时不管,其S字段全部为0):
最高权限表示方法:111111111(二进制表示)、777(八进制表示)、511(十进制表示);
最高权限这里意味着所有用户对此文件都具有读权限、写权限以及执行权限。
111000000(二进制表示):表示文件所属者具有读、写、执行权限,而同组用户和其他用户不具有任何权限;
100100100(二进制表示):表示文件所属者、同组用户以及其他用户都具有读权限,但都没有写、执行权限。
Tips:open函数O_RDONLY、O_WRONLY以及O_RDWR这三个标志表示以什么方式去打开文件,譬如以只写方式打开(open函数得到的文件描述符只能对文件进行写操作,不能读)、以只读方式打开(open函数得到的文件描述符只能对文件进行读操作,不能写)、以可读可写方式打开(open函数得到的文件描述符可对文件进行读和写操作);与文件权限之间的联系,只有用户对该文件具有相应权限时,才可以使用对应的标志去打开文件,否则会打开失败!譬如,我们的程序对该文件只有只读权限,那么执行open函数使用O_RDWR或O_WRONLY标志将会失败。关于文件权限等相关问题,将会在4.1中给大家介绍。
关于文件权限表示方法的问题,以上就给大家介绍这么多,在实际编程中,我们可以直接使用Linux中已经定义好的宏,不同的宏定义表示不同的权限,如下所示:
表 2.3.2 open函数文件权限宏
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| set-user-ID(特殊权限) set-group-ID(特殊权限) sticky(特殊权限) |
这些宏既可以单独使用,也可以通过位或运算将多个宏组合在一起,譬如:
S_IRUSR | S_IWUSR | S_IROTH
返回值:成功将返回文件描述符,文件描述符是一个非负整数;失败将返回-1。
以上就把open函数相关的基础知识给大家介绍完了,包括函数返回值、参数等信息,当然在后面的章节内容中,我们还会更加深入地给大家讲解open函数相关的知识点;接下来我们看一些open函数的简答使用示例。
open函数使用示例
(1)使用open函数打开一个已经存在的文件(例如当前目录下的app.c文件),使用只读方式打开:
- int fd = open("./app.c", O_RDONLY)
- if (-1 == fd)
- return fd;
(2)使用open函数打开一个已经存在的文件(例如当前目录下的app.c文件),使用可读可写方式打开:
- int fd = open("./app.c", O_RDWR)
- if (-1 == fd)
- return fd;
(3)使用open函数打开一个指定的文件(譬如/home/dengtao/hello),使用可读可写方式,如果该文件是一个符号链接文件,则不对其进行解引用,直接返回错误:
- int fd = open("/home/dengtao/hello", O_RDWR | O_NOFOLLOW);
- if (-1 == fd)
- return fd;
(4)使用open函数打开一个指定的文件(譬如/home/dengtao/hello),如果该文件不存在则创建该文件,创建该文件时,将文件权限设置如下:
文件所属者拥有读、写、执行权限;
同组用户与其他用户只有读权限。
使用可读可写方式打开:
- int fd = open("/home/dengtao/hello", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH);
- if (-1 == fd)
- return fd;
一.4 write写文件调用write函数可向打开的文件写入数据,其函数原型如下所示(可通过"man 2 write"查看):
- #include <unistd.h>
-
- ssize_t write(int fd, const void *buf, size_t count);
首先使用write函数需要先包含unistd.h头文件。
函数参数和返回值含义如下:
fd:文件描述符。关于文件描述符,前面已经给大家进行了简单地讲解,这里不再重述!我们需要将进行写操作的文件所对应的文件描述符传递给write函数。
buf:指定写入数据对应的缓冲区。
count:指定写入的字节数。
返回值:如果成功将返回写入的字节数(0表示未写入任何字节),如果此数字小于count参数,这不是错误,譬如磁盘空间已满,可能会发生这种情况;如果写入出错,则返回-1。
对于普通文件(我们一般操作的大部分文件都是普通文件,譬如常见的文本文件、二进制文件等),不管是读操作还是写操作,一个很重要的问题是:从文件的哪个位置开始进行读写操作?也就是IO操作所对应的位置偏移量,读写操作都是从文件的当前位置偏移量处开始,当然当前位置偏移量可以通过lseek系统调用进行设置,关于此函数后面再讲;默认情况下当前位置偏移量一般是0,也就是指向了文件起始位置,当调用read、write函数读写操作完成之后,当前位置偏移量也会向后移动对应字节数,譬如当前位置偏移量为1000个字节处,调用write()写入或read()读取500个字节之后,当前位置偏移量将会移动到1500个字节处。
一.5 read读文件调用read函数可从打开的文件中读取数据,其函数原型如下所示(可通过"man 2 read"查看):
- #include <unistd.h>
-
- ssize_t read(int fd, void *buf, size_t count);
首先使用read函数需要先包含unistd.h头文件。
函数参数和返回值含义如下:
fd:文件描述符。与write函数的fd参数意义相同。
buf:指定用于存储读取数据的缓冲区。
count:指定需要读取的字节数。
返回值:如果读取成功将返回读取到的字节数,实际读取到的字节数可能会小于count参数指定的字节数,也有可能会为0,譬如进行读操作时,当前文件位置偏移量已经到了文件末尾。实际读取到的字节数少于要求读取的字节数,譬如在到达文件末尾之前有30个字节数据,而要求读取100个字节,则read读取成功只能返回30;而下一次再调用read读,它将返回0(文件末尾)。
一.6 close关闭文件可调用close函数关闭一个已经打开的文件,其函数原型如下所示(可通过"man 2 close"查看):
- #include <unistd.h>
-
- int close(int fd);
首先使用close函数需要先包含unistd.h头文件,当我们对文件进行IO操作完成之后,后续不再对文件进行操作时,需要将文件关闭。
函数参数和返回值含义如下:
fd:文件描述符,需要关闭的文件所对应的文件描述符。
返回值:如果成功返回0,如果失败则返回-1。
除了使用close函数显式关闭文件之外,在Linux系统中,当一个进程终止时,内核会自动关闭它打开的所有文件,也就是说在我们的程序中打开了文件,如果程序终止退出时没有关闭打开的文件,那么内核会自动将程序中打开的文件关闭。很多程序都利用了这一功能而不显式地用close关闭打开的文件。
显式关闭不再需要的文件描述符往往是良好的编程习惯,会使代码在后续修改时更具有可读性,也更可靠,进而言之,文件描述符是有限资源,当不再需要时必须将其释放、归还于系统。
一.7 lseek对于每个打开的文件,系统都会记录它的读写位置偏移量,我们也把这个读写位置偏移量称为读写偏移量,记录了文件当前的读写位置,当调用read()或write()函数对文件进行读写操作时,就会从当前读写位置偏移量开始进行数据读写。
读写偏移量用于指示read()或write()函数操作时文件的起始位置,会以相对于文件头部的位置偏移量来表示,文件第一个字节数据的位置偏移量为0。
当打开文件时,会将读写偏移量设置为指向文件开始位置处,以后每次调用read()、write()将自动对其进行调整,以指向已读或已写数据后的下一字节,因此,连续的调用read()和write()函数将使得读写按顺序递增,对文件进行操作。我们先来看看lseek函数的原型,如下所示(可通过"man 2 lseek"查看):
- #include <sys/types.h>
- #include <unistd.h>
-
- off_t lseek(int fd, off_t offset, int whence);
首先调用lseek函数需要包含<sys/types.h>和<unistd.h>两个头文件。
函数参数和返回值含义如下:
fd:文件描述符。
offset:偏移量,以字节为单位。
whence:用于定义参数offset偏移量对应的参考值,该参数为下列其中一种(宏定义):
l SEEK_SET:读写偏移量将指向offset字节位置处(从文件头部开始算);
l SEEK_CUR:读写偏移量将指向当前位置偏移量 + offset字节位置处,offset可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移;
l SEEK_END:读写偏移量将指向文件末尾 + offset字节位置处,同样offset可以为正、也可以为负,如果是正数表示往后偏移、如果是负数则表示往前偏移。
返回值:成功将返回从文件头部开始算起的位置偏移量(字节为单位),也就是当前的读写位置;发生错误将返回-1。
使用示例:
(1)将读写位置移动到文件开头处:
- off_t off = lseek(fd, 0, SEEK_SET);
- if (-1 == off)
- return -1;
(2)将读写位置移动到文件末尾:
- off_t off = lseek(fd, 0, SEEK_END);
- if (-1 == off)
- return -1;
(3)将读写位置移动到偏移文件开头100个字节处:
- off_t off = lseek(fd, 100, SEEK_SET);
- if (-1 == off)
- return -1;
(4)获取当前读写位置偏移量:
- off_t off = lseek(fd, 0, SEEK_CUR);
- if (-1 == off)
- return -1;
函数执行成功将返回文件当前读写位置。