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操作,这个在我们前面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 *
与文件描述符的区别、系统调用的工作原理,以及如何高效地进行文件操作,将有助于你在编程过程中处理更复杂的文件任务,并提高程序的性能。
本篇笔记:
感谢各位大佬支持,创作不易,还请各位大佬点赞支持!!!