分段错误:在buff> 4M时,在Ubuntu的C程序中堆栈分配

这是一个大学任务的小程序:

#include <unistd.h>

#ifndef BUFFERSIZE
#define BUFFERSIZE 1
#endif

main()
{
    char buffer[BUFFERSIZE];
    int i;
    int j = BUFFERSIZE;

    i = read(0, buffer, BUFFERSIZE);

    while (i>0)
    {
        write(1, buffer, i);
        i = read(0, buffer, BUFFERSIZE);
    }

    return 0;
}

有一个替代方案使用stdio.h fread和fwrite函数。

好。 我编译了这两个版本的程序与25个不同的缓冲区大小值:1,2,4,...,2 ^我与我= 0..30

这是我编译它的一个例子:gcc -DBUFFERSIZE = 8388608 prog_sys.c -o bin / psys.8M

问题:在我的机器上(Ubuntu Precise 64,最后有更多细节)程序的所有版本都可以正常工作:./psys.1M <data

(数据是一个带有3行ascii文本的小文件。)

问题是:当缓冲区大小为8MB或更大时。 两个版本(使用系统调用或clib函数)都会使用这些缓冲区大小(分段错误)崩溃。

我测试了很多东西。 第一个版本的代码如下所示:(...)main(){char buffer [BUFFERSIZE]; int i;

    i = read(0, buffer, BUFFERSIZE);
(...)

当我调用read函数时会崩溃。 但是对于这些版本:

main()
{
    char buffer[BUFFERSIZE]; // SEGMENTATION FAULT HERE
    int i;
    int j = BUFFERSIZE;

    i = read(0, buffer, BUFFERSIZE);


main()
{
    int j = BUFFERSIZE; // SEGMENTATION FAULT HERE
    char buffer[BUFFERSIZE];
    int i;

    i = read(0, buffer, BUFFERSIZE);

他们都在主要的第一行崩溃(SEGFAULT)。 但是,如果我将缓冲区从主域移出到全局范围(因此,在堆中分配而不是堆栈),这可以正常工作:

char buffer[BUFFERSIZE]; //NOW GLOBAL AND WORKING FINE
main()
{
    int j = BUFFERSIZE;
    int i;

    i = read(0, buffer, BUFFERSIZE);

我使用Ubuntu Precise 12.04 64位和Intel i5 M 480第一代。

#uname -a
Linux hostname 3.2.0-34-generic #53-Ubuntu SMP Thu Nov 15 10:48:16 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

我不知道操作系统对堆栈的限制。 有没有办法在堆栈中分配大数据,即使这不是一个好的实践?


Linux中的堆栈大小通常是有限的。 命令ulimit -s将给出以千字节为单位的当前值。 您可以更改(通常)文件/etc/security/limits.conf的默认值。 您还可以根据权限通过代码按照每个进程对其进行更改:

#include <sys/resource.h>
// ...
struct rlimit x;
if (getrlimit(RLIMIT_STACK, &x) < 0)
    perror("getrlimit");
x.rlim_cur = RLIM_INFINITY;
if (setrlimit(RLIMIT_STACK, &x) < 0)
    perror("setrlimit");

我认为这很简单:如果你试图让缓冲区太大,它会溢出堆栈。

如果你使用malloc()来分配缓冲区,我敢打赌你没有问题。

请注意,有一个名为alloca()的函数显式分配堆栈存储。 使用alloca()与声明堆栈变量几乎是一样的,除非它在程序运行时发生。 阅读alloca()的手册页或讨论它可能会帮助您了解您的程序出了什么问题。 这是一个很好的讨论:

为什么使用alloca()不被认为是好的做法?

编辑:在评论中,@jim mcnamara向我们介绍了ulimit ,一种可以检查用户限制的命令行工具。 在这台计算机(运行Linux Mint 14,因此应该与Ubuntu 12.10相同) ulimit -s命令显示堆栈大小限制为:8192 K字节,可以很好地跟踪您描述的问题。

编辑:如果它不完全清楚,我建议你通过调用malloc()解决问题。 另一个可以接受的解决方案是静态分配内存,只要你的代码是单线程的,这个工作就可以正常工作。

你应该只对小缓冲区使用堆栈分配,这正是因为你不想炸掉你的堆栈。 如果你的缓冲区很大,或者你的代码会多次递归地调用一个函数,这样小的缓冲区会被分配许多次,那么你将会破坏你的栈并且你的代码会崩溃。 最糟糕的是,你不会得到任何警告:它会好起来的,或者你的程序已经崩溃了。

malloc()的唯一缺点是它比较慢,所以你不想在时间关键的代码中调用malloc() 。 但是对于初始设置来说很好; 一旦malloc完成,你分配的缓冲区的地址就像内存中的地址一样,就像程序地址空间中的任何其他内存地址一样。

我特别建议不要编辑系统默认值以使堆栈大小更大,因为这会使您的程序更加便于携带。 如果您调用标准C库函数(如malloc() ,则可以将代码移植到Windows,Mac,Android等,而不会造成麻烦; 如果你开始调用系统函数来改变默认的堆栈大小,你将会遇到更多的问题。 (你现在可能没有计划移植它,但计划改变了!)

链接地址: http://www.djcxy.com/p/80285.html

上一篇: Segmentation fault: Stack allocation in a C program in Ubuntu when bufffer>4M

下一篇: Processes exceeding thread stack size limit on RedHat Enterprise Linux 6?