文件操作和文件系统

1. C语言文件接操作

为了更好地理解文件系统和文件描述符,让我们回顾一下C文件接口的基础知识。我们通过一个简单的文件写入和读取的例子来展示。

FILE *fopen(const char *filename, const char *mode);
filename:要打开的文件的路径或名称。
mode:打开文件的模式,包括读("r")、写("w")、追加("a")等。

关闭文件:

int fclose(FILE *stream);
stream:要关闭的文件指针。

读取文件:

size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
ptr:指向存储读取数据的内存块的指针。
size:每个数据项的字节数。
count:要读取的数据项的数量。
stream:文件指针。

写入文件:

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
ptr:指向要写入的数据的内存块的指针。
size:每个数据项的字节数。
count:要写入的数据项的数量。
stream:文件指针。

2. 系统调用与文件操作

系统调用是操作系统提供给应用程序的接口,通过这些接口,应用程序可以请求操作系统执行特定的操作。文件操作通常涉及到一系列系统调用,以便应用程序能够在磁盘或其他存储介质上读取和写入数据。以下是文件操作的系统调用:

2.1 open 系统调用

open 系统调用用于打开或创建文件,其原型如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

pathname:要打开或创建的目标文件的路径。
flags:打开文件时的参数选项,比如 O_RDONLY、O_WRONLY、O_RDWR 等。
mode:如果使用 O_CREAT 选项,需要指定新文件的访问权限。
open:返回一个新打开的文件描述符,如果失败则返回 -1。

#include <fcntl.h>
int fd = open("example.txt", O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
    perror("open");
    return 1;
}

上述例子中,我们使用 open 打开(或创建)一个名为 example.txt 的文件,以只写方式打开,如果文件不存在则创建,权限设置为 0644。

2.2 read 系统调用

read 系统调用用于从文件描述符中读取数据:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

fd:文件描述符,表示从哪个文件或设备读取数据。
buf:指向存放读取数据的缓冲区。
count:欲读取的字节数。
read: 返回值为读取的字节数,如果返回 -1 则表示出错。

#include <unistd.h>

int fd = open("example.txt", O_RDONLY);
if (fd < 0) {
    perror("open");
    return 1;
}

char buffer[1024];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer));

if (bytesRead > 0) {
    buffer[bytesRead] = '\0';
    printf("Read from file: %s", buffer);
}

close(fd);

在这个例子中,我们使用 read 从文件中读取数据到缓冲区 buffer。

2.3 write 系统调用

write 系统调用用于向文件描述符中写入数据:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

fd:文件描述符,表示写入数据到哪个文件或设备。
buf:指向待写入数据的缓冲区。
count:欲写入的字节数。
write: 返回值为实际写入的字节数,如果返回 -1 则表示出错。

#include <fcntl.h>

int fd = open("output.txt", O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
    perror("open");
    return 1;
}

const char *message = "Hello, File!";

ssize_t bytesWritten = write(fd, message, strlen(message));
if (bytesWritten > 0) {
    printf("Write to file successful!\n");
}
close(fd);

在这个例子中,我们使用 write 将字符串写入到文件中。

2.4 close 系统调用

close 系统调用用于关闭文件描述符:

#include <unistd.h>

int close(int fd);

fd:待关闭的文件描述符。
close 返回值为 0 表示成功,-1 表示失败。

#include <fcntl.h>

int fd = open("example.txt", O_RDONLY);
if (fd < 0) {
    perror("open");
    return 1;
}

// 文件操作
close(fd);

在这个例子中,我们打开文件后执行一些文件操作,最后使用 close 关闭文件描述符。

系统调用在文件操作中扮演着重要的角色,提供了对文件的底层访问。通过 open、read、write 和 close 等系统调用,程序可以打开、读取、写入和关闭文件。这些系统调用是文件 I/O 的基础,也是理解文件系统的关键。

3. 文件描述符

进程管理: 操作系统通过管理进程的方式,将每个正在执行的进程抽象为 task_struct(PCB),然后通过链表等方式将这些结构体组织起来,以实现对进程的有序调度和管理。

