linux设备管理-dev目录下创建和寻找某一设备文件

这是一篇网上找到的文章分析的很精彩,这篇文章的分析基于网络高手文章中没有介绍的方面。
如果有什么不对的地方,请指正。
谢谢

file_operations流程跟踪
术语
描述符:其实就是结构体
在linux可以把设备看作文件并提供了和文件一样的统一的访问接口,相信大家已经有了一
点的了解,其底层对设备的不同操作主要是由file_operations描述符来控制,其封装了各
种设备的操作方法,如:open/read/write/ioctl等等。
linux中每个文件或目录都对一个名为inode的结构体,其中有一个字段i_fop就是存储文件
的操作方法file_operations实例对象。以下我们将对文件的file_operations如何赋值给
inode->i_fop进行一个简单的跟踪。
linux中,支持很多的文件系统,每个文件系统使用之前都必须要先注册,其注册的过程大
体一致,以下我们将以ext3文件系统为例跟踪file_operations的注册过程。
================================================================================
1,每一种文件系统都有自己的type,在启动时的时候有boot的参数传递告知,最后在mount的
时候根据参数确定那种文件系统。
2,在这里使用的是ext3的文件系统。其实其他的文件系统也有相应的操作。最后完成的就是
调用getsb来完成填充superblock的相关成员变量。以后的基本操作找到相应的操作函数。
================================================================================
在文件系统的初始化阶段调用register_filesystem(&ext3_fs_type)函数将以下结构:
static struct file_system_type ext3_fs_type = {
.owner   = THIS_MODULE,
.name   = "ext3",
.get_sb  = ext3_get_sb,
.kill_sb  = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
注册到全局链表file_systems(fs/filesystems.c)中。
其中函数指针get_sb在超级块全局链表super_blocks(fs/super.c)中查找文件系统对应的超级
块描述符并返回,如果没有则创建新的描述符。
当get_sb函数指针被调用,触发ext3_fill_super(fs/ext3/super.c其为get_sb参数之一的函数
指针)函数调用ext3_iget返回超级块所对应的文件系统的根目录的inode,并根据inode->i_mode
来判断文件类型赋予相应的操作,源代码(fs/ext3/inode.c)如下:
================================================================================
1,如何从path找到相应的inode这是关键
假设 app中使用了int fd = open(“/dev/ttyS0”,777);这样的怎么在内核找到相应的inode并且把
相关的inode返回给相应的调用程序?
int devtmpfs_create_node(struct device *dev)这个函数是内核调用的,在dev目录下建立相应的
设备节点名。这个函数的调用关系介绍一下,一面突然看着有点陌生的感觉。
int device_add(struct device *dev)
 |
 devtmpfs_create_node(dev);
  |
  err = vfs_mknod(nd.path.dentry->d_inode,dentry, mode, dev->devt);
device_add调用和会在注册设备和设备的相关信息,同时在dev目录下创建相应的设备节点。
下面来看看这个函数在涉及到本文相关内容的部分:

if (!nodename)
  return -ENOMEM;

