现在的位置: 首页 > 综合 > 正文

文件系统和设备之间的联系

2013年08月19日 ⁄ 综合 ⁄ 共 3357字 ⁄ 字号 评论关闭

 如果写过设备驱动,就知道我们写驱动主要是为了实现一个设备驱动接口,一组
对设备操作的方法,我这里想简单地分析一下文件系统与设备驱动之间的接口。
先来看打开操作,我自己对文件系统也不是很了解,只知道在用户空间调用了open函数,
就会在内核中调用sys_open这个系统调用,原来的0.11内核都是通过int80x系统调用门
来实现的,不过这里似乎是直接调用的嘛,在include/asm-arm/unistd.h中
static inline long open(const char *file, int flag, int mode)
{
 extern long sys_open(const char *, int, int);
 return sys_open(file, flag, mode);
}
再来看fs/open.c中的sys_open函数
asmlinkage long sys_open(const char * filename, int flags, int mode)
{
 char * tmp;
 int fd, error;

#if BITS_PER_LONG != 32
 flags |= O_LARGEFILE;
#endif
 tmp = getname(filename);
 fd = PTR_ERR(tmp);
 if (!IS_ERR(tmp)) {
  fd = get_unused_fd(); //获取一个未用的文件指示符
  if (fd >= 0) {
   struct file *f = filp_open(tmp, flags, mode);
   //打开文件,建立file类型的结构并返回其指针
   error = PTR_ERR(f);
   if (IS_ERR(f))
    goto out_error;
   fd_install(fd, f);//把文件指示符和文件指针相关联
  }
out:
  putname(tmp);
 }
 return fd;
out_error:
 put_unused_fd(fd);
 fd = error;
 goto out;
}

这样主要是在filp_open中实现了打开操作。
struct file *filp_open(const char * filename, int flags, int mode)
{
 int namei_flags, error;
 struct nameidata nd;

 namei_flags = flags;
 if ((namei_flags+1) & O_ACCMODE)
  namei_flags++;
 if (namei_flags & O_TRUNC)
  namei_flags |= 2;

 error = open_namei(filename, namei_flags, mode, &nd);
 if (!error)
  return dentry_open(nd.dentry, nd.mnt, flags);

 return ERR_PTR(error);
}
关键是要知道是怎样得到file结构体的,首先当然是要读取设备文件对应的inode结构体
这是由函数open_namei实现的,然后dentry_open来建立file结构体。
struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
{
 struct file * f;
 struct inode *inode;
 static LIST_HEAD(kill_list);
 int error;

 error = -ENFILE;
 f = get_empty_filp(); //获取空的file结构体
 if (!f)
  goto cleanup_dentry;
 f->f_flags = flags;
 f->f_mode = (flags+1) & O_ACCMODE;
 inode = dentry->d_inode;
 if (f->f_mode & FMODE_WRITE) {
  error = get_write_access(inode);
  if (error)
   goto cleanup_file;
 }

 f->f_dentry = dentry;
 f->f_vfsmnt = mnt;
 f->f_pos = 0;
 f->f_reada = 0;
 f->f_op = fops_get(inode->i_fop); //获取设备的file_operations结构
 file_move(f, &inode->i_sb->s_files);

 /* preallocate kiobuf for O_DIRECT */
 f->f_iobuf = NULL;
 f->f_iobuf_lock = 0;
 if (f->f_flags & O_DIRECT) {
  error = alloc_kiovec(1, &f->f_iobuf);
  if (error)
   goto cleanup_all;
 }

 if (f->f_op && f->f_op->open) { //执行设备驱动中的open操作
  error = f->f_op->open(inode,f);
  if (error)
   goto cleanup_all;
 }
 f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

 return f;

cleanup_all:
 if (f->f_iobuf)
  free_kiovec(1, &f->f_iobuf);
 fops_put(f->f_op);
 if (f->f_mode & FMODE_WRITE)
  put_write_access(inode);
 file_move(f, &kill_list); /* out of the way.. */
 f->f_dentry = NULL;
 f->f_vfsmnt = NULL;
cleanup_file:
 put_filp(f);
cleanup_dentry:
 dput(dentry);
 mntput(mnt);
 return ERR_PTR(error);
}
不同的文件系统会使用不同的函数取获取inode结构,对于设备文件系统,我猜想在注册设备
建立节点的时候,其inode结构中的struct file_operations *i_fop成员变量就已经被赋值
为设备驱动的file_operations了。还是以/dev/console为例,其驱动注册的file_operations
是tty_fops,于是file结构中的f_op指向的就是tty_fops结构。于是代开操作就会调用到tty_fops
中的open函数。而对于普通文件,会有默认的file_operations结构,其open函数什么也不做。
对于用register_chrdev注册的设备,显然注册的时候驱动的file_operations结构的指针并没有
传给inode结构,因为其节点是可以在注册之前就建立的,其实对于字符设备在用mknod建立imode
时赋给的file_operations也是默认的def_chr_fops,在fs/devices.c中定义
static struct file_operations def_chr_fops = {
 open:  chrdev_open,
};
于是打开字符设备的时候都会执行chrdev_open
int chrdev_open(struct inode * inode, struct file * filp)
{
 int ret = -ENODEV;

 filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
 if (filp->f_op) {
  ret = 0;
  if (filp->f_op->open != NULL) {
   lock_kernel();
   ret = filp->f_op->open(inode,filp);
   unlock_kernel();
  }
 }
 return ret;
}
ok,在chrdev_open中又用filp->f_op重新赋值,这一次赋给的是真正的设备的file_operations指针于是下此使用read,write系统调用的时候,通过filp->f_op调用的函数就是真正设备的read,write函数了。

抱歉!评论已关闭.