转自:http://linuxperf.com/?p=116
在Linux系统上,进程运行分为用户态与内核态,进入内核态之后使用的是内核栈,作为基本的安全机制,用户程序不能直接访问内核栈,所以尽管内核栈属于进程的地址空间,但与用户栈是分开的。Linux的内核栈大小是固定的,从2.6.32-520开始缺省大小是16KB,之前的kernel版本缺省大小是8KB。内核栈的大小可以修改,但要通过重新编译内核实现。以下文件定义了它的大小:
arch/x86/include/asm/page_64_types.h
8KB:
#define THREAD_ORDER 1
16KB:
#define THREAD_ORDER 2
由于内核栈的大小是有限的,就会有发生溢出的可能,比如调用嵌套太多、参数太多都会导致内核栈的使用超出设定的大小。内核栈溢出的结果往往是系统崩溃,因为溢出会覆盖掉本不该触碰的数据,首当其冲的就是thread_info — 它就在内核栈的底部,内核栈是从高地址往低地址生长的,一旦溢出首先就破坏了thread_info,thread_info里存放着指向进程的指针等关键数据,迟早会被访问到,那时系统崩溃就是必然的事。
【小知识】:把thread_info放在内核栈的底部是一个精巧的设计,在高端CPU中比如PowerPC、Itanium往往都保留了一个专门的寄存器来存放当前进程的指针,因为这个指针的使用率极高,然而x86的寄存器太少了,专门分配一个寄存器实在太奢侈,所以Linux巧妙地利用了栈寄存器,把thread_info放在内核栈的底部,这样通过栈寄存器里的指针可以很方便地算出thread_info的地址,而thread_info的第一个字段就是进程的指针。
内核栈溢出导致的系统崩溃有时会被直接报出来,比如你可能会看到:
但更多的情况是不直接报错,而是各种奇怪的panic。在分析vmcore的时候,它们的共同点是thread_info被破坏了。以下是一个实例,注意在task_struct中stack字段直接指向内核栈底部也就是thread_info的位置,我们看到thread_info显然被破坏了:cpu的值大得离谱,而且指向task的指针与task_struct的实际地址不匹配:
作为一种分析故障的手段,可以监控内核栈的大小和深度,方法如下:
1
2
|
# mount -t debugfs nodev /sys/kernel/debug
# echo 1 > /proc/sys/kernel/stack_tracer_enabled
|
然后检查下列数值,可以看到迄今为止内核栈使用的峰值和对应的backtrace:
你可以写个脚本定时收集上述数据,有助于找到导致溢出的代码。下面是一个输出结果的实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
# cat /sys/kernel/debug/tracing/stack_max_size
7272
# cat /sys/kernel/debug/tracing/stack_trace
Depth Size Location (61 entries)
----- ---- --------
0) 7080 224 select_task_rq_fair+0x3be/0x980
1) 6856 112 try_to_wake_up+0x14a/0x400
2) 6744 16 wake_up_process+0x15/0x20
3) 6728 16 wakeup_softirqd+0x35/0x40
4) 6712 48 raise_softirq_irqoff+0x4f/0x90
5) 6664 48 __blk_complete_request+0x132/0x140
6) 6616 16 blk_complete_request+0x25/0x30
7) 6600 32 scsi_done+0x2f/0x60
8) 6568 48 megasas_queue_command+0xd1/0x140 [megaraid_sas]
9) 6520 48 scsi_dispatch_cmd+0x1ac/0x340
10) 6472 96 scsi_request_fn+0x415/0x590
11) 6376 32 __generic_unplug_device+0x32/0x40
12) 6344 112 __make_request+0x170/0x500
13) 6232 224 generic_make_request+0x21e/0x5b0
14) 6008 80 submit_bio+0x8f/0x120
15) 5928 112 _xfs_buf_ioapply+0x194/0x2f0 [xfs]
16) 5816 48 xfs_buf_iorequest+0x4f/0xe0 [xfs]
17) 5768 32 xlog_bdstrat+0x2a/0x60 [xfs]
18) 5736 80 xlog_sync+0x1e0/0x3f0 [xfs]
19) 5656 48 xlog_state_release_iclog+0xb3/0xf0 [xfs]
20) 5608 144 _xfs_log_force_lsn+0x1cc/0x270 [xfs]
21) 5464 32 xfs_log_force_lsn+0x18/0x40 [xfs]
22) 5432 80 xfs_alloc_search_busy+0x10c/0x160 [xfs]
23) 5352 112 xfs_alloc_get_freelist+0x113/0x170 [xfs]
24) 5240 48 xfs_allocbt_alloc_block+0x33/0x70 [xfs]
25) 5192 240 xfs_btree_split+0xbd/0x710 [xfs]
26) 4952 96 xfs_btree_make_block_unfull+0x12d/0x190 [xfs]
27) 4856 224 xfs_btree_insrec+0x3ef/0x5a0 [xfs]
28) 4632 144 xfs_btree_insert+0x93/0x180 [xfs]
29) 4488 176 xfs_free_ag_extent+0x414/0x7e0 [xfs]
30) 4312 224 xfs_alloc_fix_freelist+0xf4/0x480 [xfs]
31) 4088 96 xfs_alloc_vextent+0x173/0x600 [xfs]
32) 3992 240 xfs_bmap_btalloc+0x167/0x9d0 [xfs]
33) 3752 16 xfs_bmap_alloc+0xe/0x10 [xfs]
34) 3736 432 xfs_bmapi+0x9f6/0x11a0 [xfs]
35) 3304 272 xfs_iomap_write_allocate+0x1c5/0x3b0 [xfs]
36) 3032 208 xfs_iomap+0x389/0x440 [xfs]
37) 2824 32 xfs_map_blocks+0x2d/0x40 [xfs]
38) 2792 272 xfs_page_state_convert+0x2f8/0x750 [xfs]
39) 2520 80 xfs_vm_writepage+0x86/0x170 [xfs]
40) 2440 32 __writepage+0x17/0x40
41) 2408 304 write_cache_pages+0x1c9/0x4a0
42) 2104 16 generic_writepages+0x24/0x30
43) 2088 48 xfs_vm_writepages+0x5e/0x80 [xfs]
44) 2040 16 do_writepages+0x21/0x40
45) 2024 128 __filemap_fdatawrite_range+0x5b/0x60
46) 1896 48 filemap_write_and_wait_range+0x5a/0x90
47) 1848 320 xfs_write+0xa2f/0xb70 [xfs]
48) 1528 16 xfs_file_aio_write+0x61/0x70 [xfs]
49) 1512 304 do_sync_readv_writev+0xfb/0x140
50) 1208 224 do_readv_writev+0xcf/0x1f0
51) 984 16 vfs_writev+0x46/0x60
52) 968 208 nfsd_vfs_write+0x107/0x430 [nfsd]
53) 760 96 nfsd_write+0xe7/0x100 [nfsd]
54) 664 112 nfsd3_proc_write+0xaf/0x140 [nfsd]
55) 552 64 nfsd_dispatch+0xfe/0x240 [nfsd]
56) 488 128 svc_process_common+0x344/0x640 [sunrpc]
57) 360 32 svc_process+0x110/0x160 [sunrpc]
58) 328 48 nfsd+0xc2/0x160 [nfsd]
59) 280 96 kthread+0x96/0xa0
60) 184 184 child_rip+0xa/0x20
|