在linux-2.6.29.6\include\linux下的fs.h中。
file_system_type结果代表Linux内核的各种网络文件系统,每一种文件系统必须要有自己的file_system_type结构。
struct file_system_type {
const char *name;
int fs_flags;
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct list_head fs_supers;
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
struct lock_class_key i_alloc_sem_key;
};
socket_fs_type结构定义代表sockfs的网络文件系统,但它并没有真实的物理介质,因此称为虚拟文件系统。虚拟文件系统的安装过程与真实文件系统不同。
static struct file_system_type sock_fs_type = {
.name = "sockfs",
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
};
Linux在启动时会执行init/main.c中的初始化函数kernel_init(),这个函数调用了do_basic_setup(),从而调用do_initcalls()函数来执行所有的init初始化函数。
kernel_init()——>do_basic_setup()——>do_initcalls()——>do_one_initcall()
static int __init kernel_init(void * unused)
{
......
do_basic_setup();
......
return 0;
}
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
* running, and memory and process management works.
*
* Now we can finally start doing some real work..
*/
static void __init do_basic_setup(void)
{
rcu_init_sched(); /* needed by module_init stage. */
init_workqueues();
usermodehelper_init();
driver_init();
init_irq_proc();
do_initcalls();
}
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __early_initcall_end; call < __initcall_end; call++)
do_one_initcall(*call); //调用每一个初始化函数
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
int do_one_initcall(initcall_t fn)
{
......
ret.result = fn(); //初始化函数
......
return ret.result;
}
do_one_initcall()就是initcall机制的执行函数。initcall机制实际上是在内核复用了GCC编译器的功能来设置内核的初始化函数。这些函数地址存放在__initcall_satrt到__initcall_end中,do_one_initcall()函数将调用所有登记在initcall中的初始化函数。在socket.c文件中可以看到core_initcall()将sock_init()函数登记到initcall中。
core_initcall(sock_init); /* early initcall */
GCC编译时把sock_init函数“安装”到initcall中,Linux内核把初始化函数都放在initcall中实现了,这样在初始化完成后,这部分函数的空间可以回收再利用;驱动程序中常用的module_init注册用户自定义的初始化函数也是通过initcall来实现的。sock_init()函数注册成功后,内核初始化时会执行sock_init()函数来登记网络文件系统。
/* initcalls are now grouped by functionality into separate
* subsections. Ordering inside the subsections is determined
* by link order.
* For backwards compatibility, initcall() puts the call in
* the device init subsection.
*
* The `id' arg to __define_initcall() is needed so that multiple initcalls
* can point at the same handler without causing duplicate-symbol build errors.
*/
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
......
#define core_initcall(fn) __define_initcall("1",fn,1)
......
#define __initcall(fn) device_initcall(fn)
......
/**
* module_init() - driver initialization entry point
* @x: function to be run at kernel boot time or module insertion
*
* module_init() will either be called during do_initcalls() (if
* builtin) or at module insertion time (if a module). There can only
* be one per module.
*/
#define module_init(x) __initcall(x);
kernel_init() ——> do_basic_setup() ——> do_initcalls() ——> do_one_initcall() ——> sock_init()
sock_init()函数主要是将sock_fs_type登记到内核安装网络文件系统。init_inodecache()是创建一块用于网络文件节点和socket的高速缓存。register_filesystem()函数的作用是将网络文件系统注册到Linux中,而kern_mount()函数则完成了其在Linux内核中的安装,并在内核中建立了网络文件系统的安装点。这两个函数均与内核当前使用的根文件系统相关。
static int __init sock_init(void)
{
/*
* Initialize sock SLAB cache.
*/
sk_init();
/*
* Initialize skbuff SLAB cache
*/
skb_init();
/*
* Initialize the protocols module.
*/
init_inodecache();
register_filesystem(&sock_fs_type); //注册网络文件系统
sock_mnt = kern_mount(&sock_fs_type); //安装网络文件系统
/* The real protocol initialization is performed in later initcalls.
*/
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
return 0;
}
在安装的过程中,内核会调用sock_fs_type数据结构的get_sb()钩子函数。
sock_init() ——> kern_mount() ——> 中间过程略 ——> sockfs_get_sb()
sock_fs_type结构中挂入到get_sb()的钩子函数为sockfs_get_sb(),由该函数完成网络文件系统的安装。
static int sockfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
struct vfsmount *mnt)
{
return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC,
mnt); //完成文件系统安装
}
get_sb_pseudo()函数的第一个参数是 sock_fs_type,第二个参数是要求创建socket名称的根目录项,第三个参数是关于网络文件系统的超级块操作函数表sockfs_ops,而最后一个参数是网络文件系统的安装点。
static struct super_operations sockfs_ops = {
.alloc_inode = sock_alloc_inode,
.destroy_inode =sock_destroy_inode,
.statfs = simple_statfs,
};
该函数表对网络文件系统的节点和目录提供了具体的操作函数,以后涉及网络文件系统的重要操作均会到该函数表中查找对应的操作函数,也就是钩子函数。例如,Linux内核在创建socket节点时会查找sockfs_ops中的alloc_inode()钩子函数,从而转到sock_alloc_inode()完成节点的建立。