1. 文件描述符
Linux系统中一切皆文件,所有打开的文件都通过文件描述符进行索引。文件描述符是一个非负整数,当打开或创建文件时,内核向该进程返回一个文件描述符用于指代该文件。
Linux内核对所有打开的文件都维护了一个文件描述符表,用于存储文件描述符。且在文件描述符表中已经默认分配了三个文件描述符:0是标准输入stdin,1是标准输出stdout,2是标准错误stderr。故打开或创建的文件描述符大于或等于3。
2. open/close
2.1 open函数
通过open函数可以打开或创建一个文件,返回的文件描述符一定是该进程尚未使用的最小描述符,大于等于3。
函数原型:
1 |
|
参数:
path:要打开的文件的路径名。
flags:打开文件的方式和选项,它是一个位掩码,可以通过按位或运算组合多个选项。常见选项包括:
- O_RDONLY —— 以只读方式打开文件
- O_WRONLY —— 以只写方式打开文件
- O_RDWR —— 以读写方式打开文件
- 可选属性:(和以上属性一起使用)
- O_APPEND —— 新数据追加到文件的尾部
- O_CREAT —— 如果文件不存在,则创建该文件
- O_TRUNC—— 如果文件存在且以写入方式打开,则清空文件内容
- O_EXCL —— 检测文件是否存在, 必须要和 O_CREAT 一起使用, 不能单独使用:O_CREAT | O_EXCL检测到文件不存在, 创建新文件;检测到文件已存在, 创建失败, 函数直接返回-1
- O_NONBLOCK ——对于设备文件,以O_NONBLOCK方式打开可以做非阻塞I/O
mode:表示文件的权限掩码,指定新建文件的权限,仅在
O_CREAT
选项被指定时有效。- 文件权限掩码是一个八进制数,每一位分别代表了不同的权限。
- 一般情况下,文件权限掩码可以分为三组:用户权限、组权限和其他用户权限。每组权限又包括读(R)、写(W)和执行(X)权限。
- 读(R):4、写(W):2、执行(X):1
- 例如:
rwxrwxrwx
:全部权限,八进制数值为777。rw-r--r--
:所有者可读写,其他用户只读,八进制数值为644。
返回值:
- 成功:返回一个非负整数,表示文件描述符。
- 失败:返回
-1
,并设置errno
来指示错误类型。
2.2 close函数
通过close函数关闭一个打开文件,释放打开时分配的文件描述符。close()原型:
1 |
|
参数:
- fd:要关闭的文件描述符。
返回值:
- 成功:返回0。
- 失败:返回-1,并设置
errno
来指示错误类型。
2.3 文件操作
打开已有文件:
1 | int fd = open("abc.txt", O_RDWR); |
创建新文件:
1 | int fd = open("./new.txt", O_CREAT|O_RDWR, 0664); |
3. read/write
3.1 read函数
用于从打开的文件中读数据
函数原型:
1 |
|
参数:
- fd: 要读取的文件描述符
- buf: 指向存放读取数据的缓冲区的指针。
- count: 要读取的字节数
返回值: 返回值的数据类型是 ssize_t
,表示带符号的整型,这样既可以返回正的字节数、0(表
示到达文件末尾)也可以返回负值-1(表示出错) 。
- 成功:返回读取的字节数,可能小于
count
,因为可能在读取count个字节之前已到达文件末尾。 - 文件结束(EOF):返回
0
,表示已读取到文件末尾。 - 失败:返回
-1
,并设置errno
来指示错误类型。
什么是缓冲区:
缓冲区(Buffer)是指在计算机中用于临时存储数据的一段内存区域。在程序中,缓冲区通常用于临时存放数据,以便稍后进行处理或传输。缓冲区的使用可以提高数据的读写效率,减少频繁的IO操作,特别是在涉及大量数据的情况下。
在文件IO操作中,缓冲区通常用于存储要读取或写入的数据。当调用
read()
系统调用从文件中读取数据时,数据首先被读取到缓冲区中,然后程序可以从缓冲区中获取数据进行进一步处理。类似地,当调用write()
系统调用向文件中写入数据时,数据首先被写入到缓冲区中,然后由系统决定何时将缓冲区中的数据写入到文件中。
3.2 write函数
用于将数据从用户空间写入到已打开文件中
函数原型:
1 |
|
参数:
- fd:要写入的文件描述符。
- buf:指向要写入数据的缓冲区的指针。
- count:往磁盘文件中写入的字节数,一般是buf的长度sizeof(buf)。
返回值:
- 成功:返回写入的字节数,可以小于
count
。 - 失败:返回
-1
,并设置errno
来指示错误类型。
3.3 示例
1 |
|
4. 阻塞和非阻塞
阻塞和非阻塞是指,程序在执行某种操作时的行为方式。
阻塞(Blocking):
在阻塞模式下,当程序执行某个操作时,如果该操作无法立即完成,程序将一直等待,直到操作完成为止。
非阻塞(Non-blocking):
在非阻塞模式下,当程序执行某个操作时,如果该操作无法立即完成,程序将立即返回而不会等待。程序可以继续执行其他任务,不会因为某个操作的未完成而被阻塞。
使用非阻塞 I/O 可以使得一个进程能够同时处理多个连接而不会被阻塞。当一个套接字上没有数据可读或者没有数据可写时,程序不会等待,而是立即返回,这样可以更有效地利用 CPU 时间。
非阻塞 I/O 结合事件驱动模型(如 epoll)可以实现异步 I/O。通过 epoll 等机制,程序可以注册对多个文件描述符的监听,一旦有事件发生,程序立即得到通知,而不需要轮询或阻塞等待。
5. fcntl函数
fcntl()可以修改一个已打开文件的属性,可以重新设置读、写、追加、非阻塞等标志,而不必重新open文件 。
函数原型:
1 |
|
参数fd
表示已打开的文件描述符,cmd
是控制命令,指定了要对文件进行的操作,arg
是与控制命令相关的参数,类型和意义取决于具体的命令。
常用命令:
F_DUPFD
:复制文件描述符,创建一个新的文件描述符,其值大于或等于第三个参数(指定的最小文件描述符值)。
fcntl(fd, F_DUPFD, newfd)
1
2
3
4
5
6
7
- `F_GETFL`:
- 获取文件状态标志(file status flags)。
- ```
fcntl(fd, F_GETFL)
F_SETFL
:设置文件状态标志为flags。
fcntl(fd, F_SETFL, flags)
1
2
3
4
5
6
7
- `F_GETFD`:
- 获取文件描述符标志(file descriptor flags)。
- ```
fcntl(fd, F_GETFD)
F_SETFD
:设置文件描述符标志为flags。
fcntl(fd, F_SETFD, flags)
1
2
3
4
5
6
7
- `F_GETLK`:
- 获取文件锁信息,并将锁信息写入提供的结构体 `lock` 中。
- ```
fcntl(fd, F_GETLK, &lock)
F_SETLK
:设置文件锁,如果无法获取锁,则调用失败。
fcntl(fd, F_SETLK, &lock)
1
2
3
4
5
6
7
- `F_SETLKW`:
- 设置文件锁,并在必要时阻塞。
- ```
fcntl(fd, F_SETLKW, &lock)
将文件设置为非阻塞:
1 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) |
6. Iseek函数
用于移动文件的指针。
每一个打开文件都有一个与其相关联的”当前文件偏移量“,即文件指针指向的位置,作用在于控制文件的读写位置
1 |
|
参数:
- fd:所操纵文件的描述符
- offset:偏移量,需要与whence搭配使用
- whence:通过该参数指定函数功能
- SEEK_SET:从文件头部开始偏移 offset 个字节
- SEEK_CUR: 从当前文件指针的位置向后偏移offset个字节
- SEEK_END: 从文件尾部向后偏移offset个字节
返回值:成功则返回新的文件偏移量(文件头部到当前位置的偏移量),失败则返回-1
lseek(fd, 0, SEEK_SET); //将文件指针移动到头部 lseek(fd, 0, SEEK_CUR); //得到当前文件指针的位置 lseek(fd, 0, SEEK_END); //得到文件的总大小
1
2
3
4
5
6
7
8
9
10
11
# 7. 复制与重定向
## 7.1 dup函数
dup函数的作用是复制一个现有的文件描述符,使多个文件描述符指向同一个文件。函数原型:
```c
#include <unisted.h>
int dup(int oldfd);
参数oldfd是被复制的文件描述符
函数调用成功后返回新的文件描述符,且是当前可用文件描述符中的最小数值;调用失败则返回-1
被复制出的新文件描述符是独立于旧的文件描述符的,二者没有连带关系。也就是说当旧的文件描述符被关闭了,复制出的新文件描述符还是可以继续使用的。
7.2 dup2函数
dup2函数可以进行文件描述符的复制,也可以进行文件描述符的重定向。重定向指的是断开文件描述符和当前文件的关联关系,与新的文件建立关联关系。
函数原型:
1 |
|
作用是复制参数 oldfd
所指向的文件描述符,创建一个新的文件描述符,新的文件描述符的值是参数 newfd
。如果 newfd
已经是一个打开的文件描述符,dup2()
会先关闭它。
dup2()
调用成功返回新的文件描述符,该文件描述符与 newfd
相同;调用失败则返回-1