linux2.6内核引入sysfs文件系统,sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统,可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图,与提供进程和状态信息的proc文件系统十分类似。
sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核的数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系,其顶级目录包括block,bus,drivers,class,power和firmware等.
它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互,该文件系统是当前系统上实际设备树的一个直观反应,它是通过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结构的时候,同时会在/sys下创建一个目录。kobject_add() -> create_dir() -> sysfs_create_dir()。
Sys系统的其他使用。
根据内核中/inlude/linux/sysfs.h中的接口函数声明,sys不仅可以创建目录,还可以创建文件,连接,bin文件,属性组等。但所有函数都必须提供kobject作为输入参数。
创建文件即是在kobject对应的目录下增加属性文件。
创建bin文件即是在kobject对应的目录下增加bin属性文件。
创建链接是创建一个连接指向对应的kobject创建的目录.
创建属性组即首先创建一个目录,然后在该目录内依次添加所有属性文件
并且这些创建都需要在kobject注册之后使用/inlude/linux/sysfs.h中定义的接口函数和kobject进行添加。
//sysfs的文件系统的所读写的信息是存放在kobject当中,那么dentry是如何与kobject联系起来的呢?是通过sysfs_dirent
//sysfs文件系统有自己的dirent结构,dirent = directory entry (目录实体)。sysfs中,每一个dentry对应了一个dirent结构,dentry->d _fsdata是一个void的指针,它指向sysfs_dirent结构
//对于sysfs下的文件夹而言,denrty, dirent, kobject之间通过指针相互联系起来。
//dentry->d_fsdata = &sysfs_dirent;
//sysfs_dirent->element = &kobject;
//kobject->dentry = &dentry;
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的初始化和挂载
Sysfs文件系统的初始化是在sysfs_init()中完成的,代码如下:
int __init sysfs_init(void)
{
int err = -ENOMEM;
//创建一个分配sysfs_dirent的cache
sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",sizeof(struct sysfs_dirent),0, 0, NULL);
if (!sysfs_dir_cachep)
goto out;
err = sysfs_inode_init();//初始化BDI(backing device infor )信息
if (err)
goto out_err;
//注册sysfs文件系统s
err = register_filesystem(&sysfs_fs_type);
if (!err) {
sysfs_mount = kern_mount(&sysfs_fs_type);//挂载sysfs文件系统
if (IS_ERR(sysfs_mount)) {
printk(KERN_ERR "sysfs: could not mount!\n");
err = PTR_ERR(sysfs_mount);
sysfs_mount = NULL;
unregister_filesystem(&sysfs_fs_type);
goto out_err;
}
} else
goto out_err;
out:
return err;
out_err:
kmem_cache_destroy(sysfs_dir_cachep);
sysfs_dir_cachep = NULL;
goto out;
}
每个kobject对应sysfs中的一个目录,kobject的每个属性对应sysfs文件系统中的文件.
struct sysfs_dirent就是用来做kobject与dentry的互相转换用的.它们的关系如下图所示:
上图表示的是一个kobject的层次结构.dentry的d_fsdata字段指定该结点所表示的sysfs_dirent.sysfs_dirent.s_parent表示它的父kobject. sysfs_dirent.s_sibling表示它的兄弟结点. sysfs_dirent.s_dir.children表示它所属的子节点.
从上图可知.如果要遍历一个结点下面的子结点,只需要找到sysfs_dirent.s_dir.children结点然后按着子节点的s_sibling域遍历即可.
当然,有时候也需要从struct sysfs_dirent导出它所属的dentry结点.我们在代码中遇到的时候再进行分析.
//Sysfs文件系统的file_system_type定义如下:
static struct file_system_type sysfs_fs_type = {
.name = "sysfs",
.get_sb = sysfs_get_sb,
.kill_sb = kill_anon_super,
};
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
BUG_ON(strchr(fs->name, '.'));
if (fs->next)
return -EBUSY;
write_lock(&file_systems_lock);
p = find_filesystem(fs->name, strlen(fs->name));//在系统的总链表中file_systems查找时否有此文件系统
if (*p)
res = -EBUSY;
else
*p = fs;//没有则将fs赋给p,即添加到file_systems链表的尾端,即注册完成
write_unlock(&file_systems_lock);
return res;
}
static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
struct file_system_type **p;
for (p=&file_systems; *p; p=&(*p)->next)//遍历file_systems链表查找
if (strlen((*p)->name) == len &&strncmp((*p)->name, name, len) == 0)
break;
return p;
}
#define kern_mount(type) kern_mount_data(type, NULL)
#define MS_KERNMOUNT (1<<22)
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
}
struct vfsmount *vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct vfsmount *mnt;
char *secdata = NULL;
int error;
if (!type)
return ERR_PTR(-ENODEV);
error = -ENOMEM;
//对于每个安装操作,内核必有在内存中保存安装点和安装标志,以及要安装文件系统与其它已安装文件系统之间的关系,这样的信息保存在已安装文件系统描述符中
mnt = alloc_vfsmnt(name);//获取一个mnt结构体,并做一些初始化工作
if (!mnt)
goto out;
if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
secdata = alloc_secdata();//分配一页
if (!secdata)
goto out_mnt;
error = security_sb_copy_data(data, secdata);//将data拷贝到secdata地址处,不过data是NULL
if (error)
goto out_free_secdata;
}
//调用文件系统控制结构体的get_sb()方法,获取文件系统的超级块,这里就是sysfs_get_sb.
error = type->get_sb(type, flags, name, data, mnt);
if (error < 0)
goto out_free_secdata;
BUG_ON(!mnt->mnt_sb);
//sysfs未实现
error = security_sb_kern_mount(mnt->mnt_sb, secdata);
if (error)
goto out_sb;
//mnt_mountpoint:指向该文件系统安装点对应的dentry;
//mnt_root:该文件系统对应的设备根目录的dentry;
mnt->mnt_mountpoint = mnt->mnt_root;//文件系统的挂载点就设置为sysfs的'/'根目录项
mnt->mnt_parent = mnt;//指向自己
up_write(&mnt->mnt_sb->s_umount);
free_secdata(secdata);
return mnt;//成功完成mnt的创建和sb超级块等信息的填充
out_sb:
dput(mnt->mnt_root);
up_write(&mnt->mnt_sb->s_umount);
deactivate_super(mnt->mnt_sb);
out_free_secdata:
free_secdata(secdata);
out_mnt:
free_vfsmnt(mnt);
out:
return ERR_PTR(error);
}
struct vfsmount *alloc_vfsmnt(const char *name)
{
struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);//从cache上摘一个slab空闲对象
if (mnt) {
//成功获取mnt内存空间,初始化之
atomic_set(&mnt->mnt_count, 1);//计数设为1
INIT_LIST_HEAD(&mnt->mnt_hash);
INIT_LIST_HEAD(&mnt->mnt_child);
INIT_LIST_HEAD(&mnt->mnt_mounts);
INIT_LIST_HEAD(&mnt->mnt_list);
INIT_LIST_HEAD(&mnt->mnt_expire);
INIT_LIST_HEAD(&mnt->mnt_share);
INIT_LIST_HEAD(&mnt->mnt_slave_list);
INIT_LIST_HEAD(&mnt->mnt_slave);
if (name) {//*name="sysfs"
int size = strlen(name) + 1;
char *newname = kmalloc(size, GFP_KERNEL);//为mnt的name分配空间
if (newname) {
memcpy(newname, name, size);
mnt->mnt_devname = newname;//name赋值给mnt结构,即"sysfs"
}
}
}
return mnt;
}
//sysfs文件系统是一个排它式的文件系统,不论被mount多少次都只产生一个sb超级块,
//如果尝试再次mount,即尝试再次调用sysfs_get_sb获取另一个sb超级块,那么将执行atomic_inc(old->s_active);增加
//已被mount的引用计数,然后如果s已经执行了alloc_super,那么调用destroy_super将其销毁,然后返回这个已被mount了的
//super_block超级块old,这样就实现了sysfs文件系统不论被mount多少次都只产生一个sb超级块的效果,所以取名为get_sb_single[luther.gliethttp]
static int sysfs_get_sb(struct file_system_type *fs_type,int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
}
int get_sb_single(struct file_system_type *fs_type,int flags, void *data, int (*fill_super)(struct super_block *, void *, int),struct vfsmount *mnt)
{
struct super_block *s;
int error;
s = sget(fs_type, compare_single, set_anon_super, NULL);//获取超级块,如果之前已经创建了sb,那么这里将不再创建,将返回上一次创建的sb,所以这表明sb超级块将只被生成1个
if (IS_ERR(s))
return PTR_ERR(s);
if (!s->s_root) {//如果是第一次mount该sysfs文件系统,那么首先填充sb超级块
s->s_flags = flags;
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);//这里对应sysfs_fill_super函数,细化sb超级块
if (error) {
up_write(&s->s_umount);
deactivate_super(s);
return error;
}
s->s_flags |= MS_ACTIVE;
}
do_remount_sb(s, flags, data, 0);//asks filesystem to change mount options,在sysfs中sysfs_ops没有实现remount_fs
return simple_set_mnt(mnt, s);//将s超级块安装到mnt这个mount节点上
}
struct super_block *sget(struct file_system_type *type,int (*test)(struct super_block *,void *),int (*set)(struct super_block *,void *),void *data)
{
struct super_block *s = NULL;
struct super_block *old;
int err;
retry:
spin_lock(&sb_lock);
if (test) {//函数指针指向compare_single()
list_for_each_entry(old, &type->fs_supers, s_instances) {//遍历文件系统中的super_block结构
if (!test(old, data))//compare_single一直返回1
continue;
if (!grab_super(old))//
goto retry;
if (s)
destroy_super(s);
return old;
}
}
if (!s) {//没有找到
spin_unlock(&sb_lock);
s = alloc_super(type);//获取一个sb超级块的控制内存,同时做部分结构初始化
if (!s)
return ERR_PTR(-ENOMEM);
goto retry;//继续尝试一次,看看是否重复了
}
err = set(s, data);//sysfs对应set_anon_super,获取一个sb超级块的设备号,major为0
if (err) {
spin_unlock(&sb_lock);
destroy_super(s);
return ERR_PTR(err);
}
s->s_type = type;//填充该sb超级块的文件系统类型type
strlcpy(s->s_id, type->name, sizeof(s->s_id));//该sb超级块的s_id为type->name,比如:"sysfs"
list_add_tail(&s->s_list, &super_blocks);//将该超级块添加到全局超级块链表super_blocks上
list_add(&s->s_instances, &type->fs_supers);//sb超级块将自己添加到该文件系统type->fs_supers管理链表上
spin_unlock(&sb_lock);
get_filesystem(type);//file_system_type结构体计数加1
return s;
}
static int compare_single(struct super_block *s, void *p)
{
return 1;
}
//type->fs_supers链表上挂上了有效的sb超级块之后,才会执行到这里
static int grab_super(struct super_block *s) __releases(sb_lock)
{
s->s_count++;//alloc_super时将设置s->s_count = S_BIAS;
spin_unlock(&sb_lock);
down_write(&s->s_umount);
if (s->s_root) {//超级块对应的目录存在,表示此文件系统已经挂载了一个有效的超级块
spin_lock(&sb_lock);
if (s->s_count > S_BIAS) {
atomic_inc(&s->s_active);
s->s_count--;
spin_unlock(&sb_lock);
return 1;//确实有一个有效的sb超级块对应的s_root目录项,那么返回1,然后由destroy_super将其销毁
}
spin_unlock(&sb_lock);
}
up_write(&s->s_umount);
put_super(s);
yield();
return 0;
}
//获取一个sb超级块的控制内存,同时做部分结构初始化
static struct super_block *alloc_super(struct file_system_type *type)
{
struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);//分配super_block结构空间
static struct super_operations default_op;
if (s) {
if (security_sb_alloc(s)) {
kfree(s);
s = NULL;
goto out;
}
INIT_LIST_HEAD(&s->s_dirty);
INIT_LIST_HEAD(&s->s_io);
INIT_LIST_HEAD(&s->s_more_io);
INIT_LIST_HEAD(&s->s_files);
INIT_LIST_HEAD(&s->s_instances);
INIT_HLIST_HEAD(&s->s_anon);
INIT_LIST_HEAD(&s->s_inodes);
init_rwsem(&s->s_umount);
mutex_init(&s->s_lock);
lockdep_set_class(&s->s_umount, &type->s_umount_key);
lockdep_set_class(&s->s_lock, &type->s_lock_key);
down_write(&s->s_umount);
s->s_count = S_BIAS;
atomic_set(&s->s_active, 1);
mutex_init(&s->s_vfs_rename_mutex);
mutex_init(&s->s_dquot.dqio_mutex);
mutex_init(&s->s_dquot.dqonoff_mutex);
init_rwsem(&s->s_dquot.dqptr_sem);
init_waitqueue_head(&s->s_wait_unfrozen);
s->s_maxbytes = MAX_NON_LFS;
s->dq_op = sb_dquot_ops;
s->s_qcop = sb_quotactl_ops;
s->s_op = &default_op;
s->s_time_gran = 1000000000;
}
out:
return s;
}
int set_anon_super(struct super_block *s, void *data)
{
int dev;
int error;
retry:
if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
return -ENOMEM;
spin_lock(&unnamed_dev_lock);
error = idr_get_new(&unnamed_dev_idr, NULL, &dev);//从radix树中,递增式的获取一个唯一整数值到&dev
spin_unlock(&unnamed_dev_lock);
if (error == -EAGAIN)
goto retry;
else if (error)
return -EAGAIN;
if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
spin_lock(&unnamed_dev_lock);
idr_remove(&unnamed_dev_idr, dev);
spin_unlock(&unnamed_dev_lock);
return -EMFILE;
}
s->s_dev = MKDEV(0, dev & MINORMASK);//生成major为0的超级块设备号
return 0;
}
int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb)
{
mnt->mnt_sb = sb;//mnt的sb
mnt->mnt_root = dget(sb->s_root);//对应的根目录项
return 0;
}
//给申请的sb超级块填充细化数据
static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;//块大小4k
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;//12
sb->s_magic = SYSFS_MAGIC;//magic代号
/*
static const struct super_operations sysfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
.delete_inode = sysfs_delete_inode,
};
*/
sb->s_op = &sysfs_ops;//超级块操作函数集
sb->s_time_gran = 1;
sysfs_sb = sb;//保存
inode = sysfs_get_inode(&sysfs_root);//生成sysfs_root中所定义要求的inode内存节点,即为根节点创建inode
if (!inode) {
pr_debug("sysfs: could not get root inode/n");
return -ENOMEM;
}
root = d_alloc_root(inode);//为文件系统根目录分配一个dentry结构,并将inode安装到此'/'根目录项上
if (!root) {
pr_debug("%s: could not get root dentry!/n",__FUNCTION__);
iput(inode);
return -ENOMEM;
}
root->d_fsdata = &sysfs_root;//根目录项的文件系统数据fsdata指向sysfs_root
sb->s_root = root;//填充sb超级块对应的根目录项
return 0;
}
struct sysfs_dirent sysfs_root = {
.s_name = "",//0空
.s_count = ATOMIC_INIT(1),
.s_flags = SYSFS_DIR,//目录
.s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
.s_ino = 1,//节点号为1
};
struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
{
struct inode *inode;
//以super_block和和和和sd->s_ino为哈希值,到哈希表中寻找相应的inode.如果不存在,则新建
inode = iget_locked(sysfs_sb, sd->s_ino);//创建新的inode节点
if (inode && (inode->i_state & I_NEW))
sysfs_init_inode(sd, inode);//这是一个新创建的inode节点,那么调用sysfs对它进一步做符合sysfs要求的初始化
return inode;
}
struct inode *iget_locked(struct super_block *sb, unsigned long ino)
{
struct hlist_head *head = inode_hashtable + hash(sb, ino);
struct inode *inode;
//在哈希表中查找inode
inode = ifind_fast(sb, head, ino);
if (inode)//第一次的话,肯定inode=NULL;
return get_new_inode_fast(sb, head, ino);//为root获取一个inode存储结构体,对于sysfs就是获取一个内存空间
//对于ext3和yaffs2文件系统就是申请flash或者硬盘上的物理空间了
}
static struct inode * get_new_inode_fast(struct super_block *sb, struct hlist_head *head, unsigned long ino)
{
struct inode * inode;
inode = alloc_inode(sb);//从sb超级块上获取一个新的inode
if (inode) {
struct inode * old;
spin_lock(&inode_lock);//锁住
old = find_inode_fast(sb, head, ino);//可能他人已经创建完成了,所以再查一遍
if (!old) {//确实ino节点号仍然没有被创建,那么我们可以安全的将inode作为ino节点号对应的inode了
inode->i_ino = ino;//节点对应的节点号
inodes_stat.nr_inodes++;
list_add(&inode->i_list, &inode_in_use);//挂到全局量inode_in_use链表上
list_add(&inode->i_sb_list, &sb->s_inodes);//挂到超级块的s_inodes链表上
hlist_add_head(&inode->i_hash, head);//将inode添加到所有节点都挂接到的hash数组inode_hashtable中.
inode->i_state = I_LOCK|I_NEW;//标记该inode为新建节点
spin_unlock(&inode_lock);//解锁
return inode;//返回这个新生成的inode节点
}
//很不走运,该ino对应的inode节点别人已经在你上边某一步时,提前搞定了,所以我们需要把刚才申请的咚咚释放掉
__iget(old);
spin_unlock(&inode_lock);
destroy_inode(inode);//销毁前面申请的inode节点,因为别人已经成功添加了.
inode = old;
wait_on_inode(inode);
}
return inode;//inode=NULL
}
static struct inode *alloc_inode(struct super_block *sb)
{
static const struct address_space_operations empty_aops;
static struct inode_operations empty_iops;
static const struct file_operations empty_fops;
struct inode *inode;
if (sb->s_op->alloc_inode)//该sb超级块提供自定义,特殊的alloc_inode函数,对于sysfs没有提供专门的函数
inode = sb->s_op->alloc_inode(sb);
else
inode = (struct inode *) kmem_cache_alloc(inode_cachep, GFP_KERNEL);//所以从cache中获取一个空闲染色的slab对象
if (inode) {//细化inode的部分结单元
struct address_space * const mapping = &inode->i_data;
inode->i_sb = sb;//指向对应的超级块
inode->i_blkbits = sb->s_blocksize_bits;
inode->i_flags = 0;
atomic_set(&inode->i_count, 1);
inode->i_op = &empty_iops;
inode->i_fop = &empty_fops;
inode->i_nlink = 1;
atomic_set(&inode->i_writecount, 0);
inode->i_size = 0;
inode->i_blocks = 0;
inode->i_bytes = 0;
inode->i_generation = 0;
#ifdef CONFIG_QUOTA
memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
#endif
inode->i_pipe = NULL;
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
inode->dirtied_when = 0;
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
kmem_cache_free(inode_cachep, (inode));
return NULL;
}
spin_lock_init(&inode->i_lock);
lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
mutex_init(&inode->i_mutex);
lockdep_set_class(&inode->i_mutex, &sb->s_type->i_mutex_key);
init_rwsem(&inode->i_alloc_sem);
lockdep_set_class(&inode->i_alloc_sem, &sb->s_type->i_alloc_sem_key);
mapping->a_ops = &empty_aops;
mapping->host = inode;
mapping->flags = 0;
mapping_set_gfp_mask(mapping, GFP_HIGHUSER_PAGECACHE);
mapping->assoc_mapping = NULL;
mapping->backing_dev_info = &default_backing_dev_info;
if (sb->s_bdev) {
struct backing_dev_info *bdi;
bdi = sb->s_bdev->bd_inode_backing_dev_info;
if (!bdi)
bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
mapping->backing_dev_info = bdi;
}
inode->i_private = NULL;
inode->i_mapping = mapping;
}
return inode;
}
static const struct super_operations sysfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
};
//sysfs_init_inode()对生成的inode进行初始化进行初始化进行初始化.显然.在mount的时候是不会生成的时候是不会生成的时候是不会生成的时候是不会生成inode的的的的.必定会进入 sysfs_init_inode()函数.
//对新创建的inode节点,进行sysfs特性格式化
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
struct bin_attribute *bin_attr;
inode->i_blocks = 0;
inode->i_mapping->a_ops = &sysfs_aops;
inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
inode->i_op = &sysfs_inode_operations;//更改inode的方法集
/*
static const struct inode_operations sysfs_inode_operations ={
.setattr = sysfs_setattr,
};
*/
inode->i_ino = sd->s_ino;//inode的节点号要和sd->s_ino目录项节点号一致
if (sd->s_iattr) {
set_inode_attr(inode, sd->s_iattr);
} else
set_default_inode_attr(inode, sd->s_mode);
switch (sysfs_type(sd)) {//SYSFS_DIR
case SYSFS_DIR://在这里,我们可以看到sysfs文件系统中的各种操作函数了
inode->i_op = &sysfs_dir_inode_operations;
inode->i_fop = &sysfs_dir_operations;
inode->i_nlink = sysfs_count_nlink(sd);
break;
case SYSFS_KOBJ_ATTR:
inode->i_size = PAGE_SIZE;
inode->i_fop = &sysfs_file_operations;
break;
case SYSFS_KOBJ_BIN_ATTR:
bin_attr = sd->s_bin_attr.bin_attr;
inode->i_size = bin_attr->size;
inode->i_fop = &bin_fops;
break;
case SYSFS_KOBJ_LINK:
inode->i_op = &sysfs_symlink_inode_operations;
break;
default:
BUG();
}
unlock_new_inode(inode);
}
//设置inode节点的缺省属性
static inline void set_default_inode_attr(struct inode * inode, mode_t mode)
{
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
struct dentry * d_alloc_root(struct inode * root_inode)
{
struct dentry *res = NULL;
if (root_inode) {
static const struct qstr name = { .name = "/", .len = 1 };
res = d_alloc(NULL, &name);//申请一个根目录项,调用dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
if (res) {
res->d_sb = root_inode->i_sb;//根目录项的超级块
res->d_parent = res;//自己指向自己
d_instantiate(res, root_inode);//将inode安装到根目录项上
}
}
return res;
}
void d_instantiate(struct dentry *entry, struct inode * inode)
{
BUG_ON(!list_empty(&entry->d_alias));
spin_lock(&dcache_lock);
if (inode)
list_add(&entry->d_alias, &inode->i_dentry);//将目录向链接到inode上,所以一个inode可以有多个目录项指向,
//这些目录项可以通过扫描inode->i_dentry链表获得
entry->d_inode = inode;//目录项对应的所管理的inode节点
fsnotify_d_instantiate(entry, inode);
spin_unlock(&dcache_lock);
security_d_instantiate(entry, inode);
}
//★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//分析到这里,sysfs的大概轮廓就出现在我们的眼前了.^_^.接下来分析sysfs文件系统中目录的创建过程
三:在sysfs文件系统中创建目录
在linux设备模型中,每注册一个kobject.就会为之创建一个目录.具体的流程在分析linux设备模型的时候再给出详细的分析.创建目录的接口为: sysfs_create_dir().代码如下:
int sysfs_create_dir(struct kobject * kobj)
{
struct sysfs_dirent *parent_sd, *sd;
int error = 0;
BUG_ON(!kobj);
//如果kobject没有指定父结点,则将其父结点指定为sysfs的根目录syfs_root
if (kobj->parent)
parent_sd = kobj->parent->sd;
else
parent_sd = &sysfs_root;
//在父目录下创建目录
error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);
//kobj->sd 指向对应的sysfs_dirent
if (!error)
kobj->sd = sd;
return error;
}
//在这里,先为结点指定父目录,然后调用create_dir()在父目录下生成结点.代码如下:
static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,const char *name, struct sysfs_dirent **p_sd)
{
//指定目录的模式
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
//分配并初始化一个sysfs_dirent
sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
if (!sd)
return -ENOMEM;
//初始化sd->s_dir.kobj字段,/*因为创建的是目录,在这里初始化s_dir*/
sd->s_dir.kobj = kobj;//设置新分配的sysfs_dirent对象的sd->s_dir.kobj字段指向kobject
//acxt是一个临时变量.它用来存放父结点的相关信息
//设置acxt->parent_sd 为父结点的sysfs_dirent.acxt->parent_inode为父结点的inode
/*在sysfs_sb中通过hash短链,核查父目录parent_sd是否确实已经存在了, 如果存在那么找到父目录的inode节点,赋值给acxt->parent_inode = inode; 并初始化acxt的parent属性为parent_sd*/
sysfs_addrm_start(&acxt, parent_sd);
//设置sd->s_parent.并按inod值按顺序链入父结点的children链表
rc = sysfs_add_one(&acxt, sd);/*将sd借助acxt结构添加到其父节点的子节点链表中*/
sysfs_addrm_finish(&acxt);/*对acxt做些清除工作*/
if (rc == 0)
*p_sd = sd;
else
sysfs_put(sd);
return rc;
}
struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
{
304 char *dup_name = NULL;
305 struct sysfs_dirent *sd;
306
307 if (type & SYSFS_COPY_NAME) {
308 name = dup_name = kstrdup(name, GFP_KERNEL);//为sysfs_dirent的名字分配内核缓冲区,并将传递进来的kobject名字复制到该缓冲区,由局部变量dup_name和参数name指向这个缓冲区。
309 if (!name)
310 return NULL;
311 }
312
313 sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);//在sysfs_dirent的缓存区sysfs_dir_cachep中分配sysfs_dirent对象,由局部变量sd指向它
314 if (!sd)
315 goto err_out1;
316 //为sysfs_dirent分配索引节点号
317 if (sysfs_alloc_ino(&sd->s_ino))
318 goto err_out2;
319
320 atomic_set(&sd->s_count, 1);
321 atomic_set(&sd->s_active, 0);
322
323 sd->s_name = name;
324 sd->s_mode = mode;
325 sd->s_flags = type;
326
327 return sd;
328
329 err_out2:
330 kmem_cache_free(sysfs_dir_cachep, sd);
331 err_out1:
332 kfree(dup_name);
333 return NULL;
}
//在这里,为子节点生成了对应的sysfs_dirent.设置了它的父结点域,并将其链入到父结点的children链表.这样,在文件系统中查找父目录下面的子结点了.
int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
/*从acxt的parent_sd的sibling中查找sd,若找到则返回错误表示已经存在该目录*/
if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
return -EEXIST;
/*初始化s_parent为acxt的parent_sd*/
sd->s_parent = sysfs_get(acxt->parent_sd);
if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
inc_nlink(acxt->parent_inode);
acxt->cnt++;
/*将sd链入其parent的sibling中*/
sysfs_link_sibling(sd);
return 0;
}
//sysfs_new_dirent()创建了一个sysfs_dirent结构。它没有在sysfs文件系统下创建inode结构。这项工作被滞后了,在sysfs_lookup()里面完成的。
//不过并没有看到相应目录或文件的dentry结构和inode结构的创建。当open打开一个sysfs文件时:
//在sys_open>do_sys_open>do_filp_open>path_openat>link_path_walk>walk_component>do_lookup>d_alloc_and_lookup里面分配了dentry结构并通过inode->i_op->lookup调用sysfs_lookup:
//dir,即是要在其中查找目录项的目录(父目录)的inode;dentry,是新创建的要保存我们查找的结构的,它的d_parent字段也是经过了适当的设置的了;nd,其path字段中保存了父目录路径的信息。
static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,struct nameidata *nd)
{
struct dentry *ret = NULL;
struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
struct sysfs_dirent *sd;
struct inode *inode;
mutex_lock(&sysfs_mutex);
//获得父目录的sysfs_dirent。
//在sysfs文件系统中inode的i_private字段和dentry的d_fsdata都是指向对应的sysfs_dirent的,所以可以从dentry->d_parent->d_fsdata中获得父目录的sysfs_dirent对象。
sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
if (!sd) {
ret = ERR_PTR(-ENOENT);
goto out_unlock;
}
//它为kobject所对应的目录创建inode
inode = sysfs_get_inode(dir->i_sb, sd);
if (!inode) {
ret = ERR_PTR(-ENOMEM);
goto out_unlock;
}
//为inode对象找到一个位于哈希链表中的、且在该索引节点的别名链表i_dentry中的dentry对象
ret = d_find_alias(inode);
if (!ret) {//找到,设置dentry->d_op指向sysfs_dentry_ops,设置dentry->d_fsdata指向对应的sysfs_dirent
dentry->d_op = &sysfs_dentry_ops;
dentry->d_fsdata = sysfs_get(sd);
d_add(dentry, inode);//向哈希队列添加dentry
} else {
d_move(ret, dentry);
iput(inode);
}
out_unlock:
mutex_unlock(&sysfs_mutex);
return ret;
}
//调用sysfs_find_dirent(parent_sd, dentry->d_name.name)来查找sysfs_dirent。这个函数根据父目录的sysfs_dirent和文件或目录的名字来查找sysfs_dirent。sysfs_find_dirent()函数定义如下:
struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,const unsigned char *name)
{
struct sysfs_dirent *sd;
for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
if (!strcmp(sd->s_name, name))
return sd;
return NULL;
}
//调用d_find_alias(inode)函数为inode对象找到一个位于哈希链表中的、且在该索引节点的别名链表i_dentry中的dentry对象。
static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
{
354 struct list_head *head, *next, *tmp;
355 struct dentry *alias, *discon_alias=NULL;
356
357 head = &inode->i_dentry;
358 next = inode->i_dentry.next;
359 while (next != head) {
360 tmp = next;
361 next = tmp->next;
362 prefetch(next);
363 alias = list_entry(tmp, struct dentry, d_alias);
364 if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
365 if (IS_ROOT(alias) &&(alias->d_flags & DCACHE_DISCONNECTED))
367 discon_alias = alias;
368 else if (!want_discon) {
369 __dget_locked(alias);
370 return alias;
371 }
372 }
373 }
374 if (discon_alias)
375 __dget_locked(discon_alias);
376 return discon_alias;
}
struct dentry * d_find_alias(struct inode *inode)
{
struct dentry *de = NULL;
if (!list_empty(&inode->i_dentry)) {
spin_lock(&dcache_lock);
de = __d_find_alias(inode, 0);
spin_unlock(&dcache_lock);
}
return de;
}
EXPORT_SYMBOL(d_find_alias);
---------------------------------------------------------------------
static inline void d_add(struct dentry *entry, struct inode *inode)
{
d_instantiate(entry, inode);
d_rehash(entry);
}
---------------------------------------------------------------------
四:在sysfs中创建一般属性文件
Kobject的每一项属性都对应在sysfs文件系统中,kobject对应的目录下的一个文件.文件名称与属性名称相同.创建一般属性的接口为sysfs_create_file().代码如下:
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
//kobject->sd: 为kobject表示目录的struct sysfs_dirent结构
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
最终会调用sysfs_add_file().参数attr.是要生成文件的属性值.
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,int type)
{
//文件对应的属性
umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
//创建一个新的sysfs_dirent.对应的名称为attr->name.即属性的名称
sd = sysfs_new_dirent(attr->name, mode, type);
if (!sd)
return -ENOMEM;
//设置属性值
sd->s_attr.attr = (void *)attr;
//将子结点的struct sysfs_dirent结构关链到父结点
sysfs_addrm_start(&acxt, dir_sd);
rc = sysfs_add_one(&acxt, sd);
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}
这个流程与创建目录的流程大部份相同.不相同的只是创建目录时,它的父目录为上一层结点,创建文件时,它的父目录就是kobject对应的struct sysfs_dirent.
这样,在kobject对应的目录下面就可以看到这个文件了.^_^
五、文件建好之后,要怎么样去读写呢?
回忆一下,在sysfs文件系统中,inode的初始化:
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{
……
…….
case SYSFS_KOBJ_ATTR:
inode->i_size = PAGE_SIZE;
inode->i_fop = &sysfs_file_operations;
……
}
sysfs_file_operations的定义如下:
const struct file_operations sysfs_file_operations = {
.read = sysfs_read_file,
.write = sysfs_write_file,
.llseek = generic_file_llseek,
.open = sysfs_open_file,
.release = sysfs_release,
.poll = sysfs_poll,
};
文件的操作全部都在这里了,我们从打开文件说起.
sysfs_open_file()代码如下:
static int sysfs_open_file(struct inode *inode, struct file *file)
{
struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
struct sysfs_buffer *buffer;
struct sysfs_ops *ops;
int error = -EACCES;
/* need attr_sd for attr and ops, its parent for kobj */
if (!sysfs_get_active_two(attr_sd))
return -ENODEV;
/* every kobject with an attribute needs a ktype assigned */
//将buffer->ops设置为kobj->ktype->sysfs_ops
if (kobj->ktype && kobj->ktype->sysfs_ops)
ops = kobj->ktype->sysfs_ops;
else {
printk(KERN_ERR "missing sysfs attribute operations for ""kobject: %s\n", kobject_name(kobj));
WARN_ON(1);
goto err_out;
}
if (file->f_mode & FMODE_WRITE) {
if (!(inode->i_mode & S_IWUGO) || !ops->store)
goto err_out;
}
if (file->f_mode & FMODE_READ) {
if (!(inode->i_mode & S_IRUGO) || !ops->show)
goto err_out;
}
error = -ENOMEM;
buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
if (!buffer)
goto err_out;
mutex_init(&buffer->mutex);
buffer->needs_read_fill = 1;
buffer->ops = ops;
file->private_data = buffer;
/* make sure we have open dirent struct */
//将buffer链至attr_sd->s_attr.open链表上
error = sysfs_get_open_dirent(attr_sd, buffer);
if (error)
goto err_free;
/* open succeeded, put active references */
sysfs_put_active_two(attr_sd);
return 0;
err_free:
kfree(buffer);
err_out:
sysfs_put_active_two(attr_sd);
return error;
}
在这段代码中,需要注意以下几个操作,
1:buffer链接在file-> private_data.具buffer还被链接在sysfs_dirent->s_attr.open.这样.VFS通过file.设备模型通过kobject->sd->s_attr.open都能找到这个要操作的 buffer
2:buffer->ops被设置为了kobject->ktype->sysfs_ops
文件的写操作入口如下:
static ssize_t sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t len;
mutex_lock(&buffer->mutex);
//将buf中的内容copy到了buffer->page
len = fill_write_buffer(buffer, buf, count);
//与设备模型的交互
if (len > 0)
len = flush_write_buffer(file->f_path.dentry, buffer, len);
//更新ppos
if (len > 0)
*ppos += len;
mutex_unlock(&buffer->mutex);
return len;
}
首先,调用fill_write_buffer()将用户空间传值下来的数值copy到buffer->page.然后再调用flush_write_buffer()与设备模型进行交互.
Flush_wirte_buffer()代码如下:
static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count)
{
struct sysfs_dirent *attr_sd = dentry->d_fsdata;
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
struct sysfs_ops * ops = buffer->ops;
int rc;
/* need attr_sd for attr and ops, its parent for kobj */
if (!sysfs_get_active_two(attr_sd))
return -ENODEV;
rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count);
sysfs_put_active_two(attr_sd);
return rc;
}
我们在分析open()操作的时候曾分析到.buffer的ops是kobject->ktype->ops.在这里,它相当于调用了kobject->ktype->ops->store().参数分别为:操作的kobject.文件对应的属性.写入的值和值的长度.
Sysfs这样设计,主要是在VFS保持一个统一的接口,因为每一个kobject对应的属性值都不相同,.相应的,操作方法也不一样,这样,在ktype中就区别开来了.
文件的读操作
相应接口为sysfs_read_file().代码如下:
static ssize_t sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct sysfs_buffer * buffer = file->private_data;
ssize_t retval = 0;
mutex_lock(&buffer->mutex);
//从设备模型中将值取出.并存入buffer->page中
if (buffer->needs_read_fill) {
retval = fill_read_buffer(file->f_path.dentry,buffer);
if (retval)
goto out;
}
//将buffer->page中的值copy到用户空间的buf
pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",__FUNCTION__, count, *ppos, buffer->page);
retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
buffer->count);
out:
mutex_unlock(&buffer->mutex);
return retval;
}
读操作的流程刚好和写操作流程相反.它先从设备模型中取值,然后再copy到用户空间.
fill_read_buffer的代码如下:
static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
{
struct sysfs_dirent *attr_sd = dentry->d_fsdata;
struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
struct sysfs_ops * ops = buffer->ops;
int ret = 0;
ssize_t count;
if (!buffer->page)
buffer->page = (char *) get_zeroed_page(GFP_KERNEL);
if (!buffer->page)
return -ENOMEM;
/* need attr_sd for attr and ops, its parent for kobj */
if (!sysfs_get_active_two(attr_sd))
return -ENODEV;
buffer->event = atomic_read(&attr_sd->s_attr.open->event);
count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);
sysfs_put_active_two(attr_sd);
/*
* The code works fine with PAGE_SIZE return but it's likely to
* indicate truncated result or overflow in normal use cases.
*/
if (count >= (ssize_t)PAGE_SIZE) {
print_symbol("fill_read_buffer: %s returned bad count\n",
(unsigned long)ops->show);
/* Try to struggle along */
count = PAGE_SIZE - 1;
}
if (count >= 0) {
buffer->needs_read_fill = 0;
buffer->count = count;
} else {
ret = count;
}
return ret;
}
在这里,我们看到,最终会调用kobject->ktype->ops->show()方法.参数含义同写操作中是一样的.
五:在sysfs中创建二进制属性文件
二制制属性通常用于firmware 中.它用来更新firmware 的固件.它的接口为sysfs_create_bin_file()
代码如下:
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
}
Sysfs_add_file()这个函数我们在之前已经分析过.在这个地方,可能会引起迷糊.因为在sysfs_add_file()中.有:
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
int type)
{
……
sd->s_attr.attr = (void *)attr;
……
}
这里为什么是sd->a_attr呢?应该是sd->
仔细观察struct 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;
};
注意中间是一个union 结构,实际上只占用一个内存空间.而且s_attr与s_bin_arre的第一个属性都为struct attribute.所以在这里, sd->a_attr与sd-> s_bin_attr;的效果是一样的.内核这样处理,又少用了一个接口.看来作者在设计的时候,花了很多的心思.
二进制的文件读写与普通属性的文件读写方式大部份都一样,所不同的是.二进制文件的读写接口分别是: sysfs_dirent ->s_bin_attr.bin_attr->read和sysfs_dirent ->s_bin_attr.bin_attr->write
六:sysfs文件系统中的链接文件
创建链接文件的接口为: sysfs_create_link().代码如下:
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;
int error;
BUG_ON(!name);
if (!kobj)
parent_sd = &sysfs_root;
else
parent_sd = kobj->sd;
error = -EFAULT;
if (!parent_sd)
goto out_put;
/* target->sd can go away beneath us but is protected with
* sysfs_assoc_lock. Fetch target_sd from it.
*/
spin_lock(&sysfs_assoc_lock);
if (target->sd)
target_sd = sysfs_get(target->sd);
spin_unlock(&sysfs_assoc_lock);
error = -ENOENT;
if (!target_sd)
goto out_put;
error = -ENOMEM;
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);
if (error)
goto out_put;
return 0;
out_put:
sysfs_put(target_sd);
sysfs_put(sd);
return error;
}
上面的操作大部份都与普通文件的创建相似,所不同的只是下面这段代码的区别:
sd->s_symlink.target_sd = target_sd;
就是在sd->s_symlink.target_sd保存到链接目的地的sysfs_dirent.
符号链接的操作如下所示:
const struct inode_operations sysfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = sysfs_follow_link,
.put_link = sysfs_put_link,
};
在通过符号链接查找文件的时候,在VFS中会调用inod->i_op->.readlink()进行操作.它的代码如下:
int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct nameidata nd;
void *cookie;
nd.depth = 0;
cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
if (!IS_ERR(cookie)) {
int res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd));
if (dentry->d_inode->i_op->put_link)
dentry->d_inode->i_op->put_link(dentry, &nd, cookie);
cookie = ERR_PTR(res);
}
return PTR_ERR(cookie);
}
它的操作和其它文件系统一样,都是通用follow_link()取得目的地的路径.然后保存到nd->saved_names[]中,然后,调用vfs_readlink()将目标路径copy到buffer中.接着,调用put_link进行事后处理工作.
Follow_link()的操作如下示:
static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)
{
int error = -ENOMEM;
unsigned long page = get_zeroed_page(GFP_KERNEL);
if (page)
error = sysfs_getlink(dentry, (char *) page);
nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
return NULL;
}
Ne_set_link()是将page中的值copy到nd->saved_name[]中.
sysfs_getlink()的代码如下:
sysfs_getlink()-àsysfs_get_target_path()
static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
struct sysfs_dirent *target_sd, char *path)
{
struct sysfs_dirent *base, *sd;
char *s = path;
int len = 0;
/* go up to the root, stop at the base */
base = parent_sd;
while (base->s_parent) {
sd = target_sd->s_parent;
while (sd->s_parent && base != sd)
sd = sd->s_parent;
if (base == sd)
break;
strcpy(s, "../");
s += 3;
base = base->s_parent;
}
/* determine end of target string for reverse fillup */
sd = target_sd;
while (sd->s_parent && sd != base) {
len += strlen(sd->s_name) + 1;
sd = sd->s_parent;
}
/* check limits */
if (len
return -EINVAL;
len--;
if ((s - path) + len > PATH_MAX)
return -ENAMETOOLONG;
/* reverse fillup of target string from target to base */
sd = target_sd;
while (sd->s_parent && sd != base) {
int slen = strlen(sd->s_name);
len -= slen;
strncpy(s + len, sd->s_name, slen);
if (len)
s[--len] = '/';
sd = sd->s_parent;
}
return 0;
}
这段代码的逻辑比较简单.它先是找到目标路径和当前路径相同的父结点,然后再沿着目标结点往相同的父结点向上走,将路径依次从缓存区后面往前面保存.
例如: /sys/eric/kernel/test链接到了/sys/sys/device.
它先找到两个路径共有的父结点/sys
此时缓存区为:/sys
然后,沿着/sys/sys/device往/sys移动.路径加从缓存区的后面往前面加.依次为:
1: /sys/ /device
2:/sys/sys/device
这样就找到了目的地的路径. ^_^.
后面sysfs_put_link()的操作就不再讲述了,它只是释放掉缓存区.