分析块设备文件接口和RAMDISK驱动程序的结构

本文详细介绍了Linux内核中的块设备接口及其层次结构,包括块设备文件接口和驱动接口,并深入分析了RAMDISK驱动程序的工作原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

描述

====
块设备接口分为块设备文件接口和块设备驱动接口两个层次,块设备文件接口面向用户建立在块缓冲之上,
块设备驱动接口面向物理设备建立在块缓冲之下.在块设备驱动程序之下还有一个tq_disk缓冲层次,块设备
驱动程序可以将对物理设备的命令序列缓冲在tq_disk任务队列之中,由wait_on_buffer()来一次性的运行它们.随
便提一下,普通文件接口建立在页缓冲之上,文件系统驱动建立在页缓冲之下,而文件系统驱动基于块缓冲,
夹在块设备文件接口和块设备驱动接口之间.

块设备驱动接口由块设备函数表和读写请求函数组成,块设备函数表由blkdevs[]数组索引,读写请求函数
由blk_dev[]数组索引.每一块设备驱动程序驱动一组设备盘,每个设备盘用block_device结构来描述,块
设备文件inode的i_bdev指向对应的block_device结构.

块设备文件接口是def_blk_fops函数表,块设备驱动程序在打开时可以用自已的文件接口替换它.
def_blk_fops与块设备驱动接口,使用块缓冲通过ll_rw_block()接口调用块设备请求函数来读写块设备.

RAMDISK是直接利用块缓冲来存储数据的内存盘驱动程序,有点象RAMFS利用页缓冲来存储文件数据,它们
有异曲同工之妙.由此还可推断出:内存盘中文件系统文件的页缓冲与它的块缓冲是共享的,就是说假如我
们在一个内存盘中建立了一个ext2文件系统,将一个可执行程序拷贝到里面,然后运行该程序,那么该程序
的代码段在进程中的物理页面就是该文件所在内存盘块缓冲的缓冲页.是不是如此还有待验证.

initrd初始化内存盘设备是一个特殊的内存盘,它在内核初始化时通过加载initrd文件建立,只读而且
只能打开一次,当它关闭时自动释放所占用内存.

与块设备驱动程序相关的全局数据和结构
====================================
static struct {
const char *name; 块设备名
struct block_device_operations *bdops; 块设备函数表
} blkdevs[MAX_BLKDEV];

int * blk_size[MAX_BLKDEV]; 每个块设备的容量表
int * blksize_size[MAX_BLKDEV]; 每个块设备的块尺寸表
int read_ahead[MAX_BLKDEV]; 每种块驱动程序允许的每次最多预读扇区数
int * hardsect_size[MAX_BLKDEV]; 每个块设备扇区尺寸表

struct block_device_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
};

内存盘驱动程序
==============
; drivers/block/rd.c:

#define MAJOR_NR RAMDISK_MAJOR

#define BLK_DEFAULT_QUEUE(_MAJOR) &blk_dev[_MAJOR].request_queue

static unsigned long rd_length[NUM_RAMDISKS]; /* Size of RAM disks in bytes */
static int rd_hardsec[NUM_RAMDISKS]; /* Size of real blocks in bytes */
static int rd_blocksizes[NUM_RAMDISKS]; /* Size of 1024 byte blocks */
static int rd_kbsize[NUM_RAMDISKS]; /* Size in blocks of 1024 bytes */
static devfs_handle_t devfs_handle;
static struct inode *rd_inode[NUM_RAMDISKS]; /* Protected device inodes */

int __init rd_init (void)
{
int i;

if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 ||
(rd_blocksize & (rd_blocksize-1)))
{
printk("RAMDISK: wrong blocksize %d, reverting to defaults/n",
rd_blocksize);
rd_blocksize = BLOCK_SIZE; 标准1024字节块长
}

if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) { 注册设备函数表
printk("RAMDISK: Could not get major %d", MAJOR_NR);
return -EIO;
}

blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), &rd_make_request); 安装读写请求函数

