>  기사  >  Linux에는 커널 파일 작업 기능이 있습니까?

Linux에는 커널 파일 작업 기능이 있습니까?

青灯夜游
青灯夜游원래의
2023-03-02 10:39:011360검색

Linux에는 커널 파일 작업 기능이 있습니다. 예를 들어 filp_open() 함수는 파일을 열 수 있고, vfs_read() 함수는 파일을 읽을 수 있으며, vfs_write() 함수는 파일을 쓸 수 있습니다. filp_close() 함수를 사용하여 파일을 닫을 수 있습니다. vfs_read 및 vfs_write 함수에서 두 번째 매개변수는 사용자 공간의 메모리 주소를 가리킵니다. 커널 공간 포인터를 직접 사용하면 "-EFALUT"가 반환됩니다.

Linux에는 커널 파일 작업 기능이 있습니까?

이 튜토리얼의 운영 환경: linux7.3 시스템, Dell G3 컴퓨터.

1. 커널 공간 파일 작업

Function Function 프로토타입
파일 열기 struct 파일 *filp_open(const char *filename, int flags, int 모드)
파일 읽기 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)
파일 닫기 INT FILP_CLOSE (Struct File *FILP, FL_OWNER_T ID)

2 VFS_READ 및 VFS의 커널 공간 및 사용자 공간

_WRITE 함수, ​​그것은 매개변수 buf가 가리키는 사용자 공간의 메모리 주소. 커널 공간의 포인터를 직접 사용하면 -EFALUT가 반환됩니다. 이는 사용된 버퍼가 사용자 공간 주소 범위를 초과하기 때문입니다. 일반 시스템 호출에서는 사용하는 버퍼가 커널 영역에 있어서는 안 됩니다. 이는 set_fs(), get_fs()를 사용하여 해결할 수 있습니다. 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)

include/asm/uaccess.h에는 다음과 같은 정의가 있습니다.

/*索引节点对象由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 *);
        /*该函数从给定文件中删除指定的属性*/
};
사용하면 다음과 같습니다.
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;
};

자세한 설명: 시스템 호출은 원래 제공됩니다. 프로그램에 의해 액세스되는 사용자 공간으로 전달된 매개변수(예: 위의 buf)에 대해 커널을 보호하기 위해 read 또는 write() 함수에서 기본적으로 사용자 공간에서 온 것으로 생각합니다. space에서는 get_fs()가 일반적으로 사용됩니다.사용자 공간 프로그램이 커널 공간을 "의도적으로" 손상시키는 것을 방지하기 위해 얻은 값을 USER_DS와 비교합니다. 이제 커널 공간에서 시스템 호출을 사용해야 합니다. read 또는 write()에 전달된 매개변수 주소는 커널 공간입니다. USER_DS(USER_DS ~ KERNEL_DS) 이상의 주소는 write() 함수에서 해당 주소가 USER_DS 범위를 초과하는 것으로 간주됩니다. , 따라서 이는 사용자 공간의 "의도적인 파괴"로 간주되므로 이 문제를 해결하기 위해 더 이상 실행이 허용되지 않습니다. set_fs(KERNEL_DS)는 KERNEL_DS에 액세스할 수 있는 공간 제한을 확장합니다. , 커널에서 시스템 호출을 원활하게 사용할 수 있도록!
3.Linux 구조체 inode 구조

rrreee

4.Linux 구조체 파일 구조

🎜🎜 구조체 파일 구조는 /linux/include/linux/fs.h(Linux 2.6.11 커널)에 정의되어 있습니다. , 해당 프로토타입은 다음과 같습니다. 🎜rrreee🎜 파일 구조는 열린 파일을 나타냅니다. 시스템의 각 열린 파일은 커널 공간에 연관된 구조 파일을 가지고 있습니다. 파일이 열릴 때 커널에 의해 생성되어 파일에서 작동하는 함수에 전달됩니다. 커널은 파일의 모든 인스턴스가 닫힌 후 이 데이터 구조를 해제합니다. 커널 생성 및 드라이버 소스 코드에서 struct 파일의 포인터는 일반적으로 file 또는 filp로 명명됩니다. 🎜🎜관련 추천: "🎜Linux 비디오 튜토리얼🎜"🎜

위 내용은 Linux에는 커널 파일 작업 기능이 있습니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.