Linux 块设备驱动实验

  1. 什么是块设备?
    块设备是针对存储设备的,比如 SD 卡、EMMCNAND FlashNor FlashSPI Flash、机械硬盘、固态硬盘等。因此块设备驱动其实就是这些存储设备驱动。
  2. block_device 结构体
  1. 注册块设备

int register_blkdev(unsigned int major, const char *name)

  1. 注销块设备

void unregister_blkdev(unsigned int major, const char *name)

     3. gendisk结构体

在该结构体下有一个重要的结构体成员变量struct gendisk linux 内核使用 gendisk 来描述一个磁盘设备。

在gendisk结构体下有一个很重要的成员变量const struct block_device_operations *fops;是块设备操作集。

  1. 申请gendisk

struct gendisk *alloc_disk(int minors)

(2)初始化

(3)将 gendisk 添加到内核

void add_disk(struct gendisk *disk)

(4)删除gendisk

void del_gendisk(struct gendisk *gp)

    4. 块设备 I/O 请求过程

(1)请求队列 request_queue

初始化请求队列:
  request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)

删除请求队列:

void blk_cleanup_queue(struct request_queue *q) 

(2)请求 request

请求队列(request_queue)里面包含的就是一系列的请求(request)

blk_fetch_request函数来一次性完成请求的获取和开启

(3)bio 结构

每个 request 里面里面会有多个 biobio 保存着最终要读写的数据、地址等信息。

在struct结构体中有两个重要的结构体struct bvec_iter与struct bio_vecbvec_iter 结构体描述了要操作的设备扇区等信息,bio_vec 数组就是 RAM 信息,比如页地址、页偏移以及长度。

使用请求队列实验程序:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>

#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>


/*定义磁盘大小、内存模式*/
#define RAMDISK_SIZE (2*1024*1024)
#define RAMDISK_NAME "ramdisk"
#define RAMDISK_MINOR 3


struct ramdisk_dev{
    int major;
    unsigned char *ramdiskbuf;//ramdisk的内存空间,模拟磁盘的空间
    struct gendisk *gendisk;
    spinlock_t lock;			/* 自旋锁 */
    struct request_queue *queue;

};
struct ramdisk_dev ramdisk;

static void ramdisk_transfer(struct request *req)
{
    /*数据传输的三要素:源 目的 长度*/
    /*内存地址 块设备地址 长度*/
    unsigned long start = blk_rq_pos(req) << 9;  	/* blk_rq_pos获取到的是扇区地址,左移9位转换为字节地址 */
	unsigned long len  = blk_rq_cur_bytes(req);		/* 大小   */
    
    /*获取bio里面的缓冲区:
    如果是读:从磁盘里面读取到的数据保存到此缓冲区里面
    如果是写:此缓冲区保存着要写入到磁盘里面的数据*/
    void *buffer = bio_data(req->bio);

    if(rq_data_dir(req) == READ) 		/* 读数据 */	
		memcpy(buffer, ramdisk.ramdiskbuf + start, len);
	else if(rq_data_dir(req) == WRITE) 	/* 写数据 */
		memcpy(ramdisk.ramdiskbuf + start, buffer, len);
}

/*请求函数*/
static void ramdisk_request_fn(struct request_queue *q)
{
    int err = 0;
	struct request *req;
	/* 循环处理请求队列中的每个请求 */
	req = blk_fetch_request(q);
	while(req != NULL) {
		/* 针对请求做具体的传输处理 */
		ramdisk_transfer(req);

		/* 判断是否为最后一个请求,如果不是的话就获取下一个请求
		 * 循环处理完请求队列中的所有请求。
		 */
		if (!__blk_end_request_cur(req, err))
			req = blk_fetch_request(q);
	}
}

