Chinaunix首页 | 论坛 | 博客
  • 博客访问: 683033
  • 博文数量: 516
  • 博客积分: 4119
  • 博客等级: 上校
  • 技术积分: 4288
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-30 17:29
文章分类

全部博文(516)

文章存档

2014年(4)

2013年(160)

2012年(352)

分类:

2012-11-01 12:38:23

原文地址:linux设备模型(sysfs) 作者:kine1314

sysfs文件系统存在于内存中,是一个虚拟文件系统,其提供了kobject对象层次的视图,可以让用户以一个简单文件系统的方式来观察系统中的各种设备的拓扑结构,使用属性对象,kobject可以导出文件的方式,将内核变量提供给用户读取或者写入。在2.6内核的系统中,都拥有sysfs文件系统。sysfs将kobject对象与目录项紧密联系,通过kobject结构体中的dentry字段实现的。

sysfs根目录喜爱包括7个目录:block,bus,class,devices,firmware,module,power. 

下面就开始源代码之旅,其实,有时候看源代码确实让人挺犯恶心的。

sysfs仅仅是一个在内存中的虚拟文件系统,并没有提供实际数据的文件。而文件集合是通过ktype字段提供的,而在kobj_type中包含一个default_attrs,这是一个attribute结构体数组,这些属性负责将内核数据映射成sysfs中的文件。这个结构体中,三个字段都很重要,因为它们决定了在sysfs中出现的会是什么。其中的name就是属性名称,出现在sysfs中的就是它,而mode则是权限。

在看init_sysfs()前,先看下sysfs_dirent结构体。

struct sysfs_dirent
{
    atomic_t        s_count;
    atomic_t        s_active;
    struct sysfs_dirent    *s_parent;
    struct sysfs_dirent    *s_sibling;
    const char        *s_name;
    union {
        struct sysfs_elem_dir        s_dir;
        struct sysfs_elem_symlink    s_symlink;
        struct sysfs_elem_attr        s_attr;
        struct sysfs_elem_bin_attr    s_bin_attr;
    };
    unsigned int        s_flags;
    ino_t            s_ino;
    umode_t            s_mode;
    struct iattr        *s_iattr;
};

struct sysfs_dirent sysfs_root = {
    .s_name        = "",
    .s_count    = ATOMIC_INIT(1),
    .s_flags    = SYSFS_DIR,
    .s_mode        = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
    .s_ino        = 1,
};

看上面这两个片段,就可以清晰的知道sysfs_dirent的作用。其中,atomic_t的作用在kobject结构体中已经说明,是引用计数。这个结构体的作用就是sysfs和kobject的一个路径。里面的这些指针,使得形成一个树状。而s_name就是名字,然后s_flags就是系统路径,s_mode就是权限。

而在初始化sysfs中,开始就有这样一句: 
kmem_cache_create("sysfs_dir_cache",sizeof(struct sysfs_dirent),0,0,NULL);然后是sysfs_inode_init()(bdi_init(&sysfs_backing_dev_info))
然后register_filesystem(&sysfs_fs_type);注册文件系统,文件系统是一个很大的结构。这里可以先忽略,只明白意思就可以了。
static struct file_system_type sysfs_fs_type = {
    .name        = "sysfs",
    .get_sb        = sysfs_get_sb,
    .kill_sb    = kill_anon_super,
};

然后:kern_mount(&sysfs_fs_type);此时基本准备好了sysfs。

然后还记得在kobject源代码分析中,kobject_add吗?这是将kobject导入到sysfs中。里面调用了create_dir()。首先要定义mode的属性。然后新建立一个路径:sysfs_new_dirent(name, mode, SYSFS_DIR);将需要添加的参数加入。sd->s_dir.kobj = kobj这步需要注意。这是指向的kobject

struct sysfs_elem_dir {
    struct kobject        *kobj;
    /* children list starts here and goes through sd->s_sibling */
    struct sysfs_dirent    *children;
};
而在函数开头声明了这样一个结构体:struct sysfs_addrm_cxt acxt;
struct sysfs_addrm_cxt {
    struct sysfs_dirent    *parent_sd;
    struct inode        *parent_inode;
    struct sysfs_dirent    *removed;
    int            cnt;
};
sysfs_addrm_start(&acxt, parent_sd);将父节点拷入这样一个结构体中,看函数的名字就知道:入口地址的开始。
rc=sysfs_add_one(&acxt, sd);增加一个我们定义的节点sd.
sysfs_addrm_finish(&acxt);增加完毕。
return rc;
至此,联系上次分析的kobject,则可以知道,当添加一个kobject的时候,系统通过指针,使得相关方,彼此指向,形成联系。