for (i = 0; i < NUM_RAMDISKS; i++) {
/* rd_size is given in kB */
rd_length[ i ] = rd_size << 10; 每个内存盘的字节容量
rd_hardsec[ i ] = rd_blocksize; 每个内存盘的扇区尺寸
rd_blocksizes[ i ] = rd_blocksize; 每个内存盘的块尺寸
rd_kbsize[ i ] = rd_size; 每个内存盘的千字节容量
}
devfs_handle = devfs_mk_dir (NULL, "rd", NULL);
devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS,
DEVFS_FL_DEFAULT, MAJOR_NR, 0,
S_IFBLK | S_IRUSR | S_IWUSR,
&fd_fops, NULL);

for (i = 0; i < NUM_RAMDISKS; i++)
register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &fd_fops, rd_size<<1);

#ifdef CONFIG_BLK_DEV_INITRD
/* We ought to separate initrd operations here */
register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &fd_fops, rd_size<<1);
#endif

hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */
blksize_size[MAJOR_NR] = rd_blocksizes; /* Avoid set_blocksize() check */
blk_size[MAJOR_NR] = rd_kbsize; /* Size of the RAM disk in kB */

/* rd_size is given in kB */
printk("RAMDISK driver initialized: "
"%d RAM disks of %dK size %d blocksize/n",
NUM_RAMDISKS, rd_size, rd_blocksize);

return 0;
}
块设备读写请求函数
------------------
static int rd_make_request(request_queue_t * q, int rw, struct buffer_head *sbh)
{
unsigned int minor;
unsigned long offset, len;
struct buffer_head *rbh;
char *bdata;


minor = MINOR(sbh->b_rdev);

if (minor >= NUM_RAMDISKS)
goto fail;


offset = sbh->b_rsector << 9; 在内存盘中的读写位置
len = sbh->b_size;

if ((offset + len) > rd_length[minor])
goto fail;

if (rw==READA)
rw=READ;
if ((rw != READ) && (rw != WRITE)) {
printk(KERN_INFO "RAMDISK: bad command: %d/n", rw);
goto fail;
}

rbh = getblk(sbh->b_rdev, sbh->b_rsector/(sbh->b_size>>9), sbh->b_size);
; 取所请求块的缓冲块
/* I think that it is safe to assume that rbh is not in HighMem, though
* sbh might be - NeilBrown
*/
bdata = bh_kmap(sbh); 取所请求读写块的数据地址
if (rw == READ) {
if (sbh != rbh) 当缓冲块与输出块相同时表示输出块已经被块缓冲索引
memcpy(bdata, rbh->b_data, rbh->b_size); 将缓冲块数据拷贝到输出块中
} else
if (sbh != rbh)
memcpy(rbh->b_data, bdata, rbh->b_size);
bh_kunmap(sbh);
mark_buffer_protected(rbh); 保护缓冲块
brelse(rbh);

sbh->b_end_io(sbh,1); 块成功刷新
return 0;
fail:
sbh->b_end_io(sbh,0); 块更新失败
return 0;
}

static struct block_device_operations fd_fops = {
open: rd_open,
release: rd_release,
ioctl: rd_ioctl,
};

static int rd_open(struct inode * inode, struct file * filp)
{
#ifdef CONFIG_BLK_DEV_INITRD
if (DEVICE_NR(inode->i_rdev) == INITRD_MINOR) {
if (!initrd_start) return -ENODEV; 如果没有加载initrd文件
initrd_users++;
filp->f_op = &initrd_fops; 对于初始化内存盘设备,替换缺省的文件IO函数表
return 0;
}
#endif

if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
return -ENXIO;

/*
* Immunize device against invalidate_buffers() and prune_icache().
*/
if (rd_inode[DEVICE_NR(inode->i_rdev)] == NULL) {
if (!inode->i_bdev) return -ENXIO;
if ((rd_inode[DEVICE_NR(inode->i_rdev)] = igrab(inode)) != NULL)
atomic_inc(&rd_inode[DEVICE_NR(inode->i_rdev)]->i_bdev->bd_openers);
; 内存盘的多个打开者
}

MOD_INC_USE_COUNT;

return 0;
}
static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int minor;

