在什么情况下c的write() - 函数写入的数据少于请求的数据?

时间:2012-10-09 08:27:21

标签: java c linux

我正在以大约10b到16000b的块写入文件,当突然每个块被切断大约4050字节(具体地,4050,4051和4074字节,按此顺序)。这意味着后续写入将覆盖应该写入的数据,从而弄乱我的数据。任何4050b以下的大块都写得很好。

不幸的是,我无法重现它。我所拥有的只是混乱的文件,所以我正在调试寻找可能导致这种情况的东西。

调试它,我意识到c的write()函数在引擎盖下调用(这是Java代码,FileChannel.write(),但是标准库调用c的write并且只检查写入的字节> 0),并且它的文档并不能保证它会写出所有要求的数据,只是它会告诉你写了多少。

我不检查在Java土地上写回的字节(但我确实得到它,函数签名几乎完全相同),所以修复很简单。但是,由于我无法重现这一点,我不知道我已经解决了实际问题。因此,我希望有些c guru可以告诉我,我冒烟,或者有合法的情况,write()一次不能写超过4050个字节。

这是在64位Linux内核版本3.0.0-26上运行。

编辑:根据以下评论展开:

我正在写一个普通的文件系统文件。我不确定在这种情况下非阻塞意味着什么,我没有使用回调或任何东西,但明确地告诉操作系统没有为每个块执行刷新到磁盘。该文件使用Java的RandomAccessFile,'rw'模式打开。

3 个答案:

答案 0 :(得分:5)

来自man 2 write

   The number of bytes written may be less than  count  if,  for  example,
   there  is  insufficient space on the underlying physical medium, or the
   RLIMIT_FSIZE resource limit is encountered (see setrlimit(2)),  or  the
   call was interrupted by a signal handler after having written less than
   count bytes.  (See also pipe(7).)

来自man 2 setrlimit

   RLIMIT_FSIZE
          The maximum size of files that the process may create.  Attempts
          to extend a file beyond this  limit  result  in  delivery  of  a
          SIGXFSZ  signal.   By default, this signal terminates a process,
          but a process can catch this signal instead, in which  case  the
          relevant  system  call  (e.g., write(2), truncate(2)) fails with
          the error EFBIG.

使用以下命令可以看到这些限制:

ulimit -Sf
ulimit -Hf

或者使用这个C程序:

#include <stdio.h>
#include <errno.h>
#include <sys/resource.h>

int main()
{
    struct rlimit rl;
    if (getrlimit(RLIMIT_FSIZE, &rl) == 0) {
        printf("%d %d\n", rl.rlim_cur, rl.rlim_max);
    } else {
        fprintf(stderr, "error: %d\n", errno);
    }
    return 0;
}

答案 1 :(得分:4)

从Linux程序员手册,写入(1):“write()将最多计数字节写入文件......”

写入多少取决于您要写入的内容以及是否使用同步写入。

E.g。如果它是一个管道,就像一个套接字,你不能写一个管道/套接字缓冲区满(在同步模式下:比缓冲区中的可用内容更多)。

答案 2 :(得分:3)

重现问题的最简单方法是让消费者放慢速度。当调用write()时,这将导致发送缓冲区几乎已满,这意味着不能写入您提供的所有数据。您可以减少发送和接收器缓冲区大小,以使此问题更加明显。