天天看点

在内核中读取磁盘相应位置的数据

涉及到一个问题,在内核态读磁盘固定的位置的数据应该怎么办。如果是在用户态,有C库函数可以做到,read, lseek 等都可以。但是在内核态呢?最直接的办法就是自己发bio去读,但这样有点麻烦,还有两种比较简单的方法。

一种是用buffer_head, 一种是用 dm_io_sync_vm。

第一种:buffer_head.

代码如下:

 22static int __init bread_init(void)

 23 {

 24

 25     struct buffer_head *bh = NULL;  //声明bh

 26     struct block_device *bdev;

 27     char *p, ch;

 28     int i;

 29     int block_size;

 30     int j;

 31

 32     bdev = open_bdev_excl(PATH, FMODE_READ, NULL); //打开设备,获取bdev

 33     if (IS_ERR(bdev)) {

 34         printk(KERN_ALERT "open bdev failed./n");

 35         goto out1;

 36     }

 37

 38     

 39     block_size = bdev->bd_block_size;

 40     

 42         bh = __bread(bdev, j, block_size);

 43         if (bh == NULL) {

 44             printk(KERN_ALERT "__bread error./n");

 45             goto out2;

 46         }

 47

 48         p = (char *)(bh->b_data);

 49         printk(KERN_ALERT "%c", p[0]);

 50         for (i = 0; i < block_size; i++) {

 51             p[i] = ch;

 52         }

 53         printk(KERN_ALERT "/n");

 54

 55         set_buffer_uptodate(bh); //写回

 56         mark_buffer_dirty(bh);

 57

 58         if (bh) {

 59             brelse(bh);

 60         }

 61     

 62     return 0;

 63 }

需要注意的问题:

1. bh只是声明,并没有为他分配空间。bh的查找似乎就是在内存中找一个,或者内核去分配,调用者不用去管。

2. 要将buffer_head写回的话,只需要 55, 56两行代码就可以了。剩下的由操作系统做的事情,我也不知道是什么。

第二种方法: dm_io_sync_vm

代码如下:

#include "dm.h"

#include "dm-io.h"

static int __init mydm_init(void)

{

    struct io_region region;

    struct block_device *bdev;

    char *buf;

    unsigned long flag, i;

    bdev = open_bdev_excl(PATH, FMODE_READ, NULL);

    if (IS_ERR(bdev)) {

        printk(KERN_ALERT "open bdev failed./n");

        goto out1;

    }

    buf = (char *)vmalloc(512);

    if (!buf) {

        printk(KERN_ALERT "alloc memory failed./n");

        goto out2;

    }

    region.bdev = bdev;

    region.sector = 0; 

    region.count = 1;

    dm_io_sync_vm(1, &region, READ, buf, &flag);

    for (i = 0; i < 512; i++) {

        printk(KERN_ALERT "%c", buf[i]);

        buf[i] = 'A';

    }

    printk(KERN_ALERT "/n");

    dm_io_sync_vm(1, &region, WRITE, buf, &flag);

    close_bdev_excl(bdev);

    return 0;

out2:

    close_bdev_excl(bdev);

out1:

    return -1;

}

做法非常简单,只要有一个io_region结构,然后调用dm_io_sync_vm函数就可以了。其实这是一系列的函数,linux内核文档中有详细的解释,但是要注意几个问题

1. dm_io_sync_vm 所使用的内存空间(也就是buf参数)必须是用vmalloc函数分配的,如果是kmalloc分配的话,会出现panic。由此看来kmalloc和vmalloc的区别比我们知道的要大。具体有什么差距我也不知道。

2. 注意头文件的引用,这样引用的原因是dm_io这一套接口并不是所有的内核版本都会有的。有的内核版本根本没有包含这些接口,甚至有些虽然包含了接口但参数不同,所以最保险的办法是把这两个文件随代码一起发布。

继续阅读