0

0

Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)

看不見的法師

看不見的法師

发布时间:2025-07-20 12:04:49

|

470人浏览过

|

来源于php中文网

原创

文章目录

1.基础认识2.再识c语言中文件接口2.1fopen()与fclose()2.2文件操作函数3.三个默认打开的文件流4.相关系统接口4.1open()4.2umask()函数4.3close()、write()、read()5.文件描述符5.1 0、1、25.2底层6.文件描述符的分配规则重定向—dup2()系统调用7.Linux中一切皆文件

1.基础认识 文件是存储在磁盘或其他存储介质上的数据集合,包括数据内容和文件属性。 在操作系统中,文件的操作通常需要通过进程来打开文件才进行,进程在打开文件时会创建一个文件描述符,用于标识这个文件。 在访问文件之前,通常需要先打开文件。通过打开文件,进程可以获取文件的句柄或文件描述符,然后可以通过读取、写入、修改文件内容来进行文件操作。文件的修改通常是通过执行相应的代码来实现的,比如写入数据、修改文件属性等。 在操作系统中,进程在打开文件时会获得一个文件描述符,这个文件描述符是进程访问该文件的标识符。一个进程可以打开多个文件,每个打开的文件都会有一个对应的文件描述符。这意味着一个进程可以同时访问多个文件,进行读取、写入等操作。 当一个文件被打开时,通常会将文件的部分或全部内容加载到内存中,以便进程可以直接访问和操作文件内容。这样可以提高文件的访问速度和效率。 在系统中,一个进程能打开多个文件。还有可能会存在多个进程同时打开多个文件 所以,打开的文件OS一定要对其进行管理,怎么管理——先描述再组织:那么内核中一定有用来描述被打开文件的结构体,并用它来定义一个个对象 操作系统中,并不是所有的文件都会被进程打开。实际上,系统中可能存在大量的文件,但并不是所有的文件都会被进程打开并加载到内存中进行操作。有些文件可能处于未打开状态,即它们仅存在于磁盘中,没有被任何进程打开 2.再识c语言中文件接口2.1fopen()与fclose()

当在 C 语言中进行文件操作时,fopen() 和 fclose() 是两个非常重要的函数。下面我将详细讲解它们的作用和用法:

fopen() :该函数用于打开一个文件,并返回一个指向 FILE 结构体的指针,该指针用于后续的文件操作。 语法:FILE *fopen(const char *filename, const char *mode);参数: filename:要打开的文件的路径和名称。mode:打开文件的模式,包括只读、只写、读写等不同选项。返回值: 如果成功打开文件,则返回指向 FILE 结构体的指针。如果打开文件失败,则返回 NULL。“r”:只读模式打开文件以供读取。如果文件不存在,打开操作将失败。如果文件不存在,则返回 NULL。“w”:只写模式打开文件以供写入。如果文件存在,则会被截断(即文件内容会被清空);如果文件不存在,则会创建一个新文件。如果文件打开成功,则返回指向文件的指针。“a”:追加模式打开文件以供写入,但是不会截断文件。新的数据会被追加到文件末尾。如果文件打开成功,则返回指向文件的指针。“r+”:读写模式(文件必须存在)打开文件以供读取和写入。文件必须存在,否则打开操作将失败。如果文件打开成功,则返回指向文件的指针。“w+”:读写模式(文件不存在则创建)打开文件以供读取和写入。如果文件存在,则会被截断;如果文件不存在,则会创建一个新文件。如果文件打开成功,则返回指向文件的指针。“a+”:读写模式(追加模式,文件不存在则创建)打开文件以供读取和写入,不会截断文件。新的数据会被追加到文件末尾。如果文件打开成功,则返回指向文件的指针。代码语言:javascript代码运行次数:0运行复制
#includeint main(){FILE* f = fopen("./test.txt", "w");//打开if (f == NULL){perror("fopen");return 1;}//在打开关闭之间,我们能进行文件操作fclose(f);//关闭return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file11
fclose() 函数: fclose() 函数用于关闭一个已打开的文件,释放文件资源并刷新缓冲区。语法:int fclose(FILE *stream);参数: stream:指向已打开文件的 FILE 结构体指针。返回值: 如果成功关闭文件,则返回 0。如果关闭文件失败,则返回 EOF。2.2文件操作函数函数原型:int chdir(const char *path);功能:将当前工作目录更改为指定的目录。参数:path 是一个字符串,表示要更改到的目录路径。返回值:如果成功,则返回 0;如果失败,则返回 -1。

fprintf():向文件写入格式化数据

函数原型:int fprintf(FILE *stream, const char *format, …);功能:将格式化的数据写入到指定文件中。示例:fprintf(file, “Hello, World!”);

