【问题标题】:Partial write()部分写入()
【发布时间】:2022-01-03 20:09:25
【问题描述】:

我似乎无法让部分 write() 工作。它从内存中消失了,我不知道为什么。

int fd = open(path, O_RDONLY);
if(fd == -1) {error handling}

const size_t read_size = 100;
size_t size = read_size;
size_t offset = 0;
size_t res = 0;
char *buff = malloc(size+1);
int lines = 0;
int pos = 0;

while((res = read(fd, buff + offset, read_size)) > 0)
    {
        if(res == -1){error handling}
        offset += res;
        buff[offset] = '\0';
        if (offset + read_size > size)
        {
                size *= 2;
                buff = realloc(buff, size+1);
        }
    }

for(size_t i = 0;buff[i] != '\0'; i++) // counting the buff lines
    {
        if(buff[i] == '\n')
        {
            lines++;
        }
    }

size = read_size;
offset = 0;
res = 0;

if(lines < 10)
{
    while((res = write(STDOUT_FILENO, buff+offset, read_size)) > 0)
    {
        offset += res;
    }
}
buff[offset] = '\0';
else{another case where the position is found where the write() needs to start printing}

这是 c 中尾部实现的一部分。还有另一个函数处理标准输入并做同样的事情(这个处理文件)。

【问题讨论】:

  • 部分写入很少发生。一种可能发生的方法是,如果您尝试将 4 KiB 的数据写入一个只剩下 1 KiB 空间的文件系统。其他方式也同样神秘。
  • 部分写入是什么意思,代码具体应该做什么?
  • 整个程序应该是函数尾部。然而,一个要求是我支持部分读/写。我的程序在 for 循环和逐字节打印时工作得非常好,但不能让它与部分 write() 一起工作。在 buff 中存储文件或标准输入数据,需要打印。所以我需要将其部分写入()到标准输出。
  • 为什么要在部分写入时增加缓冲区大小。如果有的话,它应该缩小
  • 请用文字描述第二个while 循环应该做什么?如果行数小于10,是否应该回显(写回)所有读取的数据?在这种情况下,为什么要在第二个 while 循环中调整缓冲区的大小?这没有意义。

标签: c buffer stdout write


【解决方案1】:

这可能是这样的:

// Returns 0 on success.
// Returns -1 and sets errno on error.
int write_full(int fd, void *a_buf, size_t count) {
   const char *buf = (char *)a_buf;

   while ( count > 0 ) {
      ssize_t chunk_size = write(fd, buf, count);
      if ( chunk_size < 0 )
         return -1;

      buf   += chunk_size;
      count -= chunk_size;
   }

   return 0;
}

测试很棘手。当使用非阻塞句柄写入具有阻塞消费者的管道时,我只能生成部分写入。

但这会导致错误EAGAINEWOULDBLOCK,因此如果我们临时添加代码以立即重试(这在实践中会很糟糕),我们可以看到部分写入工作。

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

// Returns 0 on success.
// Returns -1 and sets errno on error.
int write_full(int fd, void *a_buf, size_t count) {
   const char *buf = (char *)a_buf;

   while ( count > 0 ) {
      ssize_t chunk_size = write(fd, buf, count);
      if ( chunk_size < 0 && ( errno == EAGAIN || errno == EWOULDBLOCK ) ) continue;  // DEBUG
      if ( chunk_size < 0 )
         return -1;

      fprintf(stderr, "Wrote %zd\n", chunk_size);  // DEBUG
      buf   += chunk_size;
      count -= chunk_size;
   }

   return 0;
}

int main(void) {
   int fd = STDOUT_FILENO;
   fcntl(fd, F_SETFL, O_NONBLOCK);  // Make non-blocking

   const size_t n = 100000;
   char *buf = malloc(n);
   if (!buf) {
      perror("Can't allocate memory");
      exit(1);
   }

   for (size_t i=n; i--; )
      buf[i] = 'x';

   if ( write_full(fd, buf, n) < 0 ) {
      perror("Write error");
      exit(1);
   }

   free(buf);
   return 0;
}
$ gcc -Wall -Wextra -pedantic a.c -o a && ./a | perl -e'print while <>' >/dev/null
Wrote 65536
Wrote 8192
Wrote 8192
Wrote 16384
Wrote 1696

Perl 的加载时间比 C 程序的加载时间长,允许 64 KiB 管道缓冲区填满。您可以确保将sleep 2; 添加到 Perl 程序的开头。

Perl 读取 8 KiB 块,并且这样做所花费的时间比 C 程序写入所需的时间要长,因此 C 程序不断地耗尽管道缓冲区中的空间。

【讨论】:

  • 什么是a_buf和to_write?
  • 包含要写入的数据的缓冲区。要写入的字节数。有关示例,请参阅随附的测试程序。
  • 刚刚运行代码,循环似乎没有结束。我把它写到 STDOUT_FILENO 而不是 fd
  • STDOUT_FILENO1
  • 当我使用 gcc -Wall -pedantic -std=c11 file.c 运行它时,它以 Aborted (core dumped) 结束
猜你喜欢
  • 2015-10-22
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多