/*
 * @description		: 打开块设备
 * @param - dev 	: 块设备
 * @param - mode 	: 打开模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_open(struct block_device *dev, fmode_t mode)
{
	printk("ramdisk open\r\n");
	return 0;
}

/*
 * @description		: 释放块设备
 * @param - disk 	: gendisk
 * @param - mode 	: 模式
 * @return 			: 0 成功;其他 失败
 */
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{
	printk("ramdisk release\r\n");
}

/*
 * @description		: 获取磁盘信息
 * @param - dev 	: 块设备
 * @param - geo 	: 模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
	/* 这是相对于机械硬盘的概念 */
    geo->heads = 2;			/* 磁头 */
	geo->cylinders = 32;	/* 柱面 */
	geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */
	return 0;
}

static const struct block_device_operations ramdisk_fops={
    .owner	 = THIS_MODULE,
	.open	 = ramdisk_open,
	.release = ramdisk_release,
	.getgeo  = ramdisk_getgeo,
};

static int __init ramdisk_init(void)
{
    int ret =0;
    /*1、先申请内存*/
    ramdisk.ramdiskbuf=kzalloc(RAMDISK_SIZE,GFP_KERNEL);
    if(ramdisk.ramdiskbuf==NULL){
        ret=-EINVAL;
        goto ramalloc_fail;
    }
    printk("%#x\r\n",ramdisk.ramdiskbuf);
   
    /*2、注册块设备*/
    ramdisk.major=register_blkdev(0,RAMDISK_NAME);
    if(ramdisk.major<0)
        {
            printk("major_fail\r\n");
            goto major_fail;
        }

    /*3、申请磁盘*/
    ramdisk.gendisk=alloc_disk(RAMDISK_MINOR);
    if(ramdisk.gendisk==NULL){
        goto gendisk_fail;
    }

    /* 4、初始化自旋锁 */
	spin_lock_init(&ramdisk.lock);

    /*5、申请并初始化队列*/
    ramdisk.queue=blk_init_queue(&ramdisk_request_fn,&ramdisk.lock);
     if(ramdisk.queue==NULL){
        goto queue_fail;
    }

    /*6、初始化gendisk*/
    ramdisk.gendisk->major=ramdisk.major;
    ramdisk.gendisk->first_minor = 0; /* 第一个次设备号(起始次设备号)*/
	ramdisk.gendisk->fops = &ramdisk_fops; 		/* 操作函数 */
	ramdisk.gendisk->private_data = &ramdisk;	/* 私有数据 */
	ramdisk.gendisk->queue = ramdisk.queue;		/* 请求队列 */
	sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME); /* 名字 */
	set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512);	/* 设备容量(单位为扇区) */
    add_disk(ramdisk.gendisk);
    return 0;

    queue_fail:
        put_disk(ramdisk.gendisk);
    gendisk_fail:
        unregister_blkdev(ramdisk.major,RAMDISK_NAME);
    major_fail:
        kfree(ramdisk.ramdiskbuf);
    ramalloc_fail:
        return ret;    
}

static void __exit ramdisk_exit(void)
{
    del_gendisk(ramdisk.gendisk);
    put_disk(ramdisk.gendisk);
    /* 清除请求队列 */
	blk_cleanup_queue(ramdisk.queue);
    unregister_blkdev(ramdisk.major,RAMDISK_NAME);
    kfree(ramdisk.ramdiskbuf);
    printk("ramdisk_exit\r\n");
}