fscanf():从文件读取格式化数据

函数原型:int fscanf(FILE *stream, const char *format, …);功能:从指定文件中读取格式化的数据。示例:fscanf(file, “%d %s”, &num, str);

fputc():向文件写入一个字符

立即学习C语言免费学习笔记(深入)”;

函数原型:int fputc(int c, FILE *stream);功能:将一个字符写入到指定文件中。示例:fputc(‘A’, file);

fgetc():从文件读取一个字符

函数原型:int fgetc(FILE *stream);功能:从指定文件中读取一个字符。示例:char ch = fgetc(file);

fgets():用于从指定文件中读取一行数据,并将其存储到指定的缓冲区中

函数原型:char *fgets(char *str, int num, FILE *stream);功能:从指定文件中读取一行数据。示例:fgets(buffer, sizeof(buffer), file);

fputs():向文件写入一行数据

函数原型:int fputs(const char *str, FILE *stream);功能:将一行数据写入到指定文件中。示例:fputs(“Hello, World!”, file);

fwrite() 是 C 语言标准库中用于将数据块写入文件的函数。

代码语言:javascript代码运行次数:0运行复制
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr:指向要写入的数据的指针。size:要写入的每个数据项的大小(以字节为单位)。nmemb:要写入的数据项的数量。stream:指向要写入的文件的指针。代码语言:javascript代码运行次数:0运行复制
#includeint main(){FILE* f = fopen("./test.txt", "w");//打开if (f == NULL){perror("fopen");return 1;}//在打开关闭之间,我们能进行文件操作const char* str = "this's test.txt";fputs(str, f);fclose(f);//关闭return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file12
我们之前看到的输出重定向>:也是直接覆盖(默认也是以w方式打开的)

使用a方式打开(append)

代码语言:javascript代码运行次数:0运行复制
int main(){FILE* f = fopen("./test.txt", "a");//打开if (f == NULL){perror("fopen");return 1;}//在打开关闭之间,我们能进行文件操作const char* str = "this's test.txt\n";fputs(str, f);fclose(f);//关闭return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)newfile13
使用 >> 符号进行输出重定向时,会以追加模式打开文件,新的内容会被追加到文件末尾而不会清空原有内容3.三个默认打开的文件流
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file14
stdin:标准输入流,通常用于从键盘设备读取输入。stdout:标准输出流,通常用于向显示器设备输出信息。stderr:标准错误流,通常用于向控制台输出错误信息。

这三个文件流在程序启动时会自动打开,不需要显式地打开或关闭

stdinstdoutstderr 是标准C库中定义的全局变量,它们分别代表标准输入流、标准输出流和标准错误流。这些变量通常在 头文件中声明,可以直接使用。

stdin

stdin 是标准输入流,通常用于从用户输入设备(如键盘)读取数据。在程序启动时,stdin 会自动关联到标准输入设备,通常是键盘。

stdout

stdout 是标准输出流,通常用于向用户输出设备(如屏幕)输出数据。在程序启动时,stdout 会自动关联到标准输出设备,通常是屏幕。代码语言:javascript代码运行次数:0运行复制
#include #include #include #include #define FILENAME "log.txt"int main() {    printf("hello printf\n"); // 使用 printf 函数向标准输出流输出字符串    fputs("hello fputs\n", stdout); // 使用 fputs 函数向标准输出流输出字符串    const char* msg = "hello fwrite\n";    fwrite(msg, 1, strlen(msg), stdout); // 使用 fwrite 函数向标准输出流输出字符串    fprintf(stdout, "hello fprintf\n"); // 使用 fprintf 函数向标准输出流输出格式化字符串    return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file15
printf("hello printf\n");(我们经常用):函数原型:int printf(const char *format, ...);printf 是标准C库中的函数,用于向标准输出流(stdout)输出格式化字符串。在这里,printf 输出了字符串 “hello printf” 到标准输出流,并在末尾添加一个换行符。fputs("hello fputs\n", stdout);:函数原型:int fputs(const char *str, FILE *stream);fputs 是标准C库中的函数,用于向指定文件流(这里是 stdout,即标准输出流)输出字符串。在这里,fputs 输出了字符串 “hello fputs” 到标准输出流,并在末尾添加一个换行符。fwrite(msg, 1, strlen(msg), stdout);:函数原型:size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);fwrite 是标准C库中的函数,用于向指定文件流输出指定数量的字节。在这里,fwrite 输出了 msg 指向的字符串 “hello fwrite” 到标准输出流,并指定输出字符串的长度。fprintf(stdout, "hello fprintf\n");:、函数原型:int fprintf(FILE *stream, const char *format, ...);fprintf 是标准C库中的函数,用于向指定文件流输出格式化字符串。在这里,fprintf 输出了格式化字符串 “hello fprintf” 到标准输出流。

