概述sysfs文件系統【轉】

轉自:http://blog.csdn.net/npy_lp/article/details/78933292node

    內核源碼:linux-2.6.38.8.tar.bz2linux

    目標平臺:ARM體系結構數組

 

    sysfs是基於內存的文件系統,用於向用戶空間導出內核對象而且能對其進行讀寫。緩存

    一、sysfs文件系統不支持特殊文件,只支持目錄、普通文件(文本或二進制文件)和符號連接文件等三種類型,在內核中都使用struct  sysfs_dirent結構體來表示,至關於其餘文件系統在硬盤或flash裏的數據。源代碼以下所示:安全

  1. /* fs/sysfs/sysfs.h */  
  2. struct sysfs_dirent {  
  3.     atomic_t        s_count;    //struct sysfs_dirent結構體實例自身的引用計數  
  4.     atomic_t        s_active;   //struct sysfs_elem_*所涉及的外部對象的引用計數  
  5. #ifdef CONFIG_DEBUG_LOCK_ALLOC  
  6.     struct lockdep_map  dep_map; //死鎖檢測模塊,針對s_attr.attr中的key或skey  
  7. #endif  
  8.     struct sysfs_dirent *s_parent;  //指向父節點  
  9.     struct sysfs_dirent *s_sibling; //同級節點鏈表,插入到父節點的s_dir.children鏈表中  
  10.     const char      *s_name;    //文件名  
  11.   
  12.     const void      *s_ns;      //命名空間  
  13.     union {  
  14.         struct sysfs_elem_dir       s_dir;      //目錄  
  15.         struct sysfs_elem_symlink   s_symlink;  //符號連接文件  
  16.         struct sysfs_elem_attr      s_attr;     //文本文件  
  17.         struct sysfs_elem_bin_attr  s_bin_attr; //二進制文件  
  18.     };  
  19.   
  20.     unsigned int        s_flags;    //標誌,表示struct sysfs_dirent類型、命名空間類型等信息  
  21.     unsigned short      s_mode;     //文件訪問權限,包含文件類型信息  
  22.     ino_t           s_ino;          //對應於i節點號  
  23.     struct sysfs_inode_attrs *s_iattr;  //文件屬性  
  24. };  
  25.   
  26. struct sysfs_inode_attrs {  
  27.     struct iattr    ia_iattr;   //文件屬性  
  28.     void        *ia_secdata;    //安全檢測模塊所用數據  
  29.     u32     ia_secdata_len;     //ia_secdata所指數據的長度  
  30. };  
  31.   
  32. /* include/linux/fs.h */  
  33. struct iattr {  
  34.     unsigned int    ia_valid;   //文件屬性標誌  
  35.     umode_t     ia_mode;        //文件類型及其訪問權限  
  36.     uid_t       ia_uid;         //用戶ID  
  37.     gid_t       ia_gid;         //組ID  
  38.     loff_t      ia_size;        //文件大小  
  39.     struct timespec ia_atime;   //訪問時間  
  40.     struct timespec ia_mtime;   //數據修改時間  
  41.     struct timespec ia_ctime;   //元數據修改時間  
  42.   
  43.     struct file *ia_file;   //輔助信息,用於想實現ftruncate等方法的文件系統  
  44. };  

    struct  sysfs_dirent分爲四種類型,以下所示: cookie

  1. /* fs/sysfs/sysfs.h */  
  2. struct sysfs_elem_dir {  
  3.     struct kobject      *kobj;  //指向內核對象  
  4.     //子節點鏈表,只有目錄纔有可能包含文件或子目錄  
  5.     //子節點經過s_sibling成員以s_ino成員升序的方式插入到該鏈表  
  6.     struct sysfs_dirent *children;    
  7. };  
  8.   
  9. struct sysfs_elem_symlink {  
  10.     struct sysfs_dirent *target_sd; //指向所連接的節點  
  11. };  
  12.   
  13. struct sysfs_elem_attr {  
  14.     struct attribute    *attr;  //對象屬性  
  15.     struct sysfs_open_dirent *open; //文件open信息,其中buffers成員是struct sysfs_buffer的鏈表  
  16. };  
  17.   
  18. struct sysfs_elem_bin_attr {  
  19.     struct bin_attribute    *bin_attr;  //二進制的對象屬性  
  20.     struct hlist_head   buffers; //struct bin_buffer的鏈表  
  21. };  

    struct  sysfs_open_dirent等結構體的詳細信息後文說明。app

    二、sysfs文件系統的初始化是由sysfs_init函數來完成的(該函數由vfs_caches_init函數所調用的mnt_init函數調用),執行文件系統的註冊和掛載,與rootfs根文件系統的相關操做相似。源代碼以下所示:函數

  1. /* fs/sysfs/mount.c */  
  2. static struct vfsmount *sysfs_mnt;  
  3. struct kmem_cache *sysfs_dir_cachep;  
  4.   
  5. int __init sysfs_init(void)  
  6. {  
  7.     int err = -ENOMEM;  
  8.   
  9.     sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache",  
  10.                           sizeof(struct sysfs_dirent),  
  11.                           0, 0, NULL);  //建立用於struct sysfs_dirent的高速緩存  
  12.     if (!sysfs_dir_cachep)  
  13.         goto out;  
  14.           
  15.     //初始化後備存儲介質相關結構體struct backing_dev_info(sysfs文件系統基於內存,無須數據同步)  
  16.     err = sysfs_inode_init();   
  17.     if (err)  
  18.         goto out_err;  
  19.   
  20.     err = register_filesystem(&sysfs_fs_type); //註冊文件系統  
  21.     if (!err) { //成功返回零  
  22.         sysfs_mnt = kern_mount(&sysfs_fs_type); //掛載文件系統,不過沒有將sysfs_mnt所指的結構體實例插入到掛載樹中  
  23.         if (IS_ERR(sysfs_mnt)) {  
  24.             printk(KERN_ERR "sysfs: could not mount!\n");  
  25.             err = PTR_ERR(sysfs_mnt);  
  26.             sysfs_mnt = NULL;  
  27.             unregister_filesystem(&sysfs_fs_type);  
  28.             goto out_err;  
  29.         }  
  30.     } else  
  31.         goto out_err;  
  32. out:  
  33.     return err;  
  34. out_err:  
  35.     kmem_cache_destroy(sysfs_dir_cachep);  
  36.     sysfs_dir_cachep = NULL;  
  37.     goto out;  
  38. }  

    在用戶空間通常都將sysfs文件系統掛載在/sys目錄,而這裏也有一次經過kern_mount函數的掛載,這樣的話 sysfs文件系統就會掛載兩次?實際上是沒有的,後者的掛載並無將當前的struct  vfsmount結構體實例插入到掛載樹中,而是保存在全局指針sysfs_mnt中,而且會與用戶空間掛載sysfs文件系統時所建立的struct  vfsmount結構體實例共享相同的超級塊。所以,不管用戶空間的掛載操做是否執行,sysfs文件系統都會存在於內核之中(編譯內核有配置CONFIG_SYSFS選項)。sysfs文件系統的struct  file_system_type結構體實例以下所示: ui

  1. /* fs/sysfs/mount.c */  
  2. static struct file_system_type sysfs_fs_type = {  
  3.     .name       = "sysfs",  
  4.     .mount      = sysfs_mount,  
  5.     .kill_sb    = sysfs_kill_sb,  
  6. };  
  7.   
  8. static struct dentry *sysfs_mount(struct file_system_type *fs_type,  
  9.     int flags, const char *dev_name, void *data)  
  10. {  
  11.     struct sysfs_super_info *info;  
  12.     enum kobj_ns_type type;  
  13.     struct super_block *sb;  
  14.     int error;  
  15.   
  16.     info = kzalloc(sizeof(*info), GFP_KERNEL); //分配私有數據所用內存  
  17.     if (!info)  
  18.         return ERR_PTR(-ENOMEM);  
  19.           
  20.     //構建超級塊私有數據,用於sysfs_test_super和sysfs_set_super函數  
  21.     for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)   
  22.         info->ns[type] = kobj_ns_current(type);  
  23.   
  24.     sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); //查找或建立超級塊  
  25.     if (IS_ERR(sb) || sb->s_fs_info != info)    
  26.         kfree(info);  
  27.     if (IS_ERR(sb))  
  28.         return ERR_CAST(sb);  
  29.     if (!sb->s_root) { //若是根目錄項爲空指針,則說明超級塊sb是新建立的  
  30.         sb->s_flags = flags;  
  31.         error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); //填充超級塊  
  32.         if (error) {  
  33.             deactivate_locked_super(sb);  
  34.             return ERR_PTR(error);  
  35.         }  
  36.         sb->s_flags |= MS_ACTIVE;  
  37.     }  
  38.   
  39.     return dget(sb->s_root);  
  40. }  

    其中,sysfs_test_super函數用於判斷struct  sysfs_super_info結構體數據是否相同以便確認是否能夠共享超級塊,源代碼以下所示:atom

  1. /* include/linux/kobject_ns.h */  
  2. enum kobj_ns_type {  
  3.     KOBJ_NS_TYPE_NONE = 0,  
  4.     KOBJ_NS_TYPE_NET,  
  5.     KOBJ_NS_TYPES  
  6. };  
  7.   
  8. /* fs/sysfs/sysfs.h */  
  9. struct sysfs_super_info {  
  10.     const void *ns[KOBJ_NS_TYPES];  
  11. };  
  12.   
  13. /* fs/sysfs/mount.c */  
  14. static int sysfs_test_super(struct super_block *sb, void *data)  
  15. {  
  16.     struct sysfs_super_info *sb_info = sysfs_info(sb); //sb->s_fs_info  
  17.     struct sysfs_super_info *info = data;  
  18.     enum kobj_ns_type type;  
  19.     int found = 1;  
  20.   
  21.     for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {  
  22.         if (sb_info->ns[type] != info->ns[type]) //只要有任何一項的值不相同則函數返回0  
  23.             found = 0;  
  24.     }  
  25.     return found;  
  26. }  

    sysfs_fill_super函數主要用於建立sysfs文件系統根目錄所對應的目錄項及其i節點,而且將sysfs_root做爲根目錄項的私有數據。源代碼以下所示:

  1. /* fs/sysfs/mount.c */  
  2. static int sysfs_fill_super(struct super_block *sb, void *data, int silent)  
  3. {  
  4.     struct inode *inode;  
  5.     struct dentry *root;  
  6.   
  7.     sb->s_blocksize = PAGE_CACHE_SIZE; //與內存頁大小相同  
  8.     sb->s_blocksize_bits = PAGE_CACHE_SHIFT;  
  9.     sb->s_magic = SYSFS_MAGIC;  
  10.     sb->s_op = &sysfs_ops;  
  11.     sb->s_time_gran = 1;  
  12.       
  13.     //建立並初始化根目錄的i節點  
  14.     mutex_lock(&sysfs_mutex);  
  15.     inode = sysfs_get_inode(sb, &sysfs_root);  
  16.     mutex_unlock(&sysfs_mutex);  
  17.     if (!inode) {  
  18.         pr_debug("sysfs: could not get root inode\n");  
  19.         return -ENOMEM;  
  20.     }  
  21.       
  22.     //建立並初始化sysfs文件系統的根目錄項並關聯根目錄的i節點  
  23.     root = d_alloc_root(inode);  
  24.     if (!root) {  
  25.         pr_debug("%s: could not get root dentry!\n",__func__);  
  26.         iput(inode);  
  27.         return -ENOMEM;  
  28.     }  
  29.     root->d_fsdata = &sysfs_root;  //根目錄項的d_fsdata成員指向sysfs文件系統的根數據項sysfs_root  
  30.     sb->s_root = root;  
  31.     return 0;  
  32. }  
  33.   
  34. struct sysfs_dirent sysfs_root = {  
  35.     .s_name     = "",  
  36.     .s_count    = ATOMIC_INIT(1),  
  37.     .s_flags    = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),  
  38.     .s_mode     = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, //目錄文件,訪問權限0755  
  39.     .s_ino      = 1,   //起始i節點號爲1  
  40. };  
  41.   
  42. static const struct super_operations sysfs_ops = { //超級塊操做函數  
  43.     .statfs        = simple_statfs,  
  44.     .drop_inode    = generic_delete_inode,  
  45.     .evict_inode    = sysfs_evict_inode,  
  46. };  

    三、文件系統最核心的內容是要看其i節點是如何構建的,sysfs文件系統使用sysfs_init_inode函數來建立。源代碼以下所示:

  1. /* fs/sysfs/inode.c */  
  2. static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)  
  3. {  
  4.     struct bin_attribute *bin_attr;  
  5.   
  6.     inode->i_private = sysfs_get(sd); //指向引用計數遞增以後的sd  
  7.     inode->i_mapping->a_ops = &sysfs_aops; //地址空間操做函數  
  8.     inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; //後備存儲介質的相關信息  
  9.     inode->i_op = &sysfs_inode_operations;  
  10.   
  11.     set_default_inode_attr(inode, sd->s_mode);  
  12.     sysfs_refresh_inode(sd, inode);  
  13.       
  14.     switch (sysfs_type(sd)) { //struct sysfs_dirent類型  
  15.     case SYSFS_DIR: //目錄  
  16.         inode->i_op = &sysfs_dir_inode_operations;  
  17.         inode->i_fop = &sysfs_dir_operations;  
  18.         break;  
  19.     case SYSFS_KOBJ_ATTR: //文本文件  
  20.         inode->i_size = PAGE_SIZE; //文件大小固定爲一頁內存  
  21.         inode->i_fop = &sysfs_file_operations;  
  22.         break;  
  23.     case SYSFS_KOBJ_BIN_ATTR: //二進制文件  
  24.         bin_attr = sd->s_bin_attr.bin_attr;  
  25.         inode->i_size = bin_attr->size;  
  26.         inode->i_fop = &bin_fops;  
  27.         break;  
  28.     case SYSFS_KOBJ_LINK: //符號連接文件  
  29.         inode->i_op = &sysfs_symlink_inode_operations;  
  30.         break;  
  31.     default:  
  32.         BUG();  
  33.     }  
  34.   
  35.     unlock_new_inode(inode);  
  36. }  
  37.   
  38. static inline void set_default_inode_attr(struct inode * inode, mode_t mode)  
  39. {  
  40.     inode->i_mode = mode;  
  41.     inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;  
  42. }  
  43.   
  44. static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)  
  45. {  
  46.     struct sysfs_inode_attrs *iattrs = sd->s_iattr;  
  47.   
  48.     inode->i_mode = sd->s_mode;  
  49.     if (iattrs) { //sd->s_iattr爲真  
  50.         //從iattrs->ia_iattr拷貝ia_uid、ia_gid、ia_atime、ia_mtime和ia_ctime等成員的值給i節點相應的成員  
  51.         set_inode_attr(inode, &iattrs->ia_iattr);   
  52.         security_inode_notifysecctx(inode,  
  53.                         iattrs->ia_secdata,  
  54.                         iattrs->ia_secdata_len); //安全檢測模塊  
  55.     }  
  56.   
  57.     if (sysfs_type(sd) == SYSFS_DIR)  
  58.         inode->i_nlink = sysfs_count_nlink(sd); //計算目錄的硬連接數目(等於子目錄數+2)  
  59. }  

    四種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。源代碼以下所示:

  1. /* fs/sysfs/dir.c */  
  2. const struct inode_operations sysfs_dir_inode_operations = {  
  3.     .lookup     = sysfs_lookup,  
  4.     .permission = sysfs_permission,  
  5.     .setattr    = sysfs_setattr,  
  6.     .getattr    = sysfs_getattr,  
  7.     .setxattr   = sysfs_setxattr,  
  8. };  
  9.   
  10. const struct file_operations sysfs_dir_operations = {  
  11.     .read       = generic_read_dir,  
  12.     .readdir    = sysfs_readdir,  
  13.     .release    = sysfs_dir_release,  
  14.     .llseek     = generic_file_llseek,  
  15. };  
  16.   
  17. static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,  
  18.                 struct nameidata *nd) //dir爲父目錄的i節點,dentry爲被查找對象的目錄項(這時爲d_alloc函數操做以後的狀態)  
  19. {  
  20.     struct dentry *ret = NULL;  
  21.     struct dentry *parent = dentry->d_parent; //父目錄項  
  22.     struct sysfs_dirent *parent_sd = parent->d_fsdata;  
  23.     struct sysfs_dirent *sd;  
  24.     struct inode *inode;  
  25.     enum kobj_ns_type type;  
  26.     const void *ns;  
  27.   
  28.     mutex_lock(&sysfs_mutex);  
  29.       
  30.     //獲取命名空間  
  31.     type = sysfs_ns_type(parent_sd);  
  32.     ns = sysfs_info(dir->i_sb)->ns[type];  
  33.       
  34.     //從其parent_sd->s_dir.children鏈表中查找相同命名空間而且名字相同的子項  
  35.     sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name);  
  36.     if (!sd) { //該子項不存在  
  37.         ret = ERR_PTR(-ENOENT);  
  38.         goto out_unlock;  
  39.     }  
  40.   
  41.     //從inode_hashtable哈希表中查找該子項對應的i節點,若不存在則構建一個新的i節點實例  
  42.     inode = sysfs_get_inode(dir->i_sb, sd);  
  43.     if (!inode) { //構建失敗  
  44.         ret = ERR_PTR(-ENOMEM);  
  45.         goto out_unlock;  
  46.     }  
  47.       
  48.     //對於目錄來講,只能有一個目錄項,而且只有在其爲空目錄或者爲當前文件系統的根目錄時纔有可能被設置DCACHE_UNHASHED狀態  
  49.     //當i節點爲IS_ROOT和DCACHE_DISCONNECTED時,d_find_alias函數返回真  
  50.     ret = d_find_alias(inode);  
  51.     if (!ret) {  
  52.         d_set_d_op(dentry, &sysfs_dentry_ops); //sysfs_dentry_ops爲sysfs文件系統目錄項的操做函數  
  53.         dentry->d_fsdata = sysfs_get(sd);  
  54.         d_add(dentry, inode);   //調用d_instantiate函數將目錄項dentry插入到inode->i_dentry鏈表而且dentry->d_inode指向i節點inode  
  55.                                 //調用d_rehash函數將目錄項dentry插入到dentry_hashtable哈希表中  
  56.     } else {  
  57.         d_move(ret, dentry); //從新使用所返回的目錄項ret並與目錄項dentry交換d_name等數據  
  58.         iput(inode); //銷燬i節點  
  59.     }  
  60.   
  61.  out_unlock:  
  62.     mutex_unlock(&sysfs_mutex);  
  63.     return ret;  
  64. }  

    對於目錄來講,只能有一個目錄項別名。

  1. /* fs/sysfs/dir.c */  
  2. static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)  
  3. {  
  4.     struct dentry *dentry = filp->f_path.dentry; //當前目錄所對應的目錄項  
  5.     struct sysfs_dirent * parent_sd = dentry->d_fsdata;  
  6.     struct sysfs_dirent *pos = filp->private_data;  
  7.     enum kobj_ns_type type;  
  8.     const void *ns;  
  9.     ino_t ino;  
  10.       
  11.     //獲取命名空間  
  12.     type = sysfs_ns_type(parent_sd);  
  13.     ns = sysfs_info(dentry->d_sb)->ns[type];  
  14.       
  15.     //當前目錄  
  16.     if (filp->f_pos == 0) {  
  17.         ino = parent_sd->s_ino;  
  18.         if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0)  
  19.             filp->f_pos++;  
  20.     }  
  21.     //父目錄  
  22.     if (filp->f_pos == 1) {  
  23.         if (parent_sd->s_parent)  
  24.             ino = parent_sd->s_parent->s_ino;  
  25.         else  
  26.             ino = parent_sd->s_ino; //文件系統根目錄將自身看成父目錄  
  27.         if (filldir(dirent, "..", 2, filp->f_pos, ino, DT_DIR) == 0)  
  28.             filp->f_pos++;  
  29.     }  
  30.     mutex_lock(&sysfs_mutex);  
  31.     for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos); //這時filp->f_pos等於2,pos爲NULL  
  32.          pos;  
  33.          pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) { //遍歷當前目錄  
  34.         const char * name;  
  35.         unsigned int type;  
  36.         int len, ret;  
  37.   
  38.         name = pos->s_name;  
  39.         len = strlen(name);  
  40.         ino = pos->s_ino;  
  41.         type = dt_type(pos); //文件類型  
  42.         filp->f_pos = ino; //將i節點號做爲上一個struct linux_dirent實例中d_off成員的值  
  43.         filp->private_data = sysfs_get(pos);  
  44.   
  45.         mutex_unlock(&sysfs_mutex);  
  46.         ret = filldir(dirent, name, len, filp->f_pos, ino, type);  
  47.         mutex_lock(&sysfs_mutex);  
  48.         if (ret < 0)  
  49.             break;  
  50.     }  
  51.     mutex_unlock(&sysfs_mutex);  
  52.     if ((filp->f_pos > 1) && !pos) { //遍歷徹底  
  53.         filp->f_pos = INT_MAX;  
  54.         filp->private_data = NULL;  
  55.     }  
  56.     return 0;  
  57. }  
  58.   
  59. static struct sysfs_dirent *sysfs_dir_pos(const void *ns,  
  60.     struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)  
  61. {  
  62.     if (pos) {  
  63.         int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && //非SYSFS_FLAG_REMOVED項  
  64.             pos->s_parent == parent_sd &&  //屬於目錄parent_sd  
  65.             ino == pos->s_ino;  
  66.         sysfs_put(pos);  
  67.         if (!valid)  
  68.             pos = NULL; //從新遍歷該目錄  
  69.     }  
  70.     if (!pos && (ino > 1) && (ino < INT_MAX)) { //這時pos爲空指針,且i節點號的大小在有效範圍內  
  71.         pos = parent_sd->s_dir.children;  
  72.         while (pos && (ino > pos->s_ino)) //過濾掉i節點號比它小的  
  73.             pos = pos->s_sibling;  
  74.     }  
  75.     while (pos && pos->s_ns && pos->s_ns != ns) //過濾掉不是相同命名空間的  
  76.         pos = pos->s_sibling;  
  77.     return pos;  
  78. }  
  79.   
  80. static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns,  
  81.     struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos)  
  82. {  
  83.     pos = sysfs_dir_pos(ns, parent_sd, ino, pos);  
  84.     if (pos)  
  85.         pos = pos->s_sibling; //獲取下一個項  
  86.     while (pos && pos->s_ns && pos->s_ns != ns)  
  87.         pos = pos->s_sibling;  
  88.     return pos;  
  89. }  

    當在用戶空間經過ls等命令查看sysfs文件系統的目錄時,會經過系統調用getdents調用vfs_readdir,而後再調用sysfs_readdir函數。其中參數filp表示該目錄打開以後的文件指針,dirent實際爲struct  getdents_callback類型的數據,filldir爲回調函數指針,指向同名的filldir函數。源代碼以下所示:

  1. /* include/linux/kernel.h */  
  2. #define __ALIGN_KERNEL(x, a)        __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)  
  3. #define __ALIGN_KERNEL_MASK(x, mask)    (((x) + (mask)) & ~(mask))  
  4. #define ALIGN(x, a)     __ALIGN_KERNEL((x), (a))  
  5.   
  6. /* include/linux/compiler-gcc4.h */  
  7. #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)  //GCC編譯器內置函數,計算成員偏移量  
  8.   
  9. /* include/linux/stddef.h */  
  10. #undef offsetof  
  11. #ifdef __compiler_offsetof  
  12. #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)  
  13. #else  
  14. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  
  15. #endif  
  16.   
  17. /* fs/readdir.c */  
  18. struct linux_dirent {  
  19.     unsigned long   d_ino; //i節點號  
  20.     unsigned long   d_off; //偏移量,無實際意義,在sysfs文件系統的實現中,它的值爲上一個所遍歷的文件或目錄的i節點號(最後一個爲INT_MAX)  
  21.     unsigned short  d_reclen; //整個struct linux_dirent實例的長度(通過對齊修正)  
  22.     char        d_name[1]; //文件名,大小由實際文件名的長度決定(空字符結尾)  
  23. };  
  24.   
  25. struct getdents_callback {  
  26.     struct linux_dirent __user * current_dir;   //初始化爲系統調用傳入的內存地址  
  27.     struct linux_dirent __user * previous;      //指向上一個struct linux_dirent實例  
  28.     int count;  //初始化爲系統調用傳入的內存的總大小(字節數)  
  29.     int error;  //保存錯誤碼  
  30. };  
  31.   
  32. static int filldir(void * __buf, const char * name, int namlen, loff_t offset,  
  33.            u64 ino, unsigned int d_type)  
  34. {  
  35.     struct linux_dirent __user * dirent;  
  36.     struct getdents_callback * buf = (struct getdents_callback *) __buf;  
  37.     unsigned long d_ino;  
  38.     //功能等效於(len / sizeof(long) + (len % sizeof(long) ? 1 : 0)) * sizeof(long),其中len爲ALIGN的第一個參數的值  
  39.     int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,  //其中加2個字節的意義是:一個字節用來存儲字符串的結束符,  
  40.         sizeof(long));                                                      //另外一個字節用來存儲文件類型。  
  41.   
  42.     buf->error = -EINVAL;  
  43.     if (reclen > buf->count) //剩餘的內存不夠  
  44.         return -EINVAL;  
  45.     d_ino = ino;  
  46.     if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) { //d_ino的數據類型較小且致使數據溢出  
  47.         buf->error = -EOVERFLOW;  
  48.         return -EOVERFLOW;  
  49.     }  
  50.     dirent = buf->previous;  
  51.     if (dirent) {  
  52.         if (__put_user(offset, &dirent->d_off)) //以當前的offset值填充上一個struct linux_dirent實例的d_off成員  
  53.             goto efault;  
  54.     }  
  55.     dirent = buf->current_dir;  
  56.     if (__put_user(d_ino, &dirent->d_ino)) //i節點號  
  57.         goto efault;  
  58.     if (__put_user(reclen, &dirent->d_reclen)) //當前struct linux_dirent實例的總大小(字節數)  
  59.         goto efault;  
  60.     if (copy_to_user(dirent->d_name, name, namlen)) //拷貝文件名  
  61.         goto efault;  
  62.     if (__put_user(0, dirent->d_name + namlen)) //在文件名後加字符串結束符'\0'  
  63.         goto efault;  
  64.     if (__put_user(d_type, (char __user *) dirent + reclen - 1)) //最後一個字節用來保存文件類型信息  
  65.         goto efault;  
  66.     buf->previous = dirent;  
  67.     dirent = (void __user *)dirent + reclen;  
  68.     buf->current_dir = dirent; //指向下一個還沒有使用的struct linux_dirent實例  
  69.     buf->count -= reclen; //計算剩餘內存數量  
  70.     return 0;  
  71. efault:  
  72.     buf->error = -EFAULT;  
  73.     return -EFAULT;  
  74. }  

    3.二、sysfs文件系統針對文本文件和二進制文件實現了不一樣的文件內容操做函數,而i節點操做函數則相同。源代碼以下所示:

  1. /* fs/sysfs/inode.c */  
  2. static const struct inode_operations sysfs_inode_operations ={  
  3.     .permission = sysfs_permission,  
  4.     .setattr    = sysfs_setattr,  
  5.     .getattr    = sysfs_getattr,  
  6.     .setxattr   = sysfs_setxattr,  
  7. };  
  8.   
  9. /* fs/sysfs/file.c */  
  10. const struct file_operations sysfs_file_operations = { //文件文件  
  11.     .read       = sysfs_read_file,  
  12.     .write      = sysfs_write_file,  
  13.     .llseek     = generic_file_llseek,  
  14.     .open       = sysfs_open_file,  
  15.     .release    = sysfs_release,  
  16.     .poll       = sysfs_poll,  
  17. };  
  18.   
  19. /* fs/sysfs/bin.c */  
  20. const struct file_operations bin_fops = { //二進制文件  
  21.     .read       = read,  
  22.     .write      = write,  
  23.     .mmap       = mmap,  
  24.     .llseek     = generic_file_llseek,  
  25.     .open       = open,  
  26.     .release    = release,  
  27. };  

    針對普通文件,最重要的是觀察它的文件內容操做函數的實現,如open、read和write等函數。下面則以文本文件爲例,看看sysfs文件系統是如何讀寫文件的。源代碼以下所示:

  1. /* include/linux/limits.h */  
  2. #define PATH_MAX        4096  
  3.   
  4. /* fs/sysfs/file.c */  
  5. static char last_sysfs_file[PATH_MAX];  
  6.   
  7. static int sysfs_open_file(struct inode *inode, struct file *file)  
  8. {  
  9.     struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; //sysfs數據項,表示當前被打開的文件  
  10.     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; //所屬目錄所對應的內核對象  
  11.     struct sysfs_buffer *buffer;  
  12.     const struct sysfs_ops *ops;  
  13.     int error = -EACCES;  
  14.     char *p;  
  15.       
  16.     //得到該文件的全路徑並依次保存在last_sysfs_file數組的尾部  
  17.     p = d_path(&file->f_path, last_sysfs_file, sizeof(last_sysfs_file));  
  18.     if (!IS_ERR(p))  
  19.         memmove(last_sysfs_file, p, strlen(p) + 1); //將路徑移動到數組的開頭  
  20.           
  21.     //獲取活動引用計數  
  22.     if (!sysfs_get_active(attr_sd))  
  23.         return -ENODEV;  
  24.           
  25.     if (kobj->ktype && kobj->ktype->sysfs_ops) //內核對象針對所屬屬性的讀寫函數必須存在  
  26.         ops = kobj->ktype->sysfs_ops;  
  27.     else {  
  28.         WARN(1, KERN_ERR "missing sysfs attribute operations for "  
  29.                "kobject: %s\n", kobject_name(kobj));  
  30.         goto err_out;  
  31.     }  
  32.       
  33.     if (file->f_mode & FMODE_WRITE) { //寫文件  
  34.         if (!(inode->i_mode & S_IWUGO) || !ops->store) //需S_IWUGO訪問權限且store函數必須定義  
  35.             goto err_out;  
  36.     }  
  37.       
  38.     if (file->f_mode & FMODE_READ) { //讀文件  
  39.         if (!(inode->i_mode & S_IRUGO) || !ops->show) //需S_IRUGO訪問權限且show函數必須定義  
  40.             goto err_out;  
  41.     }  
  42.       
  43.     //每次打開的文本文件都對應一個struct sysfs_buffer實例  
  44.     error = -ENOMEM;  
  45.     buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);  
  46.     if (!buffer)  
  47.         goto err_out;  
  48.   
  49.     mutex_init(&buffer->mutex);  
  50.     buffer->needs_read_fill = 1;  
  51.     buffer->ops = ops;  
  52.     file->private_data = buffer; //保存在文件指針的私有數據中  
  53.   
  54.     //分配struct sysfs_open_dirent結構體實例  
  55.     error = sysfs_get_open_dirent(attr_sd, buffer);  
  56.     if (error)  
  57.         goto err_free;  
  58.           
  59.     //打開成功,釋放活動引用計數  
  60.     sysfs_put_active(attr_sd);  
  61.     return 0;  
  62.   
  63.  err_free:  
  64.     kfree(buffer);  
  65.  err_out:  
  66.     sysfs_put_active(attr_sd);  
  67.     return error;  
  68. }  
  69.   
  70. static DEFINE_SPINLOCK(sysfs_open_dirent_lock);  
  71.   
  72. static int sysfs_get_open_dirent(struct sysfs_dirent *sd,  
  73.                  struct sysfs_buffer *buffer)  
  74. {  
  75.     struct sysfs_open_dirent *od, *new_od = NULL;  
  76.   
  77.  retry:  
  78.     spin_lock_irq(&sysfs_open_dirent_lock);  
  79.   
  80.     if (!sd->s_attr.open && new_od) { //每一個文本文件的struct sysfs_dirent都有一個open成員  
  81.         sd->s_attr.open = new_od; //指向新分配並初始化的struct sysfs_open_dirent實例  
  82.         new_od = NULL;  
  83.     }  
  84.   
  85.     od = sd->s_attr.open;  
  86.     if (od) {  
  87.         atomic_inc(&od->refcnt); //遞增引用計數  
  88.         //將buffer做爲鏈表元素插入到od->buffers鏈表(該鏈表中元素的數量就是該文本文件正被打開的次數)  
  89.         list_add_tail(&buffer->list, &od->buffers);   
  90.     }  
  91.   
  92.     spin_unlock_irq(&sysfs_open_dirent_lock);  
  93.   
  94.     if (od) {  
  95.         kfree(new_od);  
  96.         return 0;  
  97.     }  
  98.   
  99.     //爲struct sysfs_open_dirent結構體實例分配內存  
  100.     new_od = kmalloc(sizeof(*new_od), GFP_KERNEL);  
  101.     if (!new_od)  
  102.         return -ENOMEM;  
  103.           
  104.     //初始化成員  
  105.     atomic_set(&new_od->refcnt, 0);  
  106.     atomic_set(&new_od->event, 1);  
  107.     init_waitqueue_head(&new_od->poll);  
  108.     INIT_LIST_HEAD(&new_od->buffers);  
  109.     goto retry;  
  110. }  

    在sysfs文件系統中,文本文件使用struct sysfs_elem_attr來表示,而該結構體有一個struct  sysfs_open_dirent類型的open成員,主要用於管理每次打開並讀/寫該文件時所使用的內存(struct  sysfs_buffer)。源代碼以下所示:

  1. /* fs/sysfs/file.c */  
  2. struct sysfs_open_dirent {  
  3.     atomic_t        refcnt; //打開次數  
  4.     atomic_t        event;  
  5.     wait_queue_head_t   poll; //等待隊列  
  6.     struct list_head    buffers; //sysfs_buffer.list的鏈表  
  7. };  
  8.   
  9. struct sysfs_buffer {  
  10.     size_t          count;  //數據大小(字節數)  
  11.     loff_t          pos;    //偏移量  
  12.     char            * page; //指向一頁內存,用於存儲數據  
  13.     const struct sysfs_ops  * ops;  //操做函數  
  14.     struct mutex        mutex;      //互斥鎖  
  15.     int         needs_read_fill;    //是否已填充數據  
  16.     int         event;  
  17.     struct list_head    list;   //插入所屬的struct sysfs_open_dirent的buffers鏈表  
  18. };  

    sysfs_read_file爲sysfs文件系統文本文件的讀函數,源代碼以下所示:

  1. /* fs/sysfs/file.c */  
  2. static ssize_t  
  3. sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  4. {  
  5.     struct sysfs_buffer * buffer = file->private_data;  //調用sysfs_open_file時所生成的  
  6.     ssize_t retval = 0;  
  7.   
  8.     mutex_lock(&buffer->mutex);  
  9.     if (buffer->needs_read_fill || *ppos == 0) { //還沒有獲取數據或者文件偏移量爲零  
  10.         retval = fill_read_buffer(file->f_path.dentry,buffer); //一次讀取  
  11.         if (retval) //獲取數據失敗(返回零表示成功)  
  12.             goto out;  
  13.     }  
  14.     pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",  
  15.          __func__, count, *ppos, buffer->page);  
  16.            
  17.     //將count個字節的數據拷貝到用戶空間內存buf,buffer->count表示可拷貝數據的最大字節數(可能需要屢次拷貝才能讀取完整個內存)  
  18.     retval = simple_read_from_buffer(buf, count, ppos, buffer->page,  
  19.                      buffer->count);   
  20. out:  
  21.     mutex_unlock(&buffer->mutex);  
  22.     return retval;  
  23. }  
  24.   
  25. static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)  
  26. {  
  27.     struct sysfs_dirent *attr_sd = dentry->d_fsdata; //sysfs數據項  
  28.     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; //所屬內核對象  
  29.     const struct sysfs_ops * ops = buffer->ops;  
  30.     int ret = 0;  
  31.     ssize_t count;  
  32.   
  33.     if (!buffer->page) //分配存儲數據的內存  
  34.         buffer->page = (char *) get_zeroed_page(GFP_KERNEL);  
  35.     if (!buffer->page) //內存分配失敗  
  36.         return -ENOMEM;  
  37.           
  38.     if (!sysfs_get_active(attr_sd)) //獲取活動引用計數  
  39.         return -ENODEV;  
  40.   
  41.     buffer->event = atomic_read(&attr_sd->s_attr.open->event);  
  42.     count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);  //從該內核對象相應的屬性中獲取數據並保存到剛纔所分配的內存中  
  43.   
  44.     sysfs_put_active(attr_sd); //釋放活動引用計數  
  45.       
  46.     if (count >= (ssize_t)PAGE_SIZE) { //至多能讀取PAGE_SIZE - 1個字節  
  47.         print_symbol("fill_read_buffer: %s returned bad count\n",  
  48.             (unsigned long)ops->show);  
  49.         /* Try to struggle along */  
  50.         count = PAGE_SIZE - 1;  
  51.     }  
  52.     if (count >= 0) {  
  53.         buffer->needs_read_fill = 0; //表示填充過數據  
  54.         buffer->count = count;  
  55.     } else {  
  56.         ret = count; //獲取失敗  
  57.     }  
  58.     return ret;  
  59. }  
  60.   
  61. /* fs/libfs.c */  
  62. ssize_t simple_read_from_buffer(void __user *to, size_t count, loff_t *ppos,  
  63.                 const void *from, size_t available)  
  64. {  
  65.     loff_t pos = *ppos;  
  66.     size_t ret;  
  67.   
  68.     if (pos < 0) //文件偏移量不能爲負數  
  69.         return -EINVAL;  
  70.     if (pos >= available || !count) //已無數據可讀或者是想讀取零個字節  
  71.         return 0;  
  72.     if (count > available - pos)  //只剩available - pos個字節  
  73.         count = available - pos;  
  74.     ret = copy_to_user(to, from + pos, count); //拷貝數據  
  75.     if (ret == count) //返回值爲沒有拷貝成功的字節數  
  76.         return -EFAULT;  
  77.     count -= ret; //得到count個字節的數據  
  78.     *ppos = pos + count; //更新文件偏移量  
  79.     return count;  
  80. }  

    sysfs_write_file爲sysfs文件系統的寫函數,源代碼以下所示:

  1. /* fs/sysfs/file.c */  
  2. static ssize_t  
  3. sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t *ppos) //ppos爲文件偏移量  
  4. {  
  5.     struct sysfs_buffer * buffer = file->private_data; //調用sysfs_open_file時所生成的  
  6.     ssize_t len;  
  7.   
  8.     mutex_lock(&buffer->mutex);  
  9.     len = fill_write_buffer(buffer, buf, count); //一次寫入,從用戶空間內存buf中拷貝count個字節到內核空間  
  10.     if (len > 0) //成功拷貝len個字節  
  11.         len = flush_write_buffer(file->f_path.dentry, buffer, len); //根據得到的數據更新該內核對象相應的屬性  
  12.     if (len > 0)  
  13.         *ppos += len; //更改文件偏移量,對buffer->page中的數據無心義,後面寫入的數據會覆蓋前面寫入的數據  
  14.     mutex_unlock(&buffer->mutex);  
  15.     return len;  
  16. }  
  17.   
  18. static int   
  19. fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t count)  
  20. {  
  21.     int error;  
  22.   
  23.     if (!buffer->page) //分配存儲數據的內存  
  24.         buffer->page = (char *)get_zeroed_page(GFP_KERNEL);  
  25.     if (!buffer->page) //內存分配失敗  
  26.         return -ENOMEM;  
  27.   
  28.     if (count >= PAGE_SIZE) //寫入的數據最多隻能爲PAGE_SIZE - 1個字節  
  29.         count = PAGE_SIZE - 1;  
  30.     error = copy_from_user(buffer->page,buf,count); //拷貝數據,成功函數返回零  
  31.     buffer->needs_read_fill = 1;  
  32.     buffer->page[count] = 0; //字符串結束符'\0'  
  33.     return error ? -EFAULT : count;  
  34. }  
  35.   
  36. static int  
  37. flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count)  
  38. {  
  39.     struct sysfs_dirent *attr_sd = dentry->d_fsdata; //sysfs數據項,這裏表示一個文本文件  
  40.     struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; //所屬內核對象  
  41.     const struct sysfs_ops * ops = buffer->ops;  
  42.     int rc;  
  43.   
  44.     if (!sysfs_get_active(attr_sd)) //獲取活動引用計數  
  45.         return -ENODEV;  
  46.   
  47.     rc = ops->store(kobj, attr_sd->s_attr.attr, buffer->page, count); //調用store函數  
  48.   
  49.     sysfs_put_active(attr_sd); //釋放活動引用計數  
  50.   
  51.     return rc;  
  52. }  

    關閉文件時,打開、讀/寫文件時所分配的內存都會釋放,並不會一直存在於內核中。

    3.三、對於符號連接文件來講,它沒有文件內容操做函數,只有i節點操做函數,其中最重要的函數爲符號連接文件的解析函數sysfs_follow_link,源代碼以下所示:

  1. /* fs/sysfs/symlink.c */  
  2. const struct inode_operations sysfs_symlink_inode_operations = {  
  3.     .setxattr   = sysfs_setxattr,  
  4.     .readlink   = generic_readlink,  
  5.     .follow_link    = sysfs_follow_link,  
  6.     .put_link   = sysfs_put_link,  
  7.     .setattr    = sysfs_setattr,  
  8.     .getattr    = sysfs_getattr,  
  9.     .permission = sysfs_permission,  
  10. };  
  11.   
  12. static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd)  
  13. {  
  14.     int error = -ENOMEM;  
  15.     unsigned long page = get_zeroed_page(GFP_KERNEL); //分配內存  
  16.     if (page) {  
  17.         error = sysfs_getlink(dentry, (char *) page);   
  18.         if (error < 0)  //成功時sysfs_getlink返回零  
  19.             free_page((unsigned long)page);  
  20.     }  
  21.     nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); //保存得到的相對路徑或錯誤碼  
  22.     return NULL;  
  23. }  
  24.   
  25. static int sysfs_getlink(struct dentry *dentry, char * path)  
  26. {  
  27.     struct sysfs_dirent *sd = dentry->d_fsdata; //sysfs數據項  
  28.     struct sysfs_dirent *parent_sd = sd->s_parent; //父sysfs數據項  
  29.     struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; //所連接到的sysfs數據項  
  30.     int error;  
  31.   
  32.     mutex_lock(&sysfs_mutex);  
  33.     error = sysfs_get_target_path(parent_sd, target_sd, path); //獲取從連接文件到連接對象的相對路徑  
  34.     mutex_unlock(&sysfs_mutex);  
  35.   
  36.     return error;  
  37. }  
  38.   
  39. //sysfs連接文件是兩個內核對象之間的連接,也就是目錄之間的連接  
  40. static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,  
  41.                  struct sysfs_dirent *target_sd, char *path)  
  42. {  
  43.     struct sysfs_dirent *base, *sd;  
  44.     char *s = path;  
  45.     int len = 0;  
  46.       
  47.     base = parent_sd;  
  48.     while (base->s_parent) { //sysfs的根數據項爲sysfs_root,該數據項的s_parent成員爲空指針  
  49.         sd = target_sd->s_parent;  
  50.         while (sd->s_parent && base != sd)  //若是base一直不等於sd,則循環直到根數據項纔會中止  
  51.             sd = sd->s_parent;  
  52.   
  53.         if (base == sd) //二者相等,這時連接文件和被連接對象的直接或間接的父目錄相同  
  54.             break;  
  55.   
  56.         strcpy(s, "../");  //拷貝字符串「../」,意味着二者不是同在這一目錄下  
  57.         s += 3;  
  58.         base = base->s_parent; //接下來將比較連接文件的上一級目錄的數據項  
  59.     }  
  60.       
  61.     //這時,base已指向連接文件和被連接對象首個共有的直接或間接的父目錄的數據項  
  62.       
  63.     //計算整個路徑的長度  
  64.     sd = target_sd;  
  65.     while (sd->s_parent && sd != base) {  
  66.         len += strlen(sd->s_name) + 1; //其中的加1表示目錄項分隔符「/」所佔的字節  
  67.         sd = sd->s_parent;  
  68.     }  
  69.       
  70.     if (len < 2) //名稱爲空字符串  
  71.         return -EINVAL;  
  72.     len--; //減去一個多餘的目錄項分隔符所佔的字節  
  73.     if ((s - path) + len > PATH_MAX) //總長度超過path數組的大小  
  74.         return -ENAMETOOLONG;  
  75.   
  76.     //從被連接對象開始以倒序的方式拷貝目錄項名稱,直到base數據項(但不包括它的)  
  77.     sd = target_sd;  
  78.     while (sd->s_parent && sd != base) {  
  79.         int slen = strlen(sd->s_name);  
  80.   
  81.         len -= slen;  
  82.         strncpy(s + len, sd->s_name, slen); //拷貝名稱  
  83.         if (len) //等於0時表示最後一個名稱的前面不須要再加分隔符  
  84.             s[--len] = '/'; //在該名稱前加目錄項分隔符「/」  
  85.   
  86.         sd = sd->s_parent; //接着處理上一級目錄  
  87.     }  
  88.   
  89.     return 0;  
  90. }  

    讀取符號連接文件內容的函數generic_readlink主要就是靠解析函數來實現的,源代碼以下所示:

  1. /* fs/namei.c */  
  2. int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)  
  3. {  
  4.     struct nameidata nd;  
  5.     void *cookie;  
  6.     int res;  
  7.   
  8.     nd.depth = 0;  
  9.     cookie = dentry->d_inode->i_op->follow_link(dentry, &nd);  
  10.     if (IS_ERR(cookie)) //返回錯誤碼  
  11.         return PTR_ERR(cookie);  
  12.   
  13.     res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); //經過nd_get_link獲取follow_link保存的路徑或錯誤碼  
  14.     if (dentry->d_inode->i_op->put_link)  
  15.         dentry->d_inode->i_op->put_link(dentry, &nd, cookie); //這裏put_link指向sysfs_put_link函數  
  16.     return res;  
  17. }  
  18.   
  19. int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link)  
  20. {  
  21.     int len;  
  22.   
  23.     len = PTR_ERR(link);  
  24.     if (IS_ERR(link)) //這裏的link也有多是錯誤碼  
  25.         goto out;  
  26.   
  27.     len = strlen(link);  
  28.     if (len > (unsigned) buflen) //路徑長度比傳入的內存大  
  29.         len = buflen;  
  30.     if (copy_to_user(buffer, link, len)) //拷貝到用戶空間內存(但不包括字符串結束符)  
  31.         len = -EFAULT;  
  32. out:  
  33.     return len;  
  34. }  
  35.   
  36. /* fs/sysfs/symlink.c */  
  37. static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)  
  38. {  
  39.     char *page = nd_get_link(nd);  
  40.     if (!IS_ERR(page)) //非錯誤碼  
  41.         free_page((unsigned long)page); //釋放內存  
  42. }  

    其中,sysfs_put_link函數執行與sysfs_follow_link函數相反的操做,這裏只是釋放由sysfs_follow_link函數分配的內存。

    四、對於sysfs文件系統來講,在用戶空間只能讀寫文件的內容,而沒法建立或刪除文件或目錄,只能在內核中經過sysfs_create_dir、sysfs_create_file等等函數來實現。

    4.一、內核對象(struct  kobject)對應於sysfs文件系統中的目錄,可以使用sysfs_create_dir函數來建立,源代碼以下所示:

  1. /* fs/sysfs/dir.c */  
  2. int sysfs_create_dir(struct kobject * kobj)  
  3. {  
  4.     enum kobj_ns_type type;  
  5.     struct sysfs_dirent *parent_sd, *sd;  
  6.     const void *ns = NULL;  
  7.     int error = 0;  
  8.   
  9.     BUG_ON(!kobj);  
  10.   
  11.     if (kobj->parent) //父內核對象爲空時,則在sysfs文件系統的根目錄下建立目錄  
  12.         parent_sd = kobj->parent->sd;  
  13.     else  
  14.         parent_sd = &sysfs_root;  
  15.           
  16.     //獲取命名空間及其類型  
  17.     if (sysfs_ns_type(parent_sd))  
  18.         ns = kobj->ktype->namespace(kobj);  
  19.     type = sysfs_read_ns_type(kobj);  
  20.   
  21.     error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);  
  22.     if (!error) //成功則返回零  
  23.         kobj->sd = sd;  //保存相應的sysfs數據項  
  24.     return error;  
  25. }  
  26.   
  27. /* include/linux/kobject.h */  
  28. static inline const char *kobject_name(const struct kobject *kobj)  
  29. {  
  30.     return kobj->name;  
  31. }  
  32.   
  33. /* fs/sysfs/dir.c */  
  34. static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,  
  35.     enum kobj_ns_type type, const void *ns, const char *name,  
  36.     struct sysfs_dirent **p_sd)  
  37. {  
  38.     umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; //文件類型爲目錄,訪問權限爲0755  
  39.     struct sysfs_addrm_cxt acxt;  
  40.     struct sysfs_dirent *sd;  
  41.     int rc;  
  42.   
  43.     //分配sysfs數據項並初始化  
  44.     sd = sysfs_new_dirent(name, mode, SYSFS_DIR); //數據項類型爲目錄  
  45.     if (!sd)  
  46.         return -ENOMEM;  
  47.   
  48.     sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); //命名空間類型,佔用s_flags倒數第二個8位  
  49.     sd->s_ns = ns; //命名空間  
  50.     sd->s_dir.kobj = kobj; //關聯內核對象  
  51.       
  52.     sysfs_addrm_start(&acxt, parent_sd); //加鎖並攜帶父數據項  
  53.     rc = sysfs_add_one(&acxt, sd); //關聯父數據項  
  54.     sysfs_addrm_finish(&acxt); //解鎖  
  55.   
  56.     if (rc == 0)  //成功返回  
  57.         *p_sd = sd;  
  58.     else  
  59.         sysfs_put(sd); //釋放數據項  
  60.   
  61.     return rc;  
  62. }  
  63.   
  64. struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)  
  65. {  
  66.     char *dup_name = NULL;  
  67.     struct sysfs_dirent *sd;  
  68.   
  69.     if (type & SYSFS_COPY_NAME) { //目錄或符號連接文件須要拷貝文件名,它們對應的都是內核對象  
  70.         name = dup_name = kstrdup(name, GFP_KERNEL); //分配內存並拷貝文件名  
  71.         if (!name) //當不爲空指針時則表示name指向新分配的內存  
  72.             return NULL;  
  73.     }  
  74.   
  75.     sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); //從sysfs_dir_cachep緩存中分配sysfs數據項  
  76.     if (!sd)  
  77.         goto err_out1;  
  78.   
  79.     if (sysfs_alloc_ino(&sd->s_ino)) //分配i節點號  
  80.         goto err_out2;  
  81.           
  82.     //引用計數  
  83.     atomic_set(&sd->s_count, 1);  
  84.     atomic_set(&sd->s_active, 0);  
  85.   
  86.     sd->s_name = name; //目錄項名稱  
  87.     sd->s_mode = mode; //文件類型及訪問權限  
  88.     sd->s_flags = type; //sysfs數據項類型,佔用s_flags低8位  
  89.   
  90.     return sd;  
  91.   
  92.  err_out2:  
  93.     kmem_cache_free(sysfs_dir_cachep, sd);  
  94.  err_out1:  
  95.     kfree(dup_name);  
  96.     return NULL;  
  97. }  
  98.   
  99. int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)  
  100. {  
  101.     int ret;  
  102.   
  103.     ret = __sysfs_add_one(acxt, sd);  
  104.     if (ret == -EEXIST) { //同名數據項已經存在則輸出告警信息  
  105.         char *path = kzalloc(PATH_MAX, GFP_KERNEL);  
  106.         WARN(1, KERN_WARNING  
  107.              "sysfs: cannot create duplicate filename '%s'\n",  
  108.              (path == NULL) ? sd->s_name :  
  109.              strcat(strcat(sysfs_pathname(acxt->parent_sd, path), "/"),  
  110.                     sd->s_name)); //數據項的全路徑  
  111.         kfree(path);  
  112.     }  
  113.   
  114.     return ret;  
  115. }  
  116.   
  117. int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)  
  118. {  
  119.     struct sysfs_inode_attrs *ps_iattr;  
  120.   
  121.     if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) //查找該父目錄下是否存在同名的數據項  
  122.         return -EEXIST; //已存在則返回錯誤碼  
  123.   
  124.     sd->s_parent = sysfs_get(acxt->parent_sd); //遞增父數據項的引用計數,而後指向該父數據項  
  125.   
  126.     sysfs_link_sibling(sd); //加入到父數據項的子數據項鍊表  
  127.   
  128.     //更新父數據項的時間戳  
  129.     ps_iattr = acxt->parent_sd->s_iattr;  
  130.     if (ps_iattr) {  
  131.         struct iattr *ps_iattrs = &ps_iattr->ia_iattr;  
  132.         ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;  
  133.     }  
  134.   
  135.     return 0;  
  136. }  
  137.   
  138. struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,  
  139.                        const void *ns,  
  140.                        const unsigned char *name)  
  141. {  
  142.     struct sysfs_dirent *sd;  
  143.   
  144.     for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { //遍歷父目錄  
  145.         if (ns && sd->s_ns && (sd->s_ns != ns)) //查找同一命名空間  
  146.             continue;  
  147.         if (!strcmp(sd->s_name, name)) //同名sysfs數據項  
  148.             return sd;  
  149.     }  
  150.     return NULL;  
  151. }  
  152.   
  153. static void sysfs_link_sibling(struct sysfs_dirent *sd)  
  154. {  
  155.     struct sysfs_dirent *parent_sd = sd->s_parent;  
  156.     struct sysfs_dirent **pos;  
  157.   
  158.     BUG_ON(sd->s_sibling);  
  159.   
  160.     for (pos = &parent_sd->s_dir.children; *pos; pos = &(*pos)->s_sibling) {  
  161.         if (sd->s_ino < (*pos)->s_ino) //升序排列  
  162.             break;  
  163.     }  
  164.       
  165.     sd->s_sibling = *pos;  
  166.     *pos = sd;  
  167. }  

    4.二、內核對象的屬性(struct  attribute)對應於sysfs文件系統中的文本文件,可以使用sysfs_create_file函數來建立,源代碼以下所示:

  1. /* fs/sysfs/file.c */  
  2. int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)  
  3. {  
  4.     BUG_ON(!kobj || !kobj->sd || !attr); //三者必須爲真  
  5.   
  6.     return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); //數據項類型爲SYSFS_KOBJ_ATTR,對應sysfs文件系統中的文本文件  
  7. }  
  8.   
  9. int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,  
  10.            int type)  
  11. {  
  12.     return sysfs_add_file_mode(dir_sd, attr, type, attr->mode); //訪問權限由內核對象的屬性自身配置  
  13. }  
  14.   
  15. int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,  
  16.             const struct attribute *attr, int type, mode_t amode)  
  17. {  
  18.     umode_t mode = (amode & S_IALLUGO) | S_IFREG; //文件類型爲普通文件  
  19.     struct sysfs_addrm_cxt acxt;  
  20.     struct sysfs_dirent *sd;  
  21.     int rc;  
  22.       
  23.     //分配sysfs數據項並初始化  
  24.     sd = sysfs_new_dirent(attr->name, mode, type);  
  25.     if (!sd)  
  26.         return -ENOMEM;  
  27.     sd->s_attr.attr = (void *)attr; //保存內核對象屬性  
  28.     sysfs_dirent_init_lockdep(sd); //初始化死鎖檢測模塊  
  29.   
  30.     sysfs_addrm_start(&acxt, dir_sd); //dir_sd對應於屬性文件所在的目錄  
  31.     rc = sysfs_add_one(&acxt, sd);  
  32.     sysfs_addrm_finish(&acxt);  
  33.   
  34.     if (rc) //失敗則銷燬sysfs數據項  
  35.         sysfs_put(sd);  
  36.   
  37.     return rc;  
  38. }  

    sysfs文件系統中的二進制文件經過sysfs_create_bin_file函數來建立,符號連接文件經過sysfs_create_link函數來建立。

相關文章
相關標籤/搜索