if (!inode || !inode->i_rdev)
return -EINVAL;

minor = MINOR(inode->i_rdev);

switch (cmd) {
case BLKFLSBUF:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
/* special: we want to release the ramdisk memory,
it's not like with the other blockdevices where
this ioctl only flushes away the buffer cache. */
if ((atomic_read(&inode->i_bdev->bd_openers) > 2))
return -EBUSY;
destroy_buffers(inode->i_rdev);
rd_blocksizes[minor] = 0;
break;

case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
return put_user(rd_kbsize[minor] << 1, (long *) arg);

case BLKROSET:
case BLKROGET:
case BLKSSZGET:
return blk_ioctl(inode->i_rdev, cmd, arg);

default:
return -EINVAL;
};

return 0;
}
static int rd_release(struct inode * inode, struct file * filp)
{
MOD_DEC_USE_COUNT;
return 0;
}

初始化内存盘的文件接口
======================
static struct file_operations initrd_fops = {
read: initrd_read,
release: initrd_release,
};
static ssize_t initrd_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
int left;

left = initrd_end - initrd_start - *ppos;
if (count > left) count = left;
if (count == 0) return 0;
copy_to_user(buf, (char *)initrd_start + *ppos, count);
*ppos += count;
return count;
}
static int initrd_release(struct inode *inode,struct file *file)
{
extern void free_initrd_mem(unsigned long, unsigned long);

lock_kernel();
if (!--initrd_users) {
blkdev_put(inode->i_bdev, BDEV_FILE);
iput(inode);
free_initrd_mem(initrd_start, initrd_end);
initrd_start = 0;
}
unlock_kernel();
return 0;
}

块设备文件IO函数
================
; fs/block_dev.c

struct block_device {
struct list_head bd_hash;
atomic_t bd_count;
/* struct address_space bd_data; */
dev_t bd_dev; /* not a kdev_t - it's a search key */
atomic_t bd_openers;
const struct block_device_operations *bd_op;
struct semaphore bd_sem; /* open/close mutex */
};

struct file_operations def_blk_fops = {
open: blkdev_open,
release: blkdev_close,
llseek: block_llseek,
read: block_read,
write: block_write,
fsync: block_fsync,
ioctl: blkdev_ioctl,
};

int blkdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENXIO;
struct block_device *bdev = inode->i_bdev;
down(&bdev->bd_sem);
lock_kernel();
if (!bdev->bd_op)
bdev->bd_op = get_blkfops(MAJOR(inode->i_rdev));
; 在设备盘打开的时候安装其块设备函数表
if (bdev->bd_op) {
ret = 0;
if (bdev->bd_op->open)
ret = bdev->bd_op->open(inode,filp); 向驱动程序发送"open"消息
if (!ret)
atomic_inc(&bdev->bd_openers);
else if (!atomic_read(&bdev->bd_openers))
bdev->bd_op = NULL;
}
unlock_kernel();
up(&bdev->bd_sem);
return ret;
}
static int blkdev_close(struct inode * inode, struct file * filp)
{
return blkdev_put(inode->i_bdev, BDEV_FILE);
}
static loff_t block_llseek(struct file *file, loff_t offset, int origin)
{
long long retval;
kdev_t dev;

switch (origin) {
case 2: 尾部定位
dev = file->f_dentry->d_inode->i_rdev;
if (blk_size[MAJOR(dev)])
offset += (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
/* else? return -EINVAL? */
break;
case 1: 相对定位
offset += file->f_pos;
}
retval = -EINVAL;
if (offset >= 0) {
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
file->f_version = ++event;
}
retval = offset;
}
return retval;
}
ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
size_t block;
loff_t offset;
ssize_t blocksize;
ssize_t blocksize_bits, i;
size_t blocks, rblocks, left;
int bhrequest, uptodate;
struct buffer_head ** bhb, ** bhe;
struct buffer_head * buflist[NBUF];
struct buffer_head * bhreq[NBUF];
unsigned int chars;
loff_t size;
kdev_t dev;
ssize_t read;

