Crash工具实战-变量解析【转】
转自:http://blog.chinaunix.net/uid-14528823-id-4358785.html
Crash工具用于解析Vmcore文件,Vmcore文件为通过kdump等手段收集的操作系统core dump信息,在不采用压缩的情况下,其相当于整个物理内存的镜像,所以其中包括了最全面、最完整的信息,对于分析定位各种疑难问题有极大的帮助。配置kdump后,在内核panic后,会自动进入kump流程,搜集Vmcore。
Crash工具即为专门用于分析vmcore文件的工具,其中提供了大量的实用分析命令,极大的提高了vmcore的分析效率。
在分析vmcore的过程中,常常需要解析内核某个流程中的关键变量的值,以便确认故障当时系统的状态,本文主要介绍变量的解析方法,主要分全局变量和局部变量两种情况。
1、全局变量解析非常简单,可通过在crash中直接p <变量名>打印,如:
点击(此处)折叠或打开
- crash> p jiffies
- jiffies = $3 = 5540265294
2、局部变量的解析比较复杂。
Vmcore搜集的仅为故障当时内存使用情况的一个快照,是静态信息,无法进行动态调试(虽然听说可以,但没见过~~),对于某个进程而言,在Vmcore中能发掘的进程上下文信息,通常只有堆栈和寄存器的值。而我们了解,通常局部变量在栈中分配,但也可能直接使用寄存器保存,所以可以(也只能)通过“寻找局部变量跟堆栈或寄存器的关系”来解析局部变量的值。所以这里分两种情况:
1)位于栈中的局部变量
这种情况比较常见,此时,局部变量必然位于某一级函数的堆栈中,该局部变量可能通过指针一级级向底层函数传递,所以可能位于多个函数的堆栈中,可以从不同的函数堆栈中解析。但解析会比较困难,难点在于难以确认相关变量在堆栈中的具体位置,解析方法很灵活,需要结合相关源代码,仔细分析流程,找到关键的点,更多的取决于分析者的经验和代码理解能力。
如下以实例说明解析过程:
vmcore中某阻塞的进程有如下的堆栈:
点击(此处)折叠或打开
- crash> bt 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
- #2 [ffff8805f3a235a0] __down at ffffffff814fa992
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
- #7 [ffff8805f3a23700] xfs_buf_read at ffffffffa0523e4c [xfs]
- #8 [ffff8805f3a23730] xfs_trans_read_buf at ffffffffa0519a98 [xfs]
- #9 [ffff8805f3a23780] xfs_read_agf at ffffffffa04cfd26 [xfs]
- #10 [ffff8805f3a237c0] xfs_alloc_read_agf at ffffffffa04cfe99 [xfs]
- #11 [ffff8805f3a237f0] xfs_alloc_fix_freelist at ffffffffa04d28a1 [xfs]
- #12 [ffff8805f3a238d0] xfs_alloc_vextent at ffffffffa04d2e16 [xfs]
可以看出,进程阻塞在信号量上,需要解析如下函数中xfs_buf_t *bp变量的值,以确认其中bp->b_sema信号量的状态。
点击(此处)折叠或打开
- void
- xfs_buf_lock(
- xfs_buf_t *bp)
- {
- trace_xfs_buf_lock(bp, _RET_IP_);
- if (atomic_read(&bp->b_io_remaining))
- blk_run_address_space(bp->b_target->bt_mapping);
- down(&bp->b_sema);
- XB_SET_OWNER(bp);
- trace_xfs_buf_lock_done(bp, _RET_IP_);
- }
该变量是通过入参从上级函数传入的,而跟踪上级函数会发现其为在上级函数_xfs_buf_find中分配的局部变量,解析方法有两种:
A、在上级函数中通过该变量与堆栈的关系解析
bp变量是在上级函数_xfs_buf_find定义局部变量,那么其通常会在该级栈中分配空间。但难点还在于如何确认该变量在堆栈中的位置,关键在于找到“寄存器和堆栈关联”的地方,还得分析关键代码流程:
点击(此处)折叠或打开
- _xfs_buf_find(
- xfs_buftarg_t *btp, /* block device target */
- xfs_off_t ioff, /* starting offset of range */
- size_t isize, /* length of range */
- xfs_buf_flags_t flags,
- xfs_buf_t *new_bp)
- {
- xfs_off_t range_base;
- size_t range_length;
- xfs_bufhash_t *hash;
- xfs_buf_t *bp, *n;
- range_base = (ioff << BBSHIFT);
- range_length = (isize << BBSHIFT);
- ...
- found:
- spin_unlock(&hash->bh_lock);
- /* Attempt to get the semaphore without sleeping,
- * if this does not work then we need to drop the
- * spinlock and do a hard attempt on the semaphore.
- */
- if (down_trylock(&bp->b_sema)) {
- if (!(flags & XBF_TRYLOCK)) {
- /* wait for buffer ownership */
- xfs_buf_lock(bp);
- XFS_STATS_INC(xb_get_locked_waited);
- ...
可以看到,bp是作为xfs_buf_lock函数的入参传入的,那这里应该会通过寄存器或其它方式进行传参,则必然会对bp所在的堆栈位置进行操作,由此应能找到bp在堆栈中的位置。
反汇编相关代码:
点击(此处)折叠或打开
- crash> dis -l _xfs_buf_find
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 431
- 0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
- 0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
- 0xffffffffa05234f4 <_xfs_buf_find+4>: push %r15
- 0xffffffffa05234f6 <_xfs_buf_find+6>: push %r14
- ...
- 0xffffffffa05235e6 <_xfs_buf_find+246>: mov %rcx,%rdi
- 0xffffffffa05235e9 <_xfs_buf_find+249>: mov %rcx,-0x58(%rbp) //可以看出rbp偏移0x58即为入参bp的值
- 0xffffffffa05235ed <_xfs_buf_find+253>: callq 0xffffffffa05233e0 <xfs_buf_lock> //此处调用xfs_buf_lock
- ...
找到调用xfs_buf_lock函数的地方,在此之前准备入参,操作了堆栈-0x58(%rbp) ,可以看出rbp偏移0x58即为入参bp的值
再看看堆栈中-0x58(%rbp)中的内容具体是啥:
点击(此处)折叠或打开
- crash> bt -f 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "xxx"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
- ffff8805f3a23440: 0000000181164d0e ffff880c20518300
- ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
- ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
- ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
- ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
- ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
- ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
- ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
- ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
- ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
- ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
- ffff8805f3a234f0: ffffffff814f9a6d
- ...
#5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- ffff8805f3a23658: ffff8805f563e600
ffff8802ecd78480
- ffff8805f3a23668: 0001400500000000 ffff8805f563e690
- ffff8805f3a23678: ffff8805f3a236b8 0000000000000001
- ffff8805f3a23688: ffff8805f3a21af8 ffff8808fe07c0c0
- ffff8805f3a23698: 0000000000014005 000000003a382231
- ffff8805f3a236a8: 0000000000000001 ffff8805f563e0c0
- rsp--> ffff8805f3a236b8: ffff8805f3a236f8 ffffffffa05237db
我们知道:
(1)栈的地址是向低地址延伸的,也就是说压栈时,sp(栈顶地址)减小。
(2)第一个压栈的上级函数的返回地址,所以其中的ffffffffa05237db为上级函数的返回地址(从上述堆栈中可以明显看出~)
在_xfs_buf_find()函数反汇编的第一句,就对rbp(即上级堆栈栈帧指针)进行了压栈,所以ffff8805f3a236f8为rbp。
0xffffffffa05234f0 <_xfs_buf_find>: push %rbp
此时的rsp应该就是ffff8805f3a236b8:接下来:
0xffffffffa05234f1 <_xfs_buf_find+1>: mov %rsp,%rbp
那么此时的rbp也就等于ffff8805f3a236b8,那么rbp-0x58就是ffff8802ecd78480即为我们苦苦寻找的bp指针了!!
通过如下命令验证下该bp指针的内容,其类型为xfs_buf_t结构体:
点击(此处)折叠或打开
- crash> struct xfs_buf_t ffff8802ecd78480
- struct xfs_buf_t {
- b_rbnode = {
- rb_parent_color = 18446612143895321536,
- rb_right = 0x0,
- rb_left = 0x0
- },
- b_file_offset = 500099736064,
- b_buffer_length = 512,
- b_hold = {
- counter = 6
- },
- b_lru_ref = {
- counter = 3
- },
- b_flags = 3145844,
- b_sema = {
- lock = {
- raw_lock = {
- slock = 151718155
- }
- },
- count = 0,
- wait_list = {
- next = 0xffff88008c3f38c8,
- prev = 0xffff8805f3a235a8
- }
- },
内容输出正常,应说明解析是正确的。
从该结构体内容可以看出,该xfs_lock被其它进程占用了,且等待队列中有进程正在等待该锁,进一步分析wait_list可以得到每个等待进行的相关信息,这里不再赘述具体方法。
B、在本级或下级函数中通过该变量与堆栈的关系解析
解析关键在于:需要找到引用该变量的关键点,比如这里的down(&bp->b_sema)函数,以bp变量所为入参,那么就必然会对该变量进行操作,比如通过寄存器传参到down函数中,在此可以寻找到蛛丝马迹。
首先,需要对xfs_buf_lock函数进行反汇编:
点击(此处)折叠或打开
- crash> dis -l xfs_buf_lock
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 933
- 0xffffffffa05233e0 <xfs_buf_lock>: push %rbp
- 0xffffffffa05233e1 <xfs_buf_lock+1>: mov %rsp,%rbp
- 0xffffffffa05233e4 <xfs_buf_lock+4>: sub $0x20,%rsp
- 0xffffffffa05233e8 <xfs_buf_lock+8>: mov %rbx,-0x18(%rbp)
- 0xffffffffa05233ec <xfs_buf_lock+12>: mov %r12,-0x10(%rbp)
- 0xffffffffa05233f0 <xfs_buf_lock+16>: mov %r13,-0x8(%rbp)
- 0xffffffffa05233f4 <xfs_buf_lock+20>: nopl 0x0(%rax,%rax,1)
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_trace.h: 324
- 0xffffffffa05233f9 <xfs_buf_lock+25>: mov 0x29768(%rip),%r13d # 0xffffffffa054cb68 <__tracepoint_xfs_buf_lock+8>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 934
- 0xffffffffa0523400 <xfs_buf_lock+32>: mov 0x8(%rbp),%r12
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 933
- 0xffffffffa0523404 <xfs_buf_lock+36>: mov %rdi,%rbx
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_trace.h: 324
- 0xffffffffa0523407 <xfs_buf_lock+39>: test %r13d,%r13d
- 0xffffffffa052340a <xfs_buf_lock+42>: jne 0xffffffffa05234bb <xfs_buf_lock+219>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/arch/x86/include/asm/atomic_64.h: 23
- 0xffffffffa0523410 <xfs_buf_lock+48>: mov 0x128(%rbx),%eax
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 936
- 0xffffffffa0523416 <xfs_buf_lock+54>: test %eax,%eax
- 0xffffffffa0523418 <xfs_buf_lock+56>: je 0xffffffffa0523420 <xfs_buf_lock+64>
- 0xffffffffa052341a <xfs_buf_lock+58>: cmpb $0x0,0x30(%rbx)
- 0xffffffffa052341e <xfs_buf_lock+62>: js 0xffffffffa0523480 <xfs_buf_lock+160>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/arch/x86/include/asm/atomic_64.h: 23
- 0xffffffffa0523420 <xfs_buf_lock+64>: mov 0x12c(%rbx),%eax
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 938
- 0xffffffffa0523426 <xfs_buf_lock+70>: test %eax,%eax
- 0xffffffffa0523428 <xfs_buf_lock+72>: jne 0xffffffffa0523458 <xfs_buf_lock+120>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 940
- 0xffffffffa052342a <xfs_buf_lock+74>: lea 0x38(%rbx),%rdi
- 0xffffffffa052342e <xfs_buf_lock+78>: callq 0xffffffff81097bd0 <down>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_trace.h: 325
- 0xffffffffa0523433 <xfs_buf_lock+83>: mov 0x2976e(%rip),%r11d # 0xffffffffa054cba8 <__tracepoint_xfs_buf_lock_done+8>
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/fs/xfs/linux-2.6/xfs_buf.c: 943
- 0xffffffffa052343a <xfs_buf_lock+90>: mov 0x8(%rbp),%r12
找到关键点:调用down()函数的地方,然后可以发现,在call down之前,就行了相关寄存器操作,可以推测是进行传参相关的准备。
可以发现有
lea 0x38(%rbx),%rdi
对比代码
down(&bp->b_sema);
可以看出,入参是bp结构体的一个成员,刚好跟0x38偏移对应,由此可推测此时的rbx寄存器为存放bp指针的寄存器。
接下来,需要寻找rbx寄存器跟堆栈的关系,需要找到从rbx向堆栈写、或从堆栈向rbx读的相关操作,而在当级函数的反汇编代码中显然没有,需要进入下级函数down()中,看看是否有相关操作,对down()进行反汇编:
点击(此处)折叠或打开
- crash> dis -l down
- /usr/src/debug/kernel-2.6.32-220.el6/linux-2.6.32-220.el6.x86_64/kernel/semaphore.c: 54
- 0xffffffff81097bd0 <down>: push %rbp
- 0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
- 0xffffffff81097bd4 <down+4>: push %rbx
- 0xffffffff81097bd5 <down+5>: sub $0x18,%rsp
- 0xffffffff81097bd9 <down+9>: nopl 0x0(%rax,%rax,1)
- 0xffffffff81097bde <down+14>: mov %rdi,%rbx
- ...
可以看出,其中第5行对rbx寄存器进行压栈,bingo!,这正是我们要寻找的,由此说明bp指针的值,必然可以在down()这一级函数的栈中找到。
解析堆栈:
点击(此处)折叠或打开
- crash> bt -f 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- ffff8805f3a23430: 0000000000000082 ffff8805f3a234a8
- ffff8805f3a23440: 0000000181164d0e ffff880c20518300
- ffff8805f3a23450: 00051200f3a21b00 ffff880c20518300
- ffff8805f3a23460: 00051200f3a21540 ffff8805f3a21af8
- ffff8805f3a23470: ffff8805f3a23fd8 000000000000f4e8
- ffff8805f3a23480: ffff8805f3a21af8 ffff880c20e1a080
- ffff8805f3a23490: ffff8805f3a21540 ffff8805f3a234d8
- ffff8805f3a234a0: 0000000000000246 ffff880c1d102400
- ffff8805f3a234b0: 0000000000000246 ffff8805f3a234d8
- ffff8805f3a234c0: ffff8802ecd784b8 7fffffffffffffff
- ffff8805f3a234d0: ffff8805f3a235a8 7fffffffffffffff
- ffff8805f3a234e0: 0000000000000200 ffff8805f3a23598
- ffff8805f3a234f0: ffffffff814f9a6d
- ...
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- ffff8805f3a235f8: ffff8805f3a23618 0000000000000292
- ffff8805f3a23608: ffff8802ecd78480 ffff8802ecd78480
- ffff8805f3a23618: ffff8805f3a23648 ffffffffa0523433
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- ffff8805f3a23628: 0000000000016ec0 0000000000016ec0
- ffff8805f3a23638: ffff8808fe07c0c0 ffff8802ecd78490
- ffff8805f3a23648: ffff8805f3a236b8 ffffffffa05235f2
down()的堆栈在第#3级,再看看down()的反汇编代码的头三句:
0xffffffff81097bd0 <down>: push %rbp
0xffffffff81097bd1 <down+1>: mov %rsp,%rbp
- 0xffffffff81097bd4 <down+4>: push %rbx
显然,接下来压栈的是rbp,即ffff8805f3a23648是rbp,即上一级堆栈的栈帧指针。
第3句,即对rbx压栈,说明rbx(即我们要找的bp指针的值)就位于down()函数堆栈中的第3个位置(第1为上级函数返回地址、第2为rbp),即:
ffff8802ecd78480
所以,我们找到了。。。。
2)位于寄存器中的局部变量
由于Vmcore只是一个内存快照的静态数据,所以其中保存的进程上下文中,只保存了最后一级函数执行时的寄存器内容,所以,如果相关局部变量位于最后一级函数中,且用寄存器保存,那么此时可以直接通过相关进程的bt上下文得到:
点击(此处)折叠或打开
- crash> bt 9242
- PID: 9242 TASK: ffff8805f3a21540 CPU: 4 COMMAND: "_0605_KLINUX_DF"
- #0 [ffff8805f3a23428] schedule at ffffffff814f8b42
- #1 [ffff8805f3a234f0] schedule_timeout at ffffffff814f9a6d
- #2 [ffff8805f3a235a0] __down at ffffffff814fa992
- #3 [ffff8805f3a235f0] down at ffffffff81097c11
- #4 [ffff8805f3a23620] xfs_buf_lock at ffffffffa0523433 [xfs]
- #5 [ffff8805f3a23650] _xfs_buf_find at ffffffffa05235f2 [xfs]
- #6 [ffff8805f3a236c0] xfs_buf_get at ffffffffa05237db [xfs]
- ..
- #24 [ffff8805f3a23f80] system_call_fastpath at ffffffff8100b0f2
- RIP: 0000003885e0ed2d RSP: 00007f43871e36e0 RFLAGS: 00003297
- RAX: 0000000000000002 RBX: ffffffff8100b0f2 RCX: 0000000000000000
- RDX: 0000000000000241 RSI: 0000000000000241 RDI: 00007f43871e3710
- RBP: 00007f43871e365c R8: 00007f43871df440 R9: 0000000000100000
- R10: 0000000000000000 R11: 0000000000003293 R12: ffffffff8117a830
- R13: ffff8805f3a23f78 R14: 00007f43871e3710 R15: 00007f43871e391c
- ORIG_RAX: 0000000000000002 CS: 0033 SS: 002b
但是,如果需要解析的局部变量位于中间流程,且使用寄存器保存,且不能在最后一级函数执行时的进程上下文中体现,那么此类局部变量就无法解析了。。
Crash工具实战-变量解析【转】的更多相关文章
- ETL利器Kettle实战应用解析系列一【Kettle使用介绍】
本系列文章主要索引如下: 一.ETL利器Kettle实战应用解析系列一[Kettle使用介绍] 二.ETL利器Kettle实战应用解析系列二 [应用场景和实战DEMO下载] 三.ETL利器Kettle ...
- 一个suse11 sp1的crash工具版本问题
这几年排查的各种类型的crash也比较多了,各种类型的也算见过,但是排查这个crash,走了不该走的弯路,事后显得很low,为了防止自己犯类似错误,也同时提醒后人,记录之. 内核是suse11,sp1 ...
- 使用Crash工具分析 Linux dump文件【转】
转自:https://blog.csdn.net/bytxl/article/details/45025183 前言 Linux 内核(以下简称内核)是一个不与特定进程相关的功能集合,内核的代码很难轻 ...
- 使用 Crash 工具分析 Linux dump 文件
转自:http://blog.csdn.net/commsea/article/details/11804897 简介: Linux 内核由于其复杂性,使得对内核出现的各种异常的追踪变得异常困难.本文 ...
- ETL利器Kettle实战应用解析系列三
本系列文章主要索引如下: 一.ETL利器Kettle实战应用解析系列一[Kettle使用介绍] 二.ETL利器Kettle实战应用解析系列二 [应用场景和实战DEMO下载] 三.ETL利器Kettle ...
- Java日志实战及解析
Java日志实战及解析 日志是程序员必须掌握的基础技能之一,如果您写的软件没有日志,可以说你没有成为一个真正意义上的程序员. 为什么要记日志? • 监控代码 • 变量变化情况, ...
- PHP入门基础(一)——标记风格、注释、表单获取、字符串类型、变量解析
PHP标记风格: //XML风格//推荐的标记风格,可以在XML文档中使用 <?php echo '<p>XML Style</p>'; ?> //简短风格——需启 ...
- (转载)ETL利器Kettle实战应用解析系列一【Kettle使用介绍】
http://www.cnblogs.com/limengqiang/archive/2013/01/16/kettleapply1.html ETL利器Kettle实战应用解析系列一[Kettle使 ...
- PHP字符串中的变量解析(+教你如何在PHP字符串中加入变量)
定义字符串的时候,用单引号或者双引号都是可以的.我个人习惯是用双引号.在输出字符串的时候,若字符串中含有字符串变量,使用单引号和双引号则是有区别的.如下面程序: 1 2 3 4 5 6 7 8 < ...
随机推荐
- oracle中查看所有表和字段以及表注释字段注释
获取表:select table_name from user_tables; //当前用户拥有的表 select table_name from all_tables; //所有用户的表 selec ...
- CSS3笔记2
1.CSS样式表分类 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...
- Golang入门教程(十)内建函数
比较常用的内建函数 参考: http://blog.csdn.net/liumiaocn/article/details/54804074
- 064、Weave网络结构分析(2019-04-04 周四)
参考https://www.cnblogs.com/CloudMan6/p/7482035.html Weave网络使用之前需要执行 eval $(weave env) ,其作用是将后续的doc ...
- [Android] Android读取Asset下文件的最简单的方法总结(用于MediaPlayer中)
方法一:getAssets().openFd //读取asset内容 private void openAssetMusic(String index) throws IOException { ...
- python模块之自定义模块
模块概述 到此之前,我们都是在一个py文件里操作,接下来,我们学习模块的内容,可以从其他文件引入内容(如函数等) 1. 什么是模块 一个py文件就是一个模块,模块是一些相似功能的集合体 2. 为什么要 ...
- wx预览图片
wx.previewImage({ current: current, // 当前显示图片的http链接 urls: this.data.imgalist // 需要预览的图片http链接列表 }) ...
- 三十二、Linux 进程与信号——不可靠信号
32.1 不可靠信号问题 发生信号时关联动作被重置为默认设置 信号可能丢失 程序片段 在进入 sig_int 与再次调用 signal 之间发生的 SIGINT 信号将不会捕获 导致进程终止 以前版本 ...
- 使用js代码将HTML Table导出为Excel
使用js代码将HTML Table导出为Excel的方法: 直接上源码 <html> <head> <meta http-equiv="Content-Type ...
- 在SQL注入中利用MySQL隐形的类型转换绕过WAF检测
web应用一般采用基于表单的身份验证方式(页面雏形如下图所示),处理逻辑就是将表单中提交的用户名和密码传递到后台数据库去查询,并根据查询结果判断是否通过身份验证.对于LAMP架构的web应用而言,处理 ...