【问题标题】:Read and write system calls w/o buffer [closed]没有缓冲区的读写系统调用[关闭]
【发布时间】:2016-08-31 11:41:41
【问题描述】:

我需要从一个文件中读取数据并将其写入 C 中的另一个文件。 我正在使用系统调用open、read 和write。

我担心我使用的缓冲区会导致我丢失一些数据。 我能以某种方式绕过缓冲区吗?

这是负责将数据写入文件的循环:

int main()
{
    unsigned char buffer[250]; 
    ssize_t ret;
    while(!timeIsUp)    
    {
        ret = read(file, &buffer[0], 256);
        if (ret > 0 && ret <= 256)
        {
           write(outpud, &buffer[0], (ssize_t) ret_fd);
        }
    }
}

【问题讨论】:

  • 请注意,int main() 未格式化为代码块的一部分。
  • Erm...你想读取数据,不将其存储在任何地方,然后再写入?此外,是什么让您认为缓冲区使您丢失数据?此外,代码无法编译。发布真实代码,而不是伪代码。
  • 注:250 != 256 ...
  • 您正在将 256 个字节读入 250 个字符的缓冲区。使用sizeof buffer 来避免幻数,或使用#define。
  • 注2:ret_fd != ret

标签: c linux system-calls


【解决方案1】:

您写出(ssize_t) ret_fd 字节,而您读取ret 字节。

这样改写:

write(outpud, &buffer[0], ret);

(更不用说这不是你给出的完整示例,许多变量未定义,文件永远不会打开。请发布真实代码。)

