轉自:http://blog.csdn.net/npy_lp/article/details/78933292node
內核源碼:linux-2.6.38.8.tar.bz2linux
目標平臺:ARM體系結構數組
sysfs是基於內存的文件系統,用於向用戶空間導出內核對象而且能對其進行讀寫。緩存
一、sysfs文件系統不支持特殊文件,只支持目錄、普通文件(文本或二進制文件)和符號連接文件等三種類型,在內核中都使用struct sysfs_dirent結構體來表示,至關於其餘文件系統在硬盤或flash裏的數據。源代碼以下所示:安全
- struct sysfs_dirent {
- atomic_t s_count;
- atomic_t s_active;
- #ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lockdep_map dep_map;
- #endif
- struct sysfs_dirent *s_parent;
- struct sysfs_dirent *s_sibling;
- const char *s_name;
-
- const void *s_ns;
- 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;
- unsigned short s_mode;
- ino_t s_ino;
- struct sysfs_inode_attrs *s_iattr;
- };
-
- struct sysfs_inode_attrs {
- struct iattr ia_iattr;
- void *ia_secdata;
- u32 ia_secdata_len;
- };
-
- struct iattr {
- unsigned int ia_valid;
- umode_t ia_mode;
- uid_t ia_uid;
- gid_t ia_gid;
- loff_t ia_size;
- struct timespec ia_atime;
- struct timespec ia_mtime;
- struct timespec ia_ctime;
-
- struct file *ia_file;
- };
struct sysfs_dirent分爲四種類型,以下所示: cookie
- struct sysfs_elem_dir {
- struct kobject *kobj;
-
-
- struct sysfs_dirent *children;
- };
-
- struct sysfs_elem_symlink {
- struct sysfs_dirent *target_sd;
- };
-
- struct sysfs_elem_attr {
- struct attribute *attr;
- struct sysfs_open_dirent *open;
- };
-
- struct sysfs_elem_bin_attr {
- struct bin_attribute *bin_attr;
- struct hlist_head buffers;
- };
struct sysfs_open_dirent等結構體的詳細信息後文說明。app
二、sysfs文件系統的初始化是由sysfs_init函數來完成的(該函數由vfs_caches_init函數所調用的mnt_init函數調用),執行文件系統的註冊和掛載,與rootfs根文件系統的相關操做相似。源代碼以下所示:函數
- static struct vfsmount *sysfs_mnt;
- struct kmem_cache *sysfs_dir_cachep;
-
- int __init sysfs_init(void)
- {
- int err = -ENOMEM;
-
- 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();
- if (err)
- goto out_err;
-
- err = register_filesystem(&sysfs_fs_type);
- if (!err) {
- sysfs_mnt = kern_mount(&sysfs_fs_type);
- if (IS_ERR(sysfs_mnt)) {
- printk(KERN_ERR "sysfs: could not mount!\n");
- err = PTR_ERR(sysfs_mnt);
- sysfs_mnt = 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;
- }
在用戶空間通常都將sysfs文件系統掛載在/sys目錄,而這裏也有一次經過kern_mount函數的掛載,這樣的話 sysfs文件系統就會掛載兩次?實際上是沒有的,後者的掛載並無將當前的struct vfsmount結構體實例插入到掛載樹中,而是保存在全局指針sysfs_mnt中,而且會與用戶空間掛載sysfs文件系統時所建立的struct vfsmount結構體實例共享相同的超級塊。所以,不管用戶空間的掛載操做是否執行,sysfs文件系統都會存在於內核之中(編譯內核有配置CONFIG_SYSFS選項)。sysfs文件系統的struct file_system_type結構體實例以下所示: ui
- static struct file_system_type sysfs_fs_type = {
- .name = "sysfs",
- .mount = sysfs_mount,
- .kill_sb = sysfs_kill_sb,
- };
-
- static struct dentry *sysfs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
- {
- struct sysfs_super_info *info;
- enum kobj_ns_type type;
- struct super_block *sb;
- int error;
-
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return ERR_PTR(-ENOMEM);
-
-
- for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
- info->ns[type] = kobj_ns_current(type);
-
- sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info);
- if (IS_ERR(sb) || sb->s_fs_info != info)
- kfree(info);
- if (IS_ERR(sb))
- return ERR_CAST(sb);
- if (!sb->s_root) {
- sb->s_flags = flags;
- error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
- if (error) {
- deactivate_locked_super(sb);
- return ERR_PTR(error);
- }
- sb->s_flags |= MS_ACTIVE;
- }
-
- return dget(sb->s_root);
- }
其中,sysfs_test_super函數用於判斷struct sysfs_super_info結構體數據是否相同以便確認是否能夠共享超級塊,源代碼以下所示:atom
- enum kobj_ns_type {
- KOBJ_NS_TYPE_NONE = 0,
- KOBJ_NS_TYPE_NET,
- KOBJ_NS_TYPES
- };
-
- struct sysfs_super_info {
- const void *ns[KOBJ_NS_TYPES];
- };
-
- static int sysfs_test_super(struct super_block *sb, void *data)
- {
- struct sysfs_super_info *sb_info = sysfs_info(sb);
- struct sysfs_super_info *info = data;
- enum kobj_ns_type type;
- int found = 1;
-
- for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
- if (sb_info->ns[type] != info->ns[type])
- found = 0;
- }
- return found;
- }
sysfs_fill_super函數主要用於建立sysfs文件系統根目錄所對應的目錄項及其i節點,而且將sysfs_root做爲根目錄項的私有數據。源代碼以下所示:
- 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;
- sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
- sb->s_magic = SYSFS_MAGIC;
- sb->s_op = &sysfs_ops;
- sb->s_time_gran = 1;
-
-
- mutex_lock(&sysfs_mutex);
- inode = sysfs_get_inode(sb, &sysfs_root);
- mutex_unlock(&sysfs_mutex);
- if (!inode) {
- pr_debug("sysfs: could not get root inode\n");
- return -ENOMEM;
- }
-
-
- root = d_alloc_root(inode);
- if (!root) {
- pr_debug("%s: could not get root dentry!\n",__func__);
- iput(inode);
- return -ENOMEM;
- }
- root->d_fsdata = &sysfs_root;
- sb->s_root = root;
- return 0;
- }
-
- struct sysfs_dirent sysfs_root = {
- .s_name = "",
- .s_count = ATOMIC_INIT(1),
- .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
- .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- .s_ino = 1,
- };
-
- static const struct super_operations sysfs_ops = {
- .statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
- .evict_inode = sysfs_evict_inode,
- };
三、文件系統最核心的內容是要看其i節點是如何構建的,sysfs文件系統使用sysfs_init_inode函數來建立。源代碼以下所示:
- static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
- {
- struct bin_attribute *bin_attr;
-
- inode->i_private = sysfs_get(sd);
- inode->i_mapping->a_ops = &sysfs_aops;
- inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;
- inode->i_op = &sysfs_inode_operations;
-
- set_default_inode_attr(inode, sd->s_mode);
- sysfs_refresh_inode(sd, inode);
-
- switch (sysfs_type(sd)) {
- case SYSFS_DIR:
- inode->i_op = &sysfs_dir_inode_operations;
- inode->i_fop = &sysfs_dir_operations;
- 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);
- }
-
- static inline void set_default_inode_attr(struct inode * inode, mode_t mode)
- {
- inode->i_mode = mode;
- inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- }
-
- static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
- {
- struct sysfs_inode_attrs *iattrs = sd->s_iattr;
-
- inode->i_mode = sd->s_mode;
- if (iattrs) {
-
- set_inode_attr(inode, &iattrs->ia_iattr);
- security_inode_notifysecctx(inode,
- iattrs->ia_secdata,
- iattrs->ia_secdata_len);
- }
-
- if (sysfs_type(sd) == SYSFS_DIR)
- inode->i_nlink = sysfs_count_nlink(sd);
- }
四種structsysfs_dirent類型對應三種文件類型,其中SYSFS_KOBJ_ATTR和SYSFS_KOBJ_BIN_ATTR都爲普通文件。文件系統針對不一樣的文件類型,i節點操做函數(struct inode_operations)和文件內容操做函數(struct file_operations)都會有不一樣的實現,而且其中的函數也是根據文件類型來決定是否實現(大部分紅員爲空指針)。
3.一、目錄的i節點操做函數和文件內容操做函數分別爲sysfs_dir_inode_operations和sysfs_dir_operations,其中i節點操做函數的create、mkdir和rmdir等成員都爲空指針,表示sysfs文件系統的目錄或文件沒法從用戶空間經過系統調用來建立和刪除。對於sysfs文件系統中的目錄來講,i節點操做函數最重要的是查找函數sysfs_lookup,文件內容操做函數最重要的是遍歷目錄函數sysfs_readdir。源代碼以下所示:
- const struct inode_operations sysfs_dir_inode_operations = {
- .lookup = sysfs_lookup,
- .permission = sysfs_permission,
- .setattr = sysfs_setattr,
- .getattr = sysfs_getattr,
- .setxattr = sysfs_setxattr,
- };
-
- const struct file_operations sysfs_dir_operations = {
- .read = generic_read_dir,
- .readdir = sysfs_readdir,
- .release = sysfs_dir_release,
- .llseek = generic_file_llseek,
- };
-
- static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
- struct nameidata *nd)
- {
- struct dentry *ret = NULL;
- struct dentry *parent = dentry->d_parent;
- struct sysfs_dirent *parent_sd = parent->d_fsdata;
- struct sysfs_dirent *sd;
- struct inode *inode;
- enum kobj_ns_type type;
- const void *ns;
-
- mutex_lock(&sysfs_mutex);
-
-
- type = sysfs_ns_type(parent_sd);
- ns = sysfs_info(dir->i_sb)->ns[type];
-
-
- sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
- if (!sd) {
- ret = ERR_PTR(-ENOENT);
- goto out_unlock;
- }
-
-
- inode = sysfs_get_inode(dir->i_sb, sd);
- if (!inode) {
- ret = ERR_PTR(-ENOMEM);
- goto out_unlock;
- }
-
-
-
- ret = d_find_alias(inode);
- if (!ret) {
- d_set_d_op(dentry, &sysfs_dentry_ops);
- dentry->d_fsdata = sysfs_get(sd);
- d_add(dentry, inode);
-
- } else {
- d_move(ret, dentry);
- iput(inode);
- }
-
- out_unlock:
- mutex_unlock(&sysfs_mutex);
- return ret;
- }
對於目錄來講,只能有一個目錄項別名。
- static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
- {
- struct dentry *dentry = filp->f_path.dentry;
- struct sysfs_dirent * parent_sd = dentry->d_fsdata;
- struct sysfs_dirent *pos = filp->private_data;
- enum kobj_ns_type type;
- const void *ns;
- ino_t ino;
-
-
- type = sysfs_ns_type(parent_sd);
- ns = sysfs_info(dentry->d_sb)->ns[type];
-
-
- if (filp->f_pos == 0) {
- ino = parent_sd->s_ino;
- if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)
- filp->f_pos++;
- }
-
- if (filp->f_pos == 1) {
- if (parent_sd->s_parent)
- ino = parent_sd->s_parent->s_ino;
- else
- ino = parent_sd->s_ino;
- if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)
- filp->f_pos++;
- }
- mutex_lock(&sysfs_mutex);
- for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos);
- pos;
- pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) {
- const char * name;
- unsigned int type;
- int len, ret;
-
- name = pos->s_name;
- len = strlen(name);
- ino = pos->s_ino;
- type = dt_type(pos);
- filp->f_pos = ino;
- filp->private_data = sysfs_get(pos);
-
- mutex_unlock(&sysfs_mutex);
- ret = filldir(dirent, name, len, filp->f_pos, ino, type);
- mutex_lock(&sysfs_mutex);
- if (ret < 0)
- break;
- }
- mutex_unlock(&sysfs_mutex);
- if ((filp->f_pos > 1) && !pos) {
- filp->f_pos = INT_MAX;
- filp->private_data = NULL;
- }
- return 0;
- }
-
- static struct sysfs_dirent *sysfs_dir_pos(const void *ns,
- struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
- {
- if (pos) {
- int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) &&
- pos->s_parent == parent_sd &&
- ino == pos->s_ino;
- sysfs_put(pos);
- if (!valid)
- pos = NULL;
- }
- if (!pos && (ino > 1) && (ino < INT_MAX)) {
- pos = parent_sd->s_dir.children;
- while (pos && (ino > pos->s_ino))
- pos = pos->s_sibling;
- }
- while (pos && pos->s_ns && pos->s_ns != ns)
- pos = pos->s_sibling;
- return pos;
- }
-
- static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,
- struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)
- {
- pos = sysfs_dir_pos(ns, parent_sd, ino, pos);
- if (pos)
- pos = pos->s_sibling;
- while (pos && pos->s_ns && pos->s_ns != ns)
- pos = pos->s_sibling;
- return pos;
- }
當在用戶空間經過ls等命令查看sysfs文件系統的目錄時,會經過系統調用getdents調用vfs_readdir,而後再調用sysfs_readdir函數。其中參數filp表示該目錄打開以後的文件指針,dirent實際爲struct getdents_callback類型的數據,filldir爲回調函數指針,指向同名的filldir函數。源代碼以下所示:
- #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
- #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
- #define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
-
- #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) //GCC編譯器內置函數,計算成員偏移量
-
- #undef offsetof
- #ifdef __compiler_offsetof
- #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
- #else
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #endif
-
- struct linux_dirent {
- unsigned long d_ino;
- unsigned long d_off;
- unsigned short d_reclen;
- char d_name[1];
- };
-
- struct getdents_callback {
- struct linux_dirent __user * current_dir;
- struct linux_dirent __user * previous;
- int count;
- int error;
- };
-
- static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
- u64 ino, unsigned int d_type)
- {
- struct linux_dirent __user * dirent;
- struct getdents_callback * buf = (struct getdents_callback *) __buf;
- unsigned long d_ino;
-
- int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
- sizeof(long));
-
- buf->error = -EINVAL;
- if (reclen > buf->count)
- return -EINVAL;
- d_ino = ino;
- if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
- buf->error = -EOVERFLOW;
- return -EOVERFLOW;
- }
- dirent = buf->previous;
- if (dirent) {
- if (__put_user(offset, &dirent->d_off))
- goto efault;
- }
- dirent = buf->current_dir;
- if (__put_user(d_ino, &dirent->d_ino))
- goto efault;
- if (__put_user(reclen, &dirent->d_reclen))
- goto efault;
- if (copy_to_user(dirent->d_name, name, namlen))
- goto efault;
- if (__put_user(0, dirent->d_name + namlen))
- goto efault;
- if (__put_user(d_type, (char __user *) dirent + reclen - 1))
- goto efault;
- buf->previous = dirent;
- dirent = (void __user *)dirent + reclen;
- buf->current_dir = dirent;
- buf->count -= reclen;
- return 0;
- efault:
- buf->error = -EFAULT;
- return -EFAULT;
- }
3.二、sysfs文件系統針對文本文件和二進制文件實現了不一樣的文件內容操做函數,而i節點操做函數則相同。源代碼以下所示:
- static const struct inode_operations sysfs_inode_operations ={
- .permission = sysfs_permission,
- .setattr = sysfs_setattr,
- .getattr = sysfs_getattr,
- .setxattr = sysfs_setxattr,
- };
-
- 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,
- };
-
- const struct file_operations bin_fops = {
- .read = read,
- .write = write,
- .mmap = mmap,
- .llseek = generic_file_llseek,
- .open = open,
- .release = release,
- };
針對普通文件,最重要的是觀察它的文件內容操做函數的實現,如open、read和write等函數。下面則以文本文件爲例,看看sysfs文件系統是如何讀寫文件的。源代碼以下所示:
- #define PATH_MAX 4096
-
- static char last_sysfs_file[PATH_MAX];
-
- 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;
- const struct sysfs_ops *ops;
- int error = -EACCES;
- char *p;
-
-
- p = d_path(&file->f_path, last_sysfs_file, sizeof(last_sysfs_file));
- if (!IS_ERR(p))
- memmove(last_sysfs_file, p, strlen(p) + 1);
-
-
- if (!sysfs_get_active(attr_sd))
- return -ENODEV;
-
- if (kobj->ktype && kobj->ktype->sysfs_ops)
- ops = kobj->ktype->sysfs_ops;
- else {
- WARN(1, KERN_ERR "missing sysfs attribute operations for "
- "kobject: %s\n", kobject_name(kobj));
- 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;
-
-
- error = sysfs_get_open_dirent(attr_sd, buffer);
- if (error)
- goto err_free;
-
-
- sysfs_put_active(attr_sd);
- return 0;
-
- err_free:
- kfree(buffer);
- err_out:
- sysfs_put_active(attr_sd);
- return error;
- }
-
- static DEFINE_SPINLOCK(sysfs_open_dirent_lock);
-
- static int sysfs_get_open_dirent(struct sysfs_dirent *sd,
- struct sysfs_buffer *buffer)
- {
- struct sysfs_open_dirent *od, *new_od = NULL;
-
- retry:
- spin_lock_irq(&sysfs_open_dirent_lock);
-
- if (!sd->s_attr.open && new_od) {
- sd->s_attr.open = new_od;
- new_od = NULL;
- }
-
- od = sd->s_attr.open;
- if (od) {
- atomic_inc(&od->refcnt);
-
- list_add_tail(&buffer->list, &od->buffers);
- }
-
- spin_unlock_irq(&sysfs_open_dirent_lock);
-
- if (od) {
- kfree(new_od);
- return 0;
- }
-
-
- new_od = kmalloc(sizeof(*new_od), GFP_KERNEL);
- if (!new_od)
- return -ENOMEM;
-
-
- atomic_set(&new_od->refcnt, 0);
- atomic_set(&new_od->event, 1);
- init_waitqueue_head(&new_od->poll);
- INIT_LIST_HEAD(&new_od->buffers);
- goto retry;
- }
在sysfs文件系統中,文本文件使用struct sysfs_elem_attr來表示,而該結構體有一個struct sysfs_open_dirent類型的open成員,主要用於管理每次打開並讀/寫該文件時所使用的內存(struct sysfs_buffer)。源代碼以下所示:
- struct sysfs_open_dirent {
- atomic_t refcnt;
- atomic_t event;
- wait_queue_head_t poll;
- struct list_head buffers;
- };
-
- struct sysfs_buffer {
- size_t count;
- loff_t pos;
- char * page;
- const struct sysfs_ops * ops;
- struct mutex mutex;
- int needs_read_fill;
- int event;
- struct list_head list;
- };
sysfs_read_file爲sysfs文件系統文本文件的讀函數,源代碼以下所示:
- 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);
- if (buffer->needs_read_fill || *ppos == 0) {
- retval = fill_read_buffer(file->f_path.dentry,buffer);
- if (retval)
- goto out;
- }
- pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
- __func__, count, *ppos, buffer->page);
-
-
- retval = simple_read_from_buffer(buf, count, ppos, buffer->page,
- buffer->count);
- out:
- mutex_unlock(&buffer->mutex);
- return retval;
- }
-
- 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;
- const 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;
-
- if (!sysfs_get_active(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(attr_sd);
-
- if (count >= (ssize_t)PAGE_SIZE) {
- print_symbol("fill_read_buffer: %s returned bad count\n",
- (unsigned long)ops->show);
-
- count = PAGE_SIZE - 1;
- }
- if (count >= 0) {
- buffer->needs_read_fill = 0;
- buffer->count = count;
- } else {
- ret = count;
- }
- return ret;
- }
-
- ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,
- const void *from, size_t available)
- {
- loff_t pos = *ppos;
- size_t ret;
-
- if (pos < 0)
- return -EINVAL;
- if (pos >= available || !count)
- return 0;
- if (count > available - pos)
- count = available - pos;
- ret = copy_to_user(to, from + pos, count);
- if (ret == count)
- return -EFAULT;
- count -= ret;
- *ppos = pos + count;
- return count;
- }
sysfs_write_file爲sysfs文件系統的寫函數,源代碼以下所示:
- 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);
- len = fill_write_buffer(buffer, buf, count);
- if (len > 0)
- len = flush_write_buffer(file->f_path.dentry, buffer, len);
- if (len > 0)
- *ppos += len;
- mutex_unlock(&buffer->mutex);
- return len;
- }
-
- static int
- fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count)
- {
- int error;
-
- if (!buffer->page)
- buffer->page = (char *)get_zeroed_page(GFP_KERNEL);
- if (!buffer->page)
- return -ENOMEM;
-
- if (count >= PAGE_SIZE)
- count = PAGE_SIZE - 1;
- error = copy_from_user(buffer->page,buf,count);
- buffer->needs_read_fill = 1;
- buffer->page[count] = 0;
- return error ? -EFAULT : count;
- }
-
- 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;
- const struct sysfs_ops * ops = buffer->ops;
- int rc;
-
- if (!sysfs_get_active(attr_sd))
- return -ENODEV;
-
- rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count);
-
- sysfs_put_active(attr_sd);
-
- return rc;
- }
關閉文件時,打開、讀/寫文件時所分配的內存都會釋放,並不會一直存在於內核中。
3.三、對於符號連接文件來講,它沒有文件內容操做函數,只有i節點操做函數,其中最重要的函數爲符號連接文件的解析函數sysfs_follow_link,源代碼以下所示:
- const struct inode_operations sysfs_symlink_inode_operations = {
- .setxattr = sysfs_setxattr,
- .readlink = generic_readlink,
- .follow_link = sysfs_follow_link,
- .put_link = sysfs_put_link,
- .setattr = sysfs_setattr,
- .getattr = sysfs_getattr,
- .permission = sysfs_permission,
- };
-
- 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);
- if (error < 0)
- free_page((unsigned long)page);
- }
- nd_set_link(nd, error ? ERR_PTR(error) : (char *)page);
- return NULL;
- }
-
- static int sysfs_getlink(struct dentry *dentry, char * path)
- {
- struct sysfs_dirent *sd = dentry->d_fsdata;
- struct sysfs_dirent *parent_sd = sd->s_parent;
- struct sysfs_dirent *target_sd = sd->s_symlink.target_sd;
- int error;
-
- mutex_lock(&sysfs_mutex);
- error = sysfs_get_target_path(parent_sd, target_sd, path);
- mutex_unlock(&sysfs_mutex);
-
- return error;
- }
-
- 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;
-
- 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;
- }
-
-
-
-
- sd = target_sd;
- while (sd->s_parent && sd != base) {
- len += strlen(sd->s_name) + 1;
- sd = sd->s_parent;
- }
-
- if (len < 2)
- return -EINVAL;
- len--;
- if ((s - path) + len > PATH_MAX)
- return -ENAMETOOLONG;
-
-
- 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;
- }
讀取符號連接文件內容的函數generic_readlink主要就是靠解析函數來實現的,源代碼以下所示:
- int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
- {
- struct nameidata nd;
- void *cookie;
- int res;
-
- nd.depth = 0;
- cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);
- if (IS_ERR(cookie))
- return PTR_ERR(cookie);
-
- 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);
- return res;
- }
-
- int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)
- {
- int len;
-
- len = PTR_ERR(link);
- if (IS_ERR(link))
- goto out;
-
- len = strlen(link);
- if (len > (unsigned) buflen)
- len = buflen;
- if (copy_to_user(buffer, link, len))
- len = -EFAULT;
- out:
- return len;
- }
-
- static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
- {
- char *page = nd_get_link(nd);
- if (!IS_ERR(page))
- free_page((unsigned long)page);
- }
其中,sysfs_put_link函數執行與sysfs_follow_link函數相反的操做,這裏只是釋放由sysfs_follow_link函數分配的內存。
四、對於sysfs文件系統來講,在用戶空間只能讀寫文件的內容,而沒法建立或刪除文件或目錄,只能在內核中經過sysfs_create_dir、sysfs_create_file等等函數來實現。
4.一、內核對象(struct kobject)對應於sysfs文件系統中的目錄,可以使用sysfs_create_dir函數來建立,源代碼以下所示:
- int sysfs_create_dir(struct kobject * kobj)
- {
- enum kobj_ns_type type;
- struct sysfs_dirent *parent_sd, *sd;
- const void *ns = NULL;
- int error = 0;
-
- BUG_ON(!kobj);
-
- if (kobj->parent)
- parent_sd = kobj->parent->sd;
- else
- parent_sd = &sysfs_root;
-
-
- if (sysfs_ns_type(parent_sd))
- ns = kobj->ktype->namespace(kobj);
- type = sysfs_read_ns_type(kobj);
-
- error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
- if (!error)
- kobj->sd = sd;
- return error;
- }
-
- static inline const char *kobject_name(const struct kobject *kobj)
- {
- return kobj->name;
- }
-
- static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
- enum kobj_ns_type type, const void *ns, 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;
-
-
- sd = sysfs_new_dirent(name, mode, SYSFS_DIR);
- if (!sd)
- return -ENOMEM;
-
- sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
- sd->s_ns = ns;
- sd->s_dir.kobj = kobj;
-
- sysfs_addrm_start(&acxt, parent_sd);
- rc = sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&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)
- {
- char *dup_name = NULL;
- struct sysfs_dirent *sd;
-
- if (type & SYSFS_COPY_NAME) {
- name = dup_name = kstrdup(name, GFP_KERNEL);
- if (!name)
- return NULL;
- }
-
- sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);
- if (!sd)
- goto err_out1;
-
- 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;
-
- err_out2:
- kmem_cache_free(sysfs_dir_cachep, sd);
- err_out1:
- kfree(dup_name);
- return NULL;
- }
-
- int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
- int ret;
-
- ret = __sysfs_add_one(acxt, sd);
- if (ret == -EEXIST) {
- char *path = kzalloc(PATH_MAX, GFP_KERNEL);
- WARN(1, KERN_WARNING
- "sysfs: cannot create duplicate filename '%s'\n",
- (path == NULL) ? sd->s_name :
- strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),
- sd->s_name));
- kfree(path);
- }
-
- return ret;
- }
-
- int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
- {
- struct sysfs_inode_attrs *ps_iattr;
-
- if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name))
- return -EEXIST;
-
- sd->s_parent = sysfs_get(acxt->parent_sd);
-
- sysfs_link_sibling(sd);
-
-
- ps_iattr = acxt->parent_sd->s_iattr;
- if (ps_iattr) {
- struct iattr *ps_iattrs = &ps_iattr->ia_iattr;
- ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
- }
-
- return 0;
- }
-
- struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
- const void *ns,
- const unsigned char *name)
- {
- struct sysfs_dirent *sd;
-
- for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) {
- if (ns && sd->s_ns && (sd->s_ns != ns))
- continue;
- if (!strcmp(sd->s_name, name))
- return sd;
- }
- return NULL;
- }
-
- static void sysfs_link_sibling(struct sysfs_dirent *sd)
- {
- struct sysfs_dirent *parent_sd = sd->s_parent;
- struct sysfs_dirent **pos;
-
- BUG_ON(sd->s_sibling);
-
- for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {
- if (sd->s_ino < (*pos)->s_ino)
- break;
- }
-
- sd->s_sibling = *pos;
- *pos = sd;
- }
4.二、內核對象的屬性(struct attribute)對應於sysfs文件系統中的文本文件,可以使用sysfs_create_file函數來建立,源代碼以下所示:
- int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
- {
- BUG_ON(!kobj || !kobj->sd || !attr);
-
- return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
- }
-
- int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
- int type)
- {
- return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
- }
-
- 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_dirent_init_lockdep(sd);
-
- sysfs_addrm_start(&acxt, dir_sd);
- rc = sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt);
-
- if (rc)
- sysfs_put(sd);
-
- return rc;
- }
sysfs文件系統中的二進制文件經過sysfs_create_bin_file函數來建立,符號連接文件經過sysfs_create_link函數來建立。