Linux学习笔记:
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
前言:
文件I/O(输入输出)操作是现代计算机系统中的重要组成部分,几乎所有的程序都需要与文件进行交互。无论是读取配置文件、写入日志文件,还是处理用户数据,文件操作都是不可避免的。而在操作系统层面,文件操作的实现往往是通过文件描述符和系统调用接口来完成的。在Linux操作系统中,文件描述符是处理文件的关键,而通过系统调用接口,程序能够直接与操作系统交互,实现文件的打开、读取、写入和关闭等操作。
C语言作为一种底层编程语言,提供了多种文件操作函数,同时,Linux操作系统提供了底层的系统调用,帮助程序实现更精细的文件控制。在这篇博客中,我们将深入探讨C语言文件I/O操作的基础,重点讨论操作系统中的文件操作机制,详细讲解Linux中的文件描述符和文件操作系统调用接口。通过这篇文章,你将能够理解文件操作的基本概念、系统调用的工作原理以及如何在C语言中实现高效的文件操作。
目录
第一部分:C语言文件I/O操作基础
1.1 打开文件:fopen()
1.2 读取文件:fread()
1.3 写入文件:fwrite()
1.4 关闭文件:fclose()
1.5 错误处理
第二部分:Linux操作系统中的文件操作
2.1 文件描述符(File Descriptor)
2.2 系统调用接口
2.2.1 open() 系统调用
2.2.2 read() 系统调用
2.2.3 write() 系统调用
2.2.4 close() 系统调用
第三部分:文件描述符与FILE *的区别
3.1 FILE *与文件描述符的区别
结论
首先我们先来讲解一下C语言中的文件I/O操作,这个在我们前面C语言的讲解中已经提到过,今天我们结合操作系统的IO操作再来回顾一下
第一部分:C语言文件I/O操作基础
在C语言中,文件I/O操作主要是通过标准库提供的FILE *
指针和一系列文件操作函数来实现的。这些函数为开发人员提供了更高层的文件操作接口,使得文件读写变得简单和方便。
1.1 打开文件:fopen()
C语言通过fopen()
函数打开文件,并返回一个FILE *
类型的指针,用于后续的文件读写操作。fopen()
的语法如下:
FILE *fopen(const char *filename, const char *mode);
filename
:表示文件的路径,文件可以是相对路径或绝对路径。mode
:文件打开的模式,决定文件的读写方式。 常见的mode
有:
模式 | 描述 |
---|---|
"r" | 以只读模式打开文件,文件必须存在。 |
"w" | 以写模式打开文件,若文件已存在则会被清空。 |
"a" | 以追加模式打开文件,若文件不存在则会创建文件。 |
"rb" | 以二进制模式只读打开文件,文件必须存在。 |
"wb" | 以二进制模式写入文件,若文件已存在则会被清空。 |
示例:
#include <stdio.h>int main() { FILE *file = fopen("example.txt", "w"); if (file == NULL) { perror("Error opening file"); return -1; } fprintf(file, "Hello, World!\n"); fclose(file); return 0;}
运行结果:
1.2 读取文件:fread()
fread()
函数从文件中读取数据,并将其存储到内存中。其语法如下:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
ptr
:指向内存的指针,用于存储读取的数据。size
:每个数据元素的大小(单位:字节)。count
:读取的元素个数。stream
:文件指针,指定从哪个文件读取数据。 示例:
#include <stdio.h>int main() { FILE *file = fopen("example.txt", "r"); if (file == NULL) { perror("Error opening file"); return -1; } char buffer[100]; size_t bytesRead = fread(buffer, sizeof(char), 100, file); buffer[bytesRead] = '\0'; // Add null terminator printf("File content: %s\n", buffer); fclose(file); return 0;}
运行结果:
1.3 写入文件:fwrite()
fwrite()
函数用于将数据从内存写入到文件中。其语法如下:
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr
:指向内存中数据的指针。size
:每个数据元素的大小(单位:字节)。count
:写入的元素个数。stream
:文件指针,指定将数据写入哪个文件。 示例:
#include <stdio.h>int main() { FILE *file = fopen("example.txt", "w"); if (file == NULL) { perror("Error opening file"); return -1; } const char *message = "Hello from C!\n"; fwrite(message, sizeof(char), 16, file); fclose(file); return 0;}
运行结果:
1.4 关闭文件:fclose()
文件操作完成后,应该通过fclose()
函数关闭文件。其语法如下:
int fclose(FILE *stream);
stream
:文件指针,指向已经打开的文件。 关闭文件后,程序无法再通过该文件指针进行任何操作。
示例:
#include <stdio.h>int main() { FILE *file = fopen("example.txt", "r"); if (file == NULL) { perror("Error opening file"); return -1; } // Perform file operations... fclose(file); return 0;}
1.5 错误处理
在文件操作中,错误处理非常重要。C语言提供了两个函数来帮助开发者检测错误:ferror()
和feof()
。
ferror(FILE *stream)
:判断是否发生了文件I/O错误。feof(FILE *stream)
:判断文件是否到达了末尾。 示例:
#include <stdio.h>int main() { FILE *file = fopen("example.txt", "r"); if (file == NULL) { perror("Error opening file"); return -1; } char buffer[100]; if (fread(buffer, sizeof(char), 100, file) == 0) { if (ferror(file)) { perror("Error reading file"); } else if (feof(file)) { printf("End of file reached.\n"); } } fclose(file); return 0;}
第二部分:Linux操作系统中的文件操作
在Linux中,文件操作是通过系统调用接口和文件描述符来完成的。文件描述符是一个整数,它表示一个打开的文件,而系统调用接口提供了底层文件操作的功能,如打开、读写和关闭文件。理解Linux中的文件描述符和系统调用接口对于深入了解文件I/O非常重要。
2.1 文件描述符(File Descriptor)
在Linux中,每个打开的文件都会被分配一个文件描述符。文件描述符是一个非负整数,操作系统内核通过它来跟踪进程与文件之间的关联。文件描述符提供了与文件进行交互的通道,可以通过它执行各种文件操作。
文件描述符 | 描述 |
---|---|
0 | 标准输入(stdin) |
1 | 标准输出(stdout) |
2 | 标准错误(stderr) |
除了标准输入、输出和错误,程序还可以使用open()
系统调用打开文件并获得其他文件描述符。
2.2 系统调用接口
Linux提供了多个系统调用来执行文件操作,常见的系统调用包括:open()
、read()
、write()
、close()
等。通过这些系统调用,程序能够直接与内核进行交互,完成文件的操作。
2.2.1 open()
系统调用
open()
系统调用用于打开一个文件并返回文件描述符。其语法如下:
int open(const char *pathname, int flags, mode_t mode);
pathname
:要打开的文件的路径。flags
:文件打开模式,决定文件的访问方式。mode
:文件权限,通常在文件创建时使用。 常见的flags
参数包括:
标志 | 描述 |
---|---|
O_RDONLY | 以只读模式打开文件 |
O_WRONLY | 以只写模式打开文件 |
O_RDWR | 以读写模式打开文件 |
O_CREAT | 文件不存在时创建文件 |
O_TRUNC | 如果文件已存在,清空文件内容 |
O_APPEND | 以追加模式打开文件 |
示例:
#include <fcntl.h>#include <unistd.h>#include <stdio.h>int main() { int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("Error opening file"); return -1; } const char *data = "Hello from Linux File Descriptor!"; write(fd, data, 31); // 写入数据到文件 close(fd); // 关闭文件描述符 return 0;}
运行结果:
2.2.2 read()
系统调用
read()
系统调用用于从文件中读取数据,并将数据存储到内存中。其语法如下:
ssize_t read(int fd, void *buf, size_t count);
fd
:文件描述符。buf
:指向内存的指针,用于存储读取的数据。count
:要读取的字节数。 示例:
#include <fcntl.h>#include <unistd.h>#include <stdio.h>int main() { int fd = open("example.txt", O_RDONLY); if (fd == -1) { perror("Error opening file"); return -1; } char buffer[100]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1); if (bytesRead == -1) { perror("Error reading file"); close(fd); return -1; } buffer[bytesRead] = '\0'; // Null-terminate the string printf("File content: %s\n", buffer); close(fd); return 0;}
运行结果:
2.2.3 write()
系统调用
write()
系统调用用于将数据写入文件。其语法如下:
ssize_t write(int fd, const void *buf, size_t count);
fd
:文件描述符。buf
:指向要写入数据的内存地址。count
:要写入的字节数。 示例:
#include <fcntl.h>#include <unistd.h>#include <stdio.h>int main() { int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("Error opening file"); return -1; } const char *message = "Hello from Linux File Descriptor!\n"; write(fd, message, 31); // Write data to file close(fd); // Close file descriptor return 0;}
运行结果:
2.2.4 close()
系统调用
close()
系统调用用于关闭文件描述符。其语法如下:
int close(int fd);
fd
:要关闭的文件描述符。 示例:
#include <fcntl.h>#include <unistd.h>#include <stdio.h>int main() { int fd = open("example.txt", O_RDONLY); if (fd == -1) { perror("Error opening file"); return -1; } // Perform file operations... close(fd); // Close the file descriptor return 0;}
第三部分:文件描述符与FILE *
的区别
虽然C语言提供了FILE *
类型和相关的标准库函数来处理文件操作,但底层实际上是通过文件描述符来进行管理的。理解FILE *
与文件描述符的区别对于深入理解文件I/O非常重要。
3.1 FILE *
与文件描述符的区别
特性 | FILE * (C标准库) | 文件描述符(Linux操作系统) |
---|---|---|
类型 | 由C标准库提供的类型 | 操作系统内核使用整数值标识 |
管理者 | 由C标准库管理 | 由操作系统内核管理 |
主要用途 | 提供更高级别的文件操作接口 | 提供更低级别的文件操作接口 |
缓冲区管理 | 提供缓冲区管理,提高效率 | 不提供缓冲区管理 |
数据访问方式 | 适用于文本文件的高级操作 | 适用于二进制文件和直接内存映射操作 |
结论
通过本文的详细讲解,您应该已经对C语言的文件I/O操作以及Linux操作系统中文件描述符和系统调用有了更深刻的理解。在C语言中,文件I/O操作主要通过FILE *
指针和标准库函数来实现,而在Linux操作系统中,文件操作通过文件描述符和底层的系统调用接口进行。通过这些系统调用,程序能够直接与操作系统交互,完成文件的打开、读写和关闭等操作。
理解FILE *
与文件描述符的区别、系统调用的工作原理,以及如何高效地进行文件操作,将有助于你在编程过程中处理更复杂的文件任务,并提高程序的性能。
本篇笔记:
感谢各位大佬支持,创作不易,还请各位大佬点赞支持!!!