为什么write()syscall因ENOSPC而失败?

发布于 2025-02-02 11:20:47 字数 1850 浏览 4 评论 0原文

根据我的理解,请写()SYSCALL从USPACE缓冲区到KSPACE缓冲区,而不是磁盘(或磁盘驱动程序),前提是O_Sync,O_Direct Flags在Open()中不使用O_Direct标志。要测试它,我创建了一个100MB Ext4文件系统,请使用以下DD命令将其填充至100%:

# mount -t ext4
/mnt1 type ext4 (rw,relatime,seclabel)

# dd if=/dev/urandom of=/mnt1/dd_infinite.txt bs=1k
dd: error writing '/mnt1/dd_infinite.txt': No space left on device
91455+0 records in
91454+0 records out
93648896 bytes (94 MB, 89 MiB) copied, 0.871788 s, 107 MB/s

# df -h
Size  Used Avail Use% Mounted on
93M   91M     0 100% /mnt1

然后,我使用以下代码创建一个文件并将一些数据写入其中。我期望fsync()能给我enospc,但是不,那是写给我eNOSPC的write()syscall。我的问题是为什么? Write()如何知道设备的任务只是将数据从USPACE复制到KSPACE时没有剩余的空间?

Linux内核默认情况下是否在Write()期间分配数据块,但以后将数据刷新?或者有一些我缺少的东西。

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

int
main(
        int             argc,
        char            *argv[])
{
        int             fd, rv;
        char            buf[80] = { 0 };
        memset(buf, 'A', 80);
        fd = open("/mnt1/hello.txt", O_CREAT | O_RDWR, 0666);
        if (fd == -1) {
                perror("open");
                exit(EXIT_FAILURE);
        }
        rv = write(fd, buf, 80);
        if (rv == -1) {
                perror("write");
                exit(EXIT_FAILURE);
        }
        rv = fsync(fd);
        if (rv == -1) {
                perror("fsync");
                exit(EXIT_FAILURE);
        }
        rv = close(fd);
        if (rv == -1) {
                perror("close");
                exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
}

输出:

# gcc enospc_file_create.c -Wall -pedantic -std=c99
# ./a.out
write: No space left on device
#

操作系统详细信息:

(RHEL 8.4) Linux 4.18.0-305.el8.x86_64

As per my understanding, write() syscall write data from uspace buffer to kspace buffer, not to the disk (or disk driver), provided O_SYNC, O_DIRECT flags are not used in open(). To test it I have created one 100MB ext4 File System, fill it to 100% using below dd command:

# mount -t ext4
/mnt1 type ext4 (rw,relatime,seclabel)

# dd if=/dev/urandom of=/mnt1/dd_infinite.txt bs=1k
dd: error writing '/mnt1/dd_infinite.txt': No space left on device
91455+0 records in
91454+0 records out
93648896 bytes (94 MB, 89 MiB) copied, 0.871788 s, 107 MB/s

# df -h
Size  Used Avail Use% Mounted on
93M   91M     0 100% /mnt1

Then I used below code to create one file and write some data into it. I was expecting fsync() to give me ENOSPC, but no it was the write() syscall which was giving me ENOSPC. My question is why? How does write() know that there is no space left on device when its task is just to copy data from uspace to kspace?

Does linux kernel by default allocate data blocks during write() but flush data into it later? Or there is something which I am missing.

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

int
main(
        int             argc,
        char            *argv[])
{
        int             fd, rv;
        char            buf[80] = { 0 };
        memset(buf, 'A', 80);
        fd = open("/mnt1/hello.txt", O_CREAT | O_RDWR, 0666);
        if (fd == -1) {
                perror("open");
                exit(EXIT_FAILURE);
        }
        rv = write(fd, buf, 80);
        if (rv == -1) {
                perror("write");
                exit(EXIT_FAILURE);
        }
        rv = fsync(fd);
        if (rv == -1) {
                perror("fsync");
                exit(EXIT_FAILURE);
        }
        rv = close(fd);
        if (rv == -1) {
                perror("close");
                exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
}

Output:

# gcc enospc_file_create.c -Wall -pedantic -std=c99
# ./a.out
write: No space left on device
#

OS details:

(RHEL 8.4) Linux 4.18.0-305.el8.x86_64

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

成熟的代价 2025-02-09 11:20:47

根据我的理解,写()SYSCALL从USPACE缓冲区写入KSPACE缓冲区,而不是磁盘(或磁盘驱动程序)

技术上讲到磁盘驱动程序(或磁盘驱动程序),这取决于驱动程序,但是您在很大程度上在很大程度上用于文件系统:对磁盘的实际写入在页面缓存中被缓冲。最终,该页面缓存被冲洗到磁盘上,要么手动(例如,由fsync(2)),要么是通过内核启发式方法自动的。

话虽这么说,许多申请根本不致电Fsync(2)。因此,如果仅在写入后才检测到设备上缺乏空间,则可能会丢失数据。这不好。因此,大多数文件系统[需要引用]将检查处理时设备上是否有足够的空间(2)。

Write()如何知道设备任务仅将数据从USPACE复制到KSPACE时没有剩余空间?

该文件系统具有一个块分配器,该分配器可以管理可用的块存储数量以及这些块的位置。在处理写入(2)时,文件系统检查磁盘上是否有足够的自由块来处理最终将编写的数据。

除了:在Linux上,Fsync(2)可以返回EnOPSC,但是在某些操作系统上,FSONNC(2)无法返回ENOSPC。例如, freebsd

As per my understanding, write() syscall write data from uspace buffer to kspace buffer, not to the disk (or disk driver)

Technically, it depends on the driver, but you're right for the most part for file systems: the actual write to disk is buffered in the page cache. Eventually, the page cache is flushed to disk either manually (eg. by fsync(2)), or automatically by kernel heuristics.

That being said, many applications do not call fsync(2) at all. So, if the lack of space on the device was only detected on writeback, then data could be lost. This is not good. So, most file systems [citation needed] will check whether there is enough space on device while handling write(2).

How does write() know that there is no space left on device when its task is just to copy data from uspace to kspace?

The file system has a block allocator that manages how much block storage is available, and where those blocks are. While handling the write(2), the file system checks whether there are enough free blocks on disk to handle the data that will eventually be written.

Aside: On Linux, fsync(2) can return ENOSPC, but on some operating systems fsync(2) cannot return ENOSPC. For example, FreeBSD.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文