【讨论】:

    【解决方案2】:

    我在您的代码中发现了 2 个逻辑错误(也有编译错误)。

    1. 已声明大小为 250 的缓冲区,您正在尝试读取/写入最多 256。 这可能会导致分段错误。

    2. 对于写入,您使用的 size 为 ret_fd 。应该是ret。

    【讨论】:

    • 对于第一点,在更一般的情况下,它是未定义的行为,所以它可能会导致分段错误,或者没有,是否有副作用,或者任何不可预测的行为
    【解决方案3】:

    您的代码不起作用(许多人对问题发表了评论,有些人已回答),即使已修复,也可能会丢失数据,但不是因为使用了缓冲区。

    考虑以下(未经测试,因此请报告您可能在评论中发现的任何错误,以便我修复它)示例:

    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    
    #ifndef  COPY_BUFFER_SIZE
    #define  COPY_BUFFER_SIZE  262144
    #endif
    
    struct copystatus {
        off_t  bytes;
        int    error;
    };
    
    struct timeout;
    extern int timeout_elapsed(struct timeout *);
    
    int copyfd(const int tofd, const int fromfd,
               struct copystatus *toinfoptr,
               struct copystatus *frominfoptr,
               struct timeout *limit)
    {
        const size_t  size = COPY_BUFFER_SIZE;
        char         *data, *p, *q;
        ssize_t       n;
        struct copystatus toinfo, frominfo;
        int           retval = 0;
    
        toinfo.bytes = 0;
        toinfo.error = 0;
        frominfo.bytes = 0;
        frominfo.bytes = 0;
    
        if (toinfoptr) *toinfoptr = toinfo;
        if (frominfoptr) *frominfoptr = frominfo;
    
        if (tofd == -1 || fromfd == -1)
            return errno = EINVAL;
    
        data = malloc(size);
        if (!data)
            return errno = ENOMEM;
    
        while (1) {
    
            if (timeout_elapsed(limit)) {
                retval = frominfo.error = ETIMEDOUT;
                break;
            }
    
            n = read(fromfd, data, size);
            if (n == -1) {
                /* Interrupted by a signal? Timeout? */
                if (errno == EINTR)
                    continue;
                /* No, an error occurred. */
                retval = frominfo.error = errno;
                break;
    
            } else
            if (n == 0)
                break; /* No more input. */
            else
            if (n < -1) {
                /* Library/kernel error. Might occur if size > 2GB. */
                retval = frominfo.error = EIO;
                break;
            }
    
            frominfo.bytes += (off_t)n;
    
            p = data;
            q = data + n;
            while (p < q) {
    
                if (timeout_elapsed(limit)) {
                    retval = toinfo.error = ETIMEDOUT;
                    break;
                }
    
                n = write(tofd, p, (size_t)(q - p));
                if (n == -1) {
                    if (errno == EINTR)
                        continue;
                    retval = toinfo.error = errno;
                    break;
                } else
                if (n < 1) {
                    /* Should not occur. */
                    retval = toinfo.error = EIO;
                    break;                 
                }
    
                p += n;
                toinfo.bytes += (off_t)n;
            }
            if (retval)
                break;
        }
    
        free(buffer);
    
        if (toinfoptr) *toinfoptr = toinfo;
        if (frominfoptr) *frominfoptr = frominfo;
    
        return errno = retval;
    }
    

    上述代码从一个描述符读取数据,将其写入另一个描述符,并进行完整的错误检查。如果成功则返回 0,否则返回 errno 错误代码。 (请注意,将返回值分配给errno 并不常见;这只是我自己的风格,只有在使用时返回值始终是有效的errno 值。)

    如果非 NULL,则两个复制状态结构将填充复制的字节数,以及特定于该描述符的错误代码。特别是,如果发生超时,则只有其中一个error 字段将设置为ETIMEDOUT。 (这是一个POSIX.1错误代码,“连接超时”,在C89/C99/C11中没有定义。)

    该函数旨在与使用信号传递(到空信号处理程序)来中断阻塞 I/O 调用的超时机制一起使用。 (在这种情况下,调用将返回-1 和errno == EINTR。)它不适用于非阻塞套接字;在这种情况下,select() 循环会更好,并使用 gettimeofday() 或 POSIX.1 clock_gettime() 来获取实际时间,在内部计算复制操作的经过时间,而无需外部超时检查器功能。在单个函数中混合阻塞/非阻塞操作是可能的,但非常混乱,难以维护和理解,因此强烈建议不要这样做。 (可以使用fcntl(descriptor,F_GETFL) &amp; O_NONBLOCK 来查看描述符是否设置为非阻塞。)

    当fromfd 引用本地文件系统上的文件时,所有读取都将返回请求的大小,-1 表示错误,或者文件末尾的短计数或零。当tofd 引用本地文件系统上的文件时,所有写入都将写入并返回请求的数量,除非发生错误,在这种情况下它们将返回-1。这确实不意味着人们应该随意假设这种情况很普遍;事实上,本地文件行为是个例外。

    在 Linux 和所有 POSIXy 系统中,字符设备和伪终端可以被视为文件,无需应用程序进行任何特殊处理。然而,那些返回短读的情况常见,有时甚至是短写。

    此外,虽然recv*() 和send*() 系列函数通常与套接字描述符一起使用,但也可以很容易地使用read() 和write()。对套接字的读取和写入返回短计数是很常见的,有时甚至会出现错误(例如,网络中断),尽管真正的长阻塞更为常见(例如,由于默认 TCP 超时较长)。

    这就是为什么上面的示例代码没有假设读写行为以任何特定方式进行。它不是最优的,因为它总是交错读取和写入,并假定阻塞描述符。如果您在现实生活中需要这样的功能,您可能会复制多个文件(并行或顺序),并使用基于select() 的非阻塞方法同时复制多个文件,这可能会更可取对于应用程序的用户,尤其是当目标文件位于不同的挂载点时。 (如果每个目标文件位于不同的挂载点,或者是字符设备或套接字,则并行方法往往更快;如果目标是文件并且位于相同的挂载点,则顺序方法更快。)

    【讨论】:

      【解决方案4】:

      高中问题,但在这里:

      #include <sys/types.h>
      #include <sys/stat.h>
      #include <fcntl.h>
      #include <unistd.h>
      
      int main(void)
      {
          char buf[2] = {'\0'};
          int readfd;
          int writefd;
      
          readfd = open("myfile", O_RDONLY);
          writefd = open("myfile1", O_WRONLY | O_CREAT);
      
          while (read(readfd, &buf, 1) != 0)
          {
                  write(writefd, buf, 1);
          }
      
          close(writefd);
          close(readfd);
          return (0);
      }
      

      readfd 是您要从中读取的文件。 writefd 是您要写入的文件。

      没有错误测试或清理。

      【讨论】:

        猜你喜欢
        • 2021-07-06
        • 1970-01-01
        • 1970-01-01
        • 2012-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多