分段错误:在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等,而不会造成麻烦; 如果你开始调用系统函数来改变默认的堆栈大小,你将会遇到更多的问题。 (你现在可能没有计划移植它,但计划改变了!)
上一篇: Segmentation fault: Stack allocation in a C program in Ubuntu when bufffer>4M
下一篇: Processes exceeding thread stack size limit on RedHat Enterprise Linux 6?