linux有核心檔案操作函數,範例filp_open()函數可用於開啟檔案、vfs_read()函數可用於讀取檔案、vfs_write()函數可用於寫入檔案、filp_close()函數可用於關閉文件。在vfs_read和vfs_write函數中,其第二個參數指向的使用者空間的記憶體位址,如果直接使用核心空間的指針,則會傳回「-EFALUT」。
本教學操作環境:linux7.3系統、Dell G3電腦。
1. 核心空間檔案操作
#功能 | 函數原型 |
---|---|
開啟檔案 | struct file *filp_open(const char *filename, int flags, int mode) |
#讀取檔案 | ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) |
#寫檔案 | # ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) |
關閉檔案 | int filp_close(struct file *filp, fl_owner_t id) |
2. 核心空間與使用者空間
在vfs_read與vfs_write函數中,其參數buf指向的使用者空間的記憶體位址,如果我們直接使用核心空間的指針,則會傳回-EFALUT。這是因為使用的緩衝區超過了使用者空間的位址範圍。一般系統呼叫會要求你使用的緩衝區不能在核心區。這個可以用set_fs()、get_fs()
來解決。
在include/asm/uaccess.h
中,有以下定義:
#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))
如果使用,如下:##
mm_segment_t fs = get_fs(); set_fs(KERNEL_FS); //vfs_write(); vfs_read(); set_fs(fs);
詳盡解釋:系統呼叫本來是提供給使用者空間的程式存取的,所以,對傳遞給它的參數(例如上面的buf),它預設會認為來自使用者空間,在read或write ()函數中,為了保護核心空間,一般會用get_fs()得到的值來和USER_DS進行比較,從而防止用戶空間程式「蓄意」破壞核心空間;而現在要在內核空間使用系統調用,此時傳遞給read或write()的參數位址就是內核空間的位址了,在USER_DS之上(USER_DS ~ KERNEL_DS),如果不做任何其它處理,在write()函數中,會認為該位址超過了USER_DS範圍,所以會認為是用戶空間的“蓄意破壞”,從而不允許進一步的執行;為了解決這個問題
set_fs(KERNEL_DS)將其能訪問的空間限制擴大到KERNEL_DS,這樣就可以在內核順利使用系統呼叫了!
3.Linux struct inode結構#
/*索引节点对象由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 *); /*该函数从给定文件中删除指定的属性*/ };
4.Linux struct file結構# struct file結構體定義在/linux/include/linux/fs.h(Linux 2.6.11核心)中,其原型是:
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; };
Linux影片教學》
以上是linux有內核檔案操作函數嗎的詳細內容。更多資訊請關注PHP中文網其他相關文章!