文件管理: 一个进程可以打开多个文件,为了对这些文件进行读写操作,操作系统将文件抽象成 file 结构体,并将这些结构体组织起来。文件结构体内部存储了文件的大部分属性。

文件描述符: 文件描述符是操作系统为每个进程分配的整数,用于标识其所打开的文件。进程通过文件描述符来管理打开的文件,通过数组指针的方式组织了打开的文件的 file 结构体,通常有三个默认的文件描述符:0(标准输入stdin)、1(标准输出stdout)、2(标准错误stderr)文件描述符表是一个数组,通过数组下标与具体文件关联

文件描述符表:是操作系统用于跟踪和管理一个进程打开的文件的数据结构。每个进程都有一个独立的文件描述符表,该表记录了该进程所打开的所有文件的信息。

文件描述符的分配规则: 文件描述符是从数组下标的最低处开始分配的。新打开的文件描述符会分配给数组中未被使用的最小的下标位置。
在这里插入图片描述
程序证明
在这里插入图片描述

4. 重定向

重定向是计算机操作系统中的一种机制,允许用户将一个程序的输入或输出从默认的位置(通常是终端或命令行窗口)切换到其他位置,比如文件或者另一个程序。这样的操作使得用户能够更灵活地控制数据的流向。

  1. 标准输入重定向(<):从文件中读取数据作为程序的标准输入。例如,command < input.txt 将文件 input.txt 的内容作为 command 程序的输入。

  2. 标准输出重定向(>):将程序的标准输出保存到文件中。例如,command > output.txt 将 command 程序的输出写入到文件 output.txt。

  3. 追加标准输出重定向(>>):类似于标准输出重定向,但是会追加内容到指定文件而不是覆盖它。例如,command >> output.txt 会将 command 程序的输出追加到文件 output.txt 的末尾。
    在这里插入图片描述
    重定向的原理

重定向的本质是通过改变程序的输入或输出目标,将数据流从默认位置切换到其他位置。标准输出重定向的例子中,原本应该输出到标准输出设备(通常是显示器),但通过重定向操作,输出被发送到了指定的文件中这是通过关闭标准输出,然后重新打开一个文件,将新的文件描述符用于输出的方式实现的

重定向输出程序例子:
在这里插入图片描述
在这个程序中,通过close(1) 关闭标准输出,然后通过 open(“log.txt”, O_CREAT | O_TRUNC | O_WRONLY, 0666) 打开一个文件(“log.txt”),并获得新的文件描述符(1)。接下来,printf 输出的内容被写入到这个新的文件描述符,而不是默认的标准输出。

重要的是要注意,虽然程序使用 printf 输出,但实际上由于我们关闭了标准输出,printf 写入的是通过 open 打开的文件描述符(“log.txt”)。这就是重定向的本质:改变数据流的方向,使得数据不再流向默认的设备,而是流向了我们指定的目标,即文件 “log.txt”。这样,原本应该在屏幕上显示的内容就被重定向到了文件中。

5. dup2 系统调用

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

#include <unistd.h>
int dup2(int oldfd, int newfd);

oldfd: 要复制的文件描述符。这是源文件描述符,它将被复制到新的文件描述符。

newfd: 要复制到的目标文件描述符。如果 newfd 已经打开,它将被关闭并且会被 oldfd 的副本所替代。如果 oldfd 和 newfd 相等,dup2 不会关闭 newf。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int file = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    
    // 将标准输出重定向到文件,使用整数值 1 代替 STDOUT_FILENO
    if (dup2(file, 1) == -1) {
        perror("dup2");
        return 1;
    }

    // 现在,printf 将输出到文件而不是标准输出
    printf("This will be written to the file.\n");

    close(file);  // 关闭文件描述符,但由于 dup2 复制了它,标准输出仍然指向文件

    return 0;
}

这个系统调用的作用是将文件描述符 oldfd 复制到 newfd,如果 newfd 已经打开,它会先关闭 newfd。如果 oldfd 和 newfd 相等,dup2 不会关闭 newfd,并返回 newfd。如果复制成功,dup2 返回新的文件描述符,如果失败,则返回 -1,并设置 errno 来指示错误类型。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值