作者lc85301 (pomelocandy)
看板C_and_CPP
标题Re: [问题] 你所不知道的C语言:函式呼叫篇
时间Wed Mar 15 15:07:10 2023
: ※ 引述《Kuba4ma ()》之铭言:
: https://reurl.cc/ZXWRl3
: 在「从递回观察函式呼叫」这个part,我的理解如下
: 用static int count去统计有多少个stack frame (或是递回执行多少次)
: 在func内增加local variable後,因为stack frame内要多塞4 byte的 y ,所以stack
: frame变大,count变小
: 目前为止还看得懂
: 但是我不懂下面这段为什麽135168要乘以4
: ```
: 60000Hex - 3f000Hex = 21000Hex = 135168Dec
: 135168 * 4 = 540672
: 这跟前面的数字很接近!
: ```
很接近这个词本身就满危险的,我们在处理电脑位址,精确是基本
位址可不是月台会有 9 3/4 位址…,也不是浮点数会有 0.300000004
: 135168不是代表stack大小吗? 乘以4是代表什麽意思
: 看完jserv的影片後还是不懂,麻烦各位解惑,谢谢
: --
:
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 203.204.54.161 (台湾)
: ※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1678592802.A.02B.html
: 推 FanFlyAway: 这里的 4 应该是指每个 int 会占用 4 bytes 03/12 12:13
: → Kuba4ma: 我的理解是524092是进到stack的次数,135168是整个stack 03/12 13:25
: → Kuba4ma: 的大小(单位是byte),但把整个stack大小乘以4的目的是什 03/12 13:25
: → Kuba4ma: 麽?stack的大小不是固定的吗? 03/12 13:25
: 推 KaryuuIssen: 可能写错了吧 我觉得写174697*3=524091接近比较合理 03/12 17:37
: → KaryuuIssen: 毕竟第三个版本的func的stack frame是第一个版本的 03/12 17:38
: → KaryuuIssen: 三倍大 03/12 17:38
: → KaryuuIssen: 而且这里的135168是pid=1程序的stack size 跟跑回圈 03/12 17:40
: → KaryuuIssen: 的程序的stack size没什麽关系 03/12 17:40
: → KaryuuIssen: 修正一下用词 是递回不是回圈 03/12 17:41
: 推 LPH66: 同意楼上, 可能原作者不知道所以把它们随意关连在一起了 03/12 19:58
综合推文,写一下我的理解跟实验结果,欢迎大家指教一下
测试环境 WSL2 / ubuntu 22.04.1
uname -a
$ LTS 5.10.16.3-microsoft-standard-WSL2
我的 infinite.c 的实验结果
(gdb) p count
$1 = 524149
=====
其实一次 stack 占多少不用猜, gdb 就能印 $rsp 了
进 gdb 後,先 start 停在 main 的进入点
(gdb) info inferior
Num Description Connection Executable
* 1 process 4884 1 (native) /tmp/infinite
$ cat /proc/4884/maps | grep stack
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
stack size = 0x21000 倒是一样的,无论 proc 1 或是 infinite
但这是假像,让我们看下去
(gdb) p $rsp
0x7fffffffe750
(gdb) b func
(gdb) c
(gdb) p $rsp
0x7fffffffe740
每次递回所用的 stack size 为 0x10 = 16 bytes
跑到 segmentation fault
(gdb) p $rsp
0x7fffff7ff000
差别在这里,系统的 stack 上限不是 0x21000,而是随执行扩大的
这个时候再去印 /proc/{PID}/maps
7fffff7ff000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
stack = 0x800000 = 8K
这可以透过 ulimit -a 得到证实
$ ulimit -a
...
-s: stack size (kbytes) 8192
...
可以的递回次数为
(0x7fffffffe750 - 0x7fffff7ff000 ) / 0x10 = 524,149
完美
那个 x4 是错误理解吧
1F:推 stupid0319: 这样搞还不如用ollydbg去实际看看程式怎麽跑 03/12 21:17
完了这是什麽我也不会,大大教我怎麽用QQ
: 推 seanwu: 想算stack size要在assembly下看才有意义,不然编译器可能 03/13 00:44
: → seanwu: 都最佳化掉了,另外大部份abi下stack pointer会有不同的al 03/13 00:44
: → seanwu: ignment要求,实际上会占得比local variable多一点 03/13 00:44
结论
1. /proc/1 跟 proc/infinite 开始时的 stack 是一样的 0x21000
但因为 infinite 会递回吃掉一堆 stack,用掉 0x21000 os 会跳进来扩大 stack
直到设定的上限 8192 KB
2. 善用 gdb ,可以直接检查 $rsp, $rbp ,不用瞎猜
3. 514149 来自 8192 KB 扣掉全域变数,每一递回用掉 16 bytes 的结果
原文那个 *4 是误解,希望有人看到能修掉
至於 Linux 怎麽做到动态扩大 process 的 stack ,对不起我举烛我也不懂QQQQ
期待高手回文帮小弟解惑
2F:推 sarafciel: 推 03/15 15:13
※ 编辑: lc85301 (114.37.188.86 台湾), 03/15/2023 15:19:27
3F:推 descent: 类似 page fault 的概念, 当 stack 的 page 用完时, 03/15 16:25
4F:→ descent: 再补上新的 page 给 stack 用。 03/15 16:25
5F:推 saxontai: 原文作者是宅色夫。看他会不会来巡田水时看到这串文吧 03/15 16:59
6F:→ saxontai: 。 03/15 16:59
我觉得这段的原作者不是 jserv,风格不太像
7F:推 nh60211as: 推 03/15 17:54
※ 编辑: lc85301 (36.231.107.106 台湾), 03/15/2023 19:27:33
8F:推 saxontai: 可能吧,看本人会不会现身说法一下 XD 03/15 20:08
9F:推 wulouise: hackmd共笔不一定是jserv本人写 03/21 21:41