单个pread可以获得的最大大小

时间:2014-03-23 23:11:35

标签: c linux posix

我使用pread一次获取大量数据。

但如果我尝试收集大量数据(例如100mb)并将其保存到数组中,我会得到一个段错误....

pread可以读取的最大字节数是否有硬限制?

#define          _FILE_OFFSET_BITS                            64 
#define          BLKGETSIZE64                                _IOR(0x12,114,size_t)
#define          _POSIX_C_SOURCE                             200809L

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

int readdata(int fp,uint64_t seekpoint, uint64_t seekwidth) {
    int16_t  buf[seekwidth];

    if (pread(fp,buf,seekwidth,seekpoint)==seekwidth) {
        printf("SUCCES READING AT: %"PRIu64"| WITH READ WIDTH: %"PRIu64"\n",seekpoint,seekwidth);
        return 1;
    } else {
        printf("ERROR READING AT: %"PRIu64"| WITH READ WIDTH: %"PRIu64"\n",seekpoint,seekwidth);
        return 2;
    }

}





int main() {

    uint64_t    readwith,
                offset;
    int         fp=open("/dev/sdc",O_RDWR);

    readwith=10000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=100000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=1000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=10000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=10000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=100000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=1000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=10000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=100000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=1000000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=10000000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=100000000000000;   offset=0;
    readdata(fp,offset,readwith);
    readwith=1000000000000000;   offset=0;
    readdata(fp,offset,readwith);

    close(fp);

}

1 个答案:

答案 0 :(得分:3)

pread可以读取的最大字节数没有硬性限制。但是,在一个连续的块中读取大量数据可能是一个坏主意。我稍后会介绍几种替代方案。

在您的特定情况下,问题可能是您尝试堆栈分配缓冲区。堆栈的可用空间有限;如果你运行cat /proc/<pid>/limits,你可以看到特定进程的内容(或只是cat /proc/self/limits来检查你正在运行的shell)。在我的情况下,恰好是8388608,或8 MB。如果您尝试使用超过此限制,则会出现段错误。

您可以使用setrlimit或特定于Linux的prlimit来增加最大堆栈大小,但这通常不被认为是好事;您的堆栈是永久分配给每个线程的东西,因此增加大小会增加每个线程分配给它的地址空间。在32位系统上(相关性较低,但仍然存在32位系统,或64位系统上的32位应用程序),这个地址空间实际上相当有限,所以只有少量线程有大量的分配的堆栈空间可能耗尽您的地址空间。采取替代方法会更好。

一种替代方法是使用malloc动态分配缓冲区。然后你只需要在需要时使用这个空间,而不是整个堆栈的所有时间。是的,您之后必须记住free数据,但在您的编程中经过一些细心的考虑并不是那么难。

另一种对大量数据有用的方法是使用mmap将文件映射到地址空间,而不是尝试将整个内容读入缓冲区。这样做是为了分配一个地址空间区域,并且只要您访问该地址空间,就会从该文件中读取数据以填充您正在读取的页面。当你想要随机访问文件时,这可能非常方便,但实际上并不是在阅读整个文件,而是会跳过文件。只会读取您实际访问的页面,而不是浪费时间将整个文件读入缓冲区,然后只访问部分文件。

如果您使用mmap,之后需要记住munmap地址空间,但如果您使用的是64位系统,那么如果您记得{{{{ 1}}如果你记得munmap已经分配了内存(在32位系统上,地址空间实际上是非常宝贵的,所以留下大的映射仍然会导致问题)。 free只会占用地址空间,而不会占用实际内存;因为它有一个文件支持,如果有内存压力,内核可以将任何脏数据写入磁盘并停止将内容保留在内存中,而对于分配的缓冲区,它需要实际保存RAM或交换空间中的数据,这通常是相当有限的资源。如果您只是使用它来读取数据,它甚至不必将脏数据刷新到磁盘,它可以释放页面并重新使用它,如果再次访问页面,它将读取它回来了。

如果您不需要一次性随机访问所有数据,那么在循环中以较小的块读取和处理数据可能会更好。然后,您可以使用堆栈分配来简化,而不必担心增加分配给堆栈的地址空间量。

编辑添加:根据您的示例代码和其他问题,您似乎尝试将整个2TB磁盘读取为单个阵列。在这种情况下,您肯定需要使用mmap,因为您可能没有足够的RAM来将整个内容保存在内存中。这是一个例子;请注意,此特定示例特定于Linux:

mmap