write()调用失败:设备上没有剩余空间:ENOSPC处理

时间:2013-07-29 07:07:15

标签: c linux file error-handling

write()来电失败errno = 28 (ENOSPC),设备上没有剩余空间。 我试图通过以下方式处理此错误。当磁盘已满时,我正在执行lseek()将文件指针移动到文件的开头。

我相信现在write()不应该失败,因为现在文件将从顶部覆盖(文件不会扩展)。 但仍然write()调用失败并出现相同的错误。请解释一下这种行为。

  if(errno == ENOSPC)
  {
      curPos = lseek(gi4LogFd, 0, SEEK_SET);
      break;
  }

2 个答案:

答案 0 :(得分:6)

仅仅因为您写入文件的开头并不意味着文件系统将写入磁盘上的相同空间或者文件开头的空间完全分配。

你可能在文件中有一个漏洞,在这种情况下,写操作无论如何都会失败。孔是许多文件系统所做的优化,它们假装文件的一部分存在,而实际上只有很多零,因此这些部分永远不会被写入磁盘,只是簿记说文件的特定部分是空的。 / p>

你可能有过量的数据到你的文件系统(许多文件系统实际上没有在磁盘上分配空间,直到数据从缓冲区缓存刷新,这可能是几秒钟,如果不是几分钟,写完后),其中无论如何写写都会失败。您获得的ENOSPC实际上可能是因为您已经将文件系统填充到超过100%的容量,并且文件系统代码在尝试刷新您之前执行的写入之前没有发现它。

您可能处于日志记录/日志记录文件系统中,在刷新日志之前不会发生实际的块分配,在这种情况下,写入将失败。与缓冲区缓存情况相同的逻辑。

你可能已经耗尽了文件系统上的一些特定预分配元数据,即使它甚至几乎没有,它也会因ENOSPC而失败。这在今天并不像过去那么普遍。

你的磁盘可能已经发现它的某些部分变坏了并且告诉文件系统不使用这些块并占用空间。

简而言之,我们无法保证文件系统的行为就像我们可以天真地认为它一旦充满就会发生。除此之外还有其他原因,永远不会填写95%以上的文件系统。几乎所有的文件系统都差不多完全是不确定的。

答案 1 :(得分:2)

仅仅因为您正在寻找文件的开头并不意味着该文件被截断。可以对文件进行随机写入。

在完整文件系统上写下一个块会导致问题。如果要截断文件,请使用truncate(2)ftruncate调用文件BEFORE lseek

尝试:

    if(errno == ENOSPC) {
        ftruncate(gi4LogFd, 0);
        lseek(gi4LogFd, 0, SEEK_SET);
        break;
    }

好的,支持日记功能的ext3文件系统不会在完整的fs上产生问题:

设定:

创建图像文件:

   dd if=/dev/zero of=/tmp/img.dsk count=8192

在4k图像文件上创建了一个ext3文件系统:

mkfs.ext3 /tmp/img.dsk
sudo mount /tmp/img.dsk /mnt/internal
sudo chown masud.users /mnt/internal

touch /mnt/internal/file.bin 

sudo dd if=/dev/urandom of=/mnt/internal/file.bin

这里dd需要sudo来确保超级用户的保留被填满。

所以现在:

df / mnt / internal / shows:

/dev/loop/0         3963  3963         0 100% /mnt/internal

使用以下代码:

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

char buf[8192];

int main(int argc, char *argv[])
{

    int rv;
    char *filename;

    if ( argc < 2 ) {
         fprintf(stderr, "provide the filename\n");
          return -1;
    }

    filename = argv[1];
    int rd = open("/dev/urandom", O_RDONLY);
    read(rd, buf, sizeof(buf));
    close(rd);

    int fd = open(filename, O_SYNC|O_RDWR);

    lseek(fd, -sizeof(buf), SEEK_END);
    rv = write(fd, buf, sizeof(buf));

    if ( rv < 0 ) {
        perror(filename);
        goto out;
    }
    lseek(fd, 0, SEEK_SET);

    rv = write(fd, "foo", 3);
    if ( rv < 0 ) {
       perror(filename);
    }
out:

   close(fd);
   return rv;


}

现在:     ./foo /mnt/internal/file.bin

成功。

所以问题是这与你的环境有什么不同?