文件描述符
对于内核而言,所有打开的文件都是通过文件描述符来引用。文件描述符是一个非负的整数,当打开一个现有文件或是创建一个新的文件时,内核返回一个文件描述符。
int open(const char pathname, int flags); int open(const char pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
int openat(int dirfd, const char pathname, int flags); int openat(int dirfd, const char pathname, int flags, mode);
The open() system call opens the file specified by pathname. If the specified file does not exist, it may optionally (if `O_CREAT` is specified in flags) be created by open().<br />In addition, zero or more file creation flags and file status flags can be bitwise-or'd in flags. The file creation flags are ` O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE, and O_TRUNC.`<br />`openat`和`open`的功能基本相似,区别在于path是否使用了绝对路径
- 如果`path`使用的绝对路径,那么参数`fd`被忽略。`openat`相当于`open`
- 否则fd参数指出了相对路径名在文件系统中的开始地址。
<a name="H21so"></a>
# create
`creat()`<br /> A call to creat() is equivalent to calling open() with flags equal to `O_CREAT|O_WRONLY|O_TRUNC.`
<a name="PeavS"></a>
# close
int close(int fd);<br />关闭一个文件描述符时还会自动释放加在该文件上的所有记录锁<br />进程终止时,自动关闭所有打开的文件
<a name="Cw1fm"></a>
# lseek
```cpp
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
每个打开的文件都有一个与之相关的当前文件偏移量,用来度量从文件开始处计算的字节数。通常读写操作都是从当前文件偏移量处开始,并且使偏移量增加所读写的字节数。
lseek() repositions the file offset of the open file description associated with the file descriptor fd to the argument offset according to the directive whence as follows:
参数offset取决于whence:
- SEEK_SET:The file offset is set to offset bytes. 偏移量设置为offset
- SEEK_CUR:The file offset is set to its current location plus offset bytes. 偏移量设置为当前值+offset
SEEK_END:The file offset is set to the size of the file plus offset bytes. 当前文件长度+offset
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read() attempts to read up to
**count**
bytes from file descriptorfd
into the buffer starting atbuf
.如果read成功,则返回读到的字节数,如果已经达到末尾,则返回0
-
write
#include<unistd.h>
ssize_t wirte(int fd, void *buf, size_t count);
若成功返回字节数,否则返回-1
- 通常返回值和count相同
- read和write都在内核执行,称之为不带缓冲的IO函数
:::info
read和write都是调用了系统调用,会发生栈的切换(系统调用int中断,用户栈切换到内核栈,对应状态从用户态切换到内核态)这一过程会有开销,因此之后会有带缓冲的IO函数,当缓冲存了一定数量时,调用read和write。
:::
文件共享
内核用三种数据结构描述打开文件
- 每个进程在进程表里都有一个记录向,记录项包含一张打开的文件描述符表。该表可以看作是一个向量表,每个描述符占用一条:
- 文件描述符
- 指向一个文件表项的指针
- 内核为所有打开文件维持一张文件表,包含
- 文件状态标准
- 文件当前的偏移量
- 指向该文件v节点的指针
- 每个打开文件都有一个v节点
- 文件类型以及各种操作函数的指针
- i节点(i-node)
如果两个独立的进程打开了同一个文件,那么:
所以只有v节点是独一无二的
每个打开该文件的进程都会获得一个各自的文件表项
如果多个进程同时访问一个文件,会出问题?
原子操作
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
调用pread、pwirte相当于调用lseek后调用read,但是是原子性操作
dup和dup2
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup和dup2的作用都是复制一个文件描述符
不同的是dup2相当于先调用close(newfd)
,在调用fcntl(oldfd,F_DUPFD,newfd)
dup2是个原子操作,而且包含上述两个调用
fcntl
fcntl可以改变已经打开的文件的属性
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl()
performs one of the operations described below on the open file descriptor fd. The operation is determined by cmd
.
- F_GETFD (void) Return (as the function result) the file descriptor flags; arg is ignored.
- F_SETFD (int) Set the file descriptor flags to the value specified by arg.
修改文件描述符或者文件状态标志时需要谨慎,要先获得现在的标志值,然后按照期望修改,不能直接指向F_SETFD\F_SETFL,这样会改变之前设置的标志位
void setfl(int fd,int flags){
int val;
if(val=fcntl(fd,F_GETFL,0)<0)
err_sys("error");
val|=flags;
if(fcntl(fd,F_SETFL,val)<0){
err_sys("error");
}
}