主设备号、次设备号分配并注册主次设备号
设备号是在驱动module中分配并注册的,也就是说,驱动module拥有这个设备号(我的理解),而/dev目录下的设备文件是根据这个设备号创建的,因此,当访问/dev目录下的设备文件时,驱动module就知道,自己该出场服务了(当然是由内核通知)。
在Linux内核看来,主设备号标识设备对应的驱动程序,告诉Linux内核使用哪一个驱动程序为该设备(也就是/dev下的设备文件)服务;而次设备号则用来标识具体且唯一的某个设备。
在内核中,用dev_t类型(其实就是一个32位的无符号整数)的变量来保存设备的主次设备号,其中高12位表示主设备号,低20位表示次设备号。
设备获得主次设备号有两种方式:一种是手动给定一个32位数,并将它与设备联系起来(即用某个函数注册);另一种是调用系统函数给设备动态分配一个主次设备号。
对于手动给定一个主次设备号,使用以下函数:
int register_chrdev_region(dev_t first,
unsigned int -count,
char *name)
其中first是我们手动给定的设备号,count是所请求的连续设备号的个数,而name是和该设备号范围关联的设备名称,它将出现在/proc/devices和sysfs中。
比如,若first为0x3FFFF0,count为0x5,那么该函数就会为5个设备注册设备号,分别是0x3FFFF0、 0x3FFFF1、 0x3FFFF2、 0x3FFFF3、 0x3FFFF4,其中0x3(高12位)为这5个设备所共有的主设备号(也就是说这5个设备都使用同一个驱动程序)。而0xFFFF0、 0xFFFF1、 0xFFFF2、 0xFFFF3、 0xFFFF4就分别是这5个设备的次设备号了。需要注意的是,若count的值太大了,那么所请求的设备号范围可能会和下一个主设备号重叠。比如若first还是为0x3FFFF0,而count为0x11,那么first+count=0x400001,也就是说为最后两个设备分配的主设备号已经不是0x3,而是0x4了!用这种方法注册设备号有一个缺点,那就是若该驱动module被其他人广泛使用,那么无法保证注册的设备号是其他人的Linux系统中未分配使用的设备号。
对于动态分配设备号,使用以下函数:
int alloc_chrdev_region(dev_t *dev,
unsigned int -firstminor,
unsigned int -count,
char *name)
该函数需要传递给它指定的第一个次设备号firstminor(一般为0)和要分配的设备数count,以及设备名,调用该函数后自动分配得到的设备号保存在dev中。动态分配设备号可以避免手动指定设备号时带来的缺点,但是它却也有自己的缺点,那就是无法预先在/dev下创建设备节点,因为动态分配设备号不能保证在每次加载驱动module时始终一致(其实若在两次加载同一个驱动module之间并没有加载其他的module,那么自动分配的设备号还是一致的,因为内核分配设备号并不是随机的,但是书上说某些内核开发人员预示不久的将来会用随机方式进行处理),不过,这个缺点可以避免,因为在加载驱动module后,我们可以读取/proc/devices文件以获得Linux内核分配给该设备的主设备号。
与主次设备号相关的3个宏:
MAJOR(dev_t dev):根据设备号dev获得主设备号;
MINOR(dev_t dev):根据设备号dev获得次设备号;
MKDEV(int major, int minor):根据主设备号major和次设备号minor构建设备号。
http://blog.chinaunix.net/u1/51562/showart_1076295.html
linux2.6内核引入sysfs文件系统,sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统,可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图,与提供进程和状态信息的proc文件系统十分类似。
sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核的数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系,其顶级目录包括block,bus,drivers,class,power和firmware等.
wugang@wugang-desktop:~$ ls /sys //运行环境ubuntu 8.04(2.6.16)
block bus class devices firmware fs kernel module power slab
它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互,该文件系统是当前系统上实际设备树的一个直观反应,它是通过kobject子系统来建立这个信息的,当一个kobject被创建的时候,对应的文件和目录也就被创建了,位于 /sys下的相关目录下,既然每个设备在sysfs中都有唯一对应的目录,那么也就可以被用户空间读写了。
你可能根本没有去关心过sysfs文件系统的挂载过程,它是这样被挂载的。
mount -t sysfs sysfs /sys
sysfs是一个特殊文件系统,并没有一个实际存放文件的介质。sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。重启后里面的信息当然就没了
sysfs文件系统与kobject结构紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject 是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,Kobject是组成设备模型的基本结构。类似于C++中的基类,它嵌入于更大的对象的对象中,用来描述设备模型的组件。如bus,devices, drivers 等。都是通过kobject连接起来了,形成了一个树状结构。这个树状结构就与/sys向对应。
注:这里对kobject结构不进行介绍了,http://blog.chinaunix.net/u1/55599/showart.php?id=1086478 详细解释。
sysfs就是利用VFS的接口去读写kobject的层次结构,建立起来的文件系统。 kobject的层次结构的注册与注销XX_register()形成的。文件系统是个很模糊广泛的概念, linux把所有的资源都看成是文件,让用户通过一个统一的文件系统操作界面,也就是同一组系统调用,对属于不同文件系统的文件进行操作。这样,就可以对用户程序隐藏各种不同文件系统的实现细节,为用户程序提供了一个统一的,抽象的,虚拟的文件系统界面,这就是所谓"VFS(Virtual Filesystem Switch)"。这个抽象出来的接口就是一组函数操作。
我们要实现一种文件系统就是要实现VFS所定义的一系列接口,file_operations, dentry_operations, inode_operations等,供上层调用。file_operations是描述对每个具体文件的操作方法(如:读,写),dentry_operations结构体指明了VFS所有目录的操作方法, 而inode_operations提供所有结点的操作方法。
举个例子,我们写C程序,open(“hello.c”, O_RDONLY),它通过系统调用的流程是这样的
open() -> /*用户空间*/
-> 系统调用-> /*通过系统调用,程序进程内核状态*/
sys_open() -> filp_open()-> dentry_open() -> file_operations->open() /*内核空间*/
不同的文件系统,调用不同的file_operations->open(),在sysfs下就是sysfs_open_file()。
我们使用不同的文件系统,就是将它们各自的文件信息都抽象到dentry和inode中去。这样对于高层来说,我们就可以不关心底层的实现,我们使用的都是一系列标准的函数调用。这就是VFS的精髓,实际上就是面向对象。
注意sysfs是典型的特殊文件。它存储的信息都是由系统动态的生成的,它动态的包含了整个机器的硬件资源情况。从sysfs读写就相当于向 kobject层次结构提取数据。
下面列举一个事例:
每当我们新增一个kobject结构的时候,同时会在/sys下创建一个目录。
kobject_add() -> create_dir() -> sysfs_create_dir()
135 int sysfs_create_dir(struct kobject * kobj)
136 {
137 struct dentry * dentry = NULL;
138 struct dentry * parent;
139 int error = 0;
140
141 BUG_ON(!kobj);
142
143 if (kobj->parent)
144 parent = kobj->parent->dentry;
145 else if (sysfs_mount && sysfs_mount->mnt_sb)
146 parent = sysfs_mount->mnt_sb->s_root;
147 else
148 return -EFAULT;
149
150 error = create_dir(kobj,parent,kobject_name(kobj),&dentry);
151 if (!error)
152 kobj->dentry = dentry;
153 return error;
154 }
143-148就是找到父辈的kobject,再调用create_dir();
95 static int create_dir(struct kobject * k, struct dentry * p,
96 const char * n, struct dentry ** d)
97 {
98 int error;
99 umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
100
101 down(&p->d_inode->i_sem);
102 *d = sysfs_get_dentry(p,n);
103 if (!IS_ERR(*d)) {
104 error = sysfs_create(*d, mode, init_dir);
105 if (!error) {
106 error = sysfs_make_dirent(p->d_fsdata, *d, k, mode,
107 SYSFS_DIR);
108 if (!error) {
109 p->d_inode->i_nlink++;
110 (*d)->d_op = &sysfs_dentry_ops;
111 d_rehash(*d);
112 }
113 }
114 if (error && (error != -EEXIST))
115 d_drop(*d);
116 dput(*d);
117 } else
118 error = PTR_ERR(*d);
119 up(&p->d_inode->i_sem);
120 return error;
121 }
*99行,设置‘文件’ 属性,101获取信号量。
(1)sysfs_get_dentry()
102行sysfs_get_dentry()。它的作用是根据父辈dentry和文件名得到 dentry结构。首先在缓存中找,如果找到就返回,找不到就用d_alloc()新建一个dentry结构。
204 static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
205 struct nameidata *nd)
206 {
207 struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata;
208 struct sysfs_dirent * sd;
209 int err = 0;
210
211 list_for_each_entry(sd, &parent_sd->s_children, s_sibling) {
212 if (sd->s_type & SYSFS_NOT_PINNED) {
213 const unsigned char * name = sysfs_get_name(sd);
214
215 if (strcmp(name, dentry->d_name.name))
216 continue;
217
218 if (sd->s_type & SYSFS_KOBJ_LINK)
219 err = sysfs_attach_link(sd, dentry);
220 else
221 err = sysfs_attach_attr(sd, dentry);
222 break;
223 }
224 }
225
226 return ERR_PTR(err);
227 }
前面讲过lookup函数的作用。它在inode代表的文件夹下查找有没有名为dentry.d_name.name的文件。如果有,就将其对应的inode结构从信息的载体中读出来。没有的话,就返回null
#define SYSFS_NOT_PINNED /
(SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK)
但是sysfs的lookup还有它不同之处。其他文件系统像ext3格式中普通文件的inode,在文件创建之时就已经创建了。但是,sysfs 不一样,它在创建普通文件时,只是先创建一个sysfs_dirent结构。创建inode的工作是推迟到lookup函数来完成的。
sysfs_attach_attr()和sysfs_attach_link()的作用就是根据dentry和sysfs_dirent新建一个inode。
(2)sysfs_create()分析 (104行)
sysfs_create()->sysfs_new_inode(mode) -> new_inode(sysfs_sb)
创建一个新的索引节点inode。sysfs_sb是sysfs的超级块(super_block)结构。mode则是inode的属性,它记录了如下信息,比如,文件类型(是文件夹,链接,还是普通文件),inode的所有者,创建时间等等。
(3)sysfs make dirent()分析 (106行)
至此,我们得到了一个dirent结构,初始化,再把它连接到上层目录的sysfs_dirent的s_children链表里去。sysfs_make_dirent()为刚刚新建出来的dentry建立一个dirent结构。并将dentry和dirent联系起来。
注:事例代码我自己还没有分析很透测,读者可以自行分析。具体代码参考:sysfs.h。明白具体调用过程。
http://www.360doc.com/content/10/0913/15/3038654_53334742.shtml