而内核在默认集合之上,继续添加新属性时也提供了相应的机制,下面就来看看对应的源代码函数:
sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
这就是内核函数中关于添加新属性的函数,看看里面的参数kobject、attribute.意思很直接,可是里面却不是像函数这样看来如此直白。但是程序思路却还算清晰。

函数的调用关系是如此的:sysfs_create_file()-->sysfs_add_file()-->sysfs_add_file_mode()-->sysfs_new_dirent()

在增加文件模块函数中,正如上面所讲解的函数,对照着看看,注意传递参数的不同。

int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,const struct attribute *attr, int type, mode_t amode)
{
    umode_t mode = (amode & S_IALLUGO) | S_IFREG;
    struct sysfs_addrm_cxt acxt;
    struct sysfs_dirent *sd;
    int rc;
    sd = sysfs_new_dirent(attr->name, mode, type);
    if (!sd)
        return -ENOMEM;
    sd->s_attr.attr = (void *)attr;
    sysfs_addrm_start(&acxt, dir_sd);
    rc = sysfs_add_one(&acxt, sd);
    sysfs_addrm_finish(&acxt);
    if (rc)
        sysfs_put(sd);
    return rc;
}

struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
{
    char *dup_name = NULL;
    struct sysfs_dirent *sd;
    if (type & SYSFS_COPY_NAME) {name = dup_name = kstrdup(name, GFP_KERNEL);}
    sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
    if (sysfs_alloc_ino(&sd->s_ino))goto err_out2;
    atomic_set(&sd->s_count, 1);
    atomic_set(&sd->s_active, 0);
    sd->s_name = name;
    sd->s_mode = mode;
    sd->s_flags = type;
    return sd;
    ......
}
其实过程无非就是建立空间,增加引用技术,建立彼此之间的连接,初始化一些需要的东西,然后找到需要挂接的地址(parent)然后增加一个节点。
而创建符号连接则需要使用int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)函数。
符号名是由name指定的,连接则是由kobj对应的目录映射到target指定的目录。下面来看源代码(其中省略一些语句):

int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
{
    struct sysfs_dirent *parent_sd = NULL;
    struct sysfs_dirent *target_sd = NULL;
    struct sysfs_dirent *sd = NULL;
    struct sysfs_addrm_cxt acxt;
//如果是头一个,则直接夫节点指向root.否则指向传入的kobj.
    if (!kobj)
        parent_sd = &sysfs_root;
    else
        parent_sd = kobj->sd;
    spin_lock(&sysfs_assoc_lock);
    if (target->sd)
        target_sd = sysfs_get(target->sd);
    spin_unlock(&sysfs_assoc_lock);
    sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);//新建路径
    if (!sd)
        goto out_put;

    sd->s_symlink.target_sd = target_sd;/目标指定
    target_sd = NULL;    /* reference is now owned by the symlink */

    sysfs_addrm_start(&acxt, parent_sd);//在入口地址连接
    error = sysfs_add_one(&acxt, sd);//增加一个节点
    sysfs_addrm_finish(&acxt);//节点增加结束
    ......
}

上面两个就是基本的三个函数,其中已经把sysfs和kobject的关系说的挺明白,如何相互关联,和sysfs虚拟文件系统如何形成。其实读源代码的目的就是弄清楚设备结构的意思,其实,就整个文件系统来说,sysfs其中还要牵扯到很多结构体,和文件的读写。这些源代码以后再读,都读完后,就会明白linux的文件系统的整个结构,头脑中就会逐渐的清晰。在linux中不是进程就是文件嘛。fs/sysfs目录下,还有很多函数,可以慢慢的读。
寻找驱动程序driver_find(drv->name,drv->bus)通过总线和name则可以进行寻找.在设备集合kset中找到对应的kset_find_obj(bus->p->drivers_kset, name);然后是:bus_add_driver(drv);这个函数很关键:首先bus_get(drv->bus),然后则是在driver的私有属性中设置:priv->driver = drv;drv->p = priv;priv->kobj.kset = bus->p->drivers_kset;让他们形成相关联的形式,这也是必须的,然后就是kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name)-->module_add_driver(drv->owner, drv)-->driver_create_file(drv, &driver_attr_uevent);这个函数中,主体就是这一句:sysfs_create_file(&drv->p->kobj, &attr->attr);与sysfs形成关联关系。-->add_bind_files(drv);然后接下来:sysfs_create_group(&drv->p->kobj, groups[i]);就完成了注册。
阅读(245) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~