/*模块的加载与卸载*/
module_init(ramdisk_init);
module_exit(ramdisk_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");

        本实验通过内存模拟扇区信息,使用kzalloc(RAMDISK_SIZE,GFP_KERNEL);语句申请一个2MB大小的内存空间。使用到请求队列的方式对扇区空间进行读写操作。blk_init_queue(&ramdisk_request_fn,&ramdisk.lock);中的ramdisk_request_fn为请求函数,在该函数中完成对扇区数据的处理。

不使用请求队列实验程序:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>

#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>


/*定义磁盘大小、内存模式*/
#define RAMDISK_SIZE (2*1024*1024)
#define RAMDISK_NAME "ramdisk"
#define RAMDISK_MINOR 3

struct ramdisk_dev{
    int major;
    unsigned char *ramdiskbuf;//ramdisk的内存空间,模拟磁盘的空间
    struct gendisk *gendisk;
    spinlock_t lock;			/* 自旋锁 */
    struct request_queue *queue;
};
struct ramdisk_dev ramdisk;

/*请求函数*/
static void ramdisk_make_request_fn(struct request_queue *q,struct bio *bio)
{
    int offset;
	struct bio_vec bvec;
	struct bvec_iter iter;
	unsigned long len = 0;

    offset=(bio->bi_iter.bi_sector)<<9;/* 获取要操作的设备的偏移地址 */

    bio_for_each_segment(bvec, bio, iter){
        char *ptr = page_address(bvec.bv_page) + bvec.bv_offset;
		len = bvec.bv_len;
        if(bio_data_dir(bio) == READ) 		/* 读数据 */	
		    memcpy(ptr, ramdisk.ramdiskbuf + offset, len);
	    else if(bio_data_dir(bio) == WRITE) 	/* 写数据 */
		    memcpy(ramdisk.ramdiskbuf + offset, ptr, len);    
    }
    set_bit(BIO_UPTODATE, &bio->bi_flags);
	bio_endio(bio, 0);
}

/*
 * @description		: 打开块设备
 * @param - dev 	: 块设备
 * @param - mode 	: 打开模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_open(struct block_device *dev, fmode_t mode)
{
	printk("ramdisk open\r\n");
	return 0;
}

/*
 * @description		: 释放块设备
 * @param - disk 	: gendisk
 * @param - mode 	: 模式
 * @return 			: 0 成功;其他 失败
 */
void ramdisk_release(struct gendisk *disk, fmode_t mode)
{
	printk("ramdisk release\r\n");
}

/*
 * @description		: 获取磁盘信息
 * @param - dev 	: 块设备
 * @param - geo 	: 模式
 * @return 			: 0 成功;其他 失败
 */
int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
	/* 这是相对于机械硬盘的概念 */
    geo->heads = 2;			/* 磁头 */
	geo->cylinders = 32;	/* 柱面 */
	geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */
	return 0;
}

static const struct block_device_operations ramdisk_fops={
    .owner	 = THIS_MODULE,
	.open	 = ramdisk_open,
	.release = ramdisk_release,
	.getgeo  = ramdisk_getgeo,
};

static int __init ramdisk_init(void)
{
    int ret =0;
    /*1、先申请内存*/
    ramdisk.ramdiskbuf=kzalloc(RAMDISK_SIZE,GFP_KERNEL);
    if(ramdisk.ramdiskbuf==NULL){
        ret=-EINVAL;
        goto ramalloc_fail;
    }
    printk("%#x\r\n",ramdisk.ramdiskbuf);
    /*2、注册块设备*/
    ramdisk.major=register_blkdev(0,RAMDISK_NAME);
    if(ramdisk.major<0)
        {
            printk("major_fail\r\n");
            goto major_fail;
        }

    /*3、申请磁盘*/
    ramdisk.gendisk=alloc_disk(RAMDISK_MINOR);
    if(ramdisk.gendisk==NULL){
        goto gendisk_fail;
    }

    /* 4、初始化自旋锁 */
	spin_lock_init(&ramdisk.lock);

    /*5、申请并初始化队列*/
    ramdisk.queue=blk_alloc_queue(GFP_KERNEL);
     if(ramdisk.queue==NULL){
        goto queue_fail;
    }

    blk_queue_make_request(ramdisk.queue,ramdisk_make_request_fn);

    /*6、初始化gendisk*/
    ramdisk.gendisk->major=ramdisk.major;
    ramdisk.gendisk->first_minor = 0;		/* 第一个次设备号(起始次设备号) */
	ramdisk.gendisk->fops = &ramdisk_fops; 		/* 操作函数 */
	ramdisk.gendisk->private_data = &ramdisk;	/* 私有数据 */
	ramdisk.gendisk->queue = ramdisk.queue;		/* 请求队列 */
	sprintf(ramdisk.gendisk->disk_name, RAMDISK_NAME); /* 名字 */
	set_capacity(ramdisk.gendisk, RAMDISK_SIZE/512);/* 设备容量(单位为扇区) */
    add_disk(ramdisk.gendisk);
    return 0;

    queue_fail:
        put_disk(ramdisk.gendisk);
    gendisk_fail:
        unregister_blkdev(ramdisk.major,RAMDISK_NAME);
    major_fail:
        kfree(ramdisk.ramdiskbuf);
    ramalloc_fail:
        return ret;   
}