 if (mode == 0)
  mode = 0600;
 if (is_blockdev(dev))
  mode |= S_IFBLK;
 else
  mode |= S_IFCHR;
  
初始化mode。在今后的函数vfsmknode中会使用。确定到底是那种文件类型,匹配相应的函数来完成
file_operation。请查看原来的分析即
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
这个函数的调用关系其实也很复杂,关键在于不同的文件系统类型有自己的mknode方法。

以上代码表示,其实在节点创建的时候就已经确定了设备节点类型了。如果是char字符类型的
设备,那么相应的操作就是下面提到的def_chr_fops。以此类推,其他的类型设备相应的操作。

2,方向的问题。用户主动调用mknode和内核初始化的时候自己创建设备。
 (a)如果是用户在命令行执行 #mknode就会找到以下函数init_special_inode。这函数其实是
 相关的文件系统的mknode函数。
 (b)如果是内核初始化的时候调用的,那么就是上述devtmpfs_create_node。这个函数也会在
 文件系统的pathlook,dolook,等VFS层的函数中创建并且初始化inode。其实任何文件包括
 设备,在linux中都是以文件来对待。区分文件的标识就是inode。

3,在内核中要打开一个文件,首先应该找到这个文件,而查找文件的过程在vfs里面是由do_path_lookup
或者path_lookup_open函数来完成的,关于文件路径查找就不分析了。层次太多设计的内容太杂。
这两个函数将用户传进来的字符串表示的文件路径转换成一个dentry结构,并建立好相应的inode和file结构,
将指向file的描述符返回用户。用户随后通过文件描述符,来访问这些数据结构。
有了以上的了解最后来看看假设我们内核初始化的时候注册了设备ttyS0这样的一个device。
open就会找到相应的inode和file operations。
================================================================================
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext3_file_inode_operations;
inode->i_fop = &ext3_file_operations;
ext3_set_aops(inode);
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = &ext3_dir_inode_operations;
inode->i_fop = &ext3_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
if (ext3_inode_is_fast_symlink(inode)) {
inode->i_op = &ext3_fast_symlink_inode_operations;
nd_terminate_link(ei->i_data, inode->i_size,
sizeof(ei->i_data) - 1);
} else {
inode->i_op = &ext3_symlink_inode_operations;
ext3_set_aops(inode);
}
} else {
inode->i_op = &ext3_special_inode_operations;
if (raw_inode->i_block[0])
init_special_inode(inode, inode->i_mode,
old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));
else
init_special_inode(inode, inode->i_mode,
new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
}
在此,我主要讲解对init_special_inode的跟踪,其他函数希望感兴趣的读者自己跟踪。
在对 init_special_inode进行跟踪之前先来说明一下linux的文件路径名查找,当要对
某个索引节点进行处理时,代码首先检查路径名中与第一个名字匹配的目录项,以获得
相应的索引节点。然后,从磁盘读出包含那个索引节点的目录文件,并检查与第二个名
字匹配的目录项,以获得相应的索引节点。对于包含在路径中的每个名字,这个过程反
复执行。那么,当要创建新文件(创建新inode)的时,系统调用其父inode的方法来创建
子inode的。
顾名思义,init_special_inode函数的作用就是初始化特殊的inode,先不多说,看下
面的源码(fs/inode.c):
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = rdev;
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = 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 i_mode (%o) for"
" inode %s:%lu/n", mode, inode->i_sb->s_id,
inode->i_ino);
}
==================================================================================
在这里一定要介绍一下内核的版本问题,其实上面的文章分析中也涉及很多概念,在2.6.28
的内核中是没有的,比如:devtmpfs。
但是cdev_map这个结构确实是在28的内核中存在,软件管理方式差不多。
==================================================================================

根据上面代码可以得出,init_special_inode对字符设备文件、块设备文件、管道文件、网
络设备文件进行处理,分别赋予inode中的file_operations变量def_chr_fops/def_blk_fops/
def_fifo_fops/bad_sock_fops。
def_chr_fops,fs/char_dev.c文件中的全局变量,源码如下:
const struct file_operations def_chr_fops = {
.open = chrdev_open,
};
chrdev_open,定义在同文件下,其调用了kobj_lookup函数在cdev_map链表中查找对应的
struct kobj_map对象。
现在我们反过来看驱动,在fs/char_dev.c文件中维护了一个struct kobj_map类型的链表cdev_map,
当注册字符设备时file_operations对象被添加到cdev描述符中,添加设备的时候调用kobj_map(
drivers/base/map.c)其最后一个参数为void×类型,传递cdev对象给kobj_map对象。
以上只是一个简单的对file_operations描述符的跟踪,基本上描述了字符设备的这一过程,由于
时间的关系以上的细节描述得不是很详细,而且也没有完全跟踪,希望有兴趣的读者能够自己跟踪。
(注:以上文章仅供参考。由于水平有限,如有问题还请谅解。
基于2.6.32的内核)。

======================================================================

相关介绍在网上很多。也很精彩,由于在查看alsa的内核初始化。

声卡的设备的注册使用了相关的概念,结合以前看过的分析稍作整理。

======================================================================

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值