1. 文件描述符 Linux系统中一切皆文件,所有打开的文件都通过文件描述符进行索引。文件描述符是一个非负整数,当打开或创建文件时,内核向该进程返回一个文件描述符用于指代该文件。
Linux内核对所有打开的文件都维护了一个文件描述符表,用于存储文件描述符。且在文件描述符表中已经默认分配了三个文件描述符:0是标准输入stdin,1是标准输出stdout,2是标准错误stderr。故打开或创建的文件描述符大于或等于3。
2. open/close 2.1 open函数 通过open函数可以打开或创建一个文件,返回的文件描述符一定是该进程尚未使用的最小描述符,大于等于3。
函数原型:
1 2 3 4 5 #include <fcntl.h> int open (const char *path, int flags) ;int open (const char *path, int flags, mode_t mode) ;
参数:
返回值:
成功:返回一个非负整数,表示文件描述符。
失败:返回 -1
,并设置 errno
来指示错误类型。
2.2 close函数 通过close函数关闭一个打开文件,释放打开时分配的文件描述符。close()原型:
1 2 #include <unisted> int close (int 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 2 #include <unisted.h> ssize_t read (int fd, void *buf, size_t count) ;
参数:
fd : 要读取的文件描述符
buf : 指向存放读取数据的缓冲区的指针。
count : 要读取的字节数
返回值: 返回值的数据类型是 ssize_t
,表示带符号的整型,这样既可以返回正的字节数、0(表 示到达文件末尾)也可以返回负值-1(表示出错) 。
成功:返回读取的字节数,可能小于 count
,因为可能在读取count个字节之前已到达文件末尾。
文件结束(EOF):返回 0
,表示已读取到文件末尾。
失败:返回 -1
,并设置 errno
来指示错误类型。
什么是缓冲区:
缓冲区(Buffer)是指在计算机中用于临时存储数据的一段内存区域。在程序中,缓冲区通常用于临时存放数据,以便稍后进行处理或传输。缓冲区的使用可以提高数据的读写效率,减少频繁的IO操作,特别是在涉及大量数据的情况下。
在文件IO操作中,缓冲区通常用于存储要读取或写入的数据。当调用 read()
系统调用从文件中读取数据时,数据首先被读取到缓冲区中,然后程序可以从缓冲区中获取数据进行进一步处理。类似地,当调用 write()
系统调用向文件中写入数据时,数据首先被写入到缓冲区中,然后由系统决定何时将缓冲区中的数据写入到文件中。
3.2 write函数 用于将数据从用户空间写入到已打开文件中
函数原型:
1 2 #include <unisted.h> ssize_t write (int fd, const void *buf, size_t count)
参数:
fd:要写入的文件描述符。
buf:指向要写入数据的缓冲区的指针。
count:往磁盘文件中写入的字节数,一般是buf的长度sizeof(buf)。
返回值:
成功:返回写入的字节数,可以小于 count
。
失败:返回 -1
,并设置 errno
来指示错误类型。
3.3 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <fcntl.h> #include <stdio.h> #include <errno.h> int main () { int fd = open ("example.txt" , O_WRONLY | O_CREAT | O_TRUNC, 0644 ); if (fd == -1 ) { perror ("open" ); return 1 ; } const char *data = "Hello, World!\n" ; ssize_t bytes_written = write (fd, data, strlen (data)); if (bytes_written == -1 ) { perror ("write" ); return 1 ; } close (fd); return 0 ; }
4. 阻塞和非阻塞 阻塞和非阻塞是指,程序在执行某种操作时的行为方式。
使用非阻塞 I/O 可以使得一个进程能够同时处理多个连接而不会被阻塞。当一个套接字上没有数据可读或者没有数据可写时,程序不会等待,而是立即返回,这样可以更有效地利用 CPU 时间。
非阻塞 I/O 结合事件驱动模型(如 epoll)可以实现异步 I/O。通过 epoll 等机制,程序可以注册对多个文件描述符的监听,一旦有事件发生,程序立即得到通知,而不需要轮询或阻塞等待。
5. fcntl函数 fcntl()可以修改一个已打开文件的属性,可以重新设置读、写、追加、非阻塞等标志,而不必重新open文件 。
函数原型:
1 2 #include <fcntl.h> int fcntl (int fd, int cmd, ... ) ;
参数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 2 #include <unisted.h> off_t lseek (int fd, off_t offset, int whence) ;
参数:
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 2 #include <unisted.h> int dup2 (int oldfd, int newfd) ;
作用是复制参数 oldfd
所指向的文件描述符,创建一个新的文件描述符,新的文件描述符的值是参数 newfd
。如果 newfd
已经是一个打开的文件描述符,dup2()
会先关闭它。
dup2()
调用成功返回新的文件描述符,该文件描述符与 newfd
相同;调用失败则返回-1