static void __exit ramdisk_exit(void)
{
    del_gendisk(ramdisk.gendisk);
    put_disk(ramdisk.gendisk);
    /* 清除请求队列 */
	blk_cleanup_queue(ramdisk.queue);
    unregister_blkdev(ramdisk.major,RAMDISK_NAME);
    kfree(ramdisk.ramdiskbuf);
}

/*模块的加载与卸载*/
module_init(ramdisk_init);
module_exit(ramdisk_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchuanqiang");

程序说明:
        本实验中改为使用blk_queue_make_request 函数设置“制造请求”函数;其中使用 blk_alloc_queue函数申请一个请求队列;使用blk_queue_make_request函数设置“制造请求”函数。使用bio_for_each_segment函数循环获取bio中的每个段,然后对其每个段进行处理。

运行测试:
        将驱动模块加载至开发板内核,然后按照如下步骤进行操作:
                1、fdisk -l         //查看磁盘信息;可以查看出设备的信息为/dev/ramdisk

                2、格式化/dev/ramdisk,使用mkfs.vfat /dev/ramdisk进行格式化,然后通过语句mount /dev/ramdisk /tmp 将设备进行挂载。

                3、挂载成功以后可以像操作U盘那样对该存储设备进行读写。

### 关于ArcGIS License Server无法启动的解决方案 当遇到ArcGIS License Server无法启动的情况,可以从以下几个方面排查并解决问题: #### 1. **检查网络配置** 确保License Server所在的计算机能够被其他客户端正常访问。如果是在局域网环境中部署了ArcGIS Server Local,则需要确认该环境下的网络设置是否允许远程连接AO组件[^1]。 #### 2. **验证服务状态** 检查ArcGIS Server Object Manager (SOM) 的运行情况。通常情况下,在Host SOM机器上需将此服务更改为由本地系统账户登录,并重启相关服务来恢复其正常工作流程[^2]。 #### 3. **审查日志文件** 查看ArcGIS License Manager的日志记录,寻找任何可能指示错误原因的信息。这些日志可以帮助识别具体是什么阻止了许可服务器的成功初始化。 #### 4. **权限问题** 确认用于启动ArcGIS License Server的服务账号具有足够的权限执行所需操作。这包括但不限于读取/写入特定目录的权利以及与其他必要进程通信的能力。 #### 5. **软件版本兼容性** 保证所使用的ArcGIS产品及其依赖项之间存在良好的版本匹配度。不一致可能会导致意外行为完全失败激活license server的功能。 #### 示例代码片段:修改服务登录身份 以下是更改Windows服务登录凭据的一个简单PowerShell脚本例子: ```powershell $serviceName = "ArcGISServerObjectManager" $newUsername = ".\LocalSystemUser" # 替换为实际用户名 $newPassword = ConvertTo-SecureString "" -AsPlainText -Force Set-Service -Name $serviceName -StartupType Automatic New-ServiceCredential -ServiceName $serviceName -Account $newUsername -Password $newPassword Restart-Service -Name $serviceName ``` 上述脚本仅作为示范用途,请依据实际情况调整参数值后再实施。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值