dev = inode->i_rdev;
blocksize = BLOCK_SIZE; 块设备的标准尺寸是1024字节
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
i = blocksize;
blocksize_bits = 0;
while (i != 1) {
blocksize_bits++;
i >>= 1;
}

offset = *ppos;
if (blk_size[MAJOR(dev)])
size = (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS; 取设备盘容量
else
size = (loff_t) INT_MAX << BLOCK_SIZE_BITS; loff_t为64位整数,取设备盘的极限容量

if (offset > size)
left = 0; left为剩余所要读的字节,offset为块设备文件指针
/* size - offset might not fit into left, so check explicitly. */
else if (size - offset > INT_MAX)
left = INT_MAX;
else
left = size - offset;
if (left > count)
left = count;
if (left <= 0)
return 0;
read = 0;
block = offset >> blocksize_bits; 求所要读的起始块号
offset &= blocksize-1; 求所要读的在块中的起点
size >>= blocksize_bits;
rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits; 求所要读的块
bhb = bhe = buflist; buflist作为环形队列,bhb是对设备的读指针,bhe是对用户的写指针
if (filp->f_reada) {
if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
if (rblocks > blocks)
blocks = rblocks;

}
if (block + blocks > size) { 如果预读越过设备边界
blocks = size - block;
if (blocks == 0)
return 0;
}

/* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.

This routine is optimized to make maximum use of the various
buffers and caches. */

do {
bhrequest = 0;
uptodate = 1;
while (blocks) {
--blocks;
*bhb = getblk(dev, block++, blocksize); 取缓冲页
if (*bhb && !buffer_uptodate(*bhb)) { 该块不在缓冲中
uptodate = 0;
bhreq[bhrequest++] = *bhb; 请求块排队
}

if (++bhb == &buflist[NBUF])
bhb = buflist; bhb是buflist上的循环指针,指向下一要读的块

/* If the block we have on hand is uptodate, go ahead
and complete processing. */
if (uptodate) 如果当前块在缓冲中
break;
if (bhb == bhe) 如果请求块队列已满
break;
}

/* Now request them all */
if (bhrequest) {
ll_rw_block(READ, bhrequest, bhreq); 读请求块
}

do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe); 等待块解锁,即读完成,对缓冲块无效
if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
left = 0;
break;
}
}
if (left < blocksize - offset)
chars = left; 如果读终止于块内,则向用户拷贝剩余字节
else
chars = blocksize - offset; 拷贝到块尾
*ppos += chars;
left -= chars;
read += chars; 已读的字节数
if (*bhe) {
copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars; 更新用户指针
} else {
while (chars-- > 0)
put_user(0,buf++); 向用户传送0字节
}
offset = 0; 开始从块边界拷贝
if (++bhe == &buflist[NBUF])
bhe = buflist; bhe是buflist上的循环指针,指向下一要向用户写的块
} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
if (bhe == bhb && !blocks)
break;
} while (left > 0);

/* Release the read-ahead blocks */
while (bhe != bhb) { 剩余未向用户写的块属于预读块
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
};
if (!read)
return -EIO;
filp->f_reada = 1;
return read;
}
ssize_t block_write(struct file * filp, const char * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
ssize_t blocksize, blocksize_bits, i, buffercount, write_error;
ssize_t block, blocks;
loff_t offset;
ssize_t chars;
ssize_t written;
struct buffer_head * bhlist[NBUF];
size_t size;
kdev_t dev = inode->i_rdev;
struct buffer_head * bh, *bufferlist[NBUF];
register char * p;

if (is_read_only(dev))
return -EPERM;

written = write_error = buffercount = 0;
blocksize = BLOCK_SIZE;
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];

i = blocksize;
blocksize_bits = 0;
while(i != 1) {
blocksize_bits++;
i >>= 1;
}

block = *ppos >> blocksize_bits;
offset = *ppos & (blocksize-1);