stderr

stderr 是标准错误流,通常用于向用户输出设备输出错误信息。在程序启动时,stderr 会自动关联到标准错误设备,通常也是屏幕。您可以使用 fprintf(stderr, ...) 等函数向 stderr 输出错误信息。4.相关系统接口4.1open()
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file16
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file17

open 函数是用于打开文件的系统调用函数。它的原型如下:

代码语言:javascript代码运行次数:0运行复制
#include int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);

pathname 是要打开的文件的路径名,flags 是打开文件的标志,mode 是文件的权限。open 函数==返回一个文件描述符(file descriptor)==用于后续对文件的读写操作。打开失败的话返回-1(不需要创建文件时,就使用两个参数就好了)

flags 参数可以是以下标志的组合(都是一个个宏):

O_RDONLY:只读O_WRONLY:只写O_RDWR:读写O_CREAT:如果文件不存在则创建O_TRUNC:如果文件存在则截断为0长度,就像之前的wO_APPEND:追加写入,就像之前的a

这些宏都只有一个比特位为1,其余为0

mode 参数指定了文件的权限,通常与 S_IRUSRS_IWUSR 等宏一起使用。如果创建文件时不加上权限,那么创建出来的文件权限是乱码。这就需要我们如果创建了文件,就要给上文件的权限。经常使用格式:0666(与我们之前讲的umask一样,0可以看成一个格式要求),但是这样创建后的权限不是最终权限,我们使用0666后,还要收到掩码的修改

代码语言:javascript代码运行次数:0运行复制
#include #include #include #include int main() {    int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);    if (fd == -1)    {        perror("open");        return 1;    }    return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file18

如果想要创造一个自己指明权限的文件,可以使用umask()函数

4.2umask()函数

umask 函数是一个系统调用,用于设置进程的文件创建屏蔽字(file mode creation mask)。文件创建屏蔽字是一个权限掩码,用于确定新建文件的默认权限。在创建新文件时,系统会根据进程的文件创建屏蔽字来屏蔽一些权限位,以确保新建文件不会拥有过于宽松的权限。

umask 函数的原型:

慧中标AI标书
慧中标AI标书

慧中标AI标书是一款AI智能辅助写标书工具。

下载
代码语言:javascript代码运行次数:0运行复制
#include mode_t umask(mode_t mask);

umask 函数接受一个参数 mask,该参数是一个权限掩码,用于指定要屏蔽的权限位。umask 函数会返回之前的文件创建屏蔽字。通常,umask 函数会在创建文件之前调用,以确保新建文件不会拥有不必要的权限。

4.3close()、write()、read()close()

close() 函数用于关闭一个已打开的文件描述符。文件描述符是一个非负整数,用于在程序中唯一标识打开的文件、设备或其他输入/输出资源。当你打开一个文件时,系统会分配一个文件描述符给你,你可以通过这个描述符来读写文件。当你完成对文件的操作后,应该使用 close() 函数来关闭它,以释放系统资源。

函数原型如下:

代码语言:javascript代码运行次数:0运行复制
#include   int close(int fd);

其中 fd 是要关闭的文件描述符。如果成功,close() 返回 0;如果失败,返回 -1 并设置全局变量 errno 以指示错误原因。

write()

write() 函数用于向打开的文件描述符写入数据。你可以使用它向普通文件、设备文件或套接字写入数据。

函数原型如下:

代码语言:javascript代码运行次数:0运行复制
#include   ssize_t write(int fd, const void *buf, size_t count);
fd 是要写入的文件描述符。buf 是一个指向要写入数据的缓冲区的指针。count 是要写入的数据的字节数。

write() 函数返回实际写入的字节数。在成功时,返回值通常等于 count,除非到达文件的末尾或发生其他错误。如果发生错误,write() 返回 -1 并设置 errno

代码语言:javascript代码运行次数:0运行复制
#include #include #include #include #includeint main() {    int fd = open("test.txt", O_WRONLY | O_CREAT, 0666);    if (fd == -1)    {        perror("open");        return 1;    }    const char* str = "hellow write\n";    write(fd, str, strlen(str));    close(fd);    return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file19
read()

read() 函数用于从文件描述符中读取数据。它的函数原型如下:

代码语言:javascript代码运行次数:0运行复制
#include ssize_t read(int fd, void *buf, size_t count);
fd 是要读取的文件描述符。buf 是一个指向存储读取数据的缓冲区的指针。count 是要读取的字节数。

