发新话题
打印

解读filesystem.c

本帖已经被作者加入个人空间

解读filesystem.c

===================================================
作者:ietf AT doit.com.cn
filesystem.c源文件来自于linux kernel 2.6.20
引用请注明出处。
===================================================
Linux VFS在挂载点通过mount和umount挂载和卸载相应的文件系统。
mount和umount文件系统工作由register_filesystem和unregister_filesystem完成。为了清晰的理解文件系统的实际加载过程,对filesystem.c做一个简单分析。为了分析方便,分析过程将嵌套在源代码中,需要原始的源文件代码,可以在指示的linux内核源文件中查找。
所有的注释由如下标记标识:
-------ietf add start-------
-------ietf add end  -------
/*
*  linux/fs/filesystems.c
*
*  Copyright (C) 1991, 1992  Linus Torvalds
*
*  table of configured filesystems
*/
-------ietf add start-------
这个文件ms是由Torvalds本人独立完成的
-------ietf add end  -------

#include <linux/syscalls.h>
-------ietf add start-------
需要用到某些系统调用的定义,如sys_sysfs()
-------ietf add end  -------
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h> /* for 'current' */
#include <asm/uaccess.h>

/*
* Handling of filesystem drivers list.
* Rules:
* Inclusion to/removals from/scanning of list are protected by spinlock.
* During the unload module must call unregister_filesystem().
* We can access the fields of list element if:
*  1) spinlock is held or
*  2) we hold the reference to the module.
* The latter can be guaranteed by call of try_module_get(); if it
* returned 0 we must skip the element, otherwise we got the reference.
* Once the reference is obtained we can drop the spinlock.
*/
-------ietf add start-------
文件系统类型结构,在src/include/linux/fs.h中描述,《Linux虚拟文件系统概述》中有详细描述
-------ietf add end  -------
static struct file_system_type *file_systems;
-------ietf add start-------
文件系统锁,该宏在src/include/linux/spinlock_types.h中定义
-------ietf add end  -------
static DEFINE_RWLOCK(file_systems_lock);

-------ietf add start-------
加载文件系统(加载文件系统模块),__module_get()在src/include/linux/Modules.h中声明,在当前版本中为空函数
-------ietf add end  -------
/* WARNING: This can be used only if we _already_ own a reference */
void get_filesystem(struct file_system_type *fs)
{
__module_get(fs->owner);
}
-------ietf add start-------
释放文件系统(减少模块引用计数或者卸载模块),__module_put()在src/include/linux/modules.h中声明,在
src/kernel/modules.c中实现,代码如下:
void module_put(struct module *module)
{
if (module) {
  unsigned int cpu = get_cpu();
  local_dec(&module->ref[cpu].count);
  /* Maybe they're waiting for us to drop reference? */
  if (unlikely(!module_is_live(module)))
   wake_up_process(module->waiter);
  put_cpu();
}
}
-------ietf add end  -------
void put_filesystem(struct file_system_type *fs)
{
module_put(fs->owner);
}

-------ietf add start-------
通过文件系统名,遍历文件系统链表,查找指定文件系统,返回该文件系统结构的指针。
-------ietf add end  -------
static struct file_system_type **find_filesystem(const char *name)
{
struct file_system_type **p;
for (p=&file_systems; *p; p=&(*p)->next)
  if (strcmp((*p)->name,name) == 0)
   break;
return p;
}