if (blk_size[MAJOR(dev)])
size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
else
size = INT_MAX;
while (count>0) {
if (block >= size)
return written ? written : -ENOSPC;
chars = blocksize - offset;
if (chars > count)
chars=count;

#if 0
/* get the buffer head */
{
struct buffer_head * (*fn)(kdev_t, int, int) = getblk;
if (chars != blocksize)
fn = bread;
bh = fn(dev, block, blocksize);
if (!bh)
return written ? written : -EIO;
if (!buffer_uptodate(bh))
wait_on_buffer(bh);
}
#else
bh = getblk(dev, block, blocksize);
if (!bh)
return written ? written : -EIO;

if (!buffer_uptodate(bh))
{ 如果要写的块不在块缓冲之中或者要读的块尚未读取
if (chars == blocksize) 写一整块
wait_on_buffer(bh); 对新缓冲块来说是空操作
else
{ 要写的位置起始于块的中间
bhlist[0] = bh;
if (!filp->f_reada || !read_ahead[MAJOR(dev)]) {
/* We do this to force the read of a single buffer */
blocks = 1; 当前位置上读取一块
} else { 从当前位置读取多个块
/* Read-ahead before write */
blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
if (block + blocks > size) blocks = size - block;
if (blocks > NBUF) blocks=NBUF;
if (!blocks) blocks = 1;
for(i=1; i<blocks; i++)
{
bhlist[ i ] = getblk (dev, block+i, blocksize);
if (!bhlist[ i ])
{
while(i >= 0) brelse(bhlist[i--]);
return written ? written : -EIO;
}
}
}
ll_rw_block(READ, blocks, bhlist); 提交写任务
for(i=1; i<blocks; i++) brelse(bhlist[ i ] );
wait_on_buffer(bh); 等待读写完成
if (!buffer_uptodate(bh)) {
brelse(bh); 如果有一个块读写错误,则返回错误
return written ? written : -EIO;
}
};
};
#endif
block++;
p = offset + bh->b_data;
offset = 0;
*ppos += chars;
written += chars;
count -= chars;
copy_from_user(p,buf,chars); 从用户拷贝数据到缓冲块
p += chars;
buf += chars;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh; 将产生的脏块排队
else
brelse(bh);
if (buffercount == NBUF){ 当脏块数超过缓冲区数
ll_rw_block(WRITE, buffercount, bufferlist); 写脏块
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[ i ]); 等待完成
if (!buffer_uptodate(bufferlist[ i ]))

write_error=1;
brelse(bufferlist[ i ]);
}
buffercount=0;
}
balance_dirty(dev);
if (write_error)
break;
}
if ( buffercount ){ 写剩余的脏块
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[ i ]);
if (!buffer_uptodate(bufferlist[ i ]))
write_error=1;
brelse(bufferlist[ i ]);
}
}
filp->f_reada = 1;
if(write_error)
return -EIO;
return written;
}
static int block_fsync(struct file *filp, struct dentry *dentry, int datasync)
{
return fsync_dev(dentry->d_inode->i_rdev); 刷新与块设备相关联的脏块
}
static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
unsigned long arg)
{
if (inode->i_bdev->bd_op->ioctl)
return inode->i_bdev->bd_op->ioctl(inode, file, cmd, arg);
return -EINVAL;
}
const struct block_device_operations * get_blkfops(unsigned int major)
{
const struct block_device_operations *ret = NULL;

/* major 0 is used for non-device mounts */
if (major && major < MAX_BLKDEV) {
#ifdef CONFIG_KMOD
if (!blkdevs[major].bdops) {
char name[20];
sprintf(name, "block-major-%d", major);
request_module(name);
}
#endif
ret = blkdevs[major].bdops;
}
return ret;
}
int blkdev_put(struct block_device *bdev, int kind)
{
int ret = 0;
kdev_t rdev = to_kdev_t(bdev->bd_dev); /* this should become bdev */
down(&bdev->bd_sem);
/* syncing will go here */
lock_kernel();
if (kind == BDEV_FILE || kind == BDEV_FS)
fsync_dev(rdev);
if (atomic_dec_and_test(&bdev->bd_openers)) {
/* invalidating buffers will go here */
invalidate_buffers(rdev);
}
if (bdev->bd_op->release) {
struct inode * fake_inode = get_empty_inode();
ret = -ENOMEM;
if (fake_inode) {
fake_inode->i_rdev = rdev;
ret = bdev->bd_op->release(fake_inode, NULL);
iput(fake_inode);
}
}
if (!atomic_read(&bdev->bd_openers))
bdev->bd_op = NULL; /* we can't rely on driver being */
/* kind to stay around. */
unlock_kernel();
up(&bdev->bd_sem);
return ret;
}
块设备文件inode的初始化
=======================
; fs/devices.c
void init_special_inode(struct inode *inode, umode_t mode, int rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = to_kdev_t(rdev);
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev);
inode->i_bdev = bdget(rdev);
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)/n", mode);
}
struct block_device *bdget(dev_t dev)
{
struct list_head * head = bdev_hashtable + hash(dev);
struct block_device *bdev, *new_bdev;
spin_lock(&bdev_lock);
bdev = bdfind(dev, head);
spin_unlock(&bdev_lock);
if (bdev)
return bdev;
new_bdev = alloc_bdev(); 使用kmem_cache_alloc()分配内存
if (!new_bdev)
return NULL;
atomic_set(&new_bdev->bd_count,1);
new_bdev->bd_dev = dev;
new_bdev->bd_op = NULL;
spin_lock(&bdev_lock);
bdev = bdfind(dev, head);
if (!bdev) {
list_add(&new_bdev->bd_hash, head);
spin_unlock(&bdev_lock);
return new_bdev;
}
spin_unlock(&bdev_lock);
destroy_bdev(new_bdev); 使用kmem_cache_free()释放内存
return bdev;
}


 
Re: 分析块设备文件接口和RAMDISK驱动程序的结构

