轉自:http://blog.csdn.net/npy_lp/article/details/78933292
內核源碼:linux-2.6.38.8.tar.bz2
目標平台:ARM體系結構
sysfs是基於內存的文件系統,用於向用戶空間導出內核對象並且能對其進行讀寫。
1、sysfs文件系統不支持特殊文件,只支持目錄、普通文件(文本或二進制文件)和符號鏈接文件等三種類型,在內核中都使用struct sysfs_dirent結構體來表示,相當於其他文件系統在硬盤或flash里的數據。源代碼如下所示:
- /* fs/sysfs/sysfs.h */
- struct sysfs_dirent {
- atomic_t s_count; //struct sysfs_dirent結構體實例自身的引用計數
- atomic_t s_active; //struct sysfs_elem_*所涉及的外部對象的引用計數
- #ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lockdep_map dep_map; //死鎖檢測模塊,針對s_attr.attr中的key或skey
- #endif
- struct sysfs_dirent *s_parent; //指向父節點
- struct sysfs_dirent *s_sibling; //同級節點鏈表,插入到父節點的s_dir.children鏈表中
- 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; //標志,表示struct sysfs_dirent類型、命名空間類型等信息
- unsigned short s_mode; //文件訪問權限,包含文件類型信息
- ino_t s_ino; //對應於i節點號
- struct sysfs_inode_attrs *s_iattr; //文件屬性
- };
- struct sysfs_inode_attrs {
- struct iattr ia_iattr; //文件屬性
- void *ia_secdata; //安全檢測模塊所用數據
- u32 ia_secdata_len; //ia_secdata所指數據的長度
- };
- /* include/linux/fs.h */
- struct iattr {
- unsigned int ia_valid; //文件屬性標志
- umode_t ia_mode; //文件類型及其訪問權限
- uid_t ia_uid; //用戶ID
- gid_t ia_gid; //組ID
- loff_t ia_size; //文件大小
- struct timespec ia_atime; //訪問時間
- struct timespec ia_mtime; //數據修改時間
- struct timespec ia_ctime; //元數據修改時間
- struct file *ia_file; //輔助信息,用於想實現ftruncate等方法的文件系統
- };
struct sysfs_dirent分為四種類型,如下所示:
- /* fs/sysfs/sysfs.h */
- struct sysfs_elem_dir {
- struct kobject *kobj; //指向內核對象
- //子節點鏈表,只有目錄才有可能包含文件或子目錄
- //子節點通過s_sibling成員以s_ino成員升序的方式插入到該鏈表
- 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; //文件open信息,其中buffers成員是struct sysfs_buffer的鏈表
- };
- struct sysfs_elem_bin_attr {
- struct bin_attribute *bin_attr; //二進制的對象屬性
- struct hlist_head buffers; //struct bin_buffer的鏈表
- };
struct sysfs_open_dirent等結構體的詳細信息后文說明。
2、sysfs文件系統的初始化是由sysfs_init函數來完成的(該函數由vfs_caches_init函數所調用的mnt_init函數調用),執行文件系統的注冊和掛載,與rootfs根文件系統的相關操作類似。源代碼如下所示:
- /* fs/sysfs/mount.c */
- 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); //創建用於struct sysfs_dirent的高速緩存
- if (!sysfs_dir_cachep)
- goto out;
- //初始化后備存儲介質相關結構體struct backing_dev_info(sysfs文件系統基於內存,無須數據同步)
- err = sysfs_inode_init();
- if (err)
- goto out_err;
- err = register_filesystem(&sysfs_fs_type); //注冊文件系統
- if (!err) { //成功返回零
- sysfs_mnt = kern_mount(&sysfs_fs_type); //掛載文件系統,不過沒有將sysfs_mnt所指的結構體實例插入到掛載樹中
- 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結構體實例如下所示:
- /* fs/sysfs/mount.c */
- 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);
- //構建超級塊私有數據,用於sysfs_test_super和sysfs_set_super函數
- 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是新創建的
- 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結構體數據是否相同以便確認是否可以共享超級塊,源代碼如下所示:
- /* include/linux/kobject_ns.h */
- enum kobj_ns_type {
- KOBJ_NS_TYPE_NONE = 0,
- KOBJ_NS_TYPE_NET,
- KOBJ_NS_TYPES
- };
- /* fs/sysfs/sysfs.h */
- struct sysfs_super_info {
- const void *ns[KOBJ_NS_TYPES];
- };
- /* fs/sysfs/mount.c */
- static int sysfs_test_super(struct super_block *sb, void *data)
- {
- struct sysfs_super_info *sb_info = sysfs_info(sb); //sb->s_fs_info
- 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]) //只要有任何一項的值不相同則函數返回0
- found = 0;
- }
- return found;
- }
sysfs_fill_super函數主要用於創建sysfs文件系統根目錄所對應的目錄項及其i節點,並且將sysfs_root作為根目錄項的私有數據。源代碼如下所示:
- /* fs/sysfs/mount.c */
- 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;
- //創建並初始化根目錄的i節點
- 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;
- }
- //創建並初始化sysfs文件系統的根目錄項並關聯根目錄的i節點
- 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; //根目錄項的d_fsdata成員指向sysfs文件系統的根數據項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, //目錄文件,訪問權限0755
- .s_ino = 1, //起始i節點號為1
- };
- static const struct super_operations sysfs_ops = { //超級塊操作函數
- .statfs = simple_statfs,
- .drop_inode = generic_delete_inode,
- .evict_inode = sysfs_evict_inode,
- };
3、文件系統最核心的內容是要看其i節點是如何構建的,sysfs文件系統使用sysfs_init_inode函數來創建。源代碼如下所示:
- /* fs/sysfs/inode.c */
- static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
- {
- struct bin_attribute *bin_attr;
- inode->i_private = sysfs_get(sd); //指向引用計數遞增之后的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)) { //struct sysfs_dirent類型
- 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) { //sd->s_iattr為真
- //從iattrs->ia_iattr拷貝ia_uid、ia_gid、ia_atime、ia_mtime和ia_ctime等成員的值給i節點相應的成員
- 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); //計算目錄的硬鏈接數目(等於子目錄數+2)
- }
四種structsysfs_dirent類型對應三種文件類型,其中SYSFS_KOBJ_ATTR和SYSFS_KOBJ_BIN_ATTR都為普通文件。文件系統針對不同的文件類型,i節點操作函數(struct inode_operations)和文件內容操作函數(struct file_operations)都會有不同的實現,並且其中的函數也是根據文件類型來決定是否實現(大部分成員為空指針)。
3.1、目錄的i節點操作函數和文件內容操作函數分別為sysfs_dir_inode_operations和sysfs_dir_operations,其中i節點操作函數的create、mkdir和rmdir等成員都為空指針,表示sysfs文件系統的目錄或文件無法從用戶空間通過系統調用來創建和刪除。對於sysfs文件系統中的目錄來說,i節點操作函數最重要的是查找函數sysfs_lookup,文件內容操作函數最重要的是遍歷目錄函數sysfs_readdir。源代碼如下所示:
- /* fs/sysfs/dir.c */
- 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) //dir為父目錄的i節點,dentry為被查找對象的目錄項(這時為d_alloc函數操作之后的狀態)
- {
- 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];
- //從其parent_sd->s_dir.children鏈表中查找相同命名空間並且名字相同的子項
- sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);
- if (!sd) { //該子項不存在
- ret = ERR_PTR(-ENOENT);
- goto out_unlock;
- }
- //從inode_hashtable哈希表中查找該子項對應的i節點,若不存在則構建一個新的i節點實例
- inode = sysfs_get_inode(dir->i_sb, sd);
- if (!inode) { //構建失敗
- ret = ERR_PTR(-ENOMEM);
- goto out_unlock;
- }
- //對於目錄來說,只能有一個目錄項,並且只有在其為空目錄或者為當前文件系統的根目錄時才有可能被設置DCACHE_UNHASHED狀態
- //當i節點為IS_ROOT和DCACHE_DISCONNECTED時,d_find_alias函數返回真
- ret = d_find_alias(inode);
- if (!ret) {
- d_set_d_op(dentry, &sysfs_dentry_ops); //sysfs_dentry_ops為sysfs文件系統目錄項的操作函數
- dentry->d_fsdata = sysfs_get(sd);
- d_add(dentry, inode); //調用d_instantiate函數將目錄項dentry插入到inode->i_dentry鏈表並且dentry->d_inode指向i節點inode
- //調用d_rehash函數將目錄項dentry插入到dentry_hashtable哈希表中
- } else {
- d_move(ret, dentry); //重新使用所返回的目錄項ret並與目錄項dentry交換d_name等數據
- iput(inode); //銷毀i節點
- }
- out_unlock:
- mutex_unlock(&sysfs_mutex);
- return ret;
- }
對於目錄來說,只能有一個目錄項別名。
- /* fs/sysfs/dir.c */
- 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); //這時filp->f_pos等於2,pos為NULL
- 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; //將i節點號作為上一個struct linux_dirent實例中d_off成員的值
- 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) && //非SYSFS_FLAG_REMOVED項
- pos->s_parent == parent_sd && //屬於目錄parent_sd
- ino == pos->s_ino;
- sysfs_put(pos);
- if (!valid)
- pos = NULL; //重新遍歷該目錄
- }
- if (!pos && (ino > 1) && (ino < INT_MAX)) { //這時pos為空指針,且i節點號的大小在有效范圍內
- pos = parent_sd->s_dir.children;
- while (pos && (ino > pos->s_ino)) //過濾掉i節點號比它小的
- 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函數。源代碼如下所示:
- /* include/linux/kernel.h */
- #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))
- /* include/linux/compiler-gcc4.h */
- #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) //GCC編譯器內置函數,計算成員偏移量
- /* include/linux/stddef.h */
- #undef offsetof
- #ifdef __compiler_offsetof
- #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
- #else
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #endif
- /* fs/readdir.c */
- struct linux_dirent {
- unsigned long d_ino; //i節點號
- unsigned long d_off; //偏移量,無實際意義,在sysfs文件系統的實現中,它的值為上一個所遍歷的文件或目錄的i節點號(最后一個為INT_MAX)
- unsigned short d_reclen; //整個struct linux_dirent實例的長度(經過對齊修正)
- char d_name[1]; //文件名,大小由實際文件名的長度決定(空字符結尾)
- };
- struct getdents_callback {
- struct linux_dirent __user * current_dir; //初始化為系統調用傳入的內存地址
- struct linux_dirent __user * previous; //指向上一個struct linux_dirent實例
- 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;
- //功能等效於(len / sizeof(long) + (len % sizeof(long) ? 1 : 0)) * sizeof(long),其中len為ALIGN的第一個參數的值
- int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, //其中加2個字節的意義是:一個字節用來存儲字符串的結束符,
- sizeof(long)); //另一個字節用來存儲文件類型。
- buf->error = -EINVAL;
- if (reclen > buf->count) //剩余的內存不夠
- return -EINVAL;
- d_ino = ino;
- if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { //d_ino的數據類型較小且導致數據溢出
- buf->error = -EOVERFLOW;
- return -EOVERFLOW;
- }
- dirent = buf->previous;
- if (dirent) {
- if (__put_user(offset, &dirent->d_off)) //以當前的offset值填充上一個struct linux_dirent實例的d_off成員
- goto efault;
- }
- dirent = buf->current_dir;
- if (__put_user(d_ino, &dirent->d_ino)) //i節點號
- goto efault;
- if (__put_user(reclen, &dirent->d_reclen)) //當前struct linux_dirent實例的總大小(字節數)
- goto efault;
- if (copy_to_user(dirent->d_name, name, namlen)) //拷貝文件名
- goto efault;
- if (__put_user(0, dirent->d_name + namlen)) //在文件名后加字符串結束符'\0'
- 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; //指向下一個尚未使用的struct linux_dirent實例
- buf->count -= reclen; //計算剩余內存數量
- return 0;
- efault:
- buf->error = -EFAULT;
- return -EFAULT;
- }
3.2、sysfs文件系統針對文本文件和二進制文件實現了不同的文件內容操作函數,而i節點操作函數則相同。源代碼如下所示:
- /* fs/sysfs/inode.c */
- static const struct inode_operations sysfs_inode_operations ={
- .permission = sysfs_permission,
- .setattr = sysfs_setattr,
- .getattr = sysfs_getattr,
- .setxattr = sysfs_setxattr,
- };
- /* fs/sysfs/file.c */
- 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,
- };
- /* fs/sysfs/bin.c */
- const struct file_operations bin_fops = { //二進制文件
- .read = read,
- .write = write,
- .mmap = mmap,
- .llseek = generic_file_llseek,
- .open = open,
- .release = release,
- };
針對普通文件,最重要的是觀察它的文件內容操作函數的實現,如open、read和write等函數。下面則以文本文件為例,看看sysfs文件系統是如何讀寫文件的。源代碼如下所示:
- /* include/linux/limits.h */
- #define PATH_MAX 4096
- /* fs/sysfs/file.c */
- 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; //sysfs數據項,表示當前被打開的文件
- struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; //所屬目錄所對應的內核對象
- struct sysfs_buffer *buffer;
- const struct sysfs_ops *ops;
- int error = -EACCES;
- char *p;
- //獲得該文件的全路徑並依次保存在last_sysfs_file數組的尾部
- 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) //需S_IWUGO訪問權限且store函數必須定義
- goto err_out;
- }
- if (file->f_mode & FMODE_READ) { //讀文件
- if (!(inode->i_mode & S_IRUGO) || !ops->show) //需S_IRUGO訪問權限且show函數必須定義
- goto err_out;
- }
- //每次打開的文本文件都對應一個struct sysfs_buffer實例
- 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; //保存在文件指針的私有數據中
- //分配struct sysfs_open_dirent結構體實例
- 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) { //每個文本文件的struct sysfs_dirent都有一個open成員
- sd->s_attr.open = new_od; //指向新分配並初始化的struct sysfs_open_dirent實例
- new_od = NULL;
- }
- od = sd->s_attr.open;
- if (od) {
- atomic_inc(&od->refcnt); //遞增引用計數
- //將buffer作為鏈表元素插入到od->buffers鏈表(該鏈表中元素的數量就是該文本文件正被打開的次數)
- list_add_tail(&buffer->list, &od->buffers);
- }
- spin_unlock_irq(&sysfs_open_dirent_lock);
- if (od) {
- kfree(new_od);
- return 0;
- }
- //為struct sysfs_open_dirent結構體實例分配內存
- 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)。源代碼如下所示:
- /* fs/sysfs/file.c */
- struct sysfs_open_dirent {
- atomic_t refcnt; //打開次數
- atomic_t event;
- wait_queue_head_t poll; //等待隊列
- struct list_head buffers; //sysfs_buffer.list的鏈表
- };
- 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; //插入所屬的struct sysfs_open_dirent的buffers鏈表
- };
sysfs_read_file為sysfs文件系統文本文件的讀函數,源代碼如下所示:
- /* fs/sysfs/file.c */
- 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; //調用sysfs_open_file時所生成的
- 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);
- //將count個字節的數據拷貝到用戶空間內存buf,buffer->count表示可拷貝數據的最大字節數(可能須要多次拷貝才能讀取完整個內存)
- 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; //sysfs數據項
- 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) { //至多能讀取PAGE_SIZE - 1個字節
- 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;
- }
- /* fs/libfs.c */
- 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) //只剩available - pos個字節
- count = available - pos;
- ret = copy_to_user(to, from + pos, count); //拷貝數據
- if (ret == count) //返回值為沒有拷貝成功的字節數
- return -EFAULT;
- count -= ret; //獲得count個字節的數據
- *ppos = pos + count; //更新文件偏移量
- return count;
- }
sysfs_write_file為sysfs文件系統的寫函數,源代碼如下所示:
- /* fs/sysfs/file.c */
- static ssize_t
- sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) //ppos為文件偏移量
- {
- struct sysfs_buffer * buffer = file->private_data; //調用sysfs_open_file時所生成的
- ssize_t len;
- mutex_lock(&buffer->mutex);
- len = fill_write_buffer(buffer, buf, count); //一次寫入,從用戶空間內存buf中拷貝count個字節到內核空間
- if (len > 0) //成功拷貝len個字節
- len = flush_write_buffer(file->f_path.dentry, buffer, len); //根據獲得的數據更新該內核對象相應的屬性
- if (len > 0)
- *ppos += len; //更改文件偏移量,對buffer->page中的數據無意義,后面寫入的數據會覆蓋前面寫入的數據
- 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) //寫入的數據最多只能為PAGE_SIZE - 1個字節
- count = PAGE_SIZE - 1;
- error = copy_from_user(buffer->page,buf,count); //拷貝數據,成功函數返回零
- buffer->needs_read_fill = 1;
- buffer->page[count] = 0; //字符串結束符'\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; //sysfs數據項,這里表示一個文本文件
- 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); //調用store函數
- sysfs_put_active(attr_sd); //釋放活動引用計數
- return rc;
- }
關閉文件時,打開、讀/寫文件時所分配的內存都會釋放,並不會一直存在於內核中。
3.3、對於符號鏈接文件來說,它沒有文件內容操作函數,只有i節點操作函數,其中最重要的函數為符號鏈接文件的解析函數sysfs_follow_link,源代碼如下所示:
- /* fs/sysfs/symlink.c */
- 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) //成功時sysfs_getlink返回零
- 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; //sysfs數據項
- struct sysfs_dirent *parent_sd = sd->s_parent; //父sysfs數據項
- struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; //所鏈接到的sysfs數據項
- int error;
- mutex_lock(&sysfs_mutex);
- error = sysfs_get_target_path(parent_sd, target_sd, path); //獲取從鏈接文件到鏈接對象的相對路徑
- mutex_unlock(&sysfs_mutex);
- return error;
- }
- //sysfs鏈接文件是兩個內核對象之間的鏈接,也就是目錄之間的鏈接
- 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) { //sysfs的根數據項為sysfs_root,該數據項的s_parent成員為空指針
- sd = target_sd->s_parent;
- while (sd->s_parent && base != sd) //如果base一直不等於sd,則循環直到根數據項才會停止
- sd = sd->s_parent;
- if (base == sd) //兩者相等,這時鏈接文件和被鏈接對象的直接或間接的父目錄相同
- break;
- strcpy(s, "../"); //拷貝字符串“../”,意味着兩者不是同在這一目錄下
- s += 3;
- base = base->s_parent; //接下來將比較鏈接文件的上一級目錄的數據項
- }
- //這時,base已指向鏈接文件和被鏈接對象首個共有的直接或間接的父目錄的數據項
- //計算整個路徑的長度
- sd = target_sd;
- while (sd->s_parent && sd != base) {
- len += strlen(sd->s_name) + 1; //其中的加1表示目錄項分隔符“/”所占的字節
- sd = sd->s_parent;
- }
- if (len < 2) //名稱為空字符串
- return -EINVAL;
- len--; //減去一個多余的目錄項分隔符所占的字節
- if ((s - path) + len > PATH_MAX) //總長度超過path數組的大小
- return -ENAMETOOLONG;
- //從被鏈接對象開始以倒序的方式拷貝目錄項名稱,直到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) //等於0時表示最后一個名稱的前面不需要再加分隔符
- s[--len] = '/'; //在該名稱前加目錄項分隔符“/”
- sd = sd->s_parent; //接着處理上一級目錄
- }
- return 0;
- }
讀取符號鏈接文件內容的函數generic_readlink主要就是靠解析函數來實現的,源代碼如下所示:
- /* fs/namei.c */
- 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)); //通過nd_get_link獲取follow_link保存的路徑或錯誤碼
- if (dentry->d_inode->i_op->put_link)
- dentry->d_inode->i_op->put_link(dentry, &nd, cookie); //這里put_link指向sysfs_put_link函數
- 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)) //這里的link也有可能是錯誤碼
- goto out;
- len = strlen(link);
- if (len > (unsigned) buflen) //路徑長度比傳入的內存大
- len = buflen;
- if (copy_to_user(buffer, link, len)) //拷貝到用戶空間內存(但不包括字符串結束符)
- len = -EFAULT;
- out:
- return len;
- }
- /* fs/sysfs/symlink.c */
- 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函數分配的內存。
4、對於sysfs文件系統來說,在用戶空間只能讀寫文件的內容,而無法創建或刪除文件或目錄,只能在內核中通過sysfs_create_dir、sysfs_create_file等等函數來實現。
4.1、內核對象(struct kobject)對應於sysfs文件系統中的目錄,可使用sysfs_create_dir函數來創建,源代碼如下所示:
- /* fs/sysfs/dir.c */
- 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) //父內核對象為空時,則在sysfs文件系統的根目錄下創建目錄
- 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; //保存相應的sysfs數據項
- return error;
- }
- /* include/linux/kobject.h */
- static inline const char *kobject_name(const struct kobject *kobj)
- {
- return kobj->name;
- }
- /* fs/sysfs/dir.c */
- 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; //文件類型為目錄,訪問權限為0755
- struct sysfs_addrm_cxt acxt;
- struct sysfs_dirent *sd;
- int rc;
- //分配sysfs數據項並初始化
- sd = sysfs_new_dirent(name, mode, SYSFS_DIR); //數據項類型為目錄
- if (!sd)
- return -ENOMEM;
- sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); //命名空間類型,占用s_flags倒數第二個8位
- 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) //當不為空指針時則表示name指向新分配的內存
- return NULL;
- }
- sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); //從sysfs_dir_cachep緩存中分配sysfs數據項
- if (!sd)
- goto err_out1;
- if (sysfs_alloc_ino(&sd->s_ino)) //分配i節點號
- 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; //sysfs數據項類型,占用s_flags低8位
- 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)) //同名sysfs數據項
- 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.2、內核對象的屬性(struct attribute)對應於sysfs文件系統中的文本文件,可使用sysfs_create_file函數來創建,源代碼如下所示:
- /* fs/sysfs/file.c */
- 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); //數據項類型為SYSFS_KOBJ_ATTR,對應sysfs文件系統中的文本文件
- }
- 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;
- //分配sysfs數據項並初始化
- 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); //dir_sd對應於屬性文件所在的目錄
- rc = sysfs_add_one(&acxt, sd);
- sysfs_addrm_finish(&acxt);
- if (rc) //失敗則銷毀sysfs數據項
- sysfs_put(sd);
- return rc;
- }
sysfs文件系統中的二進制文件通過sysfs_create_bin_file函數來創建,符號鏈接文件通過sysfs_create_link函數來創建。