/**
* register_filesystem - register a new filesystem
* @fs: the file system structure
*
* Adds the file system passed to the list of file systems the kernel
* is aware of for mount and other syscalls. Returns 0 on success,
* or a negative errno code on an error.
*
* The &struct file_system_type that is passed is linked into the kernel
* structures and must not be freed until the file system has been
* unregistered.
*/
-------ietf add start-------
根据文件系统结构,注册文件系统,mount时调用
-------ietf add end  -------
int register_filesystem(struct file_system_type * fs)
{
int res = 0;
struct file_system_type ** p;
if (fs->next)
  return -EBUSY;
-------ietf add start-------
fs指向的是已经被加载的文件系统结构,直接返回
-------ietf add end  -------
INIT_LIST_HEAD(&fs->fs_supers);
write_lock(&file_systems_lock);
-------ietf add start-------
操作文件系统链表,先获得文件系统锁
-------ietf add end  -------
p = find_filesystem(fs->name);
-------ietf add start-------
通过文件系统名,遍历内核中的文件系统,找到,则*p是指向文件系统链表中该文件系统的指针,否则为空,即最后一项的next
-------ietf add end  -------
if (*p)
  res = -EBUSY;
-------ietf add start-------
文件系统链表中存在该文件系统,直接返回
-------ietf add end  -------
else
  *p = fs;
-------ietf add start-------
不存在,则*p指向该新加入的文件系统,即将fs加载到了文件系统链表的最后一项
-------ietf add end  -------
write_unlock(&file_systems_lock);
-------ietf add start-------
释放文件系统锁
-------ietf add end  -------
return res;
}
EXPORT_SYMBOL(register_filesystem);
-------ietf add start-------
向内核输出模块名,该宏的含义和功能请参考《Linux Device Driver》edition 1/2/3都可以
-------ietf add end  -------

/**
* unregister_filesystem - unregister a file system
* @fs: filesystem to unregister
*
* Remove a file system that was previously successfully registered
* with the kernel. An error is returned if the file system is not found.
* Zero is returned on a success.
*
* Once this function has returned the &struct file_system_type structure
* may be freed or reused.
*/
-------ietf add start-------
根据文件系统结构指针,卸载文件系统,umount时调用
-------ietf add end  -------
int unregister_filesystem(struct file_system_type * fs)
{
struct file_system_type ** tmp;
write_lock(&file_systems_lock);
tmp = &file_systems;
-------ietf add start-------
获得文件系统链表头指针,并遍历该链表
-------ietf add end  -------
while (*tmp) {
  if (fs == *tmp) {
   *tmp = fs->next;
   fs->next = NULL;
   write_unlock(&file_systems_lock);
   return 0;
-------ietf add start-------
找到欲卸载的文件系统,将该项从链表中摘除,并释放锁,返回成功
-------ietf add end  -------
  }
  tmp = &(*tmp)->next;
}
write_unlock(&file_systems_lock);
return -EINVAL;
-------ietf add start-------
没有找到该文件系统,返回出错
-------ietf add end  -------
}
EXPORT_SYMBOL(unregister_filesystem);
-------ietf add start-------
计算获得指定文件系统名的文件系统在文件系统链表中的index值
-------ietf add end  -------
static int fs_index(const char __user * __name)
{
struct file_system_type * tmp;
char * name;
int err, index;
name = getname(__name);
-------ietf add start-------
将__name所指示得字符串拷入内核空间,并且如果内核定制了audit属性,增加该项进入查找access访问控制表getname()在
src/fs/namei.c中实现,源代码如下:
static int do_getname(const char __user *filename, char *page)
{
int retval;
unsigned long len = PATH_MAX;

if (!segment_eq(get_fs(), KERNEL_DS)) {
  if ((unsigned long) filename >= TASK_SIZE)
   return -EFAULT;
  if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
   len = TASK_SIZE - (unsigned long) filename;
}

retval = strncpy_from_user(page, filename, len);
if (retval > 0) {
  if (retval < len)
   return 0;
  return -ENAMETOOLONG;
} else if (!retval)
  retval = -ENOENT;
return retval;
}

char * getname(const char __user * filename)
{
char *tmp, *result;

result = ERR_PTR(-ENOMEM);
tmp = __getname();
if (tmp)  {
  int retval = do_getname(filename, tmp);

  result = tmp;
  if (retval < 0) {
   __putname(tmp);
   result = ERR_PTR(retval);
  }
}
audit_getname(result);
return result;
}
-------ietf add end  -------
err = PTR_ERR(name);
if (IS_ERR(name))
  return err;
err = -EINVAL;
read_lock(&file_systems_lock);
-------ietf add start-------
遍历查找,并通过index值自增计算结果
-------ietf add end  -------
for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
  if (strcmp(tmp->name,name) == 0) {
   err = index;
   break;
  }
}
read_unlock(&file_systems_lock);
putname(name);
-------ietf add start-------
putname()操作和getname()相反,其代码如下:
void putname(const char *name)
{
if (unlikely(!audit_dummy_context()))
  audit_putname(name);
else
  __putname(name);
}
-------ietf add end  -------
return err;
}