read() 函数会尝试从文件描述符 fd 对应的文件中读取 count 个字节的数据,并将读取的数据存储到 buf 指向的内存缓冲区中。函数返回值是实际读取的字节数。如果返回值为 0,则表示已经到达文件末尾;如果返回值为 -1,则表示读取出现错误。

以下是一个简单的示例,演示如何使用 read() 函数从文件中读取数据:

代码语言:javascript代码运行次数:0运行复制
#include #include #include int main() {    int fd = open("example.txt", O_RDONLY);    if (fd == -1) {        perror("open");        return 1;    }    char buffer[100];    ssize_t bytes_read = read(fd, buffer, sizeof(buffer));    if (bytes_read == -1) {        perror("read");        close(fd);        return 1;    }    printf("Read %zd bytes: %s\n", bytes_read, buffer);    close(fd);    return 0;}
5.文件描述符

文件描述符是一个整数,用于在操作系统中唯一标识一个被打开的文件、设备或其他I/O资源

5.1 0、1、2

所以输入输出还可以采用如下方式:

代码语言:javascript代码运行次数:0运行复制
write(1, buf, strlen(buf));

这是大家可能会问? 刚才我们才说才c语言里stdinstdoutstderr,这三个FILE*是标准输入, 标准输出,与标准错误

那上面这0、1、2是怎么回事?

代码语言:javascript代码运行次数:0运行复制
int main(){    printf("%d", stdin->_fileno);    printf("%d", stdout->_fileno);    printf("%d", stderr->_fileno);    return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file110
5.2底层
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file111

进程你怎么知道你打开了哪些文件呢?

文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件


6.文件描述符的分配规则代码语言:javascript代码运行次数:0运行复制
int main(){    close(1);    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);    printf("%d\n", fd);    return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file113

重定向—dup2()系统调用
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file111.5

dup2() 是一个系统调用,用于复制文件描述符。它的原型如下:

代码语言:javascript代码运行次数:0运行复制
#include int dup2(int oldfd, int newfd);
dup2() 系统调用的作用是将 oldfd 文件描述符复制到 newfd 文件描述符处。如果 newfd 已经打开,则会先关闭 newfd,然后将 oldfd 复制到 newfd 处;如果 newfd 等于 oldfd,则 dup2() 不会关闭 oldfd,但会返回 newfd。这个系统调用通常用于重定向标准输入、标准输出和标准错误流,例如将一个文件描述符复制到标准输出流(文件描述符 1)或标准错误流(文件描述符 2)。代码语言:javascript代码运行次数:0运行复制
#include #include #include int main(){int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);dup2(fd, 1);printf("hellow log.txt\n");return 0;}
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)file111.6

7.Linux中一切皆文件
Linux:基础IO(一.C语言文件接口与系统调用、默认打开的文件流、详解文件描述符与dup2系统调用)newfile112

在Linux中,“一切皆文件”(Everything is a file)是一个重要的概念,它体现了Linux操作系统的设计哲学。这个概念的核心思想是,Linux将所有设备、进程、网络连接、管道等抽象概念都视为文件,并通过文件系统的方式来管理和访问它们。

虚拟文件系统:Linux中的虚拟文件系统(Virtual File System,VFS)将不同类型的文件系统(如ext4、NTFS、procfs等)抽象成统一的文件接口,使得用户和应用程序可以以统一的方式访问不同的文件系统。

通过将所有这些不同的概念都视为文件,Linux提供了一种统一的接口和一致的操作方式,使得用户和开发者可以更加方便地管理和操作系统中的各种资源。

虚拟文件系统(VFS): Linux 内核中有一个虚拟文件系统(VFS),它提供了一个抽象层,使得不同类型的文件系统(如 ext4、NTFS 等)能够以统一的方式被内核访问。VFS 为所有文件提供了统一的接口,包括打开文件、读写文件、关闭文件等操作。文件描述符: Linux 中每个进程都有一个文件描述符表,用于跟踪打开的文件和设备。标准输入流 stdin、标准输出流 stdout、标准错误流 stderr 分别对应文件描述符 0、1、2。系统调用: Linux 提供了一系列系统调用(如 open()read()write()close() 等),用于在用户空间和内核空间之间进行文件操作。用户程序可以通过系统调用来打开设备文件、读取设备数据、写入设备数据等。

好啦大家,今天就到这里啦。感谢大家支持!!!

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

395

2023.06.20

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

617

2023.07.25

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

354

2023.08.02

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

257

2023.08.09

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

600

2023.09.05

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

525

2023.09.20

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

640

2023.09.20

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

601

2023.09.22

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

15

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Rust 教程
Rust 教程

共28课时 | 4.6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.3万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号