3 文件 IO:不带缓冲的 IO
一、打开、创建文件、关闭文件
文件描述符 :一个非负整数,范围是
0~OPEN_MAX-1
。内核用它来标识进程正在访问的文件。当进程创建时,默认为它打开了 3 个文件描述符,它们都链接向终端:- 0: 标准输入
- 1: 标准输出
- 2: 标准错误输出
通常我们应该使用
STDIN_FILENO
,STDOUT_FILENO
和STDERR_FILENO
来替代这三个幻数,从而提高可读性。这三个常量位于<unistd.h>
中。open
和openat
函数:打开文件#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 */);
参数:
path
:要打开或者创建文件的名字oflag
:用于指定函数的操作行为:O_RDONLY
常量:文件只读打开O_WRONLY
常量:文件只写打开O_RDWR
常量:文件读、写打开O_EXEC
常量:只执行打开O_SEARCH
常量:只搜索打开(应用于目录)。本文涉及的操作系统都没有支持该常量在上面五个常量中必须指定且只能指定一个。下面的常量是可选的(进行或运行):
O_APPEND
:每次写时都追加到文件的尾端O_CLOEXEC
:将FD_CLOEXEC
常量设置为文件描述符标志O_CREAT
:若此文件不存在则创建它。在使用此选项时,需要同时说明参数mode
(指定该文件的访问权限)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/OO_TRUNC
: 如果此文件存在,且为O_WRONLY
或者O_RDWR
成功打开,则将其长度截断为 0O_RSYNC
:使每一个read
操作等待,直到所有对文件同一部分挂起的写操作都完成。O_DSYNC
:每次write
等待物理 I/O 完成,但不包括由write
操作引起的文件属性更新所需的 I/O
mode
:文件访问权限。文件访问权限常量在<sys/stat.h>
中定义,有下列九个:S_IRUSR
:用户读S_IWUSR
:用户写S_IXUSR
:用户执行S_IRGRP
:组读S_IWGRP
:组写S_IXGRP
:组执行S_IROTH
:其他读S_IWOTH
:其他写S_IXOTH
:其他执行
对于
openat
函数,被打开的文件名由fd
和path
共同决定:- 如果
path
指定的是绝对路径,此时fd
被忽略。openat
等价于open
- 如果
path
指定的是相对路径名,则fd
是一个目录打开的文件描述符。被打开的文件的绝对路径由该fd
描述符对应的目录加上path
组合而成 - 如果
path
是一个相对路径名,而fd
是常量AT_FDCWD
,则path
相对于当前工作目录。被打开文件在当前工作目录中查找。
- 如果
返回值:
- 成功:返回文件描述符。
- 失败:返回 -1
由
open/openat
返回的文件描述符一定是最小的未使用的描述符数字。
creat
函数:创建一个新文件#include<fcntl.h> int creat(const char*path,mode_t mode);
- 参数:
path
:要创建文件的名字mode
:指定该文件的访问权限文件访问权限常量在<sys/stat.h>
中定义,有下列九个:S_IRUSR
:用户读S_IWUSR
:用户写S_IXUSR
:用户执行S_IRGRP
:组读S_IWGRP
:组写S_IXGRP
:组执行S_IROTH
:其他读S_IWOTH
:其他写S_IXOTH
:其他执行
返回值:
- 成功: 返回
O_WRONLY
打开的文件描述符 - 失败: 返回 -1
该函数等价于
open(path,O_WRONLY|O_CREAT|O_TRUNC,mode)
。注意:- 成功: 返回
- 它以只写方式打开,因此若要读取该文件,则必须先关闭,然后重新以读方式打开。
- 若文件已存在则将文件截断为 0。
- 参数:
打开和创建文件的测试。在
main
函数中调用test_open_creat()
函数:void test_open_creat() { M_TRACE("--------- Begin test_open_creat() ---------\n"); My_open("./test1",O_RDWR); // 一个存在的文件 My_open("./no_such_file1",O_RDWR); // 一个不存在的文件 My_open_with_mode("./test2",O_RDWR,S_IRUSR|S_IWUSR); // 一个存在的文件 My_open_with_mode("./no_such_file2",O_RDWR,S_IRUSR|S_IWUSR); // 一个不存在的文件 My_open_with_mode("./test3",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); // 一个存在的文件 ,带 O_CREAT 标志 My_open_with_mode("./no_such_file3",O_RDWR|O_CREAT,S_IRUSR|S_IWUSR); // 一个不存在的文件 ,带 O_CREAT 标志 My_open_with_mode("./test4",O_RDWR|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR); // 一个存在的文件 ,带 O_CREAT|O_EXCL 标志 My_open_with_mode("./no_such_file4",O_RDWR|O_CREAT|O_EXCL,S_IRUSR|S_IWUSR); // 一个不存在的文件,带 O_CREAT|O_EXCL 标志 My_creat("./test5",S_IRUSR|S_IWUSR); // 一个存在的文件 My_creat("./no_such_file5",S_IRUSR|S_IWUSR); // 一个不存在的文件 M_TRACE("--------- End test_open_creat() ---------\n\n"); }
可以看到:
- 若文件存在:
- 简单的
open()
成功打开 - 带
O_CREAT
选项的open()
成功打开 - 带
O_CREAT|O_EXCL
选项的open()
打开失败 creat()
成功打开并且将文件截断为 0
- 简单的
- 若文件不存在:
- 简单的
open()
打开失败 - 带
O_CREAT
选项的open()
成功创建文件,打开并且将文件截断为 0 - 带
O_CREAT|O_EXCL
选项的open()
成功创建文件,打开并且将文件截断为 0 creat()
成功创建文件,打开并且将文件截断为 0
- 简单的
- 若文件存在:
close
函数:关闭文件#include<unistd.h> int close(int fd);
- 参数:
fd
:待关闭文件的文件描述符
返回值:
- 成功:返回 0
- 失败:返回 -1
注意:
- 进程关闭一个文件会释放它加在该文件上的所有记录锁。
- 当一个进程终止时,内核会自动关闭它所有的打开的文件。
- 参数:
二、定位、读、写文件
lseek
函数:设置打开文件的偏移量#include<unistd.h> off_t lseek(int fd, off_t offset,int whence);
- 参数:
fd
:打开的文件的文件描述符whence
:必须是SEEK_SET
、SEEK_CUR
、SEEK_END
三个常量之一offset
:- 如果
whence
是SEEK_SET
,则将该文件的偏移量设置为距离文件开始处offset
个字节 - 如果
whence
是SEEK_CUR
,则将该文件的偏移量设置为当前值加上offset
个字节,offset
可正,可负 - 如果
whence
是SEEK_END
,则将该文件的偏移量设置为文件长度加上offset
个字节,offset
可正,可负
- 如果
返回值:
- 成功: 返回新的文件偏移量
- 失败:返回 -1
每个打开的文件都有一个与其关联的“当前文件偏移量”。它通常是个非负整数,用于度量从文件开始处计算的字节数。通常读、写操作都从当前文件偏移量处开始,并且使偏移量增加所读写的字节数。注意:
- 打开一个文件时,除非指定
O_APPEND
选项,否则系统默认将该偏移量设为 0 - 如果文件描述符指定的是一个管道、FIFO、或者网络套接字,则无法设定当前文件偏移量,则
lseek
将返回 -1 ,并且将errno
设置为ESPIPE
。 - 对于普通文件,其当前文件偏移量必须是非负值。但是某些设备运行负的偏移量出现。因此比较
lseek
的结果时,不能根据它小于 0 就认为出错。要根据是否等于 -1 来判断是否出错。 lseek
并不会引起任何 I/O 操作,lseek
仅仅将当前文件的偏移量记录在内核中。- 当前文件偏移量可以大于文件的当前长度。此时对该文件的下一次写操作将家常该文件,并且在文件中构成一个空洞。空洞中的内容位于文件中但是没有被写过,其字节被读取时都被读为 0
文件中的空洞并不要求在磁盘上占据存储区。具体处理方式与操作系统有关
- 参数:
read
函数:读取文件内容#include<unistd.h> ssize_t read(int fd,void *buf,size_t nbytes);
- 参数:
fd
:打开的文件的文件描述符buf
:存放读取内容的缓冲区的地址(由程序员手动分配)nbytes
:期望读到的字节数
返回值:
- 成功:返回读到的字节数,若已到文件尾则返回 0
- 失败:返回 -1
读操作从文件的当前偏移量开始,在成功返回之前,文件的当前偏移量会增加实际读到的字节数。有多种情况可能导致实际读到的字节数少于期望读到的字节数:
- 读普通文件时,在读到期望字节数之前到达了文件尾端
- 当从终端设备读时,通常一次最多读取一行(终端默认是行缓冲的)
- 当从网络读时,网络中的缓存机制可能造成返回值小于期望读到的字节数
- 当从管道或者
FIFO
读时,若管道包含的字节少于所需的数量,则read
只返回实际可用的字节数 - 当从某些面向记录的设备(如磁带)中读取时,一次最多返回一条记录
- 当一个信号造成中断,而已读了部分数据时。
- 参数:
write
函数:想文件写数据#include<unistd.h> ssize_t write(int fd,const void *buf,size_t nbytes);
- 参数:
fd
:打开的文件的文件描述符buf
:存放待写的数据内容的缓冲区的地址(由程序员手动分配)nbytes
:期望写入文件的字节数
返回值:
- 成功:返回已写的字节数
- 失败:返回 -1
write
的返回值通常都是与nbytes
相同。否则表示出错。write
出错的一个常见原因是磁盘写满,或者超过了一个给定进行的文件长度限制对于普通文件,写操作从文件的当前偏移量处开始。如果打开文件时指定了
O_APPEND
选项,则每次写操作之前,都会将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。
- 参数:
测试
lseek,read,write
。在main
函数中调用test_lseek_read_write
函数:void test_lseek_read_write() { M_TRACE("--------- Begin test_lseek_read_write() ---------\n"); int fd=My_open_with_mode("test",O_RDWR|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR); // 读写打开,并截断 if(-1==fd) return; // 文件打开失败 char read_buffer[20]; char write_buffer[10]; strcpy(write_buffer,"123456789"); // write_buffer 填充数字 My_read(fd,read_buffer,20); // 读文件,期望读 20 个字节 My_write(fd,write_buffer,10);// 写文件,期望写 10 个字节 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 My_lseek(fd,0,SEEK_SET);// 定位文件到头部 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 My_lseek(fd,10,SEEK_END);// 定位文件到尾部之后的 10 个字节 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 My_write(fd,write_buffer,10);// 写文件,期望写 10 个字节 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 My_lseek(fd,0,SEEK_SET);// 定位文件到头部 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 My_read(fd,read_buffer,20);// 读文件,期望读 20 个字节 close(fd); M_TRACE("--------- End test_lseek_read_write() ---------\n\n"); }
测试序列为:
- 开始文件为空,所以读取 20 个字节的
read
只读取 0 - 写入文件 10 个字节
- 读取文件。此时读和写共享一个当前文件偏移。而且当前文件偏移被
write
置于文件结尾。此时读取 0 个字节 - 执行
lseek
将当前文件偏移量重置到文件开头,返回 0(新的文件偏移量) - 读取文件,只能读取 10 个字节(因为文件此时就 10 个字节)
- 执行
lseek
将文件偏移量放到文件末尾之后的 10 个字节,返回 20(新的文件偏移量) - 读取文件。此时当前文件偏移被置于文件结尾。此时读取 0 个字节
- 写入文件 10 个字节
- 读取文件。此时当前文件偏移被
write
置于文件结尾。此时读取 0 个字节 - 执行
lseek
将当前文件偏移量重置到文件开头,返回 0(新的文件偏移量) - 读取文件,读取 20 个字节(因为文件结尾的偏移是 30 个字节)。空洞部分读取的结果都是 0
- 读取文件,只能读取 10 个字节(因为文件结尾的偏移是 30 个字节)
三、 原子操作、同步、复制、修改文件描述符
内核使用三种数据结构描述打开文件。它们之间的关系决定了一个进程与另一个进程在打开的文件之间的相互影响。
- 内核为每个进程分配一个进程表项(所有进程表项构成进程表),进程表中都有一个打开的文件描述符表。每个文件描述符占用一项,其内容为:
- 文件描述符标志
- 指向一个文件表项的指针
- 内核为每个打开的文件分配一个文件表项(所有的文件表项构成文件表)。每个文件表项的内容包括:
- 文件状态标志(读、写、添写、同步和阻塞等)
- 当前文件偏移量
- 指向该文件 v 结点表项的指针
每个打开的文件或者设备都有一个 v 结点结构。 v 结点结构的内容包括:
- 文件类型和对此文件进行各种操作函数的指针。
- 对于大多数文件, v 结点还包含了该文件的 i 结点。
这些信息都是在打开文件时从磁盘读入内存的。如 i 结点包含了文件的所有者、文件长度、指向文件实际数据在磁盘上所在位置的指针等等。 v 结点结构和 i 结点结构实际上代表了文件的实体。
现在假设进程 A 打开文件
file1
,返回文件描述符 3;进程 B 也打开文件file2
,返回文件描述符 2:内核在文件表上新增两个表项:
- 这两个文件表项指向同一个 v 结点表项
- 进程 A 、B 各自的文件描述符表项分别指向这两个文件表项;
- 对文件的操作结果:
- 进程 A 每次
write
之后,进程 A 对应的文件表项的当前文件偏移量即增加所写入的字节数。- 若这导致当前文件偏移量超过当前文件长度,则修改 i 节点的当前文件长度,设为当前文件偏移量
- 如果进程 B 用
O_APPEND
标志打开一个文件,在相应标志也设置到进程 B 对于的文件表项的文件状态标志中。- 每次进程 B 对具有追加写标志的文件执行写操作时,文件表项中的当前文件偏移量首先被置为 i 结点中的文件长度。
- 若进程 B 用
lseek
定位到文件当前的尾端,则进程 B 对应的文件表项的当前文件偏移量设置为 i 结点中的当前长度 lseek
函数只是修改文件表项中的当前文件偏移量,不进行任何 I/O 操作
- 进程 A 每次
- 内核为每个进程分配一个进程表项(所有进程表项构成进程表),进程表中都有一个打开的文件描述符表。每个文件描述符占用一项,其内容为:
可能一个进程中有多个文件描述符指向同一个文件表项。
原子操作:
- 追加一个文件时,不能通过
lseek
到末尾然后write
。要用O_APPEND
选项打开文件,然后直接write
。- 通过
lseek
到末尾然后write
时,如果多个进程同时执行这两个操作,则会引起竞争条件 - 通过
O_APPEND
选项打开文件,然后直接write
时,内核每一次在写操作之前,都会将进程的当前偏移量设置到文件的末尾,于是就不需要执行lseek
定位操作
- 通过
pread/pwrite
可以执行原子性的定位读/定位写O_CREAT|O_EXCL
选项打开文件时,可以原子性的检查文件是否存在和创建文件这两个操作。
- 追加一个文件时,不能通过
pread/pwrite
:原子定位读和原子定位写#include<unistd.h> ssize_t pread(int fd,void*buf,size_t nbytes,off_t offset); ssize_t pwrite(int fd,const void*buf,size_t nbytes,off_t offset);
- 参数:
fd
:打开的文件描述符buf
:读出数据存放的缓冲区/ 写到文件的数据的缓冲区nbytes
:预期读出/写入文件的字节数offset
:从文件指定偏移量开始执行read/write
返回:
- 成功:读到的字节数/已写的字节数
- 失败: -1
调用
pread
相当于先调用lseek
再调用read
.但是调用pread
时,无法中断其定位和读操作,并且不更新当前文件偏移量;调用pwrite
相当于先调用lseek
再调用write
.但是调用pwrite
时,无法中断其定位和写操作,并且不更新当前文件偏移量示例:在
main
函数中调用test_pread_pwrite
函数:void test_pread_pwrite() { M_TRACE("--------- Begin test_pread_pwrite() ---------\n"); int fd=My_open_with_mode("test",O_RDWR|O_TRUNC|O_CREAT,S_IRUSR|S_IWUSR); // 读写打开,并截断 if(-1==fd) return; // 文件打开失败 char read_buffer[20]; char write_buffer[20]; strcpy(write_buffer,"123456789"); // write_buffer 填充数字 // 写文件,期望写 10 个字节 My_write(fd,write_buffer,10); print_current_offset(fd); My_pread(fd,read_buffer,5,0 );// 读文件,期望读 5 个字节,从 偏移为 0 开始 print_current_offset(fd); My_pwrite(fd,write_buffer,10,8);// 写文件,期望写 10 个字节,从 偏移为 8 开始 print_current_offset(fd); close(fd); M_TRACE("--------- End test_pread_pwrite() ---------\n\n"); }
- 参数:
dup/dup2
:复制一个现有的文件描述符:#include<unistd.h> int dup(int fd); int dup2(int fd,int fd2);
- 参数:
fd
:被复制的文件描述符(已被打开)fd2
:指定的新的文件描述符(待生成)
返回值:
- 成功: 返回新的文件描述符
- 失败: 返回 -1
对于
dup
函数,返回的新的文件描述符一定是当前可用的文件描述符中最小的数字。对于dup2
函数:- 如果
fd2
已经是被打开的文件描述符且不等于fd
,则先将其关闭,然后再打开(注意关闭再打开是一个原子操作) 如果
fd2
等于fd
,则直接返回fd2
(也等于fd
),而不作任何操作任何情况下,这个返回的新的文明描述符与参数
fd
共享同一个文件表项(因此文件状态标志以及文件偏移量都会共享)。 任何情况下,这个返回的新的文明描述符的close-on-exec
标志总是被清除示例:在
main
函数中调用test_dup_dup2
函数:void test_dup_dup2() { M_TRACE("--------- Begin test_dup_dup2() ---------\n"); My_dup(0); // fd 0 已经被打开的 My_dup(100); // fd 100 未被打开 My_dup2(0,0); // fd 0 已经被打开的 My_dup2(100,100); // fd 100 未被打开 My_dup2(0,100); // fd 0 已经被打开的, fd 100 未被打开 My_dup2(101,0); // fd 0 已经被打开的, fd 100 未被打开 M_TRACE("--------- End test_dup_dup2() ---------\n\n"); }
- 参数:
UNIX 操作系统在内核中设有缓冲区,大多数磁盘 I/O 都通过缓冲区进行。当我们想文件写入数据时,内核通常都首先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘。这种方式称为延迟写。
- 当内核需要重用缓冲区来存方其他数据时,它会把所有延迟写的数据库写入磁盘
你也可以调用下列函数来显式的将延迟写的数据库写入磁盘
#include<unistd.h> int fsync(int fd); int fdatasync(int fd); void sync(void);
参数(前两个函数):
fd
:指定的打开的文件描述符
返回值(前两个函数):
- 成功:返回 0
- 失败: 返回 -1
区别:
sync
:将所有修改过的块缓冲区排入写队列,然后返回,它并不等待时机写磁盘结束fsync
:只对由fd
指定的单个文件起作用,等待写磁盘操作结束才返回fdatasync
:只对由fd
指定的单个文件起作用,等待写磁盘操作结束才返回,但是它只影响文件的数据部分(fsync
会同步更新文件的属性)update
守护进程会周期性的调用sync
函数。命令sync
也会调用sync
函数
fcntl
函数:改变已经打开的文件的属性#include<fcntl.h> int fcntl(int fd,int cmd,.../* int arg */);
参数:
fd
:已打开文件的描述符cmd
:有下列若干种:F_DUPFD
常量:复制文件描述符fd
。新文件描述符作为函数值返回。它是尚未打开的文件描述符中大于或等于arg
中的最小值。新文件描述符与fd
共享同一个文件表项,但是新描述符有自己的一套文件描述符标志,其中FD_CLOEXEC
文件描述符标志被清除F_DUPFD_CLOEXEC
常量:复制文件描述符。新文件描述符作为函数值返回。它是尚未打开的个描述符中大于或等于arg
中的最小值。新文件描述符与fd
共享同一个文件表项,但是新描述符有自己的一套文件描述符标志,其中FD_CLOEXEC
文件描述符标志被设置F_GETFD
常量:对应于fd
的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志FD_CLOEXEC
F_SETFD
常量:设置fd
的文件描述符标志为arg
F_GETFL
常量:返回fd
的文件状态标志。文件状态标志必须首先用屏蔽字O_ACCMODE
取得访问方式位,然后与O_RDONLY
、O_WRONLY
、O_RDWR
、O_EXEC
、O_SEARCH
比较(这 5 个值互斥,且并不是各占 1 位)。剩下的还有:O_APPEND
、O_NONBLOCK
、O_SYNC
、O_DSYNC
、O_RSYNC
、F_ASYNC
、O_ASYNC
F_SETFL
常量:设置fd
的文件状态标志为arg
。可以更改的标志是:O_APPEND
、O_NONBLOCK
、O_SYNC
、O_DSYNC
、O_RSYNC
、F_ASYNC
、O_ASYNC
F_GETOWN
常量:获取当前接收SIGIO
和SIGURG
信号的进程ID
或者进程组ID
F_SETOWN
常量:设置当前接收SIGIO
和SIGURG
信号的进程ID
或者进程组ID
为arg
。若arg
是个正值,则设定进程ID
;若arg
是个负值,则设定进程组ID
F_GETLK
、F_SETLK
、F_SETLKW
:获取/设置文件记录锁
arg
:依赖于具体的命令
返回值:
- 成功: 依赖于具体的命令
- 失败: 返回 -1
示例:在
main
函数中调用test_fcntl()
函数:void test_fcntl() { M_TRACE("--------- Begin test_fcntl() ---------\n"); int fd=openat(AT_FDCWD,"test.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);; if(-1==fd) // 打开文件失败 return ; //测试 My_fcntl_GETFD 和 My_fcntl_DUPFD、My_fcntl_DUPFD_CLOEXEC My_fcntl_GETFD(My_fcntl_DUPFD(fd,10)); My_fcntl_GETFD(My_fcntl_DUPFD(fd,0)); My_fcntl_GETFD(My_fcntl_DUPFD_CLOEXEC(fd,10)); My_fcntl_GETFD(My_fcntl_DUPFD_CLOEXEC(fd,0)); // 测试 My_fcntl_GETFD、My_fcntl_SETFD My_fcntl_GETFD(fd); My_fcntl_SETFD(fd,~FD_CLOEXEC); My_fcntl_GETFD(fd); My_fcntl_SETFD(fd,FD_CLOEXEC); My_fcntl_GETFD(fd); // 测试 My_fcntl_GETFL、My_fcntl_SETFL print_fl(fd,My_fcntl_GETFL(fd)); My_fcntl_SETFL(fd,O_RDWR); print_fl(fd,My_fcntl_GETFL(fd)); My_fcntl_SETFL(fd,O_RDONLY|O_NONBLOCK); print_fl(fd,My_fcntl_GETFL(fd)); // 测试 My_fcntl_GETOWN、My_fcntl_SETOWN My_fcntl_GETOWN(fd); My_fcntl_SETOWN(fd,1); My_fcntl_GETOWN(fd); close(fd); M_TRACE("--------- End test_fcntl() ---------\n\n"); }
注意:
- Linux 下,不支持文件状态标志:
F_EXEC 与
,F_SEARCH
(result&O_ACCMODE)==O_RDONLY
表达式中,&
优先级较低F_SETFL
命令:当文件读打开时,你无法将文件状态标志修改为O_WRONLY
、O_WRWR
这两种中任何一个。你只能修改:O_APPEND
、O_NONBLOCK
、O_SYNC
、O_DSYNC
、O_RSYNC
、F_ASYNC
、O_ASYNC
等标志
/dev/fd
目录:该目录下是名为0、1、2
等的文件。打开文件/dev/fd/n
等效于复制描述符(假定描述符n
是打开的)fd=open("/dev/fd/0",mod)
:fd
和文件描述符0
共享同一个文件表项。- 大多数系统忽略
mod
参数 在 Linux 操作系统上,
/dev/fd/0
是个例外,它是个底层物理文件的符号链接。因此在它上面调用creat
会导致底层文件被截断
- 开始文件为空,所以读取 20 个字节的
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论