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



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

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

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

仅仅写入文件的开头并不意味着文件系统将写入磁盘上的同一空间,也不意味着文件开头的空间已分配。

文件中可能有一个洞,在这种情况下,写入无论如何都会失败。漏洞是许多文件系统所做的优化,它们假装文件的一部分在那里,而实际上只有很多零,所以这些部分永远不会写入磁盘,只是在记账,说文件的特定部分是空的。

您可能已经向文件系统过度提交了数据(许多文件系统在从缓冲区缓存中刷新数据之前实际上不会在磁盘上分配空间,这可能需要几秒钟,如果不是几分钟的话),在这种情况下,写入无论如何都会失败。你得到的ENOSPC实际上可能是因为你已经将文件系统填充到了100%以上的容量,而文件系统代码直到它试图刷新你刚才写的内容时才发现它。

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

您可能已经用完了文件系统上某些特定的预分配元数据,即使ENOSPC还没有满,它也会失败。这在今天已经不像过去那么普遍了。

您的磁盘可能已经发现它的某些部分出现了问题,并告诉文件系统不要使用这些块,这会占用空间。

简言之,不能保证文件系统在满了之后会像我们天真地认为的那样运行。除此之外,还有其他原因使文件系统的填充率永远不会超过95%。几乎所有的文件系统在接近满时都是出了名的不确定性。

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

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

尝试:

    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来确保superuser的预留空间已满。

所以现在:

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 filenamen");
          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

成功。

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

最新更新