-------ietf add start-------
返回文件系统链表中第index个表项的文件系统名,值返回到用户空间
-------ietf add end  -------
static int fs_name(unsigned int index, char __user * buf)
{
struct file_system_type * tmp;
int len, res;
read_lock(&file_systems_lock);
-------ietf add start-------
遍历文件系统,通过index自减到指定项,当然,同时该文件系统的模块必须在内核中
-------ietf add end  -------
for (tmp = file_systems; tmp; tmp = tmp->next, index--)
  if (index <= 0 && try_module_get(tmp->owner))
   break;
read_unlock(&file_systems_lock);
if (!tmp)
  return -EINVAL;
/* OK, we got the reference, so we can safely block */
len = strlen(tmp->name) + 1;
res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
put_filesystem(tmp);
return res;
}
-------ietf add start-------
计算表中项的数目
-------ietf add end  -------
static int fs_maxindex(void)
{
struct file_system_type * tmp;
int index;
read_lock(&file_systems_lock);
for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
  ;
read_unlock(&file_systems_lock);
return index;
}
/*
* Whee.. Weird sysv syscall.
*/
-------ietf add start-------
一系统调用的方式提供,该系统调用有三种功能,通过option区分
-------ietf add end  -------
asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
{
int retval = -EINVAL;
switch (option) {
  case 1:
   retval = fs_index((const char __user *) arg1);
   break;
  case 2:
   retval = fs_name(arg1, (char __user *) arg2);
   break;
  case 3:
   retval = fs_maxindex();
   break;
}
return retval;
}
-------ietf add start-------
遍历文件系统链表,获得当前表中支持的文件系统名
-------ietf add end  -------
int get_filesystem_list(char * buf)
{
int len = 0;
struct file_system_type * tmp;
read_lock(&file_systems_lock);
tmp = file_systems;
while (tmp && len < PAGE_SIZE - 80) {
-------ietf add start-------
遍历每一项,并表明是否是基于dev的还是nodev的
-------ietf add end  -------
  len += sprintf(buf+len, "%s\t%s\n",
   (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
   tmp->name);
  tmp = tmp->next;
}
read_unlock(&file_systems_lock);
return len;
}
-------ietf add start-------
根据给定的文件系统名,获得该类型文件系统在文件系统链表中的指针。
-------ietf add end  -------
struct file_system_type *get_fs_type(const char *name)
{
struct file_system_type *fs;
read_lock(&file_systems_lock);
-------ietf add start-------
找到该文件系统的指针,如果fs为空,则该文件系统不在链表中,就需要重新加载
-------ietf add end  -------
fs = *(find_filesystem(name));
-------ietf add start-------
找到该文件系统,但是该文件系统的模块没有加载在内核空间,则需要将该空间重新加载
-------ietf add end  -------
if (fs && !try_module_get(fs->owner))
  fs = NULL;
read_unlock(&file_systems_lock);
-------ietf add start-------
加载文件系统模块
-------ietf add end  -------

if (!fs && (request_module("%s", name) == 0)) {
  read_lock(&file_systems_lock);
-------ietf add start-------
判断是否加载成功
-------ietf add end  -------
  fs = *(find_filesystem(name));
-------ietf add start-------
加载失败,则fs赋值为空。
这里尝试加载次数可以根据个人喜好多试几次,不过不能写成循环的方式,
-------ietf add end  -------
  if (fs && !try_module_get(fs->owner))
   fs = NULL;
  read_unlock(&file_systems_lock);
}
return fs;
}
EXPORT_SYMBOL(get_fs_type);
0/1

TOP

解说得挺详细的啊!不错,不错!

TOP

呵呵,谢谢支持
0/1

TOP

发新话题