Why not C/C++?
前置知识:计算机的堆栈结构
关于虚拟内存的部分解释:
text 存放程序的可执行代码。这一段通常是可读的。
.data 存放程序员初始化的静态/全局变量。比如:static int a = 3定义的变量a会存储在数据段中。
.bss 存放未初始化的静态/全局变量。操作系统将会使用0来填充这个段,所以所有没有初始化的变量都会被初始化为0。比如static int a。
heap:用于动态的内存分配,和malloc,calloc,realloc,free等函数相关。
stack: 用于存放函数内定义的局部变量,或者函数调用相关的数据,比如返回地址和参数等。
int x = 100; //data
int main()
{
// data stored on stack
int a=2;
float b=2.5;
//经过我的测试发现y在.bss
// 测试方法objdump -t a.out
static int y;
// allocate memory on heap
int *ptr = (int *) malloc(2*sizeof(int));
// values 5 and 6 stored on heap
ptr[0]=5;
ptr[1]=6;
// deallocate memory on heap
free(ptr);
return 1;
}
关于stack的结构的解释:
写一个C语言程序,超过变量分配超过堆栈大小?PS:在默认编译下每个线程的堆栈的大小是8192kB
运行下面程序会出现segmentation fault
#include <stdio.h>
#define STACK_SIZE 20000000 // 假设每个int占用4字节,这将使用大约80000KB的栈空间
int main() {
int stack[STACK_SIZE];
// 尝试初始化所有数组元素
for (int i = 0; i < STACK_SIZE; i++) {
stack[i] = i;
}
// 打印最后一个元素以确认是否达到数组末尾
printf(Last element: %d\n, stack[STACK_SIZE - 1]);
return 0;
}
解决思路
使用 -Wl,-z,stack-size
链接选项
关于链接选项的解析:
- -Wl:pass options to the linker,将后面的所有内容交给链接器而不是交给编译器
- -z:链接器的一个选项前缀,用于指定多种不同的链接器行为
- stack-size:指定stack大小
gcc -o my_program my_program.c -Wl,-z,stack-size=SIZE
使用 ulimit
:
ulimit -s 8192 # 设置堆栈大小为8MB
gcc -o my_program my_program.c
其实只要编译完成,在运行前使用ulimit就能正常运行。
使用ulimit -a可以查看所有的限制
> ulimit -a
-t: cpu time (seconds) unlimited
-f: file size (blocks) unlimited
-d: data seg size (kbytes) unlimited
-s: stack size (kbytes) 8192
-c: core file size (blocks) 0
-m: resident set size (kbytes) unlimited
-u: processes 63147
-n: file descriptors 1024
-l: locked-in-memory size (kbytes) 2027812
-v: address space (kbytes) unlimited
-x: file locks unlimited
-i: pending signals 63147
-q: bytes in POSIX msg queues 819200
-e: max nice 0
-r: max rt priority 0
-N 15: rt cpu time (microseconds) unlimited
两种方法的区别
ulimit
:
调用 getrlimit
和 setrlimit
: 根据用户输入的选项,shell 会调用适当的系统调用来获取或设置资源限制。这里就不详细展开了,主要是本人技术不到家。(最近会恶补的,别骂了,别骂了)
gcc:
编译时处理:
- GCC 将
-Wl,-z,stack-size=SIZE
选项传递给链接器(ld
)。链接器会将这个选项解释为设置线程堆栈大小的指令。
嵌入元数据:
- 链接器会在生成的可执行文件的元数据中记录这个堆栈大小设置。这些元数据在程序运行时被加载到内存中,用于指导线程的创建和管理。
运行时应用:
- 当程序运行并创建新线程时,C 库(如 glibc)的线程创建函数(如
pthread_create
)会读取这个元数据,并根据指定的堆栈大小 分配堆栈空间。这意味着每个线程在创建时会自动使用这个堆栈大小,而不需要显式地在代码中设置。
可能产生的BUG:缓冲区溢出
buffer_overflow_c(目前没上传)