今天遇到一个问题,需要探测内核中buffer cache block的大小。我想到了Kprobe这个神奇的工具,并且很好的探测到了内核中的变量值,非常的方便,在此分享一下。
采用dd等工具写设备的时候,是需要经过块设备层的buffer cache,当请求块大小小于buffer cache的block_size时,Linux的策略是首先需要从磁盘load数据至buffer cache,然后再将新写入的“局部数据”写入buffer cache。这一步骤完成之后,会将整个buffer cache标识成dirty,挂载到设备所属的radix tree上,然后定时唤醒后台writeback线程刷新dirty block至磁盘。今天对linux-3.2和linux-2.6.23的顺序写进行了对比测试,发现请求大小在512至2048之间时,Linux-3.2的性能居然比Linux-2.6.23还差。测试后得到的性能特征似乎与buffer cache的块大小有关系,因此,我采用Kprobe对两个版本的块大小进行了探测验证。
为了探测这个值,首先需要找一个合适的探测点,根据代码分析的结果,我选择在__block_write_begin函数中调用create_empty_buffers函数时的机会点,采用Kprobe插入一段代码,打印buffer cache block_size的值。探测点位置的源代码如下所示:
int __block_write_begin(struct page *page, loff_t pos, unsigned len, get_block_t *get_block) { 。。。 blocksize = 1 << inode->i_blkbits; if (!page_has_buffers(page)) create_empty_buffers(page, blocksize, 0); head = page_buffers(page); bbits = inode->i_blkbits; block = (sector_t)page->index << (PAGE_CACHE_SHIFT - bbits); 。。。 }
通过上面函数,我们知道blocksize就是buffer cache block块大小,因此,我们可以截获create_empty_buffers函数之后,打印传入的第二个参数就可以得到buffer cache块大小值了。截获create_empty_buffers函数很简单,通过kallsyms_lookup_name函数或者/proc/kallsyms就可以得到截获函数对应的内存地址。关键的问题在于截获这个函数之后,我们如果得到他的第二个参数,这就关系到函数的参数传递问题了。
在X86_64平台上,Linux的参数传递通过如下9个寄存器完成,分别为:RDI,RSI,RDX,RCX,RAX,R8,R9,R10,R11。在pre_handler函数中,我们可以得到寄存器组变量,通过寄存器组变量我们可以通过RSI寄存器得到create_empty_buffers函数传入的第二个参数值。对于Linux-2.6.23版本,函数调用过程中寄存器在栈中布局定义如下:
struct pt_regs { unsigned long r15; unsigned long r14; unsigned long r13; unsigned long r12; unsigned long rbp; unsigned long rbx; /* arguments: non interrupts/non tracing syscalls only save upto here*/ unsigned long r11; unsigned long r10; unsigned long r9; unsigned long r8; unsigned long rax; unsigned long rcx; unsigned long rdx; unsigned long rsi; unsigned long rdi; unsigned long orig_rax; /* end of arguments */ /* cpu exception frame or undefined */ unsigned long rip; unsigned long cs; unsigned long eflags; unsigned long rsp; unsigned long ss; /* top of stack page */ };
对于Linux-3.2版本,寄存器的组织结构是相同的,但是名字定义有所差别,新版本的寄存器定义如下:
struct pt_regs { unsigned long r15; unsigned long r14; unsigned long r13; unsigned long r12; unsigned long bp; unsigned long bx; /* arguments: non interrupts/non tracing syscalls only save up to here*/ unsigned long r11; unsigned long r10; unsigned long r9; unsigned long r8; unsigned long ax; unsigned long cx; unsigned long dx; unsigned long si; unsigned long di; unsigned long orig_ax; /* end of arguments */ /* cpu exception frame or undefined */ unsigned long ip; unsigned long cs; unsigned long flags; unsigned long sp; unsigned long ss; /* top of stack page */ };
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索内核
, cache
, buffer
, 大小
, page
探测
kprobe 探测点、内核漏洞的利用与防范、linux内核全局变量、内核全局变量、内核模块 全局变量,以便于您获取更多的相关知识。