读取和写入没有缓冲区的系统调用

时间:2016-08-31 11:41:41

标签: c linux system-calls

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

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

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

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);
        }
    }
}

4 个答案:

答案 0 :(得分:1)

当您读取(ssize_t) ret_fd个字节时,您会写出ret个字节。

像这样改写:

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

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

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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

#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错误代码,&#34;连接超时&#34;,未在C89 / C99 / C11中定义。)

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

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

在Linux和所有POSIXy系统中,可以将字符设备和伪终端视为文件,而无需对应用程序进行任何特殊处理。但是,对于那些返回短读取,偶尔甚至是短写入的人来说,共同

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

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

答案 3 :(得分:0)

高中问题,但在这里:

#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是您要写入的文件。

没有错误测试或清理。