Linux写操作实时跟踪

上传人:小** 文档编号:50404289 上传时间:2022-01-20 格式:DOC 页数:11 大小:189KB
收藏 版权申诉 举报 下载
Linux写操作实时跟踪_第1页
第1页 / 共11页
Linux写操作实时跟踪_第2页
第2页 / 共11页
Linux写操作实时跟踪_第3页
第3页 / 共11页
资源描述:

《Linux写操作实时跟踪》由会员分享,可在线阅读,更多相关《Linux写操作实时跟踪(11页珍藏版)》请在装配图网上搜索。

1、linux 磁盘写操作实时跟踪分类:Linux / Unix 高级应用编程2011-04-28 00:14 397 人阅读评论(0)收藏举报事实上,我总是对linux开源社区的无名英雄们怀着无限的敬意,因此除了完成工作中 需要的功能以外,首先想到的是分享,本篇文章以GPL发布,在你转发的时候,请遵循GPL 协议的规定,在此首先贴出GPL公共许可证,或许你会觉得这过于啰嗦,事实上这是必要的。请谅解。为了不妨碍大家的阅读,在此我给出GPLv3的连接地址。(GPLv3)下面进入正题,我打算分几个步骤来说明:第一磁盘写操作的过程分析;第二模块导出符号的利用;第三jprobe 禾口 kprobe 介绍

2、;第四磁盘写操作跟踪;最后还将给出一个简单的示例程序。第一磁盘写操作过程分析在linux内核中,发生一次写操作,从调用 write函数到数据发起一个写数据到具体块设 备请求之间,大致需要以下几个过程。1. 如果用户态调用了一个write函数,内核执行blkdev_file_write 函数,如果不是direct io操作方式,那么执行buffered write 操作过程,直接调用generic_file_buffered_write 函数。Buffered write操作方法会将数据直接写入Cache,并进行Cache的替换操作,在替换操作过程中需要对实际的快设备进行操作,address_s

3、pace-a_ops提供了块设备操作的方法。当数据被写入到 Cache之后,write函数就可以返回了,后继异步写入的任务绝大部分交给 了 pdflush daemon(有一部分在替换的时候做了)。2. 读操作在没有命中 Cache的情况下通过 address_space_operations方法中的readpage函数发起块设备读请求;写操作在替换 Cache或者Pdflush唤醒时发起块设备请求。发起块设备请求的过程都一样,首先根据需求构建bio结构,bio结构中包含了读写地址、长度、目的设备、回调函数等信息。构造完 bio之后,通过简单的 submit_bio函数将submit bio请

4、求转发给具体的块设备。从这里可以看出,块设备接口很简单,接口方法为(更底层函数为 generic_make_request ),数据结构为 struct bio。3. submit_bio 函数通过 generic_make_request 转发 bio,generic_make_request 是一个循环,其通过每个块设备下注册的q-make_request_fn函数与块设备进行交互。如果访问的块设备是一个有 queue的设备,那么会将系统的 _make_request函数注册到 q-make_request_fn 中;否则块设备会注册一个私有的方法。在私有的方法中,由于不存在queue队列

5、,所以不会处理具体的请求,而是通过修改bio中的方法实现bio的转发,在私有 make_request 方法中,往往会返回1,告诉generic_make_request继续转发比 bio。gen eric_make_request的执行上下文可能有两种,一种是用户上下文,另一种为pdflush所在的内核线程上下文。4. 接下来generic_make_request 再往下发就该到驱动层了。这里不属于我们讨论的范畴了我们需要监控所有要经过generic_make_request发起到驱动的bio,因此只有在bio被产生或者submit_bio的位置去拦截,读到这里,你能够想明白这个事情就足够

6、了。第二模块导出符号的利用模块函数是可以用 EXPORT_SYMBOL 宏导出的,其本来的目的是导出之后便于模块与 模块之间的代码重用,已经模块间通讯。在这里我们要讨论其另外一个用法,模块函数被导出之后,我们可以根据模块名称获取到模块函数对应的位置偏移量。这个值也就是这个模块函数的首地址。如果我们要想利用submit_bio,那首先就要保证 submit_bio函数的符号是被导出来了的。事实上我们查看linux内核源代码(blk-core.c:Line1620 ):1.2.3.4.5.6.7.8.9.cpp view pla in copy* submit_bio - submit a bio