mark_buffer_protected(rbh); 保护缓冲块

有个问题: 2.4.18的内核中mark_buffer_protected函数消失了,怎么办?

资源下载链接为: https://pan.quark.cn/s/67c535f75d4c 在机器人技术中,轨迹规划是实现机器人从一个位置平稳高效移动到另一个位置的核心环节。本资源提供了一套基于 MATLAB 的机器人轨迹规划程序,涵盖了关节空间笛卡尔空间两种规划方式。MATLAB 是一种强大的数值计算与可视化工具,凭借其灵活易用的特点,常被用于机器人控制算法的开发与仿真。 关节空间轨迹规划主要关注机器人各关节角度的变化,生成从初始配置到目标配置的连续路径。其关键知识点包括: 关节变量:指机器人各关节的旋转角度或伸缩长度。 运动学逆解:通过数学方法从末端执行器的目标位置反推关节变量。 路径平滑:确保关节变量轨迹连续且无抖动,常用方法有 S 型曲线拟合、多项式插值等。 速度加速度限制:考虑关节的实际物理限制,确保轨迹在允许的动态范围内。 碰撞避免:在规划过程中避免关节与其他物体发生碰撞。 笛卡尔空间轨迹规划直接处理机器人末端执行器在工作空间中的位置姿态变化,涉及以下内容: 工作空间:机器人可到达的所有三维空间点的集合。 路径规划:在工作空间中找到一条从起点到终点的无碰撞路径。 障碍物表示:采用二维或三维网格、Voronoi 图、Octree 等数据结构表示工作空间中的障碍物。 轨迹生成:通过样条曲线、直线插值等方法生成平滑路径。 实时更新:在规划过程中实时检测并避开新出现的障碍物。 在 MATLAB 中实现上述规划方法,可以借助其内置函数工具箱: 优化工具箱:用于解决运动学逆解路径规划中的优化问题。 Simulink:可视化建模环境,适合构建仿真复杂的控制系统。 ODE 求解器:如 ode45,用于求解机器人动力学方程轨迹执行过程中的运动学问题。 在实际应用中,通常会结合关节空间笛卡尔空间的规划方法。先在关节空间生成平滑轨迹,再通过运动学正解将关节轨迹转换为笛卡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值