linux文件系统的结构体

来源:互联网 发布:prime输入法知乎 编辑:程序博客网 时间:2024/05/17 13:42

/* *索引节点对象由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_operations是一个很重要的结构体,这个结构是字符设备驱动程序的核心,当应用程序操作设备文件时所调用的open、read、write等函数,最终会调用这个结构中指定的对应函数。struct file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。

1、 驱动与内核接口层
  驱动与内核接口层主要完成驱动模块在Linux内核的注册加载、卸载清除工作。这部分工作分别由初始化和退出函数完成。
  ① 初始化函数完成驱动模块加载:
static int __init DS18B20_init(void){
  ……
  register_chrdev(DS18B20_MAJOR,DEVICE_NAME, &DS18B20_fops);//完成设备注册
  #ifdefCONFIG_DEVFS_FS//创建设备文件系统
    devfs_mk_cdev(MKDEV(DS18B20_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
  #endif
  ……
}
  ② 退出函数完成驱动模块卸载:
static void __exit DS18B20_exit(void) {
  #ifdef CONFIG_DEVFS_FS
    devfs_remove(DEVICE_NAME);//移除设备文件
  #endif
  unregister_chrdev(DS18B20_MAJOR,DEVICE_NAME); //完成设备注销
  ……
}

2、硬件设备接口层
  硬件设备接口层用来描述驱动程序与设备的交互。这些工作通过虚拟文件系统与设备驱动程序的接口实现。这个接口由file_operation结构定义,其结构如下:
  static struct file_operations DS18B20_fops ={
  .owner=THIS_MODULE, //指向拥有该结构的模块,内核使用该结构维护模块使用计数
  .open=DS18B20_open, //打开设备函数
  .read=DS18B20_read, //读接口函数
  .write=DS18B20_write,//写接口函数
  .fasync=DS18B20_fasync, //异步通知函数
  .poll=DS18B20_poll,//poll函数
  .release=DS18B20_release, //释放设备函数
};

  2.1   打开设备函数
  打开设备函数主要完成设备的初始化。
DS18B20_open(struct inode *inode,struct file *filp) {
  Initial_Timer( );//初始化定时器,使内核模块按一定周期读温度
  Initial_Device_DS18B20();//初始化硬件
  readtemperature();//开始读取……
}
void readtemperature(void) {
  ……Temperature=DS18B20read();//读取2个8位数据,此函数完成的硬件操作时序,由当前读通道号变量指定当前通道
  DS_SLOT_NO();//将本次读通道号放入缓冲区
  DS18B20Event();//数据放入缓冲区,唤醒等待队列并启动异步通知
  if(ReleaseFlag)
  CycleTimer_Delay_Soft(hdelay);//如果没有读停止信号,通过内核定时器延时,进行下一次读,在中断服务程序中再次启动读
  ……
}
  在使用内核定时器之前需定义一个定时器结构体 static struct timer_list CycleTimer。下面是定时器的具体操作:
static void Initial_Timer(void) {
  init_timer(&CycleTimer); );//初始化定时器结构
  CycleTimer.function=DS18B20_timer; //挂接定时中断服务程序
}
         2.2   读接口函数
  用户程序执行读操作的时候可能没有可以读取的数据,此时需要让read操作等待直到有数据可以读取。在此采用等待队列使进程在无数据读取时进入等待,数据到达时唤醒。等待队列设置成一个循环缓冲区,每放入一个新数据作为缓冲区的头,存放时间最久还未被取走的数据为缓冲区的尾。
DS18B20_read( ) {
  DECLARE_WAITQUEUE(wait,current);//声明等待队列……
Next_try:
  if(DS18B20dev.head != DS18B20dev.tail) {//等待队列不为空,即有数据
  DS18B20_ret=Read_Buffer_DS18B20(); //取走缓冲区的尾
  copy_to_user( ); //读取的数据送到用户空间
}
  else { ……//等待队列为空,即没有数据
  add_wait_queue(&queue,&wait);
  current>state=TASK_INTERRUPTIBLE;//添加等待队列,声明状态为任务可中断
  while((DS18B20dev.head==DS18B20dev.tail)&&!signal_pending(current) {//进入等待
  schedule();
  current>state=TASK_INTERRUPTIBLE;
    }//如果缓冲区为空,Linux内核调度,等待通知
  current>state = TASK_RUNNING;//得到有数据的通知,声明任务状态为运行
  remove_wait_queue(&queue,&wait);//删除等待队列
  goto Next_try;//返回到读取数据
  }
}
          2.3   fasync异步通知函数
  异步通知函数向进程发送SIGIO信号,通知访问设备的进程,表示设备已经准备好I/O读写了,避免主动查询,提高程序效率。使用异步通知需增加一个struct fasync_struct的结构指针,然后实现fasync接口函数。
static struct fasync_struct *fasync;//定义一个结构体
static int DS18B20_fasync(int fd,struct file *filp,int on) {//实现接口函数
  retval = fasync_helper(fd,filp,on,&fasync);
  if ( retval<0) return retval;return 0;
}
  最后在需要向用户空间通知的地方调用内核的kill_fasync函数。在打开设备函数中提到的DS18B20Event()功能是:将数据放入循环缓冲区,唤醒等待队列并启动异步通知,其后两项功能是这样实现的:
  wake_up_interruptible(&queue);//唤醒等待队列
  if (fasync) {
  kill_fasync(&fasync,SIGIO,POLL_IN);//发送异步通知信号
  }
          2.4   poll系统调用操作接口函数
  当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,影响其他文件的读写。为了避免读写阻塞,使用poll函数。如果设备无阻塞地读,就返回POLLIN; 通常的数据已经准备好,可以读了,就返回POLLRDNORM。
static unsigned int DS18B20_poll(struct file *flip, poll_table *wait) {
  poll_wait(flip,&queue,wait);
  if(DS18B20dev.head != DS18B20dev.tail) {
    return POLLIN|POLLRDNORM;
  }
  return 0;
}
       2.5   release释放设备函数
   static intDS18B20_release(struct inode *inode,struct file *filp) {
  ReleaseFlag=0//内核停止读取温度标志
  DS18B20_fasync(1,filp,0);//关闭异步通知
  module_put(THIS_MODULE);//设备计数器减1
  return 0;
}
 写接口函数用来通知驱动。例如通知驱动读取通道2的数据,在应用程序中执行写接口函数write(fileno,&SLOT2,1),驱动设置当前读通道号为2。

 至此完成驱动接口函数。此驱动属于字符设备驱动,将源程序放在driver/char目录下。同时需要修改该目录下的Kconfig配置文件并添加 Config18B20_S3C2410选项,修改driver/char/Makefile,添加 obj$(CONFIG_18B20_S3C2410)+=S3C2410_18B20.O。最后重新配置内核,将驱动以模块形式添加到内核,这样就可以编译驱动了。

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;};

文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码中,struct file的指针通常被命名为file或filp。一下是对结构中的每个数据成员的解释:
一、
union {    struct list_head fu_list;    struct rcu_head rcuhead;}f_u;

其中的struct list_head定义在 linux/include/linux/list.h中,原型为:
struct list_head {
        struct list_head *next, *prev;
};
用于通用文件对象链表的指针。
struct rcu_head定义在linux/include/linux/rcupdate.h中,其原型为:
/**
* struct rcu_head - callback structure for use with RCU
* @next: next update requests in a list
* @func: actual update function to call after the grace period.
*/
struct rcu_head {
        struct rcu_head *next;
        void (*func)(struct rcu_head *head);
};
RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制,具体在这里有介绍:
http://www.ibm.com/developerworks/cn/linux/l-rcu/
二、
struct path             f_path;
被定义在linux/include/linux/namei.h中,其原型为:
struct path {
        struct vfsmount *mnt;
        struct dentry *dentry;
};
在早些版本的内核中并没有此结构,而是直接将path的两个数据成员作为struct file的数据成员,
struct vfsmount *mnt的作用是指出该文件的已安装的文件系统,
struct dentry *dentry是与文件相关的目录项对象。
三、
const struct file_operations    *f_op;
被定义在linux/include/linux/fs.h中,其中包含着与文件关联的操作,如:
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
等。当打开一个文件时,内核就创建一个与该文件相关联的struct file结构,其中的*f_op就指向的是
具体对该文件进行操作的函数。例如用户调用系统调用read来读取该文件的内容时,那么系统调用read最终会陷入内核调用sys_read函数,而sys_read最终会调用于该文件关联的struct file结构中的f_op->read函数对文件内容进行读取。
四、
atomic_t                f_count;
atomic_t被定义为:
typedef struct { volatile int counter; } atomic_t;
volatile修饰字段告诉gcc不要对该类型的数据做优化处理,对它的访问都是对内存的访问,而不是对寄存器的访问。 
本质是int类型,之所以这样写是让编译器对基于该类型变量的操作进行严格的类型检查。此处f_count的作用是记录对文件对象的引用计数,也即当前有多少个进程在使用该文件。
五、
unsigned int            f_flags;
当打开文件时指定的标志,对应系统调用open的int flags参数。驱动程序为了支持非阻塞型操作需要检查这个标志。
六、
mode_t                  f_mode;
对文件的读写模式,对应系统调用open的mod_t mode参数。如果驱动程序需要这个值,可以直接读取这个字段。
mod_t被定义为:
typedef unsigned int __kernel_mode_t;
typedef __kernel_mode_t         mode_t;
七、
loff_t                  f_pos;
当前的文件指针位置,即文件的读写位置。
loff_t被定义为:
typedef long long       __kernel_loff_t;
typedef __kernel_loff_t         loff_t;
八、
struct fown_struct      f_owner;
struct fown_struct在linux/include/linux/fs.h被定义,原型为:
struct fown_struct {
        rwlock_t lock;          /* protects pid, uid, euid fields */
        struct pid *pid;        /* pid or -pgrp where SIGIO should be sent */
        enum pid_type pid_type; /* Kind of process group SIGIO should be sent to */
        uid_t uid, euid;        /* uid/euid of process setting the owner */
        int signum;             /* posix.1b rt signal to be delivered on IO */
};
该结构的作用是通过信号进行I/O时间通知的数据。
九、
unsigned int            f_uid, f_gid;
标识文件的所有者id,所有者所在组的id.
十、
struct file_ra_state    f_ra;
struct file_ra_state结构被定义在/linux/include/linux/fs.h中,原型为:
struct file_ra_state {        pgoff_t start;                  /* where readahead started */        unsigned long size;             /* # of readahead pages */        unsigned long async_size;       /* do asynchronous readahead when                                           there are only # of pages ahead */                                                   unsigned long ra_pages;         /* Maximum readahead window */        unsigned long mmap_hit;         /* Cache hit stat for mmap accesses */        unsigned long mmap_miss;        /* Cache miss stat for mmap accesses */        unsigned long prev_index;       /* Cache last read() position */        unsigned int prev_offset;       /* Offset where last read() ended in a page */};

文件预读状态,文件预读算法使用的主要数据结构,当打开一个文件时,f_ra中出了perv_page(默认为-1)和ra_apges(对该文件允许的最大预读量)这两个字段外,其他的所有西端都置为0。
十一、
unsigned long           f_version;
记录文件的版本号,每次使用后都自动递增。
十二、
#ifdef CONFIG_SECURITY
        void                    *f_security;

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/44250/showart_1168098.html 
       

原创粉丝点击