Linux a des fonctions d'opération de fichiers du noyau. Par exemple, la fonction filp_open() peut être utilisée pour ouvrir des fichiers, la fonction vfs_read() peut être utilisée pour lire des fichiers, la fonction vfs_write() peut être utilisée pour écrire des fichiers et la fonction vfs_read() peut être utilisée pour écrire des fichiers. La fonction filp_close() peut être utilisée pour fermer des fichiers. Dans les fonctions vfs_read et vfs_write, le deuxième paramètre pointe vers l'adresse mémoire de l'espace utilisateur. Si le pointeur d'espace noyau est utilisé directement, "-EFALUT" sera renvoyé.
L'environnement d'exploitation de ce tutoriel : système linux7.3, ordinateur Dell G3.
1. Opération de fichier d'espace noyau
Fonction | Prototype de fonction |
---|---|
ouvrir le fichier | struct file *filp_open(const char *filename, int flags, int mode) |
Lire le fichier | ssize_t vfs_read (struct file *file, char __user *buf, size_t count, loff_t *pos) |
Write file | ssize_t vfs_write(struct file *file, const char __user *buf, size_t count , LOFF_T *POS) |
Fermer les fichiers | INT FILP_CLOSE (Struct File *FILP, FL_OWNER_T ID) |
2. Fonctions WRITE, il adresse mémoire de l'espace utilisateur pointée par le paramètre buf Si nous utilisons directement le pointeur de l'espace noyau, -EFALUT sera renvoyé. Cela est dû au fait que le tampon utilisé dépasse la plage d'adresses de l'espace utilisateur. Les appels système généraux nécessiteront que le tampon que vous utilisez ne puisse pas se trouver dans la zone du noyau. Cela peut être résolu en utilisant set_fs(), get_fs()
. Dans include/asm/uaccess.h
, il y a la définition suivante :
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) #define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) #define USER_DS MAKE_MM_SEG(PAGE_OFFSET) #define get_ds() (KERNEL_DS) #define get_fs() (current->addr_limit) #define set_fs(x) (current->addr_limit = (x))
set_fs()、get_fs()
来解决。
在include/asm/uaccess.h
中,有如下定义:
mm_segment_t fs = get_fs(); set_fs(KERNEL_FS); //vfs_write(); vfs_read(); set_fs(fs);
如果使用,如下:
/*索引节点对象由inode结构体表示,定义文件在linux/fs.h中 */ struct inode { struct hlist_node i_hash; /* 哈希表 */ struct list_head i_list; /* 索引节点链表 */ struct list_head i_dentry; /* 目录项链表 */ unsigned long i_ino; /* 节点号 */ atomic_t i_count; /* 引用记数 */ umode_t i_mode; /* 访问权限控制 */ unsigned int i_nlink; /* 硬链接数 */ uid_t i_uid; /* 使用者id */ gid_t i_gid; /* 使用者id组 */ kdev_t i_rdev; /* 实设备标识符 */ loff_t i_size; /* 以字节为单位的文件大小 */ struct timespec i_atime; /* 最后访问时间 */ struct timespec i_mtime; /* 最后修改(modify)时间 */ struct timespec i_ctime; /* 最后改变(change)时间 */ unsigned int i_blkbits; /* 以位为单位的块大小 */ unsigned long i_blksize; /* 以字节为单位的块大小 */ unsigned long i_version; /* 版本号 */ unsigned long i_blocks; /* 文件的块数 */ unsigned short i_bytes; /* 使用的字节数 */ spinlock_t i_lock; /* 自旋锁 */ struct rw_semaphore i_alloc_sem; /* 索引节点信号量 */ struct inode_operations *i_op; /* 索引节点操作表 */ struct file_operations *i_fop; /* 默认的索引节点操作 */ struct super_block *i_sb; /* 相关的超级块 */ struct file_lock *i_flock; /* 文件锁链表 */ struct address_space *i_mapping; /* 相关的地址映射 */ struct address_space i_data; /* 设备地址映射 */ struct dquot *i_dquot[MAXQUOTAS]; /* 节点的磁盘限额 */ struct list_head i_devices; /* 块设备链表 */ struct pipe_inode_info *i_pipe; /* 管道信息 */ struct block_device *i_bdev; /* 块设备驱动 */ unsigned long i_dnotify_mask; /* 目录通知掩码 */ struct dnotify_struct *i_dnotify; /* 目录通知 */ unsigned long i_state; /* 状态标志 */ unsigned long dirtied_when; /* 首次修改时间 */ unsigned int i_flags; /* 文件系统标志 */ unsigned char i_sock; /* 可能是个套接字吧 */ atomic_t i_writecount; /* 写者记数 */ void *i_security; /* 安全模块 */ __u32 i_generation; /* 索引节点版本号 */ union { void *generic_ip; /* 文件特殊信息 */ } u; }; /* *索引节点的操作inode_operations定义在linux/fs.h中 */ struct inode_operations { int (*create) (struct inode *, struct dentry *,int); /*VFS通过系统调用create()和open()来调用该函数,从而为dentry对象创建一个新的索引节点。在创建时使用mode制定初始模式*/ struct dentry * (*lookup) (struct inode *, struct dentry *); /*该韩式在特定目录中寻找索引节点,该索引节点要对应于dentry中给出的文件名*/ int (*link) (struct dentry *, struct inode *, struct dentry *); /*该函数被系统调用link()电泳,用来创建硬连接。硬链接名称由dentry参数指定,连接对象是dir目录中ld_dentry目录想所代表的文件*/ int (*unlink) (struct inode *, struct dentry *); /*该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry制动的索引节点对象*/ int (*symlink) (struct inode *, struct dentry *, const char *); /*该函数被系统电泳symlik()调用,创建符号连接,该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项*/ int (*mkdir) (struct inode *, struct dentry *, int); /*该函数被mkdir()调用,创建一个新鲁姆。创建时使用mode制定的初始模式*/ int (*rmdir) (struct inode *, struct dentry *); /*该函数被系统调用rmdir()调用,删除dir目录中的dentry目录项代表的文件*/ int (*mknod) (struct inode *, struct dentry *, int, dev_t); /*该函数被系统调用mknod()调用,创建特殊文件(设备文件、命名管道或套接字)。要创建的文件放在dir目录中,其目录项问dentry,关联的设备为rdev,初始权限由mode指定*/ int (*rename) (struct inode *, struct dentry *, struct inode *, struct dentry *); /*VFS调用该函数来移动文件。文件源路径在old_dir目录中,源文件由old_dentry目录项所指定,目标路径在new_dir目录中,目标文件由new_dentry指定*/ int (*readlink) (struct dentry *, char *, int); /*该函数被系统调用readlink()调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号链接,最大拷贝大小可达到buflen字节*/ int (*follow_link) (struct dentry *, struct nameidata *); /*该函数由VFS调用,从一个符号连接查找他指向的索引节点,由dentry指向的连接被解析*/ int (*put_link) (struct dentry *, struct nameidata *); /*在follow_link()调用之后,该函数由vfs调用进行清楚工作*/ void (*truncate) (struct inode *); /*该函数由VFS调用,修改文件的大小,在调用之前,索引节点的i_size项必须被设置成预期的大小*/ int (*permission) (struct inode *, int); /*该函数用来检查给低昂的inode所代表的文件是否允许特定的访问模式,如果允许特定的访问模式,返回0,否则返回负值的错误码。多数文件系统 都将此区域设置为null,使用VFS提供的通用方法进行检查,这种检查操作仅仅比较索引及诶但对象中的访问模式位是否和mask一致,比较复杂的系统, 比如支持访问控制链(ACL)的文件系统,需要使用特殊的permission()方法*/ int (*setattr) (struct dentry *, struct iattr *); /*该函数被notify_change调用,在修改索引节点之后,通知发生了改变事件*/ int (*getattr) (struct vfsmount *, struct dentry *, struct kstat *); /*在通知索引节点需要从磁盘中更新时,VFS会调用该函数*/ int (*setxattr) (struct dentry *, const char *, const void *, size_t, int); /*该函数由VFS调用,向dentry指定的文件设置扩展属性,属性名为name,值为value*/ ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); /*该函数被VFS调用,向value中拷贝给定文件的扩展属性name对应的数值*/ ssize_t (*listxattr) (struct dentry *, char *, size_t); /*该函数将特定文件所有属性别表拷贝到一个缓冲列表中*/ int (*removexattr) (struct dentry *, const char *); /*该函数从给定文件中删除指定的属性*/ };
详尽解释:系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它默认会认为来自用户空间,在read或write()函数中,为了保护内核空间,一般会用get_fs()
得到的值来和USER_DS进行比较,从而防止用户空间程序“蓄意”破坏内核空间;而现在要在内核空间使用系统调用,此时传递给read或write()的参数地址就是内核空间的地址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它处理,在write()函数中,会认为该地址超过了USER_DS范围,所以会认为是用户空间的“蓄意破坏”,从而不允许进一步的执行;为了解决这个问题set_fs(KERNEL_DS)
Si elle est utilisée, elle est la suivante :
struct file { /* * fu_list becomes invalid after file_free is called and queued via * fu_rcuhead for RCU freeing */ union { struct list_head fu_list; struct rcu_head fu_rcuhead; } f_u; struct path f_path; #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt const struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; loff_t f_pos; struct fown_struct f_owner; unsigned int f_uid, f_gid; struct file_ra_state f_ra; unsigned long f_version; #ifdef CONFIG_SECURITY void *f_security; #endif /* needed for tty driver, and maybe others */ void *private_data; #ifdef CONFIG_EPOLL /* Used by fs/eventpoll.c to link all the hooks to this file */ struct list_head f_ep_links; spinlock_t f_ep_lock; #endif /* #ifdef CONFIG_EPOLL */ struct address_space *f_mapping; };Explication détaillée : Les appels système sont fournis à l'origine à l'espace utilisateur Accessible par le programme, donc pour les paramètres qui lui sont transmis (comme le buf ci-dessus), il pensera par défaut qu'il provient de l'espace utilisateur dans la fonction read ou write(), afin de protéger le noyau. space,
get_fs() est généralement utilisé.
Comparez la valeur obtenue avec USER_DS pour empêcher les programmes de l'espace utilisateur d'endommager "délibérément" l'espace du noyau, et vous devez maintenant utiliser les appels système dans l'espace du noyau ; L'adresse du paramètre passée à read ou write() est l'espace du noyau. L'adresse est au-dessus de USER_DS (USER_DS ~ KERNEL_DS). Si aucun autre traitement n'est effectué, dans la fonction write(), il sera considéré que l'adresse dépasse la plage USER_DS. , cela sera donc considéré comme une "destruction délibérée" de l'espace utilisateur, donc aucune autre exécution n'est autorisée ; afin de résoudre ce problème, set_fs(KERNEL_DS)
étend la limite d'espace auquel il peut accéder à KERNEL_DS ; , afin que les appels système puissent être utilisés sans problème dans le noyau !
3.Structure d'inode de structure Linuxrrreee
4.Structure de fichier de structure Linux
La structure de fichiers représente un fichier ouvert dans le système. Chaque fichier ouvert dans le système est associé à un fichier de structure dans l'espace du noyau. Il est créé par le noyau lorsqu'un fichier est ouvert et transmis à toute fonction qui opère sur le fichier. Le noyau libère cette structure de données une fois que toutes les instances du fichier ont été fermées. Dans la création du noyau et le code source du pilote, le pointeur du fichier struct est généralement nommé file ou filp. 🎜🎜Recommandations associées : "🎜Tutoriel vidéo Linux🎜"🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!