7、 to the block device layer for I/O* rw: whether to %READ or %WRITE, or maybe to %READA (read ahead)* bio: The &struct bio which describes the I/O* submit_bio() is very similar in purpose to generic_make_request(), and* uses that function to do most of the work. Both are fairly rough* interfaces; bio

8、 must be presetup and ready for I/O.10. */11. void submit_bio( int rw, struct bio *bio)12. 13. int count = bio_sectors(bio);14.bio-bi_rw |= rw;15./*16.* If its a regular read/write or a barrier with data attached,17.* go through the normal accounting stuff before submission18.*/19.if(bio_has_data(bi

9、o) & !(rw & REQ_DISCARD) 20.if (rw & WRITE) 21.count_vm_events(PGPGOUT, count);22.else 23.task_io_account_read(bio-bi_size);24.count_vm_events(PGPGIN, count);25.26.if (unlikely(block_dump) 27.char bBDEVNAME_SIZE;28.printk(KERN_DEBUG%s(%d): %s block %Lu on %s (%u sectors)/n ,29.current-comm, task_pid

10、_nr(current),30.(rw & WRITE) ?WRITE:READ,31.(unsignedlonglong )bio-bi_sector,32.bdevname(bio-bi_bdev, b),33.count);34.35.36.generic_make_request(bio);37.38.EXPORT_SYMBOL(submit_bio);确实这个submit_bio函数是被导出了符号的,EXPORT_SYMBOL(submit_bio); 或许你觉得这来的太轻松了,事实上要分析到这一步真的需要很多耐心的,所有磁盘读写操作最终要真正的往磁盘上写文件,所有真实要到达磁盘的数

11、据bio都必将经过这个函数。虽然是导出来符合,我们能直接调用,可是直接调用似乎不能达到我们想要的效果获取到这个bio结构。反而要我们传给它一个bio结构。对这个函数如何处理呢?正如你所想的那样,我们如果能在这个函数执行的时候插入一个函数到这里拦截就对了, 就像c语言的setjmp和Iongjmp那样工作。可是在编译过了的linux内核中能实现吗?答案 是肯定的。这就是下一节所讲的kprobe机制。第三jprobe 禾口 kprobe 介绍顾名思义probe就是探头的意思,即是说在函数f1调用的时候,设置一个探头f2到这个 函数f1的位置,并且获取到 f1的参数,抛给f2,然后,跳到f2的位置执

12、行,完了之后再 回到到f1执行。更多关于kprobe的介绍请自行 google查找,在linux内核的sample目录下也有一个使 用的例子。这里只做简单的介绍,其功能简单说,就是在函数被调用的时候,能拿到这个函数的参数,做一些处理。而如果我们设置一个jprobe到submit_bio这个函数上,那么我们就可以获取到bio结构的信息了。jprobe结构定义如下:cpp view pla in copy1. struct jprobe 2. struct kprobe kp;3. void *entry;4. 那么总结一下设计思路,一句话,构建一个jprobe探头插入到submit_bio处。事

13、实上要得出这句话的结论要经历很多伤感的事情。哈哈。kprobe结构中有两个重要的成员symbol_name, addr ,symbol_name 就是那个函数的符号,这里就应该是 submit_bio的符号。addr就是这个函数的地址,就是内核函数 kallsyms_lookup_name(submit_bio)返回的值。需要注意的是在jprobe 结构中的 kprobe只能是addr或是symbol_name其中一个填入了值,如果两个都填入,在注册这个探头的 时候就会出现错误-21非法符号。举例说明,如果addr为0x1000000,那么symbol_name就该是NULL;如果symbol

14、_name 为submit_bio那么addr就该为NULL ;不能是两个都是 有效的数据。具体设置哪一个值,根据内核版本而定。(很多发型版本并没有导出 kallsyms_lookup_name ).jprobe的另一个成员是 entry,这就是我们自己定义的那个探头程序。有一点必须说明,就是注册进去的探头程序应该和被注册的函数的参数列表一致,比如void submit_bio(i ntrw, struct bio * bio)那么注册进去的探头程序也该是void submit_bio_probe( in trw, struct bio * bio).因此对于submit_bio这个函数,要想

15、注册一个jprobe探头函数给它,那么这个jprobe结构就应该类似于下面这个样子。cpp view pla in copy1. struct jprobe submit_bio_jprobe 2. .entry = (kprobe_opcode_t *) submit_bio_jprobe,3. kp = 4.addr = NULL, .symbol_name =submit_bio5.6. ;第四磁盘写操作跟踪磁盘操作的跟踪,逻辑已经很清楚了。那么具体如何去做?首先这是linux内核编程,因此我们需要写一个模块,当然你也可以傻到去修改内核,实 现一个系统调用。这里我们按照正常人的思维去做。

16、在实现的模块中,初始化的时候把这个探头程序注册进去,然后在模块释放的时候卸载这个探头程序。注册探头程序用register_jprobe,卸载用unresister_jprobe 仅此而已。似乎讲到了这里的时候,你会发现这 是一件特别简单的事情了。 内核调用也不会超过10个。也实在没有必要再详细的说下去了, 这里给出一个简单的实例程序, 其功能是打印出,哪个设备的那一个扇区之后的多少个扇区 发生了写操作。打印的格式为:device : dm-0 , comma nd : write , start : 10240 , count : 8其中device为对应的设备名,comma nd为动作,是读

17、还是写,start为动作发生的起始扇区,cou nt是start这个位置之后的多少个扇会发生comma nd类型的操作。下面给出源代码和 makefile文件dwm_mod.ccpp view pla in copy1.#include 2.#include 3.#include 4.#include 5.6.static void submit_bio_probe(int rw, struct bio * bio) 7.if (bio & bio-bi_io_vec != NULL) 8.char bBDEVNAME_SIZE;9.printk(KERNNFOdevice: %s, comm

18、and: %s, start: %10lld, count: %d /n,10.bdevname(bio-bi_bdev, b), rw & WRITE ?writeread,11.bio-bi_sector, bio_sectors(bio);12.13.jprobe_return();16.17.18.kp = 19./ can not set both addr and symbo_name20./ either set addr or symbol_name21./ if not -21 while retured22.addr = NULL,(kprobe_opcode_t *) 0

19、xc04e6e4,23.symbol_name =submit bio24.,25.;26.staticint_init my_init(void ) 27.int ret = 0;28.printk(KERNNFOsubmit_bio jprobe module install./);29.ret = register_jprobe(&m y_jprobe);30.if (ret 0) 31.printk(KERNNFOregister_jprobe failed, returned %d/n,re32.33.t);return ret;34.printk(KERNNFOPlanted jp

20、robe at %p, handler addr %p/n35.my_jprobe.kp.addr, my_jprobe.entry);36.return ret;37.38.staticvoid _exit my_exit(void ) 39.printk(KERNNFOsubmit_bio jprobe module uninstall./n);40.41.unregister_jprobe(&m y_jprobe); printk(KERNNFOjprobe at %p unregistered/n,my_jprobe.kp.addr);14. 15.static struct jpro

21、be my_jprobe = .entry = (kprobe_opcode_t *) submit_bio_probe.42. 43. module_init(my_init);44. module_exit(my_exit);45. MODULE_LICENSEGPL);Makefile:cppview pla in copy1. ifneq ($(KERNELRELEASE),)2. obj-m := dwm_mod.o3. else4. KDIR := /lib/modules/$(shell uname -r)/build5. PWD := $(shell pwd)6. defaul

22、t7. $(MAKE) -C $(KDIR) M=$(PWD) modules8. endif大家可以编译运行,看看运行结果。编译的时候,如果你的linux系统没有安装内核开发环境,请先安装。我的测试系统是 Cen tOS 5.1,内核版本是2.6.18-238.9.1.el5【注】:第一磁盘写操作的过程分析,我是在chin au nixblog http:/blogold.chi nau nix. net/u3/103428/showart_2471002.html博主吴栓的博客中看到的,在此感谢博主分析了磁盘读写的整个过程。事实上我发现很多地方都有这一篇文章,我无法确定真正发这篇博客的作者是谁,也无法与你联系。原作者发现我有侵权行为,请至邮件到 akocea .我将第一时间与你取得联系。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!