Loading [MathJax]/extensions/tex2jax.js

Lecture 1: Safety in Systems Programming | Stanford CS 110L

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:

调用 getrlimitsetrlimit: 根据用户输入的选项,shell 会调用适当的系统调用来获取或设置资源限制。这里就不详细展开了,主要是本人技术不到家。(最近会恶补的,别骂了,别骂了)

gcc:

编译时处理

  • GCC 将 -Wl,-z,stack-size=SIZE 选项传递给链接器(ld)。链接器会将这个选项解释为设置线程堆栈大小的指令。

嵌入元数据

  • 链接器会在生成的可执行文件的元数据中记录这个堆栈大小设置。这些元数据在程序运行时被加载到内存中,用于指导线程的创建和管理。

运行时应用

  • 当程序运行并创建新线程时,C 库(如 glibc)的线程创建函数(如 pthread_create)会读取这个元数据,并根据指定的堆栈大小 分配堆栈空间。这意味着每个线程在创建时会自动使用这个堆栈大小,而不需要显式地在代码中设置。

可能产生的BUG:缓冲区溢出

buffer_overflow_c(目前没上传)

Why not GC\’ed languages(Java ,Python,Go,etc.)