您的位置:首页 > 运维架构 > Linux

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

2012-11-18 04:10 691 查看
这是一篇网上找到的文章分析的很精彩,这篇文章的分析基于网络高手文章中没有介绍的方面。

如果有什么不对的地方,请指正。

谢谢

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的内核初始化